diff --git a/armsrc/appmain.c b/armsrc/appmain.c index e52af017f..0b569f4ae 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1977,10 +1977,20 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t block_no; uint8_t key_type; uint8_t key[6]; + uint8_t block_no_nested; + uint8_t key_type_nested; + uint8_t key_nested[6]; + uint8_t nr_nonces; + uint8_t reset; + uint8_t addread; + uint8_t addauth; + uint8_t incblk2; + uint8_t corruptnrar; + uint8_t corruptnrarparity; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - MifareHasStaticEncryptedNonce(payload->block_no, payload->key_type, payload->key); + MifareHasStaticEncryptedNonce(payload->block_no, payload->key_type, payload->key, payload->block_no_nested, payload->key_type_nested, payload->key_nested, payload->nr_nonces, payload->reset, payload->addread, payload->addauth, payload->incblk2, payload->corruptnrar, payload->corruptnrarparity); break; } #endif diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 60de108ce..78c50396c 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -128,7 +128,7 @@ static bool mifare_wakeup_auth(struct Crypto1State *pcs, MifareWakeupType wakeup if (key_auth_cmd != 0) { uint64_t ui64key = bytes_to_num(key, 6); - if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, NULL, NULL, NULL)) { + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, NULL, NULL, NULL, NULL, false, false)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); return false; } @@ -2796,7 +2796,7 @@ OUT: // 2B F9 1C 1B D5 08 48 48 03 A4 B1 B1 75 FF 2D 90 // ^^ ^^ -void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *key) { +void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *key, uint8_t block_no_nested, uint8_t key_type_nested, uint8_t *key_nested, uint8_t nr_nested, bool reset, bool addread, bool addauth, bool incblk2, bool corruptnrar, bool corruptnrarparity) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); @@ -2805,8 +2805,7 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * int retval = PM3_SUCCESS; - uint64_t ui64key = bytes_to_num(key, 6); - uint8_t data[1] = { NONCE_FAIL }; + uint8_t data[14] = { NONCE_FAIL }; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; @@ -2814,34 +2813,62 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - uint32_t cuid = 0; - if (iso14443a_select_card(NULL, NULL, &cuid, true, 0, true) == false) { - retval = PM3_ESOFT; - goto OUT; - } - - uint8_t key_auth_cmd = MIFARE_AUTH_KEYA + (key_type & 1); - if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, NULL, NULL, NULL)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); - retval = PM3_ESOFT; - goto OUT; - }; - - uint32_t nt = 0; uint8_t enc_counter = 0; - uint32_t ntenc = 0; + uint8_t key_auth_cmd = MIFARE_AUTH_KEYA + key_type; + uint8_t key_auth_cmd_nested = MIFARE_AUTH_KEYA + key_type_nested; + uint64_t ui64key = bytes_to_num(key, 6); + uint64_t ui64key_nested = bytes_to_num(key_nested, 6); uint32_t oldntenc = 0; - for (uint8_t i = 0; i < 3; i++) { - if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_NESTED, &nt, &ntenc, NULL)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); - retval = PM3_ESOFT; - goto OUT; - }; + bool need_first_auth = true; + uint32_t cuid; + uint32_t nt; + uint32_t old_nt; + uint32_t ntenc; + uint8_t ntencpar; + for (uint8_t i = 0; i < nr_nested; i++) { + if (need_first_auth) { + cuid = 0; - if (g_dbglevel >= DBG_INFO) { - Dbprintf("nt: %x, nt encoded: %x", nt, ntenc); + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("select"); + } + if (iso14443a_select_card(NULL, NULL, &cuid, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, &old_nt, NULL, NULL, NULL, corruptnrar, corruptnrarparity)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); + retval = PM3_ESOFT; + goto OUT; + }; + if (!reset) { + need_first_auth = false; + } + if (addread) { + uint8_t dataread[16] = {0x00}; + mifare_classic_readblock(pcs, block_no, dataread); + } + if (addauth) { + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_NESTED, &nt, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); + retval = PM3_ESOFT; + goto OUT; + } else if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("Nonce distance: %i", nonce_distance(old_nt, nt)); + } + old_nt = nt; + } } + nt = 0; + ntenc = 0; + if (mifare_classic_authex_cmd(pcs, cuid, incblk2 ? block_no_nested + i : block_no_nested, key_auth_cmd_nested, ui64key_nested, AUTH_NESTED, &nt, &ntenc, &ntencpar, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Nested auth error"); + need_first_auth = true; + } else if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("Nonce distance: %i", nonce_distance(old_nt, nt)); + } + old_nt = nt; if (oldntenc == 0) { oldntenc = ntenc; } else if (ntenc == oldntenc) { @@ -2851,6 +2878,19 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * if (enc_counter) { data[0] = NONCE_STATIC_ENC; + data[1] = (cuid >> 24) & 0xFF; + data[2] = (cuid >> 16) & 0xFF; + data[3] = (cuid >> 8) & 0xFF; + data[4] = (cuid >> 0) & 0xFF; + data[5] = (nt >> 24) & 0xFF; + data[6] = (nt >> 16) & 0xFF; + data[7] = (nt >> 8) & 0xFF; + data[8] = (nt >> 0) & 0xFF; + data[9] = (ntenc >> 24) & 0xFF; + data[10] = (ntenc >> 16) & 0xFF; + data[11] = (ntenc >> 8) & 0xFF; + data[12] = (ntenc >> 0) & 0xFF; + data[13] = ntencpar; } else { data[0] = NONCE_NORMAL; } diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index e795f8f09..510fb2bdd 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -52,7 +52,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain); // Work wi void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain); void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key); // is "magic chinese" card? void MifareHasStaticNonce(void); // Has the tag a static nonce? -void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *key); // Has the tag a static encrypted nonce? +void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *key, uint8_t block_no_nested, uint8_t key_type_nested, uint8_t *key_nested, uint8_t nr_nested, bool reset, bool addread, bool addauth, bool incblk2, bool corruptnrar, bool corruptnrarparity); // Has the tag a static encrypted nonce? // MFC GEN3 int DoGen3Cmd(uint8_t *cmd, uint8_t cmd_len); diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 901154c7b..f6e65609c 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -142,10 +142,9 @@ int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, return mifare_classic_authex(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL); } int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing) { - return mifare_classic_authex_cmd(pcs, uid, blockNo, MIFARE_AUTH_KEYA + (keyType & 0xF), ui64Key, isNested, ntptr, NULL, timing); + return mifare_classic_authex_cmd(pcs, uid, blockNo, MIFARE_AUTH_KEYA + (keyType & 0xF), ui64Key, isNested, ntptr, NULL, NULL, timing, false, false); } -int mifare_classic_authex_cmd(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t cmd, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *ntencptr, uint32_t *timing) { - +int mifare_classic_authex_cmd(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t cmd, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *ntencptr, uint8_t *ntparptr, uint32_t *timing, bool corruptnrar, bool corruptnrarparity) { // "random" reader nonce: uint8_t nr[4]; num_to_bytes(prng_successor(GetTickCount(), 32), 4, nr); @@ -161,6 +160,8 @@ int mifare_classic_authex_cmd(struct Crypto1State *pcs, uint32_t uid, uint8_t bl uint32_t nt = bytes_to_num(receivedAnswer, 4); if (ntencptr) *ntencptr = nt; + if (ntparptr) + *ntparptr = receivedAnswerPar[0]; // ----------------------------- crypto1 create if (isNested) @@ -178,9 +179,37 @@ int mifare_classic_authex_cmd(struct Crypto1State *pcs, uint32_t uid, uint8_t bl } // some statistic - if (!ntptr && (g_dbglevel >= DBG_EXTENDED)) - Dbprintf("auth uid: %08x | nr: %02x%02x%02x%02x | nt: %08x", uid, nr[0], nr[1], nr[2], nr[3], nt); - +// if (!ntptr && (g_dbglevel >= DBG_EXTENDED)) + uint32_t nr32 = nr[0] << 24 | nr[1] << 16 | nr[2] << 8 | nr[3]; + if (g_dbglevel >= DBG_EXTENDED) { + if (!isNested) { + Dbprintf("auth cmd: %02x %02x | uid: %08x | nr: %08x %s| nt: %08x %s| par: %i%i%i%i %s", + cmd, blockNo, uid, + nr32, validate_prng_nonce(nr32) ? "@" : " ", + nt, validate_prng_nonce(nt) ? "@" : " ", + (receivedAnswerPar[0] >> 7) & 1, + (receivedAnswerPar[0] >> 6) & 1, + (receivedAnswerPar[0] >> 5) & 1, + (receivedAnswerPar[0] >> 4) & 1, + validate_parity_nonce(nt, receivedAnswerPar[0], nt) ? "ok " : "bad"); + } else { + Dbprintf("auth nested cmd: %02x %02x | uid: %08x | nr: %08x %s| nt: %08x %s| par: %i%i%i%i %s| ntenc: %08x %s| parerr: %i%i%i%i", + cmd, blockNo, uid, + nr32, validate_prng_nonce(nr32) ? "@" : " ", + nt, validate_prng_nonce(nt) ? "@" : " ", + (receivedAnswerPar[0] >> 7) & 1, + (receivedAnswerPar[0] >> 6) & 1, + (receivedAnswerPar[0] >> 5) & 1, + (receivedAnswerPar[0] >> 4) & 1, + validate_parity_nonce(*ntencptr, receivedAnswerPar[0], nt) ? "ok " : "bad", + *ntencptr, validate_prng_nonce(*ntencptr) ? "@" : " ", + ((receivedAnswerPar[0] >> 7) & 1) ^ oddparity8((*ntencptr >> 24) & 0xFF), + ((receivedAnswerPar[0] >> 6) & 1) ^ oddparity8((*ntencptr >> 16) & 0xFF), + ((receivedAnswerPar[0] >> 5) & 1) ^ oddparity8((*ntencptr >> 8) & 0xFF), + ((receivedAnswerPar[0] >> 4) & 1) ^ oddparity8((*ntencptr >> 0) & 0xFF) + ); + } + } // save Nt if (ntptr) *ntptr = nt; @@ -193,16 +222,23 @@ int mifare_classic_authex_cmd(struct Crypto1State *pcs, uint32_t uid, uint8_t bl mf_nr_ar[pos] = crypto1_byte(pcs, nr[pos], 0) ^ nr[pos]; par[0] |= (((filter(pcs->odd) ^ oddparity8(nr[pos])) & 0x01) << (7 - pos)); } - // Skip 32 bits in pseudo random generator nt = prng_successor(nt, 32); // ar+parity + if (corruptnrar) { + Dbprintf("Corrupting nRaR..."); + nt ^= 1; + } for (pos = 4; pos < 8; pos++) { nt = prng_successor(nt, 8); mf_nr_ar[pos] = crypto1_byte(pcs, 0x00, 0) ^ (nt & 0xff); par[0] |= (((filter(pcs->odd) ^ oddparity8(nt & 0xff)) & 0x01) << (7 - pos)); } + if (corruptnrarparity) { + Dbprintf("Corrupting nRaR parity..."); + par[0] ^= 1; + } // Transmit reader nonce and reader answer ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar), par, NULL); @@ -890,3 +926,42 @@ int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData) { } return PM3_EFAILED; } + +bool validate_prng_nonce(uint32_t nonce) { + uint16_t x = nonce >> 16; + x = (x & 0xff) << 8 | x >> 8; + for (uint8_t i = 0; i<16; i++) { + x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; + } + x = (x & 0xff) << 8 | x >> 8; + return x == (nonce & 0xFFFF); +} + +bool validate_parity_nonce(uint32_t ntenc, uint8_t ntparenc, uint32_t nt) { + uint32_t ks = nt ^ ntenc; + ntparenc >>= 4; + uint8_t ksp = (((ks >> 16)&1) << 3) | (((ks >> 8)&1) << 2) | (((ks >> 0)&1) << 1); + uint8_t ntpar = ntparenc ^ ksp; + return (((ntpar >> 3) & 1) == oddparity8((nt>>24) & 0xFF)) && + (((ntpar >> 2) & 1) == oddparity8((nt>>16) & 0xFF)) && + (((ntpar >> 1) & 1) == oddparity8((nt>>8) & 0xFF)); +} + +int nonce_distance(uint32_t from, uint32_t to) { + if (!validate_prng_nonce(from) || !validate_prng_nonce(to)) + return -1; + if (from == to) + return 0; + uint16_t x = from; + uint16_t y = to; + x = (x & 0xff) << 8 | x >> 8; + y = (y & 0xff) << 8 | y >> 8; + uint16_t i = 1; + for(;i;i++) { + x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; + if (x==y) + return i; + } + // never reached + return -1; +} diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index 662534397..c34206af0 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -74,7 +74,7 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t // mifare classic int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested); int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing); -int mifare_classic_authex_cmd(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t cmd, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *ntencptr, uint32_t *timing); +int mifare_classic_authex_cmd(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t cmd, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *ntencptr, uint8_t *ntencparptr, uint32_t *timing, bool corruptnrar, bool corruptnrarparity); int mifare_classic_readblock(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData); int mifare_classic_readblock_ex(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData, uint8_t iso_byte); @@ -122,5 +122,7 @@ uint64_t emlGetKey(int sectorNum, int keyType); int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum); void emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum); bool emlCheckValBl(int blockNum); - +bool validate_prng_nonce(uint32_t nonce); +bool validate_parity_nonce(uint32_t ntenc, uint8_t ntparenc, uint32_t nt); +int nonce_distance(uint32_t from, uint32_t to); #endif diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 230fea8b6..b6ecd40e4 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -9618,11 +9618,164 @@ static int CmdHF14AMfInfo(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14AMfISEN(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf isen", + "Information and check Static Encrypted Nonce properties in a MIFARE Classic card\n" + "Some cards in order to extract information you need to specify key\n" + "and/or specific keys in the command line", + "hf mf isen\n" + "hf mf isen -k FFFFFFFFFFFF -v\n" + "Default behavior:\n" + "auth(blk)-auth(blk2)-auth(blk2)-...\n" + "Default behavior when wrong key2:\n" + "auth(blk)-auth(blk2) auth(blk)-auth(blk2) ...\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, "blk", "", "block number"), + arg_lit0("a", NULL, "input key type is key A (def)"), + arg_lit0("b", NULL, "input key type is key B"), + arg_int0("c", NULL, "", "input key type is key A + offset"), + arg_str0("k", "key", "", "key, 6 hex bytes"), + arg_int0(NULL, "blk2", "", "nested block number (default=same)"), + arg_lit0(NULL, "a2", "nested input key type is key A (default=same)"), + arg_lit0(NULL, "b2", "nested input key type is key B (default=same)"), + arg_int0(NULL, "c2", "", "nested input key type is key A + offset"), + arg_str0(NULL, "key2", "", "nested key, 6 hex bytes (default=same)"), + arg_int0("n", NULL, "", "number of nonces (default=2)"), + arg_lit0(NULL, "reset", "reset between attempts, even if auth was successful"), + arg_lit0(NULL, "addread", "auth(blk)-read(blk)-auth(blk2)"), + arg_lit0(NULL, "addauth", "auth(blk)-auth(blk)-auth(blk2)"), + arg_lit0(NULL, "incblk2", "auth(blk)-auth(blk2)-auth(blk2+1)-..."), + arg_lit0(NULL, "corruptnrar", "corrupt {nR}{aR}, but with correct parity"), + arg_lit0(NULL, "corruptnrarparity", "correct {nR}{aR}, but with corrupted parity"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int blockn = arg_get_int_def(ctx, 1, 0); + + uint8_t keytype = MF_KEY_A; + if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 3)) { + keytype = MF_KEY_B; + } + // Should warn if conflict with -a/-b and -c but well... + keytype = arg_get_int_def(ctx, 4, keytype); + + int keylen = 0; + uint8_t key[MIFARE_KEY_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + CLIGetHexWithReturn(ctx, 5, key, &keylen); + + int blockn_nested = arg_get_int_def(ctx, 6, blockn); + + uint8_t keytype_nested = keytype; + if (arg_get_lit(ctx, 7) && arg_get_lit(ctx, 8)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 7)) { + keytype_nested = MF_KEY_A; + } else if (arg_get_lit(ctx, 8)) { + keytype_nested = MF_KEY_B; + } + // Should warn if conflict with -a/-b and -c but well... + keytype_nested = arg_get_int_def(ctx, 9, keytype_nested); + + int keylen_nested = 0; + uint8_t key_nested[MIFARE_KEY_SIZE]; + memcpy(key_nested, key, MIFARE_KEY_SIZE); + CLIGetHexWithReturn(ctx, 10, key_nested, &keylen_nested); + + int nr_nested = arg_get_int_def(ctx, 11, 2); + + bool reset = arg_get_lit(ctx, 12); + bool addread = arg_get_lit(ctx, 13); + bool addauth = arg_get_lit(ctx, 14); + bool incblk2 = arg_get_lit(ctx, 15); + bool corruptnrar = arg_get_lit(ctx, 16); + bool corruptnrarparity = arg_get_lit(ctx, 17); + CLIParserFree(ctx); + + uint8_t dbg_curr = DBG_NONE; + if (getDeviceDebugLevel(&dbg_curr) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + if (keylen != 0 && keylen != MIFARE_KEY_SIZE) { + PrintAndLogEx(ERR, "Key length must be %u bytes", MIFARE_KEY_SIZE); + return PM3_EINVARG; + } + + if (keylen_nested != 0 && keylen_nested != MIFARE_KEY_SIZE) { + PrintAndLogEx(ERR, "Key length must be %u bytes", MIFARE_KEY_SIZE); + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); + return 0; + } + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + /* + 0: couldn't read + 1: OK, with ATS + 2: OK, no ATS + 3: proprietary Anticollision + */ + uint64_t select_status = resp.oldarg[0]; + + if (select_status == 0) { + PrintAndLogEx(DEBUG, "iso14443a card select failed"); + return select_status; + } + + if (select_status == 3) { + PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision"); + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " ---------------------"); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); + PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); + +// if (setDeviceDebugLevel(DBG_DEBUG, false) != PM3_SUCCESS) { + if (setDeviceDebugLevel(DBG_EXTENDED, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + int res = detect_classic_static_encrypted_nonce_ex(blockn, keytype, key, blockn_nested, keytype_nested, key_nested, nr_nested, reset, addread, addauth, incblk2, corruptnrar, corruptnrarparity, true); + if (res == NONCE_STATIC) + PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); + if (res == NONCE_STATIC_ENC) + PrintAndLogEx(SUCCESS, "Static enc nonce..... " _RED_("yes")); + + if (setDeviceDebugLevel(dbg_curr, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("recovery") " -----------------------"}, {"info", CmdHF14AMfInfo, IfPm3Iso14443a, "mfc card Info"}, + {"isen", CmdHF14AMfISEN, IfPm3Iso14443a, "mfc card Info Static Encrypted Nonces"}, {"darkside", CmdHF14AMfDarkside, IfPm3Iso14443a, "Darkside attack"}, {"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack"}, {"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened MIFARE Classic cards"}, diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 40242f85a..f742e9d81 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -42,6 +42,7 @@ #include "mbedtls/sha1.h" // SHA1 #include "cmdhf14a.h" #include "gen4.h" +#include "parity.h" int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { uint32_t uid = 0; @@ -1388,12 +1389,22 @@ returns: 2 = cmd failed 3 = has encrypted nonce */ -int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, uint8_t *key) { +int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, uint8_t *key, uint8_t block_no_nested, uint8_t key_type_nested, uint8_t *key_nested, uint8_t nr_nested, bool reset, bool addread, bool addauth, bool incblk2, bool corruptnrar, bool corruptnrarparity, bool verbose) { clearCommandBuffer(); - uint8_t cdata[1 + 1 + MIFARE_KEY_SIZE] = { 0 }; + uint8_t cdata[1 + 1 + MIFARE_KEY_SIZE + 1 + 1 + MIFARE_KEY_SIZE + 1 + 1 + 1 + 1 + 1 + 1 + 1] = { 0 }; cdata[0] = block_no; cdata[1] = key_type; memcpy(&cdata[2], key, MIFARE_KEY_SIZE); + cdata[8] = block_no_nested; + cdata[9] = key_type_nested; + memcpy(&cdata[10], key_nested, MIFARE_KEY_SIZE); + cdata[16] = nr_nested; + cdata[17] = reset; + cdata[18] = addread; + cdata[19] = addauth; + cdata[20] = incblk2; + cdata[21] = corruptnrar; + cdata[22] = corruptnrarparity; SendCommandNG(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, cdata, sizeof(cdata)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) { @@ -1401,11 +1412,64 @@ int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, ui if (resp.status == PM3_ESOFT) { return NONCE_FAIL; } + if (verbose && (resp.data.asBytes[0] == NONCE_STATIC_ENC)) { + uint32_t uid = resp.data.asBytes[1] << 24 | + resp.data.asBytes[2] << 16 | + resp.data.asBytes[3] << 8 | + resp.data.asBytes[4]; + uint32_t nt = resp.data.asBytes[5] << 24 | + resp.data.asBytes[6] << 16 | + resp.data.asBytes[7] << 8 | + resp.data.asBytes[8]; + uint32_t ntenc = resp.data.asBytes[9] << 24 | + resp.data.asBytes[10] << 16 | + resp.data.asBytes[11] << 8 | + resp.data.asBytes[12]; + uint8_t ntencparenc = resp.data.asBytes[13]; + + // recompute nt on client, just because + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + uint64_t ui64key = bytes_to_num(key_nested, 6); + crypto1_init(pcs, ui64key); // key_nested + uint32_t ks = crypto1_word(pcs, ntenc ^ uid, 1); + uint32_t mynt = ks ^ ntenc; + if (mynt != nt) { + PrintAndLogEx(ERR, "Client computed nT " _YELLOW_("%08x") " does not match ARM computed nT " _YELLOW_("%08x"), mynt, nt); + } + ntencparenc >>= 4; + // [...] Additionally, the bit of keystream used to encrypt the parity bits is reused to encrypt the next bit of plaintext. + // we can decrypt first 3 parity bits, not last one as it's using future keystream + uint8_t ksp = (((ks >> 16)&1) << 3) | (((ks >> 8)&1) << 2) | (((ks >> 0)&1) << 1); + uint8_t ntencpar = ntencparenc ^ ksp; + if (validate_prng_nonce(nt)) { + PrintAndLogEx(INFO, "nTenc " _GREEN_("%08x") " par {" _YELLOW_("%i%i%i%i") "}=" _YELLOW_("%i%i%ix") " | ks " _GREEN_("%08x") " | nT " _GREEN_("%08x") " par " _YELLOW_("%i%i%i%i")" | lfsr16 index " _GREEN_("%i"), + ntenc, + (ntencparenc >> 3) & 1, (ntencparenc >> 2) & 1, (ntencparenc >> 1) & 1, ntencparenc & 1, + (ntencpar >> 3) & 1, (ntencpar >> 2) & 1, (ntencpar >> 1) & 1, + ks, nt, + oddparity8((nt>>24) & 0xFF), oddparity8((nt>>16) & 0xFF), oddparity8((nt>>8) & 0xFF), oddparity8(nt & 0xFF), + nonce_distance(0x0100, nt)); + } else { + PrintAndLogEx(INFO, "nTenc " _GREEN_("%08x") " par {" _YELLOW_("%i%i%i%i") "}=" _YELLOW_("%i%i%ix") " | ks " _YELLOW_("%08x") " | nT " _YELLOW_("%08x") " par " _YELLOW_("%i%i%i%i") " | " _RED_("not lfsr16") " (wrong key)", + ntenc, + (ntencparenc >> 3) & 1, (ntencparenc >> 2) & 1, (ntencparenc >> 1) & 1, ntencparenc & 1, + (ntencpar >> 3) & 1, (ntencpar >> 2) & 1, (ntencpar >> 1) & 1, + ks, nt, + oddparity8((nt>>24) & 0xFF), oddparity8((nt>>16) & 0xFF), oddparity8((nt>>8) & 0xFF), oddparity8(nt & 0xFF) + ); + } + } return resp.data.asBytes[0]; } return NONCE_FAIL; } +int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, uint8_t *key) { + return detect_classic_static_encrypted_nonce_ex(block_no, key_type, key, block_no, key_type, key, 3, false, false, false, false, false, false, false); +} + // try to see if card responses to "Chinese magic backdoor" commands. // returns flag uint16_t detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key) { diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index 661cad078..df66377f3 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -106,6 +106,7 @@ int detect_classic_prng(void); int detect_classic_nackbug(bool verbose); uint16_t detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key); int detect_classic_static_nonce(void); +int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, uint8_t *key, uint8_t block_no_nested, uint8_t key_type_nested, uint8_t *key_nested, uint8_t nr_nested, bool reset, bool addread, bool addauth, bool incblk2, bool corruptnrar, bool corruptnrarparity, bool verbose); int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, uint8_t *key); bool detect_mfc_ev1_signature(void); int read_mfc_ev1_signature(uint8_t *signature);