iclass chk / lookup / loclass - speedups

This commit is contained in:
iceman1001 2020-11-28 13:46:16 +01:00
commit 4bb63577c6
5 changed files with 543 additions and 189 deletions

View file

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

View file

@ -39,13 +39,14 @@
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#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 [i1] )
rk(key64, i, temp_output);
//y [i] = DES enc (rk(K cus , i), y [i1] )
@ -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};
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;
}
}
/*
*A uint32 has room for 4 bytes, we'll only need 24 of those bits to bruteforce up to three bytes,
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
*/
//uint32_t brute = startvalue;
/*
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);
*/
/*
* 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();

View file

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

View file

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

View file

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