diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 655c8f689..752613e20 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -295,6 +295,7 @@ static void print_picopass_info(const picopass_hdr *hdr) { fuse_config(hdr); mem_app_config(hdr); } + static void print_picopass_header(const picopass_hdr *hdr) { PrintAndLogEx(INFO, "--------------------------- " _CYAN_("card") " ---------------------------"); PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); @@ -741,6 +742,7 @@ static int CmdHFiClassELoad(const char *Cmd) { PrintAndLogEx(SUCCESS, "sent %d bytes of data to device emulator memory", bytes_sent); return PM3_SUCCESS; } + static int CmdHFiClassESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass esave", @@ -858,6 +860,7 @@ static int CmdHFiClassEView(const char *Cmd) { free(dump); return PM3_SUCCESS; } + static int CmdHFiClassDecrypt(const char *Cmd) { CLIParserContext *clictx; CLIParserInit(&clictx, "hf iclass decrypt", @@ -2703,8 +2706,8 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass chk", "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag", - "hf iclass chk -f dictionaries/iclass_default_keys.dic\n" - "hf iclass chk -f dictionaries/iclass_default_keys.dic --elite"); + "hf iclass chk -f iclass_default_keys.dic\n" + "hf iclass chk -f iclass_default_keys.dic --elite"); void *argtable[] = { arg_param_begin, @@ -2883,14 +2886,15 @@ out: return PM3_SUCCESS; } + // this method tries to identify in which configuration mode a iCLASS / iCLASS SE reader is in. // Standard or Elite / HighSecurity mode. It uses a default key dictionary list in order to work. static int CmdHFiClassLookUp(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass lookup", "Lookup keys takes some sniffed trace data and tries to verify what key was used against a dictionary file", - "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f dictionaries/iclass_default_keys.dic\n" - "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f dictionaries/iclass_default_keys.dic --elite"); + "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f iclass_default_keys.dic\n" + "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f iclass_default_keys.dic --elite"); void *argtable[] = { arg_param_begin, @@ -2952,14 +2956,10 @@ static int CmdHFiClassLookUp(const char *Cmd) { uint8_t CCNR[12]; uint8_t MAC_TAG[4] = { 0, 0, 0, 0 }; - iclass_prekey_t *prekey = NULL; - - // time - uint64_t t1 = msclock(); - // stupid copy.. CCNR is a combo of epurse and reader nonce memcpy(CCNR, epurse, 8); memcpy(CCNR + 8, macs, 4); + memcpy(MAC_TAG, macs + 4, 4); PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(csn, sizeof(csn))); PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(epurse, sizeof(epurse))); @@ -2967,6 +2967,9 @@ static int CmdHFiClassLookUp(const char *Cmd) { PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); + // run time + uint64_t t1 = msclock(); + uint8_t *keyBlock = NULL; uint32_t keycount = 0; @@ -2978,7 +2981,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { } //iclass_prekey_t - prekey = calloc(keycount, sizeof(iclass_prekey_t)); + iclass_prekey_t *prekey = calloc(keycount, sizeof(iclass_prekey_t)); if (!prekey) { free(keyBlock); return PM3_EMALLOC; @@ -3011,7 +3014,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { } t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "time in iclass lookup " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); + PrintAndLogEx(SUCCESS, "time in iclass lookup " _YELLOW_("%.3f") " seconds", (float)t1 / 1000.0); free(prekey); free(keyBlock); @@ -3019,43 +3022,150 @@ static int CmdHFiClassLookUp(const char *Cmd) { return PM3_SUCCESS; } -// precalc diversified keys and their MAC -void GenerateMacFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elite, uint8_t *keys, uint32_t keycnt, iclass_premac_t *list) { +typedef struct { + uint8_t thread_idx; + uint8_t use_raw; + uint8_t use_elite; + uint32_t keycnt; + uint8_t csn[8]; + uint8_t cc_nr[12]; + uint8_t *keys; + union { + iclass_premac_t *premac; + iclass_prekey_t *prekey; + } list; +} PACKED iclass_thread_arg_t; + +static size_t iclass_tc = 1; + +static void* bf_generate_mac(void *thread_arg) { + + iclass_thread_arg_t *targ = (iclass_thread_arg_t *)thread_arg; + const uint8_t idx = targ->thread_idx; + const uint8_t use_raw = targ->use_raw; + const uint8_t use_elite = targ->use_elite; + const uint32_t keycnt = targ->keycnt; + + uint8_t *keys = targ->keys; + iclass_premac_t *list = targ->list.premac; + + uint8_t csn[8]; + uint8_t cc_nr[12]; + memcpy(csn, targ->csn, sizeof(csn)); + memcpy(cc_nr, targ->cc_nr, sizeof(cc_nr)); + uint8_t key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -//iceman: threading - for (uint32_t i = 0; i < keycnt; i++) { + + for (uint32_t i = idx; i < keycnt; i += iclass_tc) { memcpy(key, keys + 8 * i, 8); if (use_raw) memcpy(div_key, key, 8); else - HFiClassCalcDivKey(CSN, key, div_key, use_elite); + HFiClassCalcDivKey(csn, key, div_key, use_elite); - doMAC(CCNR, div_key, list[i].mac); + doMAC(cc_nr, div_key, list[i].mac); } + return NULL; +} + +// precalc diversified keys and their MAC +void GenerateMacFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elite, uint8_t *keys, uint32_t keycnt, iclass_premac_t *list) { + + iclass_tc = num_CPUs(); + pthread_t threads[iclass_tc]; + iclass_thread_arg_t args[iclass_tc]; + // init thread arguments + for (uint8_t i = 0; i < iclass_tc; i++) { + args[i].thread_idx = i; + args[i].use_raw = use_raw; + args[i].use_elite = use_elite; + args[i].keycnt = keycnt; + args[i].keys = keys; + args[i].list.premac = list; + + memcpy(args[i].csn, CSN, sizeof(args[i].csn) ); + memcpy(args[i].cc_nr, CCNR, sizeof(args[i].cc_nr) ); + } + + for (int i = 0; i < iclass_tc; i++) { + int res = pthread_create(&threads[i], NULL, bf_generate_mac, (void *)&args[i]); + if (res) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "Failed to create pthreads. Quitting"); + return; + } + } + + for (int i = 0; i < iclass_tc; i++) + pthread_join(threads[i], NULL); +} + +static void* bf_generate_mackey(void *thread_arg) { + + iclass_thread_arg_t *targ = (iclass_thread_arg_t *)thread_arg; + const uint8_t idx = targ->thread_idx; + const uint8_t use_raw = targ->use_raw; + const uint8_t use_elite = targ->use_elite; + const uint32_t keycnt = targ->keycnt; + + uint8_t *keys = targ->keys; + iclass_prekey_t *list = targ->list.prekey; + + uint8_t csn[8]; + uint8_t cc_nr[12]; + memcpy(csn, targ->csn, sizeof(csn)); + memcpy(cc_nr, targ->cc_nr, sizeof(cc_nr)); + + uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + for (uint32_t i = idx; i < keycnt; i += iclass_tc) { + + memcpy(list[i].key, keys + 8 * i, 8); + + if (use_raw) + memcpy(div_key, list[i].key, 8); + else + HFiClassCalcDivKey(csn, list[i].key, div_key, use_elite); + + doMAC(cc_nr, div_key, list[i].mac); + } + return NULL; } void GenerateMacKeyFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elite, uint8_t *keys, uint32_t keycnt, iclass_prekey_t *list) { - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -//iceman: threading - for (uint32_t i = 0; i < keycnt; i++) { - - memcpy(list[i].key, keys + 8 * i, 8); - - // generate diversifed key - if (use_raw) - memcpy(div_key, list[i].key, 8); - else - HFiClassCalcDivKey(CSN, list[i].key, div_key, use_elite); - - // generate MAC - doMAC(CCNR, div_key, list[i].mac); + iclass_tc = num_CPUs(); + pthread_t threads[iclass_tc]; + iclass_thread_arg_t args[iclass_tc]; + // init thread arguments + for (uint8_t i = 0; i < iclass_tc; i++) { + args[i].thread_idx = i; + args[i].use_raw = use_raw; + args[i].use_elite = use_elite; + args[i].keycnt = keycnt; + args[i].keys = keys; + args[i].list.prekey = list; + + memcpy(args[i].csn, CSN, sizeof(args[i].csn) ); + memcpy(args[i].cc_nr, CCNR, sizeof(args[i].cc_nr) ); } + + for (int i = 0; i < iclass_tc; i++) { + int res = pthread_create(&threads[i], NULL, bf_generate_mackey, (void *)&args[i]); + if (res) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "Failed to create pthreads. Quitting"); + return; + } + } + + for (int i = 0; i < iclass_tc; i++) + pthread_join(threads[i], NULL); + + PrintAndLogEx(NORMAL, ""); } // print diversified keys @@ -3250,7 +3360,7 @@ static command_t CommandTable[] = { {"encrypt", CmdHFiClassEncryptBlk, AlwaysAvailable, "[options..] Encrypt given block data"}, {"decrypt", CmdHFiClassDecrypt, AlwaysAvailable, "[options..] Decrypt given block data or tag dump file" }, {"managekeys", CmdHFiClassManageKeys, AlwaysAvailable, "[options..] Manage keys to use with iclass commands"}, - {"permute", CmdHFiClassPermuteKey, IfPm3Iclass, " Permute function from 'heart of darkness' paper"}, + {"permutekey", CmdHFiClassPermuteKey, IfPm3Iclass, " Permute function from 'heart of darkness' paper"}, {"view", CmdHFiClassView, AlwaysAvailable, "[options..] Display content from tag dump file"}, {NULL, NULL, NULL, NULL} diff --git a/client/src/loclass/elite_crack.c b/client/src/loclass/elite_crack.c index 380738476..250fdc2ad 100644 --- a/client/src/loclass/elite_crack.c +++ b/client/src/loclass/elite_crack.c @@ -39,13 +39,14 @@ #include #include #include +#include #include #include "cipherutils.h" #include "cipher.h" #include "ikeys.h" #include "elite_crack.h" #include "fileutils.h" -#include "des.h" +#include "mbedtls/des.h" #include "util_posix.h" /** @@ -70,8 +71,7 @@ * @param dest */ void permutekey(uint8_t key[8], uint8_t dest[8]) { - int i; - for (i = 0 ; i < 8 ; i++) { + for (uint8_t i = 0 ; i < 8 ; i++) { dest[i] = (((key[7] & (0x80 >> i)) >> (7 - i)) << 7) | (((key[6] & (0x80 >> i)) >> (7 - i)) << 6) | (((key[5] & (0x80 >> i)) >> (7 - i)) << 5) | @@ -164,12 +164,16 @@ rk(x [0] . . . x [7] , n + 1) = rk(rl(x [0] ) . . . rl(x [7] ), n) **/ static void rk(uint8_t *key, uint8_t n, uint8_t *outp_key) { memcpy(outp_key, key, 8); - uint8_t j; while (n-- > 0) { - for (j = 0; j < 8 ; j++) - outp_key[j] = rl(outp_key[j]); + outp_key[0] = rl(outp_key[0]); + outp_key[1] = rl(outp_key[1]); + outp_key[2] = rl(outp_key[2]); + outp_key[3] = rl(outp_key[3]); + outp_key[4] = rl(outp_key[4]); + outp_key[5] = rl(outp_key[5]); + outp_key[6] = rl(outp_key[6]); + outp_key[7] = rl(outp_key[7]); } - return; } static mbedtls_des_context ctx_enc; @@ -214,16 +218,22 @@ void hash2(uint8_t *key64, uint8_t *outp_keytable) { uint8_t z[8][8] = {{0}, {0}}; uint8_t temp_output[8] = {0}; //calculate complement of key - int i; - for (i = 0; i < 8; i++) - key64_negated[i] = ~key64[i]; + key64_negated[0] = ~key64[0]; + key64_negated[1] = ~key64[1]; + key64_negated[2] = ~key64[2]; + key64_negated[3] = ~key64[3]; + key64_negated[4] = ~key64[4]; + key64_negated[5] = ~key64[5]; + key64_negated[6] = ~key64[6]; + key64_negated[7] = ~key64[7]; // Once again, key is on iclass-format desencrypt_iclass(key64, key64_negated, z[0]); -// PrintAndLogEx(NORMAL, ""); -// PrintAndLogEx(INFO, "High security custom key (Kcus):"); -// PrintAndLogEx(INFO, "z0 %s", sprint_hex(z[0],8)); + if (g_debugMode > 0) { + PrintAndLogEx(DEBUG, "High security custom key (Kcus):"); + PrintAndLogEx(DEBUG, "z0 %s", sprint_hex(z[0],8)); + } uint8_t y[8][8] = {{0}, {0}}; @@ -232,7 +242,7 @@ void hash2(uint8_t *key64, uint8_t *outp_keytable) { desdecrypt_iclass(z[0], key64_negated, y[0]); // PrintAndLogEx(INFO, "y0 %s", sprint_hex(y[0],8)); - for (i = 1; i < 8; i++) { + for (uint8_t i = 1; i < 8; i++) { // z [i] = DES dec (rk(K cus , i), z [i−1] ) rk(key64, i, temp_output); //y [i] = DES enc (rk(K cus , i), y [i−1] ) @@ -242,7 +252,7 @@ void hash2(uint8_t *key64, uint8_t *outp_keytable) { } if (outp_keytable != NULL) { - for (i = 0 ; i < 8 ; i++) { + for (uint8_t i = 0 ; i < 8 ; i++) { memcpy(outp_keytable + i * 16, y[i], 8); memcpy(outp_keytable + 8 + i * 16, z[i], 8); } @@ -280,25 +290,121 @@ static int _readFromDump(uint8_t dump[], dumpdata *item, uint8_t i) { return 0; } */ -//static uint32_t startvalue = 0; -/** - * @brief Performs brute force attack against a dump-data item, containing csn, cc_nr and mac. - *This method calculates the hash1 for the CSN, and determines what bytes need to be bruteforced - *on the fly. If it finds that more than three bytes need to be bruteforced, it aborts. - *It updates the keytable with the findings, also using the upper half of the 16-bit ints - *to signal if the particular byte has been cracked or not. - * - * @param dump The dumpdata from iclass reader attack. - * @param keytable where to write found values. - * @return - */ -int bruteforceItem(dumpdata item, uint16_t keytable[]) { - int found = false; - uint8_t key_sel_p[8] = {0}; - uint8_t div_key[8] = {0}; - uint8_t key_sel[8] = {0}; - uint8_t calculated_MAC[4] = {0}; +typedef struct { + int thread_idx; + uint32_t endmask; + uint8_t numbytes_to_recover; + uint8_t bytes_to_recover[3]; + uint8_t key_index[8]; + uint16_t keytable[128]; + loclass_dumpdata_t item; +} loclass_thread_arg_t; + +typedef struct { + uint8_t values[3]; +} loclass_thread_ret_t; + +static size_t loclass_tc = 1; +static int loclass_found = 0; + +static void* bf_thread(void* thread_arg) { + + loclass_thread_arg_t *targ = (loclass_thread_arg_t *)thread_arg; + const uint32_t endmask = targ->endmask; + const uint8_t numbytes_to_recover = targ->numbytes_to_recover; + uint32_t brute = targ->thread_idx; + + uint8_t csn[8]; + uint8_t cc_nr[12]; + uint8_t mac[4]; + uint8_t key_index[8]; + uint8_t bytes_to_recover[3]; + uint16_t keytable[128]; + + memcpy(csn, targ->item.csn, sizeof(csn)); + memcpy(cc_nr, targ->item.cc_nr, sizeof(cc_nr)); + memcpy(mac, targ->item.mac, sizeof(mac)); + memcpy(key_index, targ->key_index, sizeof(key_index)); + memcpy(bytes_to_recover, targ->bytes_to_recover, sizeof(bytes_to_recover)); + memcpy(keytable, targ->keytable, sizeof(keytable)); + + int found; + while (!(brute & endmask)) { + + found = __atomic_load_n (&loclass_found, __ATOMIC_SEQ_CST); + + if (found != 0xFF) return NULL; + + //Update the keytable with the brute-values + for (uint8_t i = 0; i < numbytes_to_recover; i++) { + keytable[bytes_to_recover[i]] &= 0xFF00; + keytable[bytes_to_recover[i]] |= (brute >> (i * 8) & 0xFF); + } + + uint8_t key_sel[8] = {0}; + + // Piece together the key + key_sel[0] = keytable[key_index[0]] & 0xFF; + key_sel[1] = keytable[key_index[1]] & 0xFF; + key_sel[2] = keytable[key_index[2]] & 0xFF; + key_sel[3] = keytable[key_index[3]] & 0xFF; + key_sel[4] = keytable[key_index[4]] & 0xFF; + key_sel[5] = keytable[key_index[5]] & 0xFF; + key_sel[6] = keytable[key_index[6]] & 0xFF; + key_sel[7] = keytable[key_index[7]] & 0xFF; + + // Permute from iclass format to standard format + + uint8_t key_sel_p[8] = {0}; + permutekey_rev(key_sel, key_sel_p); + + // Diversify + uint8_t div_key[8] = {0}; + diversifyKey(csn, key_sel_p, div_key); + + // Calc mac + uint8_t calculated_MAC[4] = {0}; + doMAC(cc_nr, div_key, calculated_MAC); + + // success + if (memcmp(calculated_MAC, mac, 4) == 0) { + + loclass_thread_ret_t *r = (loclass_thread_ret_t*)malloc(sizeof(loclass_thread_ret_t)); + + for (uint8_t i = 0 ; i < numbytes_to_recover; i++) { + r->values[i] = keytable[bytes_to_recover[i]] & 0xFF; + } + __atomic_store_n(&loclass_found, targ->thread_idx, __ATOMIC_SEQ_CST); + pthread_exit ((void*)r); + } + + brute += loclass_tc; + +#define _CLR_ "\x1b[0K" + + if (numbytes_to_recover == 3) { + if ((brute > 0) && ((brute & 0xFFFF) == 0)) { + PrintAndLogEx(INPLACE, "[ %02x %02x %02x ] %8u / %u", bytes_to_recover[0], bytes_to_recover[1], bytes_to_recover[2] , brute, 0xFFFFFF); + } + } else if (numbytes_to_recover == 2) { + if ((brute > 0) && ((brute & 0x3F) == 0)) + PrintAndLogEx(INPLACE, "[ %02x %02x ] %5u / %u" _CLR_ , bytes_to_recover[0], bytes_to_recover[1], brute, 0xFFFF); + } else { + if ((brute > 0) && ((brute & 0x1F) == 0)) + PrintAndLogEx(INPLACE, "[ %02x ] %3u / %u" _CLR_, bytes_to_recover[0], brute, 0xFF); + } + } + pthread_exit(NULL); + + void* dummyptr = NULL; + return dummyptr; +} + +int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { + + // reset thread signals + loclass_found = 0xFF; //Get the key index (hash1) uint8_t key_index[8] = {0}; @@ -317,13 +423,12 @@ int bruteforceItem(dumpdata item, uint16_t keytable[]) { * Only the lower eight bits correspond to the (hopefully cracked) key-value. **/ uint8_t bytes_to_recover[3] = {0}; - uint8_t numbytes_to_recover = 0 ; - int i; - for (i = 0; i < 8; i++) { - if (keytable[key_index[i]] & (CRACKED | BEING_CRACKED)) continue; + uint8_t numbytes_to_recover = 0; + for (uint8_t i = 0; i < 8; i++) { + if (keytable[key_index[i]] & (LOCLASS_CRACKED | LOCLASS_BEING_CRACKED)) continue; bytes_to_recover[numbytes_to_recover++] = key_index[i]; - keytable[key_index[i]] |= BEING_CRACKED; + keytable[key_index[i]] |= LOCLASS_BEING_CRACKED; if (numbytes_to_recover > 3) { PrintAndLogEx(FAILED, "The CSN requires > 3 byte bruteforce, not supported"); @@ -331,18 +436,141 @@ int bruteforceItem(dumpdata item, uint16_t keytable[]) { PrintAndLogEx(INFO, "HASH1 %s", sprint_hex(key_index, 8)); PrintAndLogEx(NORMAL, ""); //Before we exit, reset the 'BEING_CRACKED' to zero - keytable[bytes_to_recover[0]] &= ~BEING_CRACKED; - keytable[bytes_to_recover[1]] &= ~BEING_CRACKED; - keytable[bytes_to_recover[2]] &= ~BEING_CRACKED; + keytable[bytes_to_recover[0]] &= ~LOCLASS_BEING_CRACKED; + keytable[bytes_to_recover[1]] &= ~LOCLASS_BEING_CRACKED; + keytable[bytes_to_recover[2]] &= ~LOCLASS_BEING_CRACKED; return PM3_ESOFT; } } + if (numbytes_to_recover == 0) { + PrintAndLogEx(INFO, "No bytes to recover, exiting"); + return PM3_ESOFT; + } + + loclass_thread_arg_t args[loclass_tc]; + // init thread arguments + for (int i = 0; i < loclass_tc; i++) { + args[i].thread_idx = i; + args[i].numbytes_to_recover = numbytes_to_recover; + args[i].endmask = 1 << 8 * numbytes_to_recover; + + memcpy((void*)&args[i].item, (void*)&item, sizeof(loclass_dumpdata_t)); + memcpy(args[i].bytes_to_recover, bytes_to_recover, sizeof(args[i].bytes_to_recover) ); + memcpy(args[i].key_index, key_index, sizeof(args[i].key_index) ); + memcpy(args[i].keytable, keytable, sizeof(args[i].keytable)); + } + + pthread_t threads[loclass_tc]; + // create threads + for (int i = 0; i < loclass_tc; i++) { + int res = pthread_create(&threads[i], NULL, bf_thread, (void *)&args[i]); + if (res) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "Failed to create pthreads. Quitting"); + return PM3_ESOFT; + } + } + // wait for threads to terminate: + void* ptrs[loclass_tc]; + for (int i = 0; i < loclass_tc; i++) + pthread_join(threads[i], &ptrs[i]); + + // was it a success? + int res = PM3_SUCCESS; + if (loclass_found == 0xFF) { + res = PM3_ESOFT; + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "Failed to recover %d bytes using the following CSN", numbytes_to_recover); + PrintAndLogEx(INFO, "CSN %s", sprint_hex(item.csn, 8)); + + //Before we exit, reset the 'BEING_CRACKED' to zero + for (uint8_t i = 0; i < numbytes_to_recover; i++) { + keytable[bytes_to_recover[i]] &= 0xFF; + keytable[bytes_to_recover[i]] |= LOCLASS_CRACK_FAILED; + } + + } else { + loclass_thread_ret_t ice = *((loclass_thread_ret_t *)ptrs[loclass_found]); + + for (uint8_t i = 0; i < numbytes_to_recover; i++) { + keytable[bytes_to_recover[i]] = ice.values[i]; + keytable[bytes_to_recover[i]] &= 0xFF; + keytable[bytes_to_recover[i]] |= LOCLASS_CRACKED; + } + for (uint8_t i = 0; i < loclass_tc; i++) { + free(ptrs[i]); + } + } + + memset(args, 0x00, sizeof(args)); + memset(threads, 0x00, sizeof(threads)); + return res; +} + +/** + * @brief Performs brute force attack against a dump-data item, containing csn, cc_nr and mac. + *This method calculates the hash1 for the CSN, and determines what bytes need to be bruteforced + *on the fly. If it finds that more than three bytes need to be bruteforced, it aborts. + *It updates the keytable with the findings, also using the upper half of the 16-bit ints + *to signal if the particular byte has been cracked or not. + * + * @param dump The dumpdata from iclass reader attack. + * @param keytable where to write found values. + * @return + */ +/* +int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { + + //Get the key index (hash1) + uint8_t key_index[8] = {0}; + hash1(item.csn, key_index); +*/ /* - *A uint32 has room for 4 bytes, we'll only need 24 of those bits to bruteforce up to three bytes, - */ - //uint32_t brute = startvalue; + * Determine which bytes to retrieve. A hash is typically + * 01010000454501 + * We go through that hash, and in the corresponding keytable, we put markers + * on what state that particular index is: + * - CRACKED (this has already been cracked) + * - BEING_CRACKED (this is being bruteforced now) + * - CRACK_FAILED (self-explaining...) + * + * The markers are placed in the high area of the 16 bit key-table. + * Only the lower eight bits correspond to the (hopefully cracked) key-value. + **/ + + +/* + uint8_t bytes_to_recover[3] = {0}; + uint8_t numbytes_to_recover = 0 ; + for (uint8_t i = 0; i < 8; i++) { + if (keytable[key_index[i]] & (LOCLASS_CRACKED | LOCLASS_BEING_CRACKED)) continue; + + bytes_to_recover[numbytes_to_recover++] = key_index[i]; + keytable[key_index[i]] |= LOCLASS_BEING_CRACKED; + + if (numbytes_to_recover > 3) { + PrintAndLogEx(FAILED, "The CSN requires > 3 byte bruteforce, not supported"); + PrintAndLogEx(INFO, "CSN %s", sprint_hex(item.csn, 8)); + PrintAndLogEx(INFO, "HASH1 %s", sprint_hex(key_index, 8)); + PrintAndLogEx(NORMAL, ""); + //Before we exit, reset the 'BEING_CRACKED' to zero + keytable[bytes_to_recover[0]] &= ~LOCLASS_BEING_CRACKED; + keytable[bytes_to_recover[1]] &= ~LOCLASS_BEING_CRACKED; + keytable[bytes_to_recover[2]] &= ~LOCLASS_BEING_CRACKED; + return PM3_ESOFT; + } + } + + uint8_t key_sel_p[8] = {0}; + uint8_t div_key[8] = {0}; + uint8_t key_sel[8] = {0}; + uint8_t calculated_MAC[4] = {0}; + + + //A uint32 has room for 4 bytes, we'll only need 24 of those bits to bruteforce up to three bytes, uint32_t brute = 0; +*/ /* Determine where to stop the bruteforce. A 1-byte attack stops after 256 tries, (when brute reaches 0x100). And so on... @@ -350,16 +578,17 @@ int bruteforceItem(dumpdata item, uint16_t keytable[]) { bytes_to_recover = 2 --> endmask = 0x000010000 bytes_to_recover = 3 --> endmask = 0x001000000 */ - +/* uint32_t endmask = 1 << 8 * numbytes_to_recover; PrintAndLogEx(NORMAL, "----------------------------"); - for (i = 0 ; i < numbytes_to_recover && numbytes_to_recover > 1; i++) - PrintAndLogEx(INFO, "Bruteforcing byte %d", bytes_to_recover[i]); + for (uint8_t i = 0 ; i < numbytes_to_recover && numbytes_to_recover > 1; i++) + PrintAndLogEx(INFO, "Bruteforcing %d", bytes_to_recover[i]); + bool found = false; while (!found && !(brute & endmask)) { //Update the keytable with the brute-values - for (i = 0; i < numbytes_to_recover; i++) { + for (uint8_t i = 0; i < numbytes_to_recover; i++) { keytable[bytes_to_recover[i]] &= 0xFF00; keytable[bytes_to_recover[i]] |= (brute >> (i * 8) & 0xFF); } @@ -376,16 +605,15 @@ int bruteforceItem(dumpdata item, uint16_t keytable[]) { //Permute from iclass format to standard format permutekey_rev(key_sel, key_sel_p); - //Diversify + diversifyKey(item.csn, key_sel_p, div_key); - //Calc mac doMAC(item.cc_nr, div_key, calculated_MAC); // success if (memcmp(calculated_MAC, item.mac, 4) == 0) { PrintAndLogEx(NORMAL, ""); - for (i = 0 ; i < numbytes_to_recover; i++) { - PrintAndLogEx(INFO, "%d: 0x%02x", bytes_to_recover[i], 0xFF & keytable[bytes_to_recover[i]]); + for (uint8_t i = 0 ; i < numbytes_to_recover; i++) { + PrintAndLogEx(SUCCESS, "%d: 0x%02x", bytes_to_recover[i], keytable[bytes_to_recover[i]] & 0xFF); } found = true; break; @@ -393,9 +621,7 @@ int bruteforceItem(dumpdata item, uint16_t keytable[]) { brute++; if ((brute & 0xFFFF) == 0) { - PrintAndLogEx(NORMAL, "%3d," NOLF, (brute >> 16) & 0xFF); - if (((brute >> 16) % 0x10) == 0) - PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INPLACE, "%3d", (brute >> 16) & 0xFF); } } @@ -408,19 +634,20 @@ int bruteforceItem(dumpdata item, uint16_t keytable[]) { errors = PM3_ESOFT; //Before we exit, reset the 'BEING_CRACKED' to zero - for (i = 0; i < numbytes_to_recover; i++) { + for (uint8_t i = 0; i < numbytes_to_recover; i++) { keytable[bytes_to_recover[i]] &= 0xFF; - keytable[bytes_to_recover[i]] |= CRACK_FAILED; + keytable[bytes_to_recover[i]] |= LOCLASS_CRACK_FAILED; } } else { //PrintAndLogEx(SUCCESS, "DES calcs: %u", brute); - for (i = 0; i < numbytes_to_recover; i++) { + for (uint8_t i = 0; i < numbytes_to_recover; i++) { keytable[bytes_to_recover[i]] &= 0xFF; - keytable[bytes_to_recover[i]] |= CRACKED; + keytable[bytes_to_recover[i]] |= LOCLASS_CRACKED; } } return errors; } +*/ /** * From dismantling iclass-paper: @@ -434,7 +661,7 @@ int bruteforceItem(dumpdata item, uint16_t keytable[]) { * @param master_key where to put the master key * @return 0 for ok, 1 for failz */ -int calculateMasterKey(uint8_t first16bytes[], uint64_t master_key[]) { +int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { mbedtls_des_context ctx_e; uint8_t z_0[8] = {0}; @@ -457,9 +684,14 @@ int calculateMasterKey(uint8_t first16bytes[], uint64_t master_key[]) { mbedtls_des_setkey_enc(&ctx_e, z_0_rev); mbedtls_des_crypt_ecb(&ctx_e, y_0, key64_negated); - int i; - for (i = 0; i < 8 ; i++) - key64[i] = ~key64_negated[i]; + key64[0] = ~key64_negated[0]; + key64[1] = ~key64_negated[1]; + key64[2] = ~key64_negated[2]; + key64[3] = ~key64_negated[3]; + key64[4] = ~key64_negated[4]; + key64[5] = ~key64_negated[5]; + key64[6] = ~key64_negated[6]; + key64[7] = ~key64_negated[7]; // Can we verify that the key is correct? // Once again, key is on iclass-format @@ -468,21 +700,20 @@ int calculateMasterKey(uint8_t first16bytes[], uint64_t master_key[]) { mbedtls_des_setkey_enc(&ctx_e, key64_stdformat); mbedtls_des_crypt_ecb(&ctx_e, key64_negated, result); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "-- High security custom key (Kcus) --"); - PrintAndLogEx(SUCCESS, "Standard format " _GREEN_("%s"), sprint_hex(key64_stdformat, 8)); - PrintAndLogEx(SUCCESS, "iClass format %s", sprint_hex(key64, 8)); - if (master_key != NULL) - memcpy(master_key, key64, 8); + if (kcus != NULL) + memcpy(kcus, key64, 8); - PrintAndLogEx(NORMAL, "\n"); if (memcmp(z_0, result, 4) != 0) { PrintAndLogEx(WARNING, _RED_("Failed to verify") " calculated master key (k_cus)! Something is wrong."); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, _GREEN_("Key verified ok!")); + PrintAndLogEx(SUCCESS, "----- " _CYAN_("High security custom key (Kcus)") " -----"); + PrintAndLogEx(SUCCESS, "Standard format %s", sprint_hex(key64_stdformat, 8)); + PrintAndLogEx(SUCCESS, "iCLASS format " _GREEN_("%s"), sprint_hex(key64, 8)); + PrintAndLogEx(SUCCESS, "Key verified (" _GREEN_("ok") ")"); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } /** @@ -494,25 +725,29 @@ int calculateMasterKey(uint8_t first16bytes[], uint64_t master_key[]) { */ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { uint8_t i; - size_t itemsize = sizeof(dumpdata); - uint64_t t1 = msclock(); - - dumpdata *attack = (dumpdata *) calloc(itemsize, sizeof(uint8_t)); + size_t itemsize = sizeof(loclass_dumpdata_t); + loclass_dumpdata_t *attack = (loclass_dumpdata_t *) calloc(itemsize, sizeof(uint8_t)); if (attack == NULL) { PrintAndLogEx(WARNING, "failed to allocate memory"); return PM3_EMALLOC; } + loclass_tc = num_CPUs(); + PrintAndLogEx(INFO, "bruteforce using " _YELLOW_("%zu") " threads", loclass_tc); + int res = 0; + + uint64_t t1 = msclock(); for (i = 0 ; i * itemsize < dumpsize ; i++) { memcpy(attack, dump + i * itemsize, itemsize); - res += bruteforceItem(*attack, keytable); + res = bruteforceItem(*attack, keytable); if (res != PM3_SUCCESS) break; } free(attack); t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds", t1 / 1000); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "time " _YELLOW_("%" PRIu64) " seconds", t1 / 1000); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "loclass exiting. Try run " _YELLOW_("`hf iclass sim -t 2`") " again and collect new data"); @@ -524,11 +759,10 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { // indicate crack-status. Those must be discarded for the // master key calculation uint8_t first16bytes[16] = {0}; - for (i = 0 ; i < 16 ; i++) { first16bytes[i] = keytable[i] & 0xFF; - if (!(keytable[i] & CRACKED)) { + if ((keytable[i] & LOCLASS_CRACKED) != LOCLASS_CRACKED) { PrintAndLogEx(WARNING, "Warning: we are missing byte %d, custom key calculation will fail...", i); return PM3_ESOFT; } @@ -620,7 +854,7 @@ static int _test_iclass_key_permutation(void) { return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Iclass key permutation (%s)", _GREEN_("OK")); + PrintAndLogEx(SUCCESS, " Iclass key permutation (%s)", _GREEN_("ok")); return PM3_SUCCESS; } @@ -640,8 +874,8 @@ static int _testHash1(void) { } int testElite(bool slowtests) { - PrintAndLogEx(INFO, "Testing iClass Elite functinality..."); - PrintAndLogEx(INFO, "Testing hash2"); + PrintAndLogEx(INFO, "Testing iClass Elite functionality"); + PrintAndLogEx(INFO, "Testing hash2..."); uint8_t k_cus[8] = {0x5B, 0x7C, 0x62, 0xC4, 0x91, 0xC1, 0x1B, 0x39}; /** @@ -661,20 +895,19 @@ int testElite(bool slowtests) { */ uint8_t keytable[128] = {0}; hash2(k_cus, keytable); - printarr_human_readable("Hash2", keytable, 128); + printarr_human_readable("---------------------- Hash2 ----------------------", keytable, sizeof(keytable)); if (keytable[3] == 0xA1 && keytable[0x30] == 0xA3 && keytable[0x6F] == 0x95) { - PrintAndLogEx(SUCCESS, " Hash2 (%s)", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, " hash2 (%s)", _GREEN_("ok")); } int res = PM3_SUCCESS; PrintAndLogEx(INFO, "Testing hash1..."); res += _testHash1(); - PrintAndLogEx(INFO, " hash1 (%s)", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " hash1 (%s)", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail") ); PrintAndLogEx(INFO, "Testing key diversification..."); res += _test_iclass_key_permutation(); - if (res == PM3_SUCCESS) - PrintAndLogEx(INFO, " key diversification (%s)", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " key diversification (%s)", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail") ); if (slowtests) res += _testBruteforce(); diff --git a/client/src/loclass/elite_crack.h b/client/src/loclass/elite_crack.h index a9de5dbd1..8f48dac8a 100644 --- a/client/src/loclass/elite_crack.h +++ b/client/src/loclass/elite_crack.h @@ -38,6 +38,22 @@ #ifndef ELITE_CRACK_H #define ELITE_CRACK_H + +//Crack status, see below +#define LOCLASS_CRACKED 0x0100 +#define LOCLASS_BEING_CRACKED 0x0200 +#define LOCLASS_CRACK_FAILED 0x0400 + +/** + This is how we expect each 'entry' in a dumpfile to look +**/ +typedef struct { + uint8_t csn[8]; + uint8_t cc_nr[12]; + uint8_t mac[4]; +} loclass_dumpdata_t; + + void permutekey(uint8_t key[8], uint8_t dest[8]); /** * Permutes a key from iclass specific format to NIST format @@ -46,10 +62,6 @@ void permutekey(uint8_t key[8], uint8_t dest[8]); * @param dest */ void permutekey_rev(uint8_t key[8], uint8_t dest[8]); -//Crack status, see below -#define CRACKED 0x0100 -#define BEING_CRACKED 0x0200 -#define CRACK_FAILED 0x0400 /** * Perform a bruteforce against a file which has been saved by pm3 @@ -69,7 +81,7 @@ int bruteforceFile(const char *filename, uint16_t keytable[]); */ int bruteforceFileNoKeys(const char *filename); /** - * @brief Same as bruteforcefile, but uses a an array of dumpdata instead + * @brief Same as bruteforcefile, but uses a an array of loclass_dumpdata_t instead * @param dump * @param dumpsize * @param keytable @@ -77,15 +89,6 @@ int bruteforceFileNoKeys(const char *filename); */ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]); -/** - This is how we expect each 'entry' in a dumpfile to look -**/ -typedef struct { - uint8_t csn[8]; - uint8_t cc_nr[12]; - uint8_t mac[4]; -} dumpdata; - /** * @brief Performs brute force attack against a dump-data item, containing csn, cc_nr and mac. *This method calculates the hash1 for the CSN, and determines what bytes need to be bruteforced @@ -93,11 +96,11 @@ typedef struct { *It updates the keytable with the findings, also using the upper half of the 16-bit ints *to signal if the particular byte has been cracked or not. * - * @param dump The dumpdata from iclass reader attack. + * @param loclass_dumpdata_t The dumpdata from iclass reader attack. * @param keytable where to write found values. * @return */ -int bruteforceItem(dumpdata item, uint16_t keytable[]); +int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]); /** * Hash1 takes CSN as input, and determines what bytes in the keytable will be used * when constructing the K_sel. @@ -118,7 +121,7 @@ void hash2(uint8_t *key64, uint8_t *outp_keytable); * @param master_key where to put the master key * @return 0 for ok, 1 for failz */ -int calculateMasterKey(uint8_t first16bytes[], uint64_t master_key[]); +int calculateMasterKey(uint8_t first16bytes[], uint8_t master_key[]); /** * @brief Test function @@ -141,5 +144,4 @@ int testElite(bool slowtests); **/ - #endif diff --git a/client/src/loclass/ikeys.c b/client/src/loclass/ikeys.c index 181f23db1..38adc04b0 100644 --- a/client/src/loclass/ikeys.c +++ b/client/src/loclass/ikeys.c @@ -70,7 +70,7 @@ From "Dismantling iclass": #include "fileutils.h" #include "cipherutils.h" -#include "des.h" +#include "mbedtls/des.h" uint8_t pi[35] = { 0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, @@ -80,9 +80,6 @@ uint8_t pi[35] = { 0x72, 0x74, 0x78 }; -static mbedtls_des_context ctx_enc; -static mbedtls_des_context ctx_dec; - /** * @brief The key diversification algorithm uses 6-bit bytes. * This implementation uses 64 bit uint to pack seven of them into one @@ -94,16 +91,19 @@ static mbedtls_des_context ctx_dec; * @param n bitnumber * @return */ -static uint8_t getSixBitByte(uint64_t c, int n) { +#define getSixBitByte(c, n) ((uint8_t)(((c) >> (42 - 6 * (n))) & 0x3F)) +/* +static inline uint8_t getSixBitByte(uint64_t c, int n) { return (c >> (42 - 6 * n)) & 0x3F; } - +*/ /** * @brief Puts back a six-bit 'byte' into a uint64_t. * @param c buffer * @param z the value to place there * @param n bitnumber. */ + static void pushbackSixBitByte(uint64_t *c, uint8_t z, int n) { //0x XXXX YYYY ZZZZ ZZZZ ZZZZ // ^z0 ^z7 @@ -210,14 +210,14 @@ static void permute(BitstreamIn *p_in, uint64_t z, int l, int r, BitstreamOut *o return; bool pn = tailBit(p_in); - if (pn) { // pn = 1 + if (pn) { + // pn = 1 uint8_t zl = getSixBitByte(z, l); - push6bits(out, zl + 1); permute(p_in, z, l + 1, r, out); - } else { // otherwise + } else { + // otherwise uint8_t zr = getSixBitByte(z, r); - push6bits(out, zr); permute(p_in, z, l, r + 1, out); } @@ -226,6 +226,7 @@ static void permute(BitstreamIn *p_in, uint64_t z, int l, int r, BitstreamOut *o static void printState(const char *desc, uint64_t c) { if (g_debugMode == 0) return; + char s[60] = {0}; snprintf(s, sizeof(s), "%s : ", desc); @@ -254,8 +255,10 @@ static void printState(const char *desc, uint64_t c) { void hash0(uint64_t c, uint8_t k[8]) { c = swapZvalues(c); + if (g_debugMode > 0) { PrintAndLogEx(DEBUG, " | x| y|z0|z1|z2|z3|z4|z5|z6|z7|"); printState("origin", c); + } //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] // x = 8 bits // y = 8 bits @@ -266,9 +269,7 @@ void hash0(uint64_t c, uint8_t k[8]) { for (int n = 0; n < 4 ; n++) { uint8_t zn = getSixBitByte(c, n); - uint8_t zn4 = getSixBitByte(c, n + 4); - uint8_t _zn = (zn % (63 - n)) + n; uint8_t _zn4 = (zn4 % (64 - n)) + n; @@ -276,17 +277,18 @@ void hash0(uint64_t c, uint8_t k[8]) { pushbackSixBitByte(&zP, _zn4, n + 4); } - printState("0|0|z'", zP); + if (g_debugMode > 0) printState("0|0|z'", zP); uint64_t zCaret = check(zP); - printState("0|0|z^", zP); + + if (g_debugMode > 0) printState("0|0|z^", zP); uint8_t p = pi[x % 35]; if (x & 1) //Check if x7 is 1 p = ~p; - PrintAndLogEx(DEBUG, "p: %02x", p); + if (g_debugMode > 0) PrintAndLogEx(DEBUG, " p : %02x", p); BitstreamIn p_in = { &p, 8, 0 }; uint8_t outbuffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; @@ -301,9 +303,8 @@ void hash0(uint64_t c, uint8_t k[8]) { zTilde >>= 16; - printState("0|0|z~", zTilde); + if (g_debugMode > 0) printState("0|0|z~", zTilde); -// int zerocounter = 0 ; for (int i = 0; i < 8; i++) { // the key on index i is first a bit from y // then six bits from z, @@ -317,8 +318,6 @@ void hash0(uint64_t c, uint8_t k[8]) { // First, place y(7-i) leftmost in k k[i] |= (y << (7 - i)) & 0x80 ; - - uint8_t zTilde_i = getSixBitByte(zTilde, i); // zTildeI is now on the form 00XXXXXX // with one leftshift, it'll be @@ -342,9 +341,6 @@ void hash0(uint64_t c, uint8_t k[8]) { k[i] |= zTilde_i & 0x7E; k[i] |= (~p_i) & 1; } -// if ((k[i] & 1) == 0) { -// zerocounter++; -// } } } /** @@ -354,18 +350,17 @@ void hash0(uint64_t c, uint8_t k[8]) { * @param div_key */ void diversifyKey(uint8_t *csn, uint8_t *key, uint8_t *div_key) { - // Prepare the DES key - mbedtls_des_setkey_enc(&ctx_enc, key); uint8_t crypted_csn[8] = {0}; // Calculate DES(CSN, KEY) + mbedtls_des_context ctx_enc; + mbedtls_des_setkey_enc(&ctx_enc, key); mbedtls_des_crypt_ecb(&ctx_enc, csn, crypted_csn); + mbedtls_des_free(&ctx_enc); //Calculate HASH0(DES)) uint64_t c_csn = x_bytes_to_num(crypted_csn, sizeof(crypted_csn)); - //uint64_t crypted_csn_swapped = swapZvalues(crypt_csn); - hash0(c_csn, div_key); } /* @@ -420,28 +415,38 @@ typedef struct { uint8_t uid[8]; uint8_t t_key[8]; uint8_t div_key[8]; -} Testcase; +} testcase_t; -static int testDES(Testcase testcase) { +static int testDES(uint8_t *key, testcase_t testcase) { uint8_t des_encrypted_csn[8] = {0}; uint8_t decrypted[8] = {0}; uint8_t div_key[8] = {0}; + + mbedtls_des_context ctx_enc; + mbedtls_des_context ctx_dec; + + mbedtls_des_setkey_enc(&ctx_enc, key); + mbedtls_des_setkey_dec(&ctx_dec, key); + int retval = mbedtls_des_crypt_ecb(&ctx_enc, testcase.uid, des_encrypted_csn); retval |= mbedtls_des_crypt_ecb(&ctx_dec, des_encrypted_csn, decrypted); + mbedtls_des_free(&ctx_enc); + mbedtls_des_free(&ctx_dec); + if (memcmp(testcase.uid, decrypted, 8) != 0) { //Decryption fail PrintAndLogEx(FAILED, "Encryption <-> Decryption FAIL"); - printarr("Input", testcase.uid, 8); - printarr("Decrypted", decrypted, 8); + printarr(" input", testcase.uid, 8); + printarr(" decrypted", decrypted, 8); retval = 1; } if (memcmp(des_encrypted_csn, testcase.t_key, 8) != 0) { //Encryption fail PrintAndLogEx(FAILED, "Encryption != Expected result"); - printarr("Output", des_encrypted_csn, 8); - printarr("Expected", testcase.t_key, 8); + printarr(" output", des_encrypted_csn, 8); + printarr(" expected", testcase.t_key, 8); retval = 1; } uint64_t crypted_csn = x_bytes_to_num(des_encrypted_csn, 8); @@ -453,7 +458,7 @@ static int testDES(Testcase testcase) { printarr(" csn ", testcase.uid, 8); printarr("{csn} ", des_encrypted_csn, 8); printarr("hash0 ", div_key, 8); - printarr("Expected", testcase.div_key, 8); + printarr(" expected", testcase.div_key, 8); retval = 1; } return retval; @@ -461,9 +466,12 @@ static int testDES(Testcase testcase) { static bool des_getParityBitFromKey(uint8_t key) { // The top 7 bits is used bool parity = ((key & 0x80) >> 7) - ^ ((key & 0x40) >> 6) ^ ((key & 0x20) >> 5) - ^ ((key & 0x10) >> 4) ^ ((key & 0x08) >> 3) - ^ ((key & 0x04) >> 2) ^ ((key & 0x02) >> 1); + ^ ((key & 0x40) >> 6) + ^ ((key & 0x20) >> 5) + ^ ((key & 0x10) >> 4) + ^ ((key & 0x08) >> 3) + ^ ((key & 0x04) >> 2) + ^ ((key & 0x02) >> 1); return !parity; } @@ -480,11 +488,11 @@ static void des_checkParity(uint8_t *key) { if (fails) { PrintAndLogEx(FAILED, "parity fails: %d", fails); } else { - PrintAndLogEx(SUCCESS, "Key syntax is with parity bits inside each byte"); + PrintAndLogEx(SUCCESS, " Key syntax is with parity bits inside each byte (%s)", _GREEN_("ok")); } } -Testcase testcases[] = { +testcase_t testcases[] = { {{0x8B, 0xAC, 0x60, 0x1F, 0x53, 0xB8, 0xED, 0x11}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x02, 0x04, 0x06, 0x08, 0x01, 0x03, 0x05, 0x07}}, {{0xAE, 0x51, 0xE5, 0x62, 0xE7, 0x9A, 0x99, 0x39}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, {0x04, 0x02, 0x06, 0x08, 0x01, 0x03, 0x05, 0x07}}, @@ -555,14 +563,14 @@ Testcase testcases[] = { {{0}, {0}, {0}} }; -static int testKeyDiversificationWithMasterkeyTestcases(void) { +static int testKeyDiversificationWithMasterkeyTestcases(uint8_t *key) { int i, error = 0; uint8_t empty[8] = {0}; PrintAndLogEx(INFO, "Testing encryption/decryption"); for (i = 0; memcmp(testcases + i, empty, 8); i++) - error += testDES(testcases[i]); + error += testDES(key, testcases[i]); if (error) PrintAndLogEx(FAILED, "%d errors occurred (%d testcases)", error, i); @@ -581,7 +589,7 @@ static int testCryptedCSN(uint64_t crypted_csn, uint64_t expected) { PrintAndLogEx(DEBUG, ""); PrintAndLogEx(DEBUG, " {csn} %"PRIx64, crypted_csn); PrintAndLogEx(DEBUG, " {csn-revz} %"PRIx64, crypted_csn_swapped); - PrintAndLogEx(DEBUG, " hash0 %"PRIx64 " (%s)", resultbyte, (resultbyte == expected) ? _GREEN_("OK") : _RED_("FAIL")); + PrintAndLogEx(DEBUG, " hash0 %"PRIx64 " (%s)", resultbyte, (resultbyte == expected) ? _GREEN_("ok") : _RED_("fail")); if (resultbyte != expected) { PrintAndLogEx(DEBUG, " expected " _YELLOW_("%"PRIx64), expected); @@ -590,19 +598,22 @@ static int testCryptedCSN(uint64_t crypted_csn, uint64_t expected) { return PM3_SUCCESS; } -static int testDES2(uint64_t csn, uint64_t expected) { +static int testDES2(uint8_t *key, uint64_t csn, uint64_t expected) { uint8_t result[8] = {0}; uint8_t input[8] = {0}; PrintAndLogEx(DEBUG, " csn %"PRIx64, csn); x_num_to_bytes(csn, 8, input); + mbedtls_des_context ctx_enc; + mbedtls_des_setkey_enc(&ctx_enc, key); mbedtls_des_crypt_ecb(&ctx_enc, input, result); + mbedtls_des_free(&ctx_enc); uint64_t crypt_csn = x_bytes_to_num(result, 8); PrintAndLogEx(DEBUG, " {csn} %"PRIx64, crypt_csn); - PrintAndLogEx(DEBUG, " expected %"PRIx64 " (%s)", expected, (expected == crypt_csn) ? _GREEN_("OK") : _RED_("FAIL")); + PrintAndLogEx(DEBUG, " expected %"PRIx64 " (%s)", expected, (expected == crypt_csn) ? _GREEN_("ok") : _RED_("fail") ); if (expected != crypt_csn) return PM3_ESOFT; @@ -619,8 +630,7 @@ static int doTestsWithKnownInputs(void) { PrintAndLogEx(INFO, "Testing DES encryption"); uint8_t key[8] = {0x6c, 0x8d, 0x44, 0xf9, 0x2a, 0x2d, 0x01, 0xbf}; - mbedtls_des_setkey_enc(&ctx_enc, key); - testDES2(0xbbbbaaaabbbbeeee, 0xd6ad3ca619659e6b); + testDES2(key, 0xbbbbaaaabbbbeeee, 0xd6ad3ca619659e6b); PrintAndLogEx(INFO, "Testing hashing algorithm"); @@ -681,11 +691,10 @@ int doKeyTests(void) { PrintAndLogEx(SUCCESS, "Key present"); PrintAndLogEx(SUCCESS, "Checking key parity..."); des_checkParity(key); - mbedtls_des_setkey_enc(&ctx_enc, key); - mbedtls_des_setkey_dec(&ctx_dec, key); + // Test hashing functions PrintAndLogEx(SUCCESS, "The following tests require the correct 8-byte master key"); - testKeyDiversificationWithMasterkeyTestcases(); + testKeyDiversificationWithMasterkeyTestcases(key); } } PrintAndLogEx(INFO, "Testing key diversification with non-sensitive keys..."); diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index de3a50c5e..6edfc80ea 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -433,7 +433,7 @@ while true; do "Paradox - ID: 004209dea FC: 96 Card: 40426, Checksum: b2, Raw: 0f55555695596a6a9999a59a"; then break; fi if ! CheckExecute slow "lf T55 presco test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_presco.pm3; lf search 1'" "Presco ID found"; then break; fi if ! CheckExecute slow "lf T55 presco test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_presco.pm3; lf presco demod'" \ - "Presco - Card: 1E8021D9, Raw: 10D0000000000000000000001E8021D9"; then break; fi + "Presco Site code: 30 User code: 8665 Full code: 1E8021D9 Raw: 10D0000000000000000000001E8021D9"; then break; fi if ! CheckExecute slow "lf T55 pyramid test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_pyramid.pm3; lf search 1'" "Pyramid ID found"; then break; fi if ! CheckExecute slow "lf T55 pyramid test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_pyramid.pm3; lf pyramid demod'" \ "Pyramid - len: 26, FC: 123 Card: 11223 - Wiegand: 2f657ae, Raw: 00010101010101010101016eb35e5da4"; then break; fi @@ -450,7 +450,7 @@ while true; do echo -e "\n${C_BLUE}Testing HF:${C_NC}" if ! CheckExecute "hf mf offline text" "$CLIENTBIN -c 'hf mf'" "at_enc"; then break; fi if ! CheckExecute slow retry ignore "hf mf hardnested long test" "$CLIENTBIN -c 'hf mf hardnested t 1 000000000000'" "found:"; then break; fi - if ! CheckExecute slow "hf iclass long test" "$CLIENTBIN -c 'hf iclass loclass --long'" "verified ok"; then break; fi + if ! CheckExecute slow "hf iclass long test" "$CLIENTBIN -c 'hf iclass loclass --long'" "verified (ok)"; then break; fi if ! CheckExecute slow "emv long test" "$CLIENTBIN -c 'emv test -l'" "Test(s) \[ ok"; then break; fi if ! $SLOWTESTS; then if ! CheckExecute "hf iclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "key diversification (ok)"; then break; fi