mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-20 21:33:47 -07:00
hf mf isen for analyzing some static encrypted nonces MFC
This commit is contained in:
parent
de326fc772
commit
e7c4ae2b1e
8 changed files with 385 additions and 40 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue