From: pwpiwi Date: Thu, 23 Mar 2017 17:06:14 +0000 (+0100) Subject: Deduplicate mfkey32 and mfkey64 X-Git-Tag: v3.0.0~37^2~1 X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/commitdiff_plain/4cb4b588c276f8d3c3b4a18dc292d3bfd0e497b9 Deduplicate mfkey32 and mfkey64 - rename client/nonce2key.[ch] to mfkey.[ch] - leave only main() wrapper in tools/mfkey - add mfkey32 and mfkey64 to .gitignore --- diff --git a/.gitignore b/.gitignore index 4af73fb1..d08c67b8 100644 --- a/.gitignore +++ b/.gitignore @@ -14,14 +14,16 @@ *.dll *.moc.cpp *.z +version.c + *.exe -proxmark proxmark3 flasher -version.c lua luac fpga_compress +mfkey32 +mfkey64 fpga/* !fpga/tests @@ -34,5 +36,3 @@ fpga/* !fpga/xst_hf.scr !fpga/go.bat !fpga/sim.tcl - - diff --git a/client/Makefile b/client/Makefile index ac7e6f98..1df870b5 100644 --- a/client/Makefile +++ b/client/Makefile @@ -58,7 +58,7 @@ CORESRCS = uart.c \ CMDSRCS = crapto1/crapto1.c\ crapto1/crypto1.c\ - nonce2key.c\ + mfkey.c\ loclass/cipher.c \ loclass/cipherutils.c \ loclass/des.c \ diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 47660a6e..7a6aaa3b 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -19,7 +19,7 @@ #include "ui.h" #include "mifarehost.h" #include "mifare.h" -#include "nonce2key.h" +#include "mfkey.h" #define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up diff --git a/client/mfkey.c b/client/mfkey.c new file mode 100644 index 00000000..60cfcbbf --- /dev/null +++ b/client/mfkey.c @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// 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 "mfkey.h" + +#include "crapto1/crapto1.h" + + +// recover key from 2 different reader responses on same tag challenge +bool mfkey32(nonces_t data, uint64_t *outputkey) { + struct Crypto1State *s,*t; + uint64_t outkey = 0; + uint64_t key = 0; // recovered key + bool isSuccess = false; + uint8_t counter = 0; + + s = lfsr_recovery32(data.ar ^ prng_successor(data.nonce, 64), 0); + + for(t = s; t->odd | t->even; ++t) { + lfsr_rollback_word(t, 0, 0); + lfsr_rollback_word(t, data.nr, 1); + lfsr_rollback_word(t, data.cuid ^ data.nonce, 0); + crypto1_get_lfsr(t, &key); + crypto1_word(t, data.cuid ^ data.nonce, 0); + crypto1_word(t, data.nr2, 1); + if (data.ar2 == (crypto1_word(t, 0, 0) ^ prng_successor(data.nonce, 64))) { + //PrintAndLog("Found Key: [%012" PRIx64 "]",key); + outkey = key; + counter++; + if (counter == 20) break; + } + } + isSuccess = (counter == 1); + *outputkey = ( isSuccess ) ? outkey : 0; + crypto1_destroy(s); + /* //un-comment to save all keys to a stats.txt file + FILE *fout; + if ((fout = fopen("stats.txt","ab")) == NULL) { + PrintAndLog("Could not create file name stats.txt"); + return 1; + } + fprintf(fout, "mfkey32,%d,%08x,%d,%s,%04x%08x,%.0Lf\r\n", counter, data.cuid, data.sector, (data.keytype) ? "B" : "A", (uint32_t)(outkey>>32) & 0xFFFF,(uint32_t)(outkey&0xFFFFFFFF),(long double)t1); + fclose(fout); + */ + return isSuccess; +} + +// recover key from 2 reader responses on 2 different tag challenges +bool mfkey32_moebius(nonces_t data, uint64_t *outputkey) { + struct Crypto1State *s, *t; + uint64_t outkey = 0; + uint64_t key = 0; // recovered key + bool isSuccess = false; + int counter = 0; + + s = lfsr_recovery32(data.ar ^ prng_successor(data.nonce, 64), 0); + + for(t = s; t->odd | t->even; ++t) { + lfsr_rollback_word(t, 0, 0); + lfsr_rollback_word(t, data.nr, 1); + lfsr_rollback_word(t, data.cuid ^ data.nonce, 0); + crypto1_get_lfsr(t, &key); + + crypto1_word(t, data.cuid ^ data.nonce2, 0); + crypto1_word(t, data.nr2, 1); + if (data.ar2 == (crypto1_word(t, 0, 0) ^ prng_successor(data.nonce2, 64))) { + //PrintAndLog("Found Key: [%012" PRIx64 "]",key); + outkey=key; + ++counter; + if (counter==20) + break; + } + } + isSuccess = (counter == 1); + *outputkey = ( isSuccess ) ? outkey : 0; + crypto1_destroy(s); + /* // un-comment to output all keys to stats.txt + FILE *fout; + if ((fout = fopen("stats.txt","ab")) == NULL) { + PrintAndLog("Could not create file name stats.txt"); + return 1; + } + fprintf(fout, "moebius,%d,%08x,%d,%s,%04x%08x,%0.Lf\r\n", counter, data.cuid, data.sector, (data.keytype) ? "B" : "A", (uint32_t) (outkey>>32),(uint32_t)(outkey&0xFFFFFFFF),(long double)t1); + fclose(fout); + */ + return isSuccess; +} + +// recover key from reader response and tag response of one authentication sequence +int mfkey64(nonces_t data, uint64_t *outputkey){ + uint64_t key = 0; // recovered key + uint32_t ks2; // keystream used to encrypt reader response + uint32_t ks3; // keystream used to encrypt tag response + struct Crypto1State *revstate; + + // Extract the keystream from the messages + ks2 = data.ar ^ prng_successor(data.nonce, 64); + ks3 = data.at ^ prng_successor(data.nonce, 96); + revstate = lfsr_recovery64(ks2, ks3); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, data.nr, 1); + lfsr_rollback_word(revstate, data.cuid ^ data.nonce, 0); + crypto1_get_lfsr(revstate, &key); + // PrintAndLog("Found Key: [%012" PRIx64 "]", key); + crypto1_destroy(revstate); + *outputkey = key; + + return 0; +} + + diff --git a/client/mfkey.h b/client/mfkey.h new file mode 100644 index 00000000..ad5a863d --- /dev/null +++ b/client/mfkey.h @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// 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 +//----------------------------------------------------------------------------- + +#ifndef MFKEY_H +#define MFKEY_H + +#include +#include + +typedef struct { + uint32_t cuid; + uint8_t sector; + uint8_t keytype; + uint32_t nonce; + uint32_t ar; + uint32_t nr; + uint32_t at; + uint32_t nonce2; + uint32_t ar2; + uint32_t nr2; + } nonces_t; + +extern bool mfkey32(nonces_t data, uint64_t *outputkey); +extern bool mfkey32_moebius(nonces_t data, uint64_t *outputkey); +extern int mfkey64(nonces_t data, uint64_t *outputkey); + +#endif diff --git a/client/mifarehost.c b/client/mifarehost.c index cbd79cf7..c6c6877e 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -8,6 +8,8 @@ // mifare commands //----------------------------------------------------------------------------- +#include "mifarehost.h" + #include #include #include @@ -20,7 +22,6 @@ #include "ui.h" #include "util.h" #include "iso14443crc.h" -#include "mifarehost.h" // mifare tracer flags used in mfTraceDecode() #define TRACE_IDLE 0x00 diff --git a/client/mifarehost.h b/client/mifarehost.h index a9db4ec3..c5644442 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -8,6 +8,9 @@ // High frequency ISO14443A commands //----------------------------------------------------------------------------- +#ifndef MIFAREHOST_H +#define MIFAREHOST_H + #include #include #include "data.h" @@ -42,3 +45,5 @@ extern int isBlockTrailer(int blockN); extern int loadTraceCard(uint8_t *tuid); extern int saveTraceCard(void); extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); + +#endif diff --git a/client/nonce2key.c b/client/nonce2key.c deleted file mode 100644 index acd551e4..00000000 --- a/client/nonce2key.c +++ /dev/null @@ -1,137 +0,0 @@ -//----------------------------------------------------------------------------- -// 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 -#include -#include -#include "mifarehost.h" -#include "util.h" -#include "crapto1/crapto1.h" - -// recover key from 2 different reader responses on same tag challenge -bool mfkey32(nonces_t data, uint64_t *outputkey) { - struct Crypto1State *s,*t; - uint64_t outkey = 0; - uint64_t key = 0; // recovered key - bool isSuccess = false; - uint8_t counter = 0; - - uint64_t t1 = msclock(); - - s = lfsr_recovery32(data.ar ^ prng_successor(data.nonce, 64), 0); - - for(t = s; t->odd | t->even; ++t) { - lfsr_rollback_word(t, 0, 0); - lfsr_rollback_word(t, data.nr, 1); - lfsr_rollback_word(t, data.cuid ^ data.nonce, 0); - crypto1_get_lfsr(t, &key); - crypto1_word(t, data.cuid ^ data.nonce, 0); - crypto1_word(t, data.nr2, 1); - if (data.ar2 == (crypto1_word(t, 0, 0) ^ prng_successor(data.nonce, 64))) { - //PrintAndLog("Found Key: [%012" PRIx64 "]",key); - outkey = key; - counter++; - if (counter == 20) break; - } - } - isSuccess = (counter == 1); - t1 = msclock() - t1; - //if ( t1 > 0 ) PrintAndLog("Time in mfkey32: %.1f seconds \nFound %d possible keys", (float)t1/1000.0, counter); - *outputkey = ( isSuccess ) ? outkey : 0; - crypto1_destroy(s); - /* //un-comment to save all keys to a stats.txt file - FILE *fout; - if ((fout = fopen("stats.txt","ab")) == NULL) { - PrintAndLog("Could not create file name stats.txt"); - return 1; - } - fprintf(fout, "mfkey32,%d,%08x,%d,%s,%04x%08x,%.0Lf\r\n", counter, data.cuid, data.sector, (data.keytype) ? "B" : "A", (uint32_t)(outkey>>32) & 0xFFFF,(uint32_t)(outkey&0xFFFFFFFF),(long double)t1); - fclose(fout); - */ - return isSuccess; -} - -// recover key from 2 reader responses on 2 different tag challenges -bool mfkey32_moebius(nonces_t data, uint64_t *outputkey) { - struct Crypto1State *s, *t; - uint64_t outkey = 0; - uint64_t key = 0; // recovered key - bool isSuccess = false; - int counter = 0; - - //PrintAndLog("Enter mfkey32_moebius"); - uint64_t t1 = msclock(); - - s = lfsr_recovery32(data.ar ^ prng_successor(data.nonce, 64), 0); - - for(t = s; t->odd | t->even; ++t) { - lfsr_rollback_word(t, 0, 0); - lfsr_rollback_word(t, data.nr, 1); - lfsr_rollback_word(t, data.cuid ^ data.nonce, 0); - crypto1_get_lfsr(t, &key); - - crypto1_word(t, data.cuid ^ data.nonce2, 0); - crypto1_word(t, data.nr2, 1); - if (data.ar2 == (crypto1_word(t, 0, 0) ^ prng_successor(data.nonce2, 64))) { - //PrintAndLog("Found Key: [%012" PRIx64 "]",key); - outkey=key; - ++counter; - if (counter==20) - break; - } - } - isSuccess = (counter == 1); - t1 = msclock() - t1; - // PrintAndLog("Time in mfkey32_moebius: %.1f seconds \nFound %d possible keys", (float)t1/1000.0, counter); - *outputkey = ( isSuccess ) ? outkey : 0; - crypto1_destroy(s); - /* // un-comment to output all keys to stats.txt - FILE *fout; - if ((fout = fopen("stats.txt","ab")) == NULL) { - PrintAndLog("Could not create file name stats.txt"); - return 1; - } - fprintf(fout, "moebius,%d,%08x,%d,%s,%04x%08x,%0.Lf\r\n", counter, data.cuid, data.sector, (data.keytype) ? "B" : "A", (uint32_t) (outkey>>32),(uint32_t)(outkey&0xFFFFFFFF),(long double)t1); - fclose(fout); - */ - return isSuccess; -} - -// recover key from reader response and tag response of one authentication sequence -int mfkey64(nonces_t data, uint64_t *outputkey){ - uint64_t key = 0; // recovered key - uint32_t ks2; // keystream used to encrypt reader response - uint32_t ks3; // keystream used to encrypt tag response - struct Crypto1State *revstate; - - // PrintAndLog("Enter mfkey64"); - uint64_t t1 = msclock(); - - // Extract the keystream from the messages - ks2 = data.ar ^ prng_successor(data.nonce, 64); - ks3 = data.at ^ prng_successor(data.nonce, 96); - revstate = lfsr_recovery64(ks2, ks3); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, data.nr, 1); - lfsr_rollback_word(revstate, data.cuid ^ data.nonce, 0); - crypto1_get_lfsr(revstate, &key); - // PrintAndLog("Found Key: [%012" PRIx64 "]", key); - crypto1_destroy(revstate); - *outputkey = key; - - t1 = msclock() - t1; - // PrintAndLog("Time in mfkey64: %.1f seconds \n", (float)t1/1000.0); - return 0; -} diff --git a/client/nonce2key.h b/client/nonce2key.h deleted file mode 100644 index 795f239d..00000000 --- a/client/nonce2key.h +++ /dev/null @@ -1,36 +0,0 @@ -//----------------------------------------------------------------------------- -// 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 -//----------------------------------------------------------------------------- - -#ifndef __NONCE2KEY_H -#define __NONCE2KEY_H - -#include -#include - -typedef struct { - uint32_t cuid; - uint8_t sector; - uint8_t keytype; - uint32_t nonce; - uint32_t ar; - uint32_t nr; - uint32_t at; - uint32_t nonce2; - uint32_t ar2; - uint32_t nr2; - } nonces_t; - -bool mfkey32(nonces_t data, uint64_t *outputkey); -bool mfkey32_moebius(nonces_t data, uint64_t *outputkey); -int mfkey64(nonces_t data, uint64_t *outputkey); - -#endif diff --git a/common/crapto1/readme b/common/crapto1/readme new file mode 100644 index 00000000..d57fa3e0 --- /dev/null +++ b/common/crapto1/readme @@ -0,0 +1,27 @@ +CRAPTO1 +------- + Provides a set of library functions which aid the verification + of crypto1 weaknesses. + + In short a partial implementation of: + Dismantling MIFARE Classic + URL: http://www.sos.cs.ru.nl/applications/rfid/2008-esorics.pdf + Flavio D. Garcia, Gerhard de Koning Gans, Ruben Muijrers, + Peter van Rossum, Roel Verdult, Ronny Wichers Schreur, Bart Jacobs + Institute for Computing and Information Sciences, + Radboud University Nijmegen, The Netherlands + {{flaviog,petervr,ronny,bart}@cs, {gkoningg,rmuijrer,rverdult}@sci}.ru.nl + and + Wirelessly Pickpocketing a Mifare Classic Card + URL: http://www.cs.ru.nl/~flaviog/publications/Pickpocketing.Mifare.pdf + Flavio D. Garcia, Peter van Rossum, Roel Verdult, Ronny Wichers Schreur + Radboud University Nijmegen, The Netherlands + {flaviog,petervr,rverdult,ronny}@cs.ru.nl + and + THE DARK SIDE OF SECURITY BY OBSCURITY + URL: http://eprint.iacr.org/2009/137 + and Cloning MiFare Classic Rail and Building Passes, Anywhere, Anytime + Nicolas T. Courtois + University College London, Computer Science, + Gower street, WC1E 6BT, London, UK + diff --git a/tools/mfkey/Makefile b/tools/mfkey/Makefile index 73ce032f..da7d431a 100755 --- a/tools/mfkey/Makefile +++ b/tools/mfkey/Makefile @@ -1,10 +1,10 @@ -VPATH = ../../common/crapto1 +VPATH = ../../common/crapto1 ../../client CC = gcc LD = gcc -CFLAGS = -I../../common -Wall -O4 +CFLAGS = -I../../common -I../../client -Wall -O4 LDFLAGS = -OBJS = crypto1.o crapto1.o +OBJS = crypto1.o crapto1.o util.o mfkey.o EXES = mfkey32 mfkey64 WINEXES = $(patsubst %, %.exe, $(EXES)) @@ -13,7 +13,7 @@ all: $(OBJS) $(EXES) %.o : %.c $(CC) $(CFLAGS) -c -o $@ $< -% : %.c +% : %.c $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $< clean: diff --git a/tools/mfkey/mfkey32.c b/tools/mfkey/mfkey32.c index a018c5df..0b62f051 100755 --- a/tools/mfkey/mfkey32.c +++ b/tools/mfkey/mfkey32.c @@ -1,67 +1,77 @@ #include -#include "crapto1/crapto1.h" +#include #include #include +#include "crapto1/crapto1.h" +#include "mfkey.h" +#include "util.h" +// 32 bit recover key from 2 nonces +#include int main (int argc, char *argv[]) { - struct Crypto1State *s,*t; - uint64_t key; // recovered key - uint32_t uid; // serial number - uint32_t nt; // tag challenge - uint32_t nr0_enc; // first encrypted reader challenge - uint32_t ar0_enc; // first encrypted reader response - uint32_t nr1_enc; // second encrypted reader challenge - uint32_t ar1_enc; // second encrypted reader response + + nonces_t data; uint32_t ks2; // keystream used to encrypt reader response + uint64_t key; // recovered key - printf("MIFARE Classic key recovery - based 32 bits of keystream\n"); + printf("MIFARE Classic key recovery - based on 32 bits of keystream\n"); printf("Recover key from two 32-bit reader authentication answers only!\n\n"); - if (argc < 7) { - printf(" syntax: %s <{nr_0}> <{ar_0}> <{nr_1}> <{ar_1}>\n\n",argv[0]); + if (argc != 7 && argc != 8) { + printf(" syntax: %s <{nr_0}> <{ar_0}> [] <{nr_1}> <{ar_1}>\n", argv[0]); + printf(" (you may omit nt1 if it is equal to nt0)\n\n"); return 1; } - sscanf(argv[1],"%x",&uid); - sscanf(argv[2],"%x",&nt); - sscanf(argv[3],"%x",&nr0_enc); - sscanf(argv[4],"%x",&ar0_enc); - sscanf(argv[5],"%x",&nr1_enc); - sscanf(argv[6],"%x",&ar1_enc); + bool moebius_attack = (argc == 8); + + sscanf(argv[1],"%x",&data.cuid); + sscanf(argv[2],"%x",&data.nonce); + data.nonce2 = data.nonce; + sscanf(argv[3],"%x",&data.nr); + sscanf(argv[4],"%x",&data.ar); + if (moebius_attack) { + sscanf(argv[5],"%x",&data.nonce2); + sscanf(argv[6],"%x",&data.nr2); + sscanf(argv[7],"%x",&data.ar2); + } else { + sscanf(argv[5],"%x",&data.nr2); + sscanf(argv[6],"%x",&data.ar2); + } printf("Recovering key for:\n"); - printf(" uid: %08x\n",uid); - printf(" nt: %08x\n",nt); - printf(" {nr_0}: %08x\n",nr0_enc); - printf(" {ar_0}: %08x\n",ar0_enc); - printf(" {nr_1}: %08x\n",nr1_enc); - printf(" {ar_1}: %08x\n",ar1_enc); + printf(" uid: %08x\n",data.cuid); + printf(" nt0: %08x\n",data.nonce); + printf(" {nr_0}: %08x\n",data.nr); + printf(" {ar_0}: %08x\n",data.ar); + printf(" nt1: %08x\n",data.nonce2); + printf(" {nr_1}: %08x\n",data.nr2); + printf(" {ar_1}: %08x\n",data.ar2); + uint64_t start_time = msclock(); + // Generate lfsr succesors of the tag challenge printf("\nLFSR succesors of the tag challenge:\n"); - printf(" nt': %08x\n",prng_successor(nt, 64)); - printf(" nt'': %08x\n",prng_successor(nt, 96)); + printf(" nt': %08x\n",prng_successor(data.nonce, 64)); + printf(" nt'': %08x\n",prng_successor(data.nonce, 96)); // Extract the keystream from the messages printf("\nKeystream used to generate {ar} and {at}:\n"); - ks2 = ar0_enc ^ prng_successor(nt, 64); + ks2 = data.ar ^ prng_successor(data.nonce, 64); printf(" ks2: %08x\n",ks2); - s = lfsr_recovery32(ar0_enc ^ prng_successor(nt, 64), 0); - - for(t = s; t->odd | t->even; ++t) { - lfsr_rollback_word(t, 0, 0); - lfsr_rollback_word(t, nr0_enc, 1); - lfsr_rollback_word(t, uid ^ nt, 0); - crypto1_get_lfsr(t, &key); - crypto1_word(t, uid ^ nt, 0); - crypto1_word(t, nr1_enc, 1); - if (ar1_enc == (crypto1_word(t, 0, 0) ^ prng_successor(nt, 64))) { - printf("\nFound Key: [%012" PRIx64 "]\n\n",key); - break; - } + bool success; + if (moebius_attack) { + success = mfkey32_moebius(data, &key); + } else { + success = mfkey32(data, &key); + } + + if (success) { + printf("Recovered key: %012" PRIx64 "\n", key); + } else { + printf("Couldn't recover key.\n"); } - free(s); - return 0; + printf("Time spent: %1.2f seconds\n", (float)(msclock() - start_time)/1000.0); } diff --git a/tools/mfkey/mfkey64.c b/tools/mfkey/mfkey64.c index 4b9b29ee..4e9876c5 100755 --- a/tools/mfkey/mfkey64.c +++ b/tools/mfkey/mfkey64.c @@ -1,105 +1,101 @@ +#include +#include #include #include "crapto1/crapto1.h" -#include -#include - -int main (int argc, char *argv[]) { - struct Crypto1State *revstate; - uint64_t key; // recovered key - uint32_t uid; // serial number - uint32_t nt; // tag challenge - uint32_t nr_enc; // encrypted reader challenge - uint32_t ar_enc; // encrypted reader response - uint32_t at_enc; // encrypted tag response - uint32_t ks2; // keystream used to encrypt reader response - uint32_t ks3; // keystream used to encrypt tag response +#include "util.h" - printf("MIFARE Classic key recovery - based 64 bits of keystream\n"); - printf("Recover key from only one complete authentication!\n\n"); +int main (int argc, char *argv[]) +{ + uint32_t uid; // serial numDber + uint32_t nt; // tag challenge + uint32_t nr_enc; // encrypted reader challenge + uint32_t ar_enc; // encrypted reader response + uint32_t at_enc; // encrypted tag response + uint64_t key = 0; // recovered key + struct Crypto1State *revstate; + uint32_t ks2; // keystream used to encrypt reader response + uint32_t ks3; // keystream used to encrypt tag response - if (argc < 6 ) { - printf(" syntax: %s <{nr}> <{ar}> <{at}> [enc] [enc...]\n\n", argv[0]); - return 1; - } + printf("MIFARE Classic key recovery - based on 64 bits of keystream\n"); + printf("Recover key from only one complete authentication!\n\n"); - int encc = argc - 6; - int enclen[encc]; - uint8_t enc[encc][120]; + if (argc < 6 ) { + printf(" syntax: %s <{nr}> <{ar}> <{at}> [enc] [enc...]\n\n", argv[0]); + return 1; + } - sscanf(argv[1], "%x", &uid); - sscanf(argv[2], "%x", &nt); - sscanf(argv[3], "%x", &nr_enc); - sscanf(argv[4], "%x", &ar_enc); - sscanf(argv[5], "%x", &at_enc); - for (int i = 0; i < encc; i++) { - enclen[i] = strlen(argv[i + 6]) / 2; - for (int i2 = 0; i2 < enclen[i]; i2++) { - sscanf(argv[i+6] + i2*2,"%2x", (unsigned int *)&enc[i][i2]); - } - } - printf("Recovering key for:\n"); + int encc = argc - 6; + int enclen[encc]; + uint8_t enc[encc][120]; - printf(" uid: %08x\n", uid); - printf(" nt: %08x\n", nt); - printf(" {nr}: %08x\n", nr_enc); - printf(" {ar}: %08x\n", ar_enc); - printf(" {at}: %08x\n", at_enc); - for (int i = 0; i < encc; i++) { - printf("{enc%d}: ", i); - for (int i2 = 0; i2 < enclen[i]; i2++) { - printf("%02x", enc[i][i2]); - } - printf("\n"); - } + sscanf(argv[1], "%x", &uid); + sscanf(argv[2], "%x", &nt); + sscanf(argv[3], "%x", &nr_enc); + sscanf(argv[4], "%x", &ar_enc); + sscanf(argv[5], "%x", &at_enc); + for (int i = 0; i < encc; i++) { + enclen[i] = strlen(argv[i + 6]) / 2; + for (int i2 = 0; i2 < enclen[i]; i2++) { + sscanf(argv[i+6] + i2*2,"%2x", (unsigned int*)&enc[i][i2]); + } + } + printf("Recovering key for:\n"); + printf(" uid: %08x\n", uid); + printf(" nt: %08x\n", nt); + printf(" {nr}: %08x\n", nr_enc); + printf(" {ar}: %08x\n", ar_enc); + printf(" {at}: %08x\n", at_enc); + for (int i = 0; i < encc; i++) { + printf("{enc%d}: ", i); + for (int i2 = 0; i2 < enclen[i]; i2++) { + printf("%02x", enc[i][i2]); + } + printf("\n"); + } - /* - uint32_t uid = 0x9c599b32; - uint32_t tag_challenge = 0x82a4166c; - uint32_t nr_enc = 0xa1e458ce; - uint32_t reader_response = 0x6eea41e0; - uint32_t tag_response = 0x5cadf439; -*/ - // Generate lfsr succesors of the tag challenge - printf("\nLFSR succesors of the tag challenge:\n"); - printf(" nt': %08x\n",prng_successor(nt, 64)); - printf(" nt'': %08x\n",prng_successor(nt, 96)); + printf("\nLFSR successors of the tag challenge:\n"); + printf(" nt' : %08x\n",prng_successor(nt, 64)); + printf(" nt'': %08x\n",prng_successor(nt, 96)); - // Extract the keystream from the messages - printf("\nKeystream used to generate {ar} and {at}:\n"); - ks2 = ar_enc ^ prng_successor(nt, 64); - ks3 = at_enc ^ prng_successor(nt, 96); - printf(" ks2: %08x\n",ks2); - printf(" ks3: %08x\n",ks3); + // Extract the keystream from the messages + ks2 = ar_enc ^ prng_successor(nt, 64); + ks3 = at_enc ^ prng_successor(nt, 96); + + uint64_t start_time = msclock(); + revstate = lfsr_recovery64(ks2, ks3); + uint64_t time_spent = msclock() - start_time; + printf("Time spent in lfsr_recovery64(): %1.2f seconds\n", (float)time_spent/1000.0); + printf("\nKeystream used to generate {ar} and {at}:\n"); + printf(" ks2: %08x\n",ks2); + printf(" ks3: %08x\n",ks3); - revstate = lfsr_recovery64(ks2, ks3); + // Decrypting communication using keystream if presented + if (argc > 6 ) { + printf("\nDecrypted communication:\n"); + uint8_t ks4; + int rollb = 0; + for (int i = 0; i < encc; i++) { + printf("{dec%d}: ", i); + for (int i2 = 0; i2 < enclen[i]; i2++) { + ks4 = crypto1_byte(revstate, 0, 0); + printf("%02x", ks4 ^ enc[i][i2]); + rollb += 1; + } + printf("\n"); + } + for (int i = 0; i < rollb; i++) { + lfsr_rollback_byte(revstate, 0, 0); + } + } - // Decrypting communication using keystream if presented - if (argc > 6 ) { - printf("\nDecrypted communication:\n"); - uint8_t ks4; - int rollb = 0; - for (int i = 0; i < encc; i++) { - printf("{dec%d}: ", i); - for (int i2 = 0; i2 < enclen[i]; i2++) { - ks4 = crypto1_byte(revstate, 0, 0); - printf("%02x", ks4 ^ enc[i][i2]); - rollb += 1; - } - printf("\n"); - } - for (int i = 0; i < rollb; i++) { - lfsr_rollback_byte(revstate, 0, 0); - } - } - - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, nr_enc, 1); - lfsr_rollback_word(revstate, uid ^ nt, 0); - crypto1_get_lfsr(revstate, &key); - printf("\nFound Key: [%012" PRIx64"]\n\n",key); - crypto1_destroy(revstate); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, nr_enc, 1); + lfsr_rollback_word(revstate, uid ^ nt, 0); + crypto1_get_lfsr(revstate, &key); + crypto1_destroy(revstate); - return 0; + printf("\nFound Key: [%012" PRIx64"]\n\n",key); + }