hf mf isen for analyzing some static encrypted nonces MFC

This commit is contained in:
Philippe Teuwen 2024-07-29 22:09:41 +02:00
commit e7c4ae2b1e
8 changed files with 385 additions and 40 deletions

View file

@ -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

View file

@ -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;
uint8_t enc_counter = 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;
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_EXTENDED) {
Dbprintf("select");
}
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 (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;
};
uint32_t nt = 0;
uint8_t enc_counter = 0;
uint32_t ntenc = 0;
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 (!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;
};
if (g_dbglevel >= DBG_INFO) {
Dbprintf("nt: %x, nt encoded: %x", nt, ntenc);
} 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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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", "<dec>", "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, "<dec>", "input key type is key A + offset"),
arg_str0("k", "key", "<hex>", "key, 6 hex bytes"),
arg_int0(NULL, "blk2", "<dec>", "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", "<dec>", "nested input key type is key A + offset"),
arg_str0(NULL, "key2", "<hex>", "nested key, 6 hex bytes (default=same)"),
arg_int0("n", NULL, "<dec>", "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"},

View file

@ -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) {

View file

@ -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);