diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c index 996d43b1e..9da227d58 100644 --- a/client/src/cipurse/cipursecrypto.c +++ b/client/src/cipurse/cipursecrypto.c @@ -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); diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h index c2f8c2de4..53c171a0c 100644 --- a/client/src/cipurse/cipursecrypto.h +++ b/client/src/cipurse/cipursecrypto.h @@ -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]; diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index d693f2295..1bb3e83d5 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -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", "", "key id"), + arg_str0("k", "key", "", "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; }