From 4d1890959621d354b096ba202ae7eb8a704d62aa Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 26 Jun 2017 21:45:22 +0200 Subject: [PATCH] ADD: 'hf 14a read' - detection if found Mifare Classic tag has a weak or hardend PRNG. Thanks to @doegox for implementing it in nfc-tools/mfoc Its a beauty :) --- client/cmdhf14a.c | 74 +++++++++++++++++++++----------------- client/mifarehost.c | 31 ++++++++++++++++ client/mifarehost.h | 3 ++ client/nonce2key/crapto1.c | 17 +++++++-- client/nonce2key/crapto1.h | 2 ++ 5 files changed, 91 insertions(+), 36 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 21c97fa2..99f1426d 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -214,9 +214,11 @@ int CmdHF14AReader(const char *Cmd) { PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); + bool isMifareClassic = true; switch (card.sak) { case 0x00: - + isMifareClassic = false; + // ******** is card of the MFU type (UL/ULC/NTAG/ etc etc) ul_switch_off_field(); @@ -409,6 +411,14 @@ int CmdHF14AReader(const char *Cmd) { // disconnect SendCommand(&cDisconnect); + + if (isMifareClassic) { + if ( detect_classic_prng() ) + PrintAndLog("Prng detection: WEAK (darkside)"); + else + PrintAndLog("Prng detection: HARDEND (hardnested)"); + } + return select_status; } @@ -450,15 +460,15 @@ int CmdHF14ACUIDs(const char *Cmd) { // ## simulate iso14443a tag // ## greg - added ability to specify tag UID int CmdHF14ASim(const char *Cmd) { - bool errors = FALSE; + bool errors = false; uint8_t flags = 0; uint8_t tagtype = 1; uint8_t cmdp = 0; uint8_t uid[10] = {0,0,0,0,0,0,0,0,0,0}; int uidlen = 0; - bool useUIDfromEML = TRUE; - bool setEmulatorMem = FALSE; - bool verbose = FALSE; + bool useUIDfromEML = true; + bool setEmulatorMem = false; + bool verbose = false; nonces_t data[1]; while(param_getchar(Cmd, cmdp) != 0x00) { @@ -471,7 +481,7 @@ int CmdHF14ASim(const char *Cmd) { // Retrieve the tag type tagtype = param_get8ex(Cmd, cmdp+1, 0, 10); if (tagtype == 0) - errors = TRUE; + errors = true; cmdp += 2; break; case 'u': @@ -482,17 +492,17 @@ int CmdHF14ASim(const char *Cmd) { //case 20: flags |= FLAG_10B_UID_IN_DATA; break; case 14: flags |= FLAG_7B_UID_IN_DATA; break; case 8: flags |= FLAG_4B_UID_IN_DATA; break; - default: errors = TRUE; break; + default: errors = true; break; } if (!errors) { PrintAndLog("Emulating ISO/IEC 14443 type A tag with %d byte UID (%s)", uidlen>>1, sprint_hex(uid, uidlen>>1)); - useUIDfromEML = FALSE; + useUIDfromEML = false; } cmdp += 2; break; case 'v': case 'V': - verbose = TRUE; + verbose = true; cmdp++; break; case 'x': @@ -502,12 +512,12 @@ int CmdHF14ASim(const char *Cmd) { break; case 'e': case 'E': - setEmulatorMem = TRUE; + setEmulatorMem = true; cmdp++; break; default: PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = TRUE; + errors = true; break; } if(errors) break; @@ -541,32 +551,31 @@ int CmdHF14ASim(const char *Cmd) { int CmdHF14ASniff(const char *Cmd) { int param = 0; - uint8_t ctmp = param_getchar(Cmd, 0) ; - if (ctmp == 'h' || ctmp == 'H') return usage_hf_14a_sniff(); - + uint8_t ctmp; for (int i = 0; i < 2; i++) { ctmp = param_getchar(Cmd, i); + if (ctmp == 'h' || ctmp == 'H') return usage_hf_14a_sniff(); if (ctmp == 'c' || ctmp == 'C') param |= 0x01; if (ctmp == 'r' || ctmp == 'R') param |= 0x02; } - UsbCommand c = {CMD_SNOOP_ISO_14443a, {param, 0, 0}}; - clearCommandBuffer(); - SendCommand(&c); - return 0; + UsbCommand c = {CMD_SNOOP_ISO_14443a, {param, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; } int CmdHF14ACmdRaw(const char *cmd) { UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; bool reply=1; - bool crc = FALSE; - bool power = FALSE; - bool active = FALSE; - bool active_select = FALSE; + bool crc = false; + bool power = false; + bool active = false; + bool active_select = false; uint16_t numbits=0; - bool bTimeout = FALSE; + bool bTimeout = false; uint32_t timeout=0; - bool topazmode = FALSE; + bool topazmode = false; char buf[5]=""; int i=0; uint8_t data[USB_CMD_DATA_SIZE]; @@ -586,19 +595,19 @@ int CmdHF14ACmdRaw(const char *cmd) { case 'h': return usage_hf_14a_raw(); case 'r': - reply = FALSE; + reply = false; break; case 'c': - crc = TRUE; + crc = true; break; case 'p': - power = TRUE; + power = true; break; case 'a': - active = TRUE; + active = true; break; case 's': - active_select = TRUE; + active_select = true; break; case 'b': sscanf(cmd+i+2,"%d",&temp); @@ -608,7 +617,7 @@ int CmdHF14ACmdRaw(const char *cmd) { i-=2; break; case 't': - bTimeout = TRUE; + bTimeout = true; sscanf(cmd+i+2,"%d",&temp); timeout = temp; i+=3; @@ -616,7 +625,7 @@ int CmdHF14ACmdRaw(const char *cmd) { i-=2; break; case 'T': - topazmode = TRUE; + topazmode = true; break; default: return usage_hf_14a_raw(); @@ -706,8 +715,7 @@ int CmdHF14ACmdRaw(const char *cmd) { return 0; } -static void waitCmd(uint8_t iSelect) -{ +static void waitCmd(uint8_t iSelect) { UsbCommand resp; uint16_t len = 0; diff --git a/client/mifarehost.c b/client/mifarehost.c index 5d39221f..59a2e0d7 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -675,3 +675,34 @@ int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, crypto1_destroy(s); return 0; } + +/* Detect Tag Prng, +* function performs a partial AUTH, where it tries to authenticate against block0, key A, but only collects tag nonce. +* the tag nonce is check to see if it has a predictable PRNG. +* @returns +* TRUE if tag uses WEAK prng (ie Darkside attack possible) +* FALSE is tag uses HARDEND prng (ie hardnested attack possible, with known key) +*/ +bool detect_classic_prng(){ + + UsbCommand resp, respA; + uint8_t cmd[] = {MIFARE_AUTH_KEYA, 0x00}; + uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC; + + UsbCommand cAuth = {CMD_READER_ISO_14443a, {flags, sizeof(cmd), 0}}; + memcpy(cAuth.d.asBytes, cmd, sizeof(cmd)); + + clearCommandBuffer(); + SendCommand(&cAuth); + WaitForResponse(CMD_ACK, &resp); + WaitForResponse(CMD_ACK, &respA); + + // if select tag failed. + if ( resp.arg[0] == 0 ) { + printf("Error: selecting tag failed, can't detect prng\n"); + return false; + } + + uint32_t nonce = bytes_to_num(respA.d.asBytes, respA.arg[0]); + return validate_prng_nonce(nonce); +} \ No newline at end of file diff --git a/client/mifarehost.h b/client/mifarehost.h index e172bd39..99885b4f 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -25,6 +25,7 @@ #include "nonce2key/crapto1.h" #include "iso14443crc.h" #include "protocols.h" +#include "mifare.h" #define NESTED_SECTOR_RETRY 10 @@ -84,4 +85,6 @@ int isBlockTrailer(int blockN); int loadTraceCard(uint8_t *tuid, uint8_t uidlen); int saveTraceCard(void); int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); + +extern bool detect_classic_prng(); #endif \ No newline at end of file diff --git a/client/nonce2key/crapto1.c b/client/nonce2key/crapto1.c index 9f6f7f6b..0df11534 100644 --- a/client/nonce2key/crapto1.c +++ b/client/nonce2key/crapto1.c @@ -19,7 +19,7 @@ */ #include "crapto1.h" #include - +#include #if !defined LOWMEM && defined __GNUC__ static uint8_t filterlut[1 << 20]; static void __attribute__((constructor)) fill_lut() @@ -399,6 +399,17 @@ int nonce_distance(uint32_t from, uint32_t to) return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; } +/** validate_prng_nonce + * Determine if nonce is deterministic. ie: Suspectable to Darkside attack. + * returns + * true = weak prng + * false = hardend prng + */ +bool validate_prng_nonce(uint32_t nonce) { + // init prng table: + nonce_distance(nonce, nonce); + return ((65535 - dist[nonce >> 16] + dist[nonce & 0xffff]) % 65535) == 16; +} static uint32_t fastfwd[2][8] = { { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, @@ -501,7 +512,7 @@ struct Crypto1State* lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8] odd = lfsr_prefix_ks(ks, 1); even = lfsr_prefix_ks(ks, 0); - s = statelist = malloc((sizeof *statelist) << 20); + s = statelist = malloc((sizeof *statelist) << 24); if(!s || !odd || !even) { free(statelist); statelist = 0; @@ -531,7 +542,7 @@ struct Crypto1State* lfsr_common_prefix_ex(uint32_t pfx, uint8_t ks[8]) odd = lfsr_prefix_ks(ks, 1); even = lfsr_prefix_ks(ks, 0); - s = statelist = malloc((sizeof *statelist) << 20); + s = statelist = malloc((sizeof *statelist) << 24); if(!s || !odd || !even) { free(statelist); statelist = 0; diff --git a/client/nonce2key/crapto1.h b/client/nonce2key/crapto1.h index 1cbebe5d..a6f081af 100644 --- a/client/nonce2key/crapto1.h +++ b/client/nonce2key/crapto1.h @@ -21,6 +21,7 @@ #define CRAPTO1_H__ #include +#include #include "bucketsort.h" #ifdef __cplusplus @@ -49,6 +50,7 @@ uint8_t lfsr_rollback_bit(struct Crypto1State* s, uint32_t in, int fb); uint8_t lfsr_rollback_byte(struct Crypto1State* s, uint32_t in, int fb); uint32_t lfsr_rollback_word(struct Crypto1State* s, uint32_t in, int fb); int nonce_distance(uint32_t from, uint32_t to); +extern bool validate_prng_nonce(uint32_t nonce); #define SWAPENDIAN(x)\ (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16)