auth code done. needs tests

This commit is contained in:
merlokk 2021-05-29 18:37:23 +03:00
parent 37daaa2120
commit 1a937b33c4
3 changed files with 140 additions and 19 deletions

View file

@ -23,6 +23,74 @@
uint8_t AESData0[CIPURSE_AES_KEY_LENGTH] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static void bin_xor(uint8_t *d1, uint8_t *d2, size_t len) {
for(size_t i = 0; i < len; i++)
d1[i] = d1[i] ^ d2[i];
}
static void bin_ext(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
if (srclen > dstlen)
memcpy(dst, &src[srclen - dstlen], dstlen);
else
memcpy(dst, src, dstlen);
}
static void bin_pad(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
memset(dst, 0, dstlen);
if (srclen <= dstlen)
memcpy(&dst[dstlen - srclen], src, srclen);
else
memcpy(dst, src, dstlen);
}
static void bin_pad2(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
memset(dst, 0, dstlen);
uint8_t dbl[srclen * 2];
memcpy(dbl, src, srclen);
memcpy(&dbl[srclen], src, srclen);
bin_pad(dst, dstlen, dbl, srclen * 2);
}
static uint64_t rotateLeft48(uint64_t src) {
uint64_t dst = src << 1;
if (dst && 0x0001000000000000UL != 0) {
dst = dst | 1;
dst = dst & 0x0000ffffffffffffUL;
}
return dst;
}
static uint64_t computeNLM48(uint64_t x, uint64_t y) {
uint64_t res = 0;
for (int i = 0; i < 48; i++) {
res = rotateLeft48(res);
if (res & 1)
res = res ^ CIPURSE_POLY;
y = rotateLeft48(y);
if (y & 1)
res = res ^ x;
}
return res;
}
static void computeNLM(uint8_t *res, uint8_t *x, uint8_t *y) {
uint64_t x64 = 0;
uint64_t y64 = 0;
for (int i = 0; i < 6; i++) {
x64 = (x64 << 8) | x[i];
y64 = (y64 << 8) | y[i];
}
uint64_t res64 = computeNLM48(x64, y64);
for (int i = 0; i < 6; i++) {
res[5 - i] = res64 & 0xff;
res64 = res64 >> 8;
}
}
static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {
/* // session key derivation function
// kP := NLM(EXT(kID), rP)
@ -50,25 +118,39 @@ static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {
uint8_t temp1[CIPURSE_AES_KEY_LENGTH] = {0};
uint8_t temp2[CIPURSE_AES_KEY_LENGTH] = {0};
uint8_t kp[CIPURSE_SECURITY_PARAM_N] = {0};
// session key derivation function
// kP := NLM(EXT(kID), rP)
// k0 := AES(key=PAD2(kP) XOR PAD(rT),kID) XOR kID
// k0 := AES(key=PAD2(kP) XOR PAD(rT),kID) XOR kID
bin_ext(temp1, CIPURSE_SECURITY_PARAM_N, ctx->key, CIPURSE_AES_KEY_LENGTH);
computeNLM(kp, ctx->rP, temp1); // param sizes == 6 bytes
bin_pad2(temp1, CIPURSE_AES_KEY_LENGTH, kp, CIPURSE_SECURITY_PARAM_N);
bin_pad(temp2, CIPURSE_AES_KEY_LENGTH, ctx->rT, CIPURSE_SECURITY_PARAM_N);
bin_xor(temp1, temp2, CIPURSE_AES_KEY_LENGTH);
// session key K0
aes_encode(NULL, temp1, ctx->key, ctx->k0, CIPURSE_AES_KEY_LENGTH);
bin_xor(ctx->k0, ctx->key, CIPURSE_AES_KEY_LENGTH);
// first frame key k1, function to calculate k1,
// k1 := AES(key = RP; k0 XOR RT) XOR (k0 XOR RT)
memcpy(temp1, ctx->k0, CIPURSE_AES_KEY_LENGTH);
bin_xor(temp1, ctx->RT, CIPURSE_AES_KEY_LENGTH);
aes_encode(NULL, ctx->RP, temp1, temp2, CIPURSE_AES_KEY_LENGTH);
bin_xor(temp1, temp2, CIPURSE_AES_KEY_LENGTH);
memcpy(ctx->frameKey, temp1, CIPURSE_AES_KEY_LENGTH);
// function to caluclate cP := AES(key=k0, RP).
// terminal response
aes_encode(NULL, ctx->k0, ctx->RP, ctx->Cp, CIPURSE_AES_KEY_LENGTH);
aes_encode(NULL, ctx->k0, ctx->RP, ctx->cP, CIPURSE_AES_KEY_LENGTH);
}
static void CipurseCGenerateCT(uint8_t *k0, uint8_t *RT, uint8_t *CT) {
aes_encode(NULL, k0, RT, CT, CIPURSE_AES_KEY_LENGTH);
}
// from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/securemessaging/CipurseSecureMessage.java#L68
void CipurseCGetKVV(uint8_t *key, uint8_t *kvv) {
uint8_t res[16] = {0};
aes_encode(NULL, key, AESData0, res, CIPURSE_AES_KEY_LENGTH);

View file

@ -17,6 +17,7 @@
#define CIPURSE_AES_KEY_LENGTH 16
#define CIPURSE_SECURITY_PARAM_N 6
#define OSPT_MAC_LENGTH 8
#define CIPURSE_POLY 0x35b088cce172UL
#define member_size(type, member) sizeof(((type *)0)->member)
@ -31,14 +32,14 @@ typedef struct CipurseContextS {
uint8_t keyId;
uint8_t key[CIPURSE_AES_KEY_LENGTH];
uint8_t RP[16];
uint8_t rP[6];
uint8_t RT[16];
uint8_t rT[6];
uint8_t RP[CIPURSE_AES_KEY_LENGTH];
uint8_t rP[CIPURSE_SECURITY_PARAM_N];
uint8_t RT[CIPURSE_AES_KEY_LENGTH];
uint8_t rT[CIPURSE_SECURITY_PARAM_N];
uint8_t k0[16];
uint8_t cP[16];
uint8_t CT[16];
uint8_t k0[CIPURSE_AES_KEY_LENGTH];
uint8_t cP[CIPURSE_AES_KEY_LENGTH];
uint8_t CT[CIPURSE_AES_KEY_LENGTH];
uint8_t frameKey[CIPURSE_AES_KEY_LENGTH];
uint8_t frameKeyNext[CIPURSE_AES_KEY_LENGTH];

View file

@ -85,8 +85,42 @@ static int CmdHFCipurseAuth(const char *Cmd) {
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
uint8_t keyId = 1;
uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
SetAPDULogging(true);
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf 14a sim",
"Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID",
"hf 14a sim -t 1 --uid 11223344 -> MIFARE Classic 1k\n");
void *argtable[] = {
arg_param_begin,
arg_lit0("a", "apdu", "show APDU requests and responses"),
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", "keyid", "<dec>", "key id"),
arg_str0("k", "key", "<hex>", "key for authenticate"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool APDULogging = arg_get_lit(ctx, 1);
bool verbose = arg_get_lit(ctx, 2);
keyId = arg_get_int_def(ctx, 3, 1);
uint8_t hdata[250] = {0};
int hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen);
if (hdatalen && hdatalen != 16) {
PrintAndLogEx(ERR, "ERROR: key length for AES128 must be 16 bytes only.");
CLIParserFree(ctx);
return PM3_EINVARG;
}
if (hdatalen)
memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
SetAPDULogging(APDULogging);
CLIParserFree(ctx);
int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
@ -95,14 +129,13 @@ static int CmdHFCipurseAuth(const char *Cmd) {
return PM3_ESOFT;
}
uint8_t keyId = 1;
uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
CipurseContext ctx = {0};
CipurseCSetKey(&ctx, 1, key);
CipurseContext cpc = {0};
CipurseCSetKey(&cpc, 1, key);
uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
CipurseCGetKVV(key, kvv);
PrintAndLogEx(INFO, "Key: %s KVV: %s", sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
if (verbose)
PrintAndLogEx(INFO, "Key: %s KVV: %s", sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
// get RP, rP
res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
@ -111,16 +144,21 @@ static int CmdHFCipurseAuth(const char *Cmd) {
DropField();
return PM3_ESOFT;
}
CipurseCSetRandomFromPICC(&ctx, buf);
CipurseCSetRandomFromPICC(&cpc, buf);
// make auth data
uint8_t authparams[16 + 16 + 6] = {0};
CipurseCAuthenticateHost(&ctx, authparams);
CipurseCAuthenticateHost(&cpc, authparams);
// authenticate
res = CIPURSEMutalAuthenticate(keyId, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000 || len != 0x16) {
PrintAndLogEx(ERR, "Cipurse authentication error. Card returns 0x%04x.", sw);
if (sw == 0x6988)
PrintAndLogEx(ERR, "Cipurse authentication error. Wrong key.");
else if ((sw == 0x6A88))
PrintAndLogEx(ERR, "Cipurse authentication error. Wrong key number.");
else PrintAndLogEx(ERR, "Cipurse authentication error. Card returns 0x%04x.", sw);
DropField();
return PM3_ESOFT;
}