mirror of
https://github.com/Proxmark/proxmark3.git
synced 2025-08-23 22:45:27 -07:00
hf mf hardnested:
- fix: prevent hf mf hardnested r from reading past end of file - fix: Sum(a0) calculation was wrong - fix: MifareAcquireEncryptedNonces() delivered wrong parity bits - add: implemented calculation of Hypergeometric Probabilities - add: implemented guessing of Sum(a8) - add: stop acquiring nonces when probability for correct Sum(a8) exceeds a given threshold
This commit is contained in:
parent
00950b1c16
commit
af57d9d5c6
3 changed files with 384 additions and 168 deletions
|
@ -618,7 +618,6 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
|
|||
pcs = &mpcs;
|
||||
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE];
|
||||
int16_t isOK = 0;
|
||||
uint8_t nt_enc1[4];
|
||||
uint8_t par_enc[1];
|
||||
uint8_t nt_par_enc = 0;
|
||||
uint8_t buf[USB_CMD_DATA_SIZE];
|
||||
|
@ -631,15 +630,16 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
|
|||
ui64Key = bytes_to_num(datain, 6);
|
||||
bool initialize = flags & 0x0001;
|
||||
bool slow = flags & 0x0002;
|
||||
bool field_off = flags & 0x0004;
|
||||
|
||||
#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication according to NXP documentation
|
||||
#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation)
|
||||
#define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication
|
||||
|
||||
LED_A_ON();
|
||||
LED_C_OFF();
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
if (initialize) {
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
clear_trace();
|
||||
set_tracing(true);
|
||||
}
|
||||
|
@ -648,11 +648,12 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
|
|||
|
||||
uint16_t num_nonces = 0;
|
||||
bool have_uid = false;
|
||||
for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE - 4 - 4 - 1; ) {
|
||||
for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE - 9; ) {
|
||||
|
||||
// Test if the action was cancelled
|
||||
if(BUTTON_PRESS()) {
|
||||
isOK = -2;
|
||||
isOK = 2;
|
||||
field_off = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -670,7 +671,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
|
|||
}
|
||||
have_uid = true;
|
||||
} else { // no need for anticollision. We can directly select the card
|
||||
if(!iso14443a_select_card(uid, NULL, &cuid, false, cascade_levels)) {
|
||||
if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels)) {
|
||||
if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)");
|
||||
continue;
|
||||
}
|
||||
|
@ -701,22 +702,18 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
|
|||
|
||||
num_nonces++;
|
||||
if (num_nonces % 2) {
|
||||
memcpy(nt_enc1, receivedAnswer, 4);
|
||||
nt_par_enc = par_enc[0];
|
||||
memcpy(buf+i, receivedAnswer, 4);
|
||||
nt_par_enc = par_enc[0] & 0xf0;
|
||||
} else {
|
||||
nt_par_enc |= par_enc[0];
|
||||
memcpy(&buf[i], nt_enc1, 4);
|
||||
i += 4;
|
||||
memcpy(&buf[i], receivedAnswer, 4);
|
||||
i += 4;
|
||||
memcpy(&buf[i], &nt_par_enc, 1);
|
||||
i += 1;
|
||||
nt_par_enc |= par_enc[0] >> 4;
|
||||
memcpy(buf+i+4, receivedAnswer, 4);
|
||||
memcpy(buf+i+8, &nt_par_enc, 1);
|
||||
i += 9;
|
||||
}
|
||||
|
||||
// wait for the card to become ready again
|
||||
while(GetCountSspClk() < timeout);
|
||||
|
||||
|
||||
}
|
||||
|
||||
LED_C_OFF();
|
||||
|
@ -724,14 +721,15 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
|
|||
crypto1_destroy(pcs);
|
||||
|
||||
LED_B_ON();
|
||||
memcpy(&cuid, uid+(cascade_levels-1)*3, 4);
|
||||
cmd_send(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf));
|
||||
LED_B_OFF();
|
||||
|
||||
if (MF_DBGLEVEL >= 3) DbpString("AcquireEncryptedNonces finished");
|
||||
|
||||
if (field_off) {
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
LEDsoff();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -871,8 +871,9 @@ int CmdHF14AMfNestedHard(const char *Cmd)
|
|||
int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_read, nonce_file_write, slow);
|
||||
if (isOK) {
|
||||
switch (isOK) {
|
||||
case -1 : PrintAndLog("Error: No response from Proxmark.\n"); break;
|
||||
case -2 : PrintAndLog("Button pressed. Aborted.\n"); break;
|
||||
case 1 : PrintAndLog("Error: No response from Proxmark.\n"); break;
|
||||
case 2 : PrintAndLog("Button pressed. Aborted.\n"); break;
|
||||
case 3 : PrintAndLog("File error. Aborted.\n"); break;
|
||||
default : PrintAndLog("Unknown Error.\n");
|
||||
}
|
||||
return 2;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <math.h>
|
||||
#include "mifarehost.h"
|
||||
#include "proxmark3.h"
|
||||
|
||||
|
@ -195,66 +196,79 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo
|
|||
|
||||
|
||||
typedef struct noncelistentry {
|
||||
uint32_t NonceAndPar; // concatenated last 24 bits of nonce and parity
|
||||
uint32_t nonce_enc;
|
||||
uint8_t par_enc;
|
||||
void *next;
|
||||
} noncelistentry_t;
|
||||
|
||||
|
||||
typedef struct noncelist {
|
||||
uint16_t num;
|
||||
uint8_t Sum;
|
||||
uint16_t Sum;
|
||||
bool updated;
|
||||
noncelistentry_t *first;
|
||||
} noncelist_t;
|
||||
|
||||
|
||||
noncelist_t nonces[256];
|
||||
uint16_t first_byte_Sum = 0;
|
||||
uint16_t first_byte_num = 0;
|
||||
static noncelist_t nonces[256];
|
||||
static uint16_t first_byte_Sum = 0;
|
||||
static uint16_t first_byte_num = 0;
|
||||
static uint8_t best_first_byte;
|
||||
static uint16_t guessed_Sum8;
|
||||
static float guessed_Sum8_confidence;
|
||||
|
||||
|
||||
int add_nonce(uint32_t nonce, uint8_t par)
|
||||
static int add_nonce(uint32_t nonce_enc, uint8_t par_enc)
|
||||
{
|
||||
uint8_t first_byte = nonce >> 24;
|
||||
uint32_t NonceAndPar = (nonce << 8) | (par & 0x07);
|
||||
uint8_t first_byte = nonce_enc >> 24;
|
||||
noncelistentry_t *p1 = nonces[first_byte].first;
|
||||
noncelistentry_t *p2 = NULL;
|
||||
|
||||
if (p1 == NULL) { // first nonce with this 1st byte
|
||||
first_byte_num++;
|
||||
first_byte_Sum += parity((nonce & 0xff000000) | (par & 0x08)); // 1st byte sum property
|
||||
first_byte_Sum += parity((nonce_enc & 0xff000000) | (par_enc & 0x08) | 0x01); // 1st byte sum property. Note: added XOR 1
|
||||
// printf("Adding nonce 0x%08x, par_enc 0x%02x, parity(0x%08x) = %d\n",
|
||||
// nonce_enc,
|
||||
// par_enc,
|
||||
// (nonce_enc & 0xff000000) | (par_enc & 0x08) |0x01,
|
||||
// parity((nonce_enc & 0xff000000) | (par_enc & 0x08) | 0x01));
|
||||
}
|
||||
|
||||
while (p1 != NULL && p1->NonceAndPar < NonceAndPar) {
|
||||
while (p1 != NULL && (p1->nonce_enc & 0x00ff0000) < (nonce_enc & 0x00ff0000)) {
|
||||
p2 = p1;
|
||||
p1 = p1->next;
|
||||
}
|
||||
|
||||
if (p1 == NULL) { // need to add at the end of the list
|
||||
if (p2 == NULL) { // list is empty yet
|
||||
if (p2 == NULL) { // list is empty yet. Add first entry.
|
||||
p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t));
|
||||
} else {
|
||||
} else { // add new entry at end of existing list.
|
||||
p2 = p2->next = malloc(sizeof(noncelistentry_t));
|
||||
}
|
||||
} else if (p1->NonceAndPar != NonceAndPar) { // quite unlikely for hardened cards, but don't want to add a nonce twice
|
||||
} else if ((p1->nonce_enc & 0x00ff0000) != (nonce_enc & 0x00ff0000)) { // found distinct 2nd byte. Need to insert.
|
||||
if (p2 == NULL) { // need to insert at start of list
|
||||
p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t));
|
||||
} else {
|
||||
p2 = p2->next = malloc(sizeof(noncelistentry_t));
|
||||
}
|
||||
} else {
|
||||
return (first_byte_num==256?first_byte_Sum:-1);
|
||||
} else { // we have seen this 2nd byte before. Nothing to add or insert.
|
||||
return (0);
|
||||
}
|
||||
|
||||
// add or insert new data
|
||||
p2->next = p1;
|
||||
p2->NonceAndPar = NonceAndPar;
|
||||
nonces[first_byte].num++;
|
||||
nonces[first_byte].Sum += parity(NonceAndPar & 0xff000004); // 2nd byte sum property
|
||||
p2->nonce_enc = nonce_enc;
|
||||
p2->par_enc = par_enc;
|
||||
|
||||
return (first_byte_num==256?first_byte_Sum:-1);
|
||||
nonces[first_byte].num++;
|
||||
nonces[first_byte].Sum += parity((nonce_enc & 0x00ff0000) | (par_enc & 0x04)); // 2nd byte sum property. Note: added XOR 1
|
||||
nonces[first_byte].updated = true; // indicates that we need to recalculate the Sum(a8) probability for this first byte
|
||||
|
||||
return (1); // new nonce added
|
||||
}
|
||||
|
||||
|
||||
uint16_t SumPropertyOdd(struct Crypto1State *s)
|
||||
static uint16_t SumPropertyOdd(struct Crypto1State *s)
|
||||
{
|
||||
uint16_t oddsum = 0;
|
||||
for (uint16_t j = 0; j < 16; j++) {
|
||||
|
@ -270,7 +284,7 @@ uint16_t SumPropertyOdd(struct Crypto1State *s)
|
|||
}
|
||||
|
||||
|
||||
uint16_t SumPropertyEven(struct Crypto1State *s)
|
||||
static uint16_t SumPropertyEven(struct Crypto1State *s)
|
||||
{
|
||||
uint16_t evensum = 0;
|
||||
for (uint16_t j = 0; j < 16; j++) {
|
||||
|
@ -286,7 +300,7 @@ uint16_t SumPropertyEven(struct Crypto1State *s)
|
|||
}
|
||||
|
||||
|
||||
uint16_t SumProperty(struct Crypto1State *s)
|
||||
static uint16_t SumProperty(struct Crypto1State *s)
|
||||
{
|
||||
uint16_t sum_odd = SumPropertyOdd(s);
|
||||
uint16_t sum_even = SumPropertyEven(s);
|
||||
|
@ -294,18 +308,110 @@ uint16_t SumProperty(struct Crypto1State *s)
|
|||
}
|
||||
|
||||
|
||||
void TestSumProperty()
|
||||
static double p_hypergeometric(uint16_t N, uint16_t K, uint16_t n, uint16_t k)
|
||||
{
|
||||
// for efficient computation we are using the recursive definition
|
||||
// (K-k+1) * (n-k+1)
|
||||
// P(X=k) = P(X=k-1) * --------------------
|
||||
// k * (N-K-n+k)
|
||||
// and
|
||||
// (N-K)*(N-K-1)*...*(N-K-n+1)
|
||||
// P(X=0) = -----------------------------
|
||||
// N*(N-1)*...*(N-n+1)
|
||||
|
||||
if (n-k > N-K || k > K) return 0.0; // avoids log(x<=0) in calculation below
|
||||
if (k == 0) {
|
||||
// use logarithms to avoid overflow with huge factorials (double type can only hold 170!)
|
||||
double log_result = 0.0;
|
||||
for (int16_t i = N-K; i >= N-K-n+1; i--) {
|
||||
log_result += log(i);
|
||||
}
|
||||
for (int16_t i = N; i >= N-n+1; i--) {
|
||||
log_result -= log(i);
|
||||
}
|
||||
return exp(log_result);
|
||||
} else {
|
||||
if (n-k == N-K) { // special case. The published recursion below would fail with a divide by zero exception
|
||||
double log_result = 0.0;
|
||||
for (int16_t i = k+1; i <= n; i++) {
|
||||
log_result += log(i);
|
||||
}
|
||||
for (int16_t i = K+1; i <= N; i++) {
|
||||
log_result -= log(i);
|
||||
}
|
||||
return exp(log_result);
|
||||
} else { // recursion
|
||||
return (p_hypergeometric(N, K, n, k-1) * (K-k+1) * (n-k+1) / (k * (N-K-n+k)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static float sum_probability(uint16_t K, uint16_t n, uint16_t k)
|
||||
{
|
||||
const uint16_t N = 256;
|
||||
|
||||
const float p[257] = {
|
||||
0.0289, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0049, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.4180, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0049, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
|
||||
0.0289 };
|
||||
|
||||
if (k > K || p[K] == 0.0) return 0.0;
|
||||
|
||||
double p_T_is_k_when_S_is_K = p_hypergeometric(N, K, n, k);
|
||||
double p_S_is_K = p[K];
|
||||
double p_T_is_k = 0;
|
||||
for (uint16_t i = 0; i <= 256; i++) {
|
||||
if (p[i] != 0.0) {
|
||||
p_T_is_k += p[i] * p_hypergeometric(N, i, n, k);
|
||||
}
|
||||
}
|
||||
return(p_T_is_k_when_S_is_K * p_S_is_K / p_T_is_k);
|
||||
}
|
||||
|
||||
|
||||
static void Tests()
|
||||
{
|
||||
#define NUM_STATISTICS 100000
|
||||
uint32_t statistics[257];
|
||||
struct Crypto1State cs;
|
||||
time_t time1 = clock();
|
||||
|
||||
for (uint16_t i = 0; i < 257; i++) {
|
||||
statistics[i] = 0;
|
||||
}
|
||||
time_t time1 = clock();
|
||||
|
||||
#define NUM_STATISTICS 1000000
|
||||
for (uint32_t i = 0; i < NUM_STATISTICS; i++) {
|
||||
for (uint64_t i = 0; i < NUM_STATISTICS; i++) {
|
||||
cs.odd = (rand() & 0xfff) << 12 | (rand() & 0xfff);
|
||||
cs.even = (rand() & 0xfff) << 12 | (rand() & 0xfff);
|
||||
uint16_t sum_property = SumProperty(&cs);
|
||||
|
@ -313,49 +419,74 @@ void TestSumProperty()
|
|||
if (i%(NUM_STATISTICS/100) == 0) printf(".");
|
||||
}
|
||||
|
||||
printf("\nCalculated %d Sum properties in %0.3f seconds (%0.0f calcs/second)\n", NUM_STATISTICS, ((float)clock() - time1)/CLOCKS_PER_SEC, NUM_STATISTICS/((float)clock() - time1)*CLOCKS_PER_SEC);
|
||||
printf("\nTests: Calculated %d Sum properties in %0.3f seconds (%0.0f calcs/second)\n", NUM_STATISTICS, ((float)clock() - time1)/CLOCKS_PER_SEC, NUM_STATISTICS/((float)clock() - time1)*CLOCKS_PER_SEC);
|
||||
for (uint16_t i = 0; i < 257; i++) {
|
||||
if (statistics[i] != 0) {
|
||||
printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/NUM_STATISTICS);
|
||||
}
|
||||
}
|
||||
|
||||
// printf("Tests: probabilities for n=15, k=5:\n");
|
||||
// for (uint16_t i = 0; i < 257; i++) {
|
||||
// if (statistics[i] != 0) {
|
||||
// printf("p[%3d] = %0.4f\n", i, sum_probability(i, 15, 5));
|
||||
// }
|
||||
// }
|
||||
|
||||
printf("\nTests: Hypergeometric Probability for selected parameters\n");
|
||||
printf("p_hypergeometric(256, 206, 255, 206) = %0.8f\n", p_hypergeometric(256, 206, 255, 206));
|
||||
printf("p_hypergeometric(256, 206, 255, 205) = %0.8f\n", p_hypergeometric(256, 206, 255, 205));
|
||||
printf("p_hypergeometric(256, 156, 1, 1) = %0.8f\n", p_hypergeometric(256, 156, 1, 1));
|
||||
printf("p_hypergeometric(256, 156, 1, 0) = %0.8f\n", p_hypergeometric(256, 156, 1, 0));
|
||||
printf("p_hypergeometric(256, 1, 1, 1) = %0.8f\n", p_hypergeometric(256, 1, 1, 1));
|
||||
printf("p_hypergeometric(256, 1, 1, 0) = %0.8f\n", p_hypergeometric(256, 1, 1, 0));
|
||||
|
||||
struct Crypto1State *pcs;
|
||||
pcs = crypto1_create(0xffffffffffff);
|
||||
printf("\nTests: Sum(a0)=%d for key = 0xffffffffffff\n", SumProperty(pcs));
|
||||
crypto1_destroy(pcs);
|
||||
pcs = crypto1_create(0xa0a1a2a3a4a5);
|
||||
printf("\nTests: Sum(a0)=%d for key = 0xa0a1a2a3a4a5\n", SumProperty(pcs));
|
||||
crypto1_destroy(pcs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow)
|
||||
static float estimate_second_byte_sum(void)
|
||||
{
|
||||
float confidence = guessed_Sum8_confidence;
|
||||
for (uint16_t first_byte = 0; first_byte < 256; first_byte++) {
|
||||
if (nonces[first_byte].updated) {
|
||||
for (uint16_t sum = 0; sum <= 256; sum++) {
|
||||
float prob = sum_probability(sum, nonces[first_byte].num, nonces[first_byte].Sum);
|
||||
if (prob > confidence) {
|
||||
confidence = prob;
|
||||
best_first_byte = first_byte;
|
||||
guessed_Sum8 = sum;
|
||||
}
|
||||
}
|
||||
nonces[first_byte].updated = false;
|
||||
}
|
||||
}
|
||||
return confidence;
|
||||
}
|
||||
|
||||
|
||||
static int read_nonce_file(void)
|
||||
{
|
||||
UsbCommand resp;
|
||||
FILE *fnonces = NULL;
|
||||
uint32_t total_num_nonces = 0;
|
||||
uint32_t flags = 0;
|
||||
bool initialize = true;
|
||||
clock_t time1;
|
||||
uint32_t cuid;
|
||||
uint8_t trgBlockNo;
|
||||
uint8_t trgKeyType;
|
||||
uint8_t read_buf[9];
|
||||
uint32_t nt_enc1, nt_enc2;
|
||||
uint8_t par_enc;
|
||||
uint32_t cuid;
|
||||
int total_num_nonces = 0;
|
||||
|
||||
// initialize the list of nonces
|
||||
for (uint16_t i = 0; i < 256; i++) {
|
||||
nonces[i].num = 0;
|
||||
nonces[i].Sum = 0;
|
||||
nonces[i].first = NULL;
|
||||
}
|
||||
first_byte_num = 0;
|
||||
first_byte_Sum = 0;
|
||||
|
||||
//StateList_t statelists[2];
|
||||
//struct Crypto1State *p1, *p2, *p3, *p4;
|
||||
|
||||
if (nonce_file_read) {
|
||||
// don't acquire nonces, use pre-acquired data from file nonces.bin
|
||||
if ((fnonces = fopen("nonces.bin","rb")) == NULL) {
|
||||
PrintAndLog("Could not open file nonces.bin");
|
||||
return 1;
|
||||
}
|
||||
|
||||
PrintAndLog("Reading nonces from file nonces.bin...");
|
||||
if (fread(read_buf, 1, 6, fnonces) == 0) {
|
||||
PrintAndLog("File reading error.");
|
||||
|
@ -366,95 +497,181 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc
|
|||
trgBlockNo = bytes_to_num(read_buf+4, 1);
|
||||
trgKeyType = bytes_to_num(read_buf+5, 1);
|
||||
|
||||
while (!feof(fnonces)) {
|
||||
fread(read_buf, 1, 9, fnonces);
|
||||
while (fread(read_buf, 1, 9, fnonces) == 9) {
|
||||
nt_enc1 = bytes_to_num(read_buf, 4);
|
||||
nt_enc2 = bytes_to_num(read_buf+4, 4);
|
||||
par_enc = bytes_to_num(read_buf+8, 1);
|
||||
//printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4);
|
||||
//printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f);
|
||||
add_nonce(nt_enc1, par_enc >> 4);
|
||||
add_nonce(nt_enc2, par_enc & 0x0f);
|
||||
total_num_nonces += 2;
|
||||
}
|
||||
fclose(fnonces);
|
||||
PrintAndLog("Read %d nonces from file", total_num_nonces);
|
||||
PrintAndLog("Read %d nonces from file. cuid=%08x, Block=%d, Keytype=%c", total_num_nonces, cuid, trgBlockNo, trgKeyType==0?'A':'B');
|
||||
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_write, bool slow)
|
||||
{
|
||||
clock_t time1 = clock();
|
||||
bool initialize = true;
|
||||
bool field_off = false;
|
||||
bool finished = false;
|
||||
uint32_t flags = 0;
|
||||
uint8_t write_buf[9];
|
||||
uint32_t total_num_nonces = 0;
|
||||
uint32_t next_thousand = 1000;
|
||||
uint32_t total_added_nonces = 0;
|
||||
FILE *fnonces = NULL;
|
||||
UsbCommand resp;
|
||||
uint32_t cuid;
|
||||
|
||||
#define CONFIDENCE_THRESHOLD 0.95 // Collect nonces until we are certain enough to have guessed Sum(a8) correctly
|
||||
|
||||
// acquire nonces.
|
||||
time1 = clock();
|
||||
do {
|
||||
clearCommandBuffer();
|
||||
|
||||
do {
|
||||
flags = 0;
|
||||
flags |= initialize ? 0x0001 : 0;
|
||||
flags |= slow ? 0x0002 : 0;
|
||||
flags |= field_off ? 0x0004 : 0;
|
||||
UsbCommand c = {CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags}};
|
||||
memcpy(c.d.asBytes, key, 6);
|
||||
|
||||
SendCommand(&c);
|
||||
|
||||
initialize = false;
|
||||
if (field_off) finished = true;
|
||||
|
||||
if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (resp.arg[0]) {
|
||||
return resp.arg[0]; // error during nested
|
||||
}
|
||||
if (initialize) {
|
||||
if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) return 1;
|
||||
if (resp.arg[0]) return resp.arg[0]; // error during nested_hard
|
||||
|
||||
cuid = resp.arg[1];
|
||||
uint16_t num_acquired_nonces = resp.arg[2];
|
||||
|
||||
//PrintAndLog("Received %d nonces", num_acquired_nonces);
|
||||
|
||||
// PrintAndLog("Acquiring nonces for CUID 0x%08x", cuid);
|
||||
if (nonce_file_write && fnonces == NULL) {
|
||||
if ((fnonces = fopen("nonces.bin","wb")) == NULL) {
|
||||
PrintAndLog("Could not create file nonces.bin");
|
||||
return 1;
|
||||
return 3;
|
||||
}
|
||||
PrintAndLog("Writing acquired nonces to binary file nonces.bin");
|
||||
num_to_bytes(cuid, 4, write_buf);
|
||||
fwrite(write_buf, 1, 4, fnonces);
|
||||
fwrite(&trgBlockNo, 1, 1, fnonces);
|
||||
fwrite(&trgKeyType, 1, 1, fnonces);
|
||||
}
|
||||
PrintAndLog("Writing acquired nonces to binary file nonces.bin...");
|
||||
fwrite(&cuid, 1, sizeof(cuid), fnonces);
|
||||
fwrite(&trgBlockNo, 1, sizeof(trgBlockNo), fnonces);
|
||||
fwrite(&trgKeyType, 1, sizeof(trgKeyType), fnonces);
|
||||
}
|
||||
|
||||
if (!initialize) {
|
||||
uint32_t nt_enc1, nt_enc2;
|
||||
uint8_t par_enc;
|
||||
uint16_t num_acquired_nonces = resp.arg[2];
|
||||
uint8_t *bufp = resp.d.asBytes;
|
||||
for (uint16_t i = 0; i < num_acquired_nonces/2; i++) {
|
||||
nt_enc1 = bytes_to_num(bufp, sizeof(nt_enc1));
|
||||
bufp += sizeof(nt_enc1);
|
||||
nt_enc2 = bytes_to_num(bufp, sizeof(nt_enc2));
|
||||
bufp += sizeof(nt_enc2);
|
||||
par_enc = bytes_to_num(bufp, sizeof(par_enc));
|
||||
bufp += sizeof(par_enc);
|
||||
|
||||
add_nonce(nt_enc1, par_enc >> 4);
|
||||
add_nonce(nt_enc2, par_enc & 0x0f);
|
||||
for (uint16_t i = 0; i < num_acquired_nonces; i+=2) {
|
||||
nt_enc1 = bytes_to_num(bufp, 4);
|
||||
nt_enc2 = bytes_to_num(bufp+4, 4);
|
||||
par_enc = bytes_to_num(bufp+8, 1);
|
||||
|
||||
//printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4);
|
||||
total_added_nonces += add_nonce(nt_enc1, par_enc >> 4);
|
||||
//printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f);
|
||||
total_added_nonces += add_nonce(nt_enc2, par_enc & 0x0f);
|
||||
|
||||
|
||||
if (nonce_file_write) {
|
||||
uint8_t buffer[9];
|
||||
num_to_bytes(nt_enc1, 4, buffer);
|
||||
num_to_bytes(nt_enc2, 4, buffer+4);
|
||||
num_to_bytes(par_enc, 1, buffer+8);
|
||||
fwrite(buffer, 1, 9, fnonces);
|
||||
fwrite(bufp, 1, 9, fnonces);
|
||||
}
|
||||
|
||||
bufp += 9;
|
||||
}
|
||||
|
||||
total_num_nonces += num_acquired_nonces;
|
||||
}
|
||||
|
||||
if (first_byte_num == 256 ) {
|
||||
// printf("first_byte_num = %d, first_byte_Sum = %d\n", first_byte_num, first_byte_Sum);
|
||||
float last_confidence = guessed_Sum8_confidence;
|
||||
guessed_Sum8_confidence = estimate_second_byte_sum();
|
||||
if (guessed_Sum8_confidence > last_confidence || total_num_nonces > next_thousand) {
|
||||
next_thousand = (total_num_nonces/1000+1) * 1000;
|
||||
PrintAndLog("Acquired %5d nonces (%5d with distinct bytes 0 and 1). Guessed Sum(a8) = %3d for first nonce byte = 0x%02x, probability for correct guess = %1.2f%%",
|
||||
total_num_nonces,
|
||||
total_added_nonces,
|
||||
guessed_Sum8,
|
||||
best_first_byte,
|
||||
guessed_Sum8_confidence*100);
|
||||
}
|
||||
if (guessed_Sum8_confidence >= CONFIDENCE_THRESHOLD) {
|
||||
field_off = true; // switch off field with next SendCommand and then finish
|
||||
}
|
||||
}
|
||||
|
||||
if (!initialize) {
|
||||
if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) return 1;
|
||||
if (resp.arg[0]) return resp.arg[0]; // error during nested_hard
|
||||
}
|
||||
|
||||
initialize = false;
|
||||
|
||||
} while (!finished);
|
||||
|
||||
} while (total_num_nonces < 5000);
|
||||
|
||||
if (nonce_file_write) {
|
||||
fclose(fnonces);
|
||||
}
|
||||
|
||||
PrintAndLog("Acquired a total of %d nonces at a rate of %d nonces/minute", total_num_nonces, total_num_nonces*60*CLOCKS_PER_SEC/(clock() - time1));
|
||||
PrintAndLog("Acquired a total of %d nonces in %1.1f seconds (%d nonces/minute)",
|
||||
total_num_nonces,
|
||||
((float)clock()-time1)/CLOCKS_PER_SEC,
|
||||
total_num_nonces*60*CLOCKS_PER_SEC/(clock()-time1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow)
|
||||
{
|
||||
|
||||
// initialize the list of nonces
|
||||
for (uint16_t i = 0; i < 256; i++) {
|
||||
nonces[i].num = 0;
|
||||
nonces[i].Sum = 0;
|
||||
nonces[i].first = NULL;
|
||||
nonces[i].updated = true;
|
||||
}
|
||||
first_byte_num = 0;
|
||||
first_byte_Sum = 0;
|
||||
guessed_Sum8 = 0;
|
||||
best_first_byte = 0;
|
||||
guessed_Sum8_confidence = 0.0;
|
||||
|
||||
//StateList_t statelists[2];
|
||||
//struct Crypto1State *p1, *p2, *p3, *p4;
|
||||
|
||||
if (nonce_file_read) { // use pre-acquired data from file nonces.bin
|
||||
if (read_nonce_file() != 0) {
|
||||
return 3;
|
||||
}
|
||||
guessed_Sum8_confidence = estimate_second_byte_sum();
|
||||
} else { // acquire nonces.
|
||||
uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow);
|
||||
if (is_OK != 0) {
|
||||
return is_OK;
|
||||
}
|
||||
}
|
||||
|
||||
PrintAndLog("first_byte_num: %d, first_byte_Sum: %d", first_byte_num, first_byte_Sum);
|
||||
Tests();
|
||||
|
||||
TestSumProperty();
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Sum(a0) = %d", first_byte_Sum);
|
||||
PrintAndLog("Guess for Sum(a8) = %d for first nonce byte = 0x%02x, n = %d, k = %d, probability for correct guess = %1.0f%%\n",
|
||||
guessed_Sum8,
|
||||
best_first_byte,
|
||||
nonces[best_first_byte].num,
|
||||
nonces[best_first_byte].Sum,
|
||||
guessed_Sum8_confidence*100);
|
||||
|
||||
PrintAndLog("Generation of candidate list and brute force phase not yet implemented");
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue