Merge pull request #243 from pwpiwi/cleanup

Deduplicate mfkey32 and mfkey64
This commit is contained in:
Iceman 2017-03-24 08:19:23 +01:00 committed by GitHub
commit f513388ee0
11 changed files with 192 additions and 170 deletions

8
.gitignore vendored
View file

@ -14,14 +14,16 @@
*.dll *.dll
*.moc.cpp *.moc.cpp
*.z *.z
version.c
*.exe *.exe
proxmark
proxmark3 proxmark3
flasher flasher
version.c
lua lua
luac luac
fpga_compress fpga_compress
mfkey32
mfkey64
fpga/* fpga/*
!fpga/tests !fpga/tests
@ -34,5 +36,3 @@ fpga/*
!fpga/xst_hf.scr !fpga/xst_hf.scr
!fpga/go.bat !fpga/go.bat
!fpga/sim.tcl !fpga/sim.tcl

View file

@ -58,7 +58,7 @@ CORESRCS = uart.c \
CMDSRCS = crapto1/crapto1.c\ CMDSRCS = crapto1/crapto1.c\
crapto1/crypto1.c\ crapto1/crypto1.c\
nonce2key.c\ mfkey.c\
loclass/cipher.c \ loclass/cipher.c \
loclass/cipherutils.c \ loclass/cipherutils.c \
loclass/des.c \ loclass/des.c \

View file

@ -19,7 +19,7 @@
#include "ui.h" #include "ui.h"
#include "mifarehost.h" #include "mifarehost.h"
#include "mifare.h" #include "mifare.h"
#include "nonce2key.h" #include "mfkey.h"
#define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up #define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up

View file

@ -10,15 +10,11 @@
// MIFARE Darkside hack // MIFARE Darkside hack
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#include "nonce2key.h" #include "mfkey.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "mifarehost.h"
#include "util.h"
#include "crapto1/crapto1.h" #include "crapto1/crapto1.h"
// recover key from 2 different reader responses on same tag challenge // recover key from 2 different reader responses on same tag challenge
bool mfkey32(nonces_t data, uint64_t *outputkey) { bool mfkey32(nonces_t data, uint64_t *outputkey) {
struct Crypto1State *s,*t; struct Crypto1State *s,*t;
@ -27,8 +23,6 @@ bool mfkey32(nonces_t data, uint64_t *outputkey) {
bool isSuccess = false; bool isSuccess = false;
uint8_t counter = 0; uint8_t counter = 0;
uint64_t t1 = msclock();
s = lfsr_recovery32(data.ar ^ prng_successor(data.nonce, 64), 0); s = lfsr_recovery32(data.ar ^ prng_successor(data.nonce, 64), 0);
for(t = s; t->odd | t->even; ++t) { for(t = s; t->odd | t->even; ++t) {
@ -46,8 +40,6 @@ bool mfkey32(nonces_t data, uint64_t *outputkey) {
} }
} }
isSuccess = (counter == 1); 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; *outputkey = ( isSuccess ) ? outkey : 0;
crypto1_destroy(s); crypto1_destroy(s);
/* //un-comment to save all keys to a stats.txt file /* //un-comment to save all keys to a stats.txt file
@ -70,9 +62,6 @@ bool mfkey32_moebius(nonces_t data, uint64_t *outputkey) {
bool isSuccess = false; bool isSuccess = false;
int counter = 0; int counter = 0;
//PrintAndLog("Enter mfkey32_moebius");
uint64_t t1 = msclock();
s = lfsr_recovery32(data.ar ^ prng_successor(data.nonce, 64), 0); s = lfsr_recovery32(data.ar ^ prng_successor(data.nonce, 64), 0);
for(t = s; t->odd | t->even; ++t) { for(t = s; t->odd | t->even; ++t) {
@ -92,8 +81,6 @@ bool mfkey32_moebius(nonces_t data, uint64_t *outputkey) {
} }
} }
isSuccess = (counter == 1); 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; *outputkey = ( isSuccess ) ? outkey : 0;
crypto1_destroy(s); crypto1_destroy(s);
/* // un-comment to output all keys to stats.txt /* // un-comment to output all keys to stats.txt
@ -115,9 +102,6 @@ int mfkey64(nonces_t data, uint64_t *outputkey){
uint32_t ks3; // keystream used to encrypt tag response uint32_t ks3; // keystream used to encrypt tag response
struct Crypto1State *revstate; struct Crypto1State *revstate;
// PrintAndLog("Enter mfkey64");
uint64_t t1 = msclock();
// Extract the keystream from the messages // Extract the keystream from the messages
ks2 = data.ar ^ prng_successor(data.nonce, 64); ks2 = data.ar ^ prng_successor(data.nonce, 64);
ks3 = data.at ^ prng_successor(data.nonce, 96); ks3 = data.at ^ prng_successor(data.nonce, 96);
@ -131,7 +115,7 @@ int mfkey64(nonces_t data, uint64_t *outputkey){
crypto1_destroy(revstate); crypto1_destroy(revstate);
*outputkey = key; *outputkey = key;
t1 = msclock() - t1;
// PrintAndLog("Time in mfkey64: %.1f seconds \n", (float)t1/1000.0);
return 0; return 0;
} }

View file

@ -10,8 +10,8 @@
// MIFARE Darkside hack // MIFARE Darkside hack
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#ifndef __NONCE2KEY_H #ifndef MFKEY_H
#define __NONCE2KEY_H #define MFKEY_H
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
@ -29,8 +29,8 @@ typedef struct {
uint32_t nr2; uint32_t nr2;
} nonces_t; } nonces_t;
bool mfkey32(nonces_t data, uint64_t *outputkey); extern bool mfkey32(nonces_t data, uint64_t *outputkey);
bool mfkey32_moebius(nonces_t data, uint64_t *outputkey); extern bool mfkey32_moebius(nonces_t data, uint64_t *outputkey);
int mfkey64(nonces_t data, uint64_t *outputkey); extern int mfkey64(nonces_t data, uint64_t *outputkey);
#endif #endif

View file

@ -8,6 +8,8 @@
// mifare commands // mifare commands
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#include "mifarehost.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -20,7 +22,6 @@
#include "ui.h" #include "ui.h"
#include "util.h" #include "util.h"
#include "iso14443crc.h" #include "iso14443crc.h"
#include "mifarehost.h"
// mifare tracer flags used in mfTraceDecode() // mifare tracer flags used in mfTraceDecode()
#define TRACE_IDLE 0x00 #define TRACE_IDLE 0x00

View file

@ -8,6 +8,9 @@
// High frequency ISO14443A commands // High frequency ISO14443A commands
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#ifndef MIFAREHOST_H
#define MIFAREHOST_H
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "data.h" #include "data.h"
@ -42,3 +45,5 @@ extern int isBlockTrailer(int blockN);
extern int loadTraceCard(uint8_t *tuid); extern int loadTraceCard(uint8_t *tuid);
extern int saveTraceCard(void); extern int saveTraceCard(void);
extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len);
#endif

27
common/crapto1/readme Normal file
View file

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

View file

@ -1,10 +1,10 @@
VPATH = ../../common/crapto1 VPATH = ../../common/crapto1 ../../client
CC = gcc CC = gcc
LD = gcc LD = gcc
CFLAGS = -I../../common -Wall -O4 CFLAGS = -I../../common -I../../client -Wall -O4
LDFLAGS = LDFLAGS =
OBJS = crypto1.o crapto1.o OBJS = crypto1.o crapto1.o util.o mfkey.o
EXES = mfkey32 mfkey64 EXES = mfkey32 mfkey64
WINEXES = $(patsubst %, %.exe, $(EXES)) WINEXES = $(patsubst %, %.exe, $(EXES))
@ -13,7 +13,7 @@ all: $(OBJS) $(EXES)
%.o : %.c %.o : %.c
$(CC) $(CFLAGS) -c -o $@ $< $(CC) $(CFLAGS) -c -o $@ $<
% : %.c % : %.c $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $< $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $<
clean: clean:

View file

@ -1,67 +1,76 @@
#include <inttypes.h> #include <inttypes.h>
#include "crapto1/crapto1.h" #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "crapto1/crapto1.h"
#include "mfkey.h"
#include "util.h"
// 32 bit recover key from 2 nonces
int main (int argc, char *argv[]) { 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
uint32_t ks2; // keystream used to encrypt reader response
printf("MIFARE Classic key recovery - based 32 bits of keystream\n"); nonces_t data;
uint32_t ks2; // keystream used to encrypt reader response
uint64_t key; // recovered key
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"); printf("Recover key from two 32-bit reader authentication answers only!\n\n");
if (argc < 7) { if (argc != 7 && argc != 8) {
printf(" syntax: %s <uid> <nt> <{nr_0}> <{ar_0}> <{nr_1}> <{ar_1}>\n\n",argv[0]); printf(" syntax: %s <uid> <nt0> <{nr_0}> <{ar_0}> [<nt1>] <{nr_1}> <{ar_1}>\n", argv[0]);
printf(" (you may omit nt1 if it is equal to nt0)\n\n");
return 1; return 1;
} }
sscanf(argv[1],"%x",&uid); bool moebius_attack = (argc == 8);
sscanf(argv[2],"%x",&nt);
sscanf(argv[3],"%x",&nr0_enc); sscanf(argv[1],"%x",&data.cuid);
sscanf(argv[4],"%x",&ar0_enc); sscanf(argv[2],"%x",&data.nonce);
sscanf(argv[5],"%x",&nr1_enc); data.nonce2 = data.nonce;
sscanf(argv[6],"%x",&ar1_enc); 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("Recovering key for:\n");
printf(" uid: %08x\n",uid); printf(" uid: %08x\n",data.cuid);
printf(" nt: %08x\n",nt); printf(" nt0: %08x\n",data.nonce);
printf(" {nr_0}: %08x\n",nr0_enc); printf(" {nr_0}: %08x\n",data.nr);
printf(" {ar_0}: %08x\n",ar0_enc); printf(" {ar_0}: %08x\n",data.ar);
printf(" {nr_1}: %08x\n",nr1_enc); printf(" nt1: %08x\n",data.nonce2);
printf(" {ar_1}: %08x\n",ar1_enc); 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 // Generate lfsr succesors of the tag challenge
printf("\nLFSR succesors of the tag challenge:\n"); printf("\nLFSR succesors of the tag challenge:\n");
printf(" nt': %08x\n",prng_successor(nt, 64)); printf(" nt': %08x\n",prng_successor(data.nonce, 64));
printf(" nt'': %08x\n",prng_successor(nt, 96)); printf(" nt'': %08x\n",prng_successor(data.nonce, 96));
// Extract the keystream from the messages // Extract the keystream from the messages
printf("\nKeystream used to generate {ar} and {at}:\n"); 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); printf(" ks2: %08x\n",ks2);
s = lfsr_recovery32(ar0_enc ^ prng_successor(nt, 64), 0); bool success;
if (moebius_attack) {
for(t = s; t->odd | t->even; ++t) { success = mfkey32_moebius(data, &key);
lfsr_rollback_word(t, 0, 0); } else {
lfsr_rollback_word(t, nr0_enc, 1); success = mfkey32(data, &key);
lfsr_rollback_word(t, uid ^ nt, 0); }
crypto1_get_lfsr(t, &key);
crypto1_word(t, uid ^ nt, 0); if (success) {
crypto1_word(t, nr1_enc, 1); printf("Recovered key: %012" PRIx64 "\n", key);
if (ar1_enc == (crypto1_word(t, 0, 0) ^ prng_successor(nt, 64))) { } else {
printf("\nFound Key: [%012" PRIx64 "]\n\n",key); printf("Couldn't recover key.\n");
break;
}
} }
free(s);
return 0; printf("Time spent: %1.2f seconds\n", (float)(msclock() - start_time)/1000.0);
} }

View file

@ -1,105 +1,101 @@
#include <stdio.h>
#include <strings.h>
#include <inttypes.h> #include <inttypes.h>
#include "crapto1/crapto1.h" #include "crapto1/crapto1.h"
#include <stdio.h> #include "util.h"
#include <string.h>
int main (int argc, char *argv[]) { int main (int argc, char *argv[])
struct Crypto1State *revstate; {
uint64_t key; // recovered key uint32_t uid; // serial numDber
uint32_t uid; // serial number uint32_t nt; // tag challenge
uint32_t nt; // tag challenge uint32_t nr_enc; // encrypted reader challenge
uint32_t nr_enc; // encrypted reader challenge uint32_t ar_enc; // encrypted reader response
uint32_t ar_enc; // encrypted reader response uint32_t at_enc; // encrypted tag response
uint32_t at_enc; // encrypted tag response uint64_t key = 0; // recovered key
uint32_t ks2; // keystream used to encrypt reader response struct Crypto1State *revstate;
uint32_t ks3; // keystream used to encrypt tag response uint32_t ks2; // keystream used to encrypt reader response
uint32_t ks3; // keystream used to encrypt tag response
printf("MIFARE Classic key recovery - based 64 bits of keystream\n"); printf("MIFARE Classic key recovery - based on 64 bits of keystream\n");
printf("Recover key from only one complete authentication!\n\n"); printf("Recover key from only one complete authentication!\n\n");
if (argc < 6 ) { if (argc < 6 ) {
printf(" syntax: %s <uid> <nt> <{nr}> <{ar}> <{at}> [enc] [enc...]\n\n", argv[0]); printf(" syntax: %s <uid> <nt> <{nr}> <{ar}> <{at}> [enc] [enc...]\n\n", argv[0]);
return 1; return 1;
} }
int encc = argc - 6; int encc = argc - 6;
int enclen[encc]; int enclen[encc];
uint8_t enc[encc][120]; uint8_t enc[encc][120];
sscanf(argv[1], "%x", &uid); sscanf(argv[1], "%x", &uid);
sscanf(argv[2], "%x", &nt); sscanf(argv[2], "%x", &nt);
sscanf(argv[3], "%x", &nr_enc); sscanf(argv[3], "%x", &nr_enc);
sscanf(argv[4], "%x", &ar_enc); sscanf(argv[4], "%x", &ar_enc);
sscanf(argv[5], "%x", &at_enc); sscanf(argv[5], "%x", &at_enc);
for (int i = 0; i < encc; i++) { for (int i = 0; i < encc; i++) {
enclen[i] = strlen(argv[i + 6]) / 2; enclen[i] = strlen(argv[i + 6]) / 2;
for (int i2 = 0; i2 < enclen[i]; i2++) { for (int i2 = 0; i2 < enclen[i]; i2++) {
sscanf(argv[i+6] + i2*2,"%2x", (unsigned int *)&enc[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("Recovering key for:\n");
printf(" nt: %08x\n", nt); printf(" uid: %08x\n", uid);
printf(" {nr}: %08x\n", nr_enc); printf(" nt: %08x\n", nt);
printf(" {ar}: %08x\n", ar_enc); printf(" {nr}: %08x\n", nr_enc);
printf(" {at}: %08x\n", at_enc); printf(" {ar}: %08x\n", ar_enc);
for (int i = 0; i < encc; i++) { printf(" {at}: %08x\n", at_enc);
printf("{enc%d}: ", i); for (int i = 0; i < encc; i++) {
for (int i2 = 0; i2 < enclen[i]; i2++) { printf("{enc%d}: ", i);
printf("%02x", enc[i][i2]); for (int i2 = 0; i2 < enclen[i]; i2++) {
} printf("%02x", enc[i][i2]);
printf("\n"); }
} printf("\n");
}
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
uint32_t uid = 0x9c599b32; ks2 = ar_enc ^ prng_successor(nt, 64);
uint32_t tag_challenge = 0x82a4166c; ks3 = at_enc ^ prng_successor(nt, 96);
uint32_t nr_enc = 0xa1e458ce;
uint32_t reader_response = 0x6eea41e0; uint64_t start_time = msclock();
uint32_t tag_response = 0x5cadf439; revstate = lfsr_recovery64(ks2, ks3);
*/ uint64_t time_spent = msclock() - start_time;
// Generate lfsr succesors of the tag challenge printf("Time spent in lfsr_recovery64(): %1.2f seconds\n", (float)time_spent/1000.0);
printf("\nLFSR succesors of the tag challenge:\n"); printf("\nKeystream used to generate {ar} and {at}:\n");
printf(" nt': %08x\n",prng_successor(nt, 64)); printf(" ks2: %08x\n",ks2);
printf(" nt'': %08x\n",prng_successor(nt, 96)); printf(" ks3: %08x\n",ks3);
// Extract the keystream from the messages // Decrypting communication using keystream if presented
printf("\nKeystream used to generate {ar} and {at}:\n"); if (argc > 6 ) {
ks2 = ar_enc ^ prng_successor(nt, 64); printf("\nDecrypted communication:\n");
ks3 = at_enc ^ prng_successor(nt, 96); uint8_t ks4;
printf(" ks2: %08x\n",ks2); int rollb = 0;
printf(" ks3: %08x\n",ks3); 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);
}
}
revstate = lfsr_recovery64(ks2, ks3); 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);
// Decrypting communication using keystream if presented printf("\nFound Key: [%012" PRIx64"]\n\n",key);
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);
return 0;
} }