2 // bruteforce the upper 16bits of a partial key recovered from mf_nonce_brute.
3 // J-run's original idea was a two part recovery vector with first a offline trace and then online for 2 bytes.
5 // This idea is two use only offline, to recover a nested authentication key.
6 // Assumption, we get a read/write command after a nested auth, we need 22 bytes of data.
10 #define __STDC_FORMAT_MACROS
20 #include "crapto1/crapto1.h"
22 #include "iso14443crc.h"
23 #include <util_posix.h>
25 #define AEND "\x1b[0m"
26 #define _RED_(s) "\x1b[31m" s AEND
27 #define _GREEN_(s) "\x1b[32m" s AEND
28 #define _YELLOW_(s) "\x1b[33m" s AEND
29 #define _CYAN_(s) "\x1b[36m" s AEND
31 // a global mutex to prevent interlaced printing from different threads
32 pthread_mutex_t print_lock
;
34 #define ENC_LEN (4 + 16 + 2)
35 //--------------------- define options here
37 typedef struct thread_args
{
45 uint8_t enc
[ENC_LEN
]; // next encrypted command + a full read/write
48 //------------------------------------------------------------------
49 uint8_t cmds
[8][2] = {
50 {ISO14443A_CMD_READBLOCK
, 18},
51 {ISO14443A_CMD_WRITEBLOCK
, 18},
52 {MIFARE_AUTH_KEYA
, 0},
53 {MIFARE_AUTH_KEYB
, 0},
56 {MIFARE_CMD_RESTORE
, 6},
57 {MIFARE_CMD_TRANSFER
, 0}
60 static int global_found
= 0;
61 static int thread_count
= 2;
63 static int param_getptr(const char *line
, int *bg
, int *en
, int paramnum
) {
65 int len
= strlen(line
);
71 while (line
[*bg
] == ' ' || line
[*bg
] == '\t')(*bg
)++;
76 for (i
= 0; i
< paramnum
; i
++) {
77 while (line
[*bg
] != ' ' && line
[*bg
] != '\t' && line
[*bg
] != '\0')(*bg
)++;
78 while (line
[*bg
] == ' ' || line
[*bg
] == '\t')(*bg
)++;
80 if (line
[*bg
] == '\0') return 1;
84 while (line
[*en
] != ' ' && line
[*en
] != '\t' && line
[*en
] != '\0')(*en
)++;
91 static int param_gethex_to_eol(const char *line
, int paramnum
, uint8_t *data
, int maxdatalen
, int *datalen
) {
96 if (param_getptr(line
, &bg
, &en
, paramnum
)) return 1;
102 if (line
[indx
] == '\t' || line
[indx
] == ' ') {
107 if (isxdigit(line
[indx
])) {
108 buf
[strlen(buf
) + 1] = 0x00;
109 buf
[strlen(buf
)] = line
[indx
];
111 // if we have symbols other than spaces and hex
115 if (*datalen
>= maxdatalen
) {
116 // if we don't have space in buffer and have symbols to translate
120 if (strlen(buf
) >= 2) {
121 sscanf(buf
, "%x", &temp
);
122 data
[*datalen
] = (uint8_t)(temp
& 0xff);
131 //error when not completed hex bytes
137 static void hex_to_buffer(const uint8_t *buf
, const uint8_t *hex_data
, const size_t hex_len
, const size_t hex_max_len
,
138 const size_t min_str_len
, const size_t spaces_between
, bool uppercase
) {
140 if (buf
== NULL
) return;
142 char *tmp_base
= (char *)buf
;
143 char *tmp
= tmp_base
;
146 size_t max_len
= (hex_len
> hex_max_len
) ? hex_max_len
: hex_len
;
148 for (i
= 0; i
< max_len
; ++i
, tmp
+= 2 + spaces_between
) {
149 snprintf(tmp
, hex_max_len
- (tmp
- tmp_base
), (uppercase
) ? "%02X" : "%02x", (unsigned int) hex_data
[i
]);
151 for (size_t j
= 0; j
< spaces_between
; j
++)
152 snprintf(tmp
+ 2 + j
, hex_max_len
- (2 + j
+ (tmp
- tmp_base
)), " ");
155 i
*= (2 + spaces_between
);
157 size_t mlen
= min_str_len
> i
? min_str_len
: 0;
158 if (mlen
> hex_max_len
)
161 for (; i
< mlen
; i
++, tmp
+= 1)
162 snprintf(tmp
, hex_max_len
- (tmp
- tmp_base
), " ");
168 static char *sprint_hex_inrow_ex(const uint8_t *data
, const size_t len
, const size_t min_str_len
) {
169 static char buf
[100] = {0};
170 hex_to_buffer((uint8_t *)buf
, data
, len
, sizeof(buf
) - 1, min_str_len
, 0, true);
174 static bool checkValidCmdByte(uint8_t *cmd
, uint16_t n
) {
179 for (int i
= 0; i
< 8; ++i
) {
180 if (cmd
[0] == cmds
[i
][0]) {
183 ok
= CheckCrc14443(CRC_14443_A
, cmd
, 4);
185 if (cmds
[i
][1] > 0 && n
>= cmds
[i
][1])
186 ok
= CheckCrc14443(CRC_14443_A
, cmd
+ 4, cmds
[i
][1]);
196 static void *brute_thread(void *arguments
) {
198 struct thread_args
*args
= (struct thread_args
*) arguments
;
199 uint64_t key
= args
->part_key
;
200 uint8_t local_enc
[args
->enc_len
];
201 memcpy(local_enc
, args
->enc
, args
->enc_len
);
203 for (uint64_t count
= args
->idx
; count
< 0xFFFF; count
+= thread_count
) {
205 if (__atomic_load_n(&global_found
, __ATOMIC_ACQUIRE
) == 1) {
211 // Init cipher with key
212 struct Crypto1State
*pcs
= crypto1_create(key
);
214 // NESTED decrypt nt with help of new key
215 crypto1_word(pcs
, args
->nt_enc
^ args
->uid
, 1);
216 crypto1_word(pcs
, args
->nr_enc
, 1);
217 crypto1_word(pcs
, 0, 0);
218 crypto1_word(pcs
, 0, 0);
221 uint8_t dec
[args
->enc_len
];
222 for (int i
= 0; i
< args
->enc_len
; i
++)
223 dec
[i
] = crypto1_byte(pcs
, 0x00, 0) ^ local_enc
[i
];
225 crypto1_destroy(pcs
);
227 if (checkValidCmdByte(dec
, args
->enc_len
) == false) {
230 __sync_fetch_and_add(&global_found
, 1);
232 // lock this section to avoid interlacing prints from different threats
233 pthread_mutex_lock(&print_lock
);
234 printf("\nenc: %s\n", sprint_hex_inrow_ex(local_enc
, args
->enc_len
, 0));
235 printf("dec: %s\n", sprint_hex_inrow_ex(dec
, args
->enc_len
, 0));
236 printf("\nValid Key found [ " _GREEN_("%012" PRIx64
) " ]\n\n", key
);
237 pthread_mutex_unlock(&print_lock
);
245 static int usage(void) {
246 printf(" syntax: mf_trace_brute <uid> <partial key> <{nt}> <{nr}> [<{next_command + 18 bytes}>]\n\n");
250 int main(int argc
, const char *argv
[]) {
251 printf("Mifare classic nested auth key recovery Phase 2\n");
252 if (argc
< 3) return usage();
254 uint32_t uid
= 0; // serial number
255 uint32_t part_key
= 0; // last 4 keys of key
256 uint32_t nt_enc
= 0; // noncce tag
257 uint32_t nr_enc
= 0; // nonce reader encrypted
259 sscanf(argv
[1], "%x", &uid
);
260 sscanf(argv
[2], "%x", &part_key
);
261 sscanf(argv
[3], "%x", &nt_enc
);
262 sscanf(argv
[4], "%x", &nr_enc
);
265 uint8_t enc
[ENC_LEN
] = {0}; // next encrypted command + a full read/write
266 param_gethex_to_eol(argv
[5], 0, enc
, sizeof(enc
), &enc_len
);
268 printf("-------------------------------------------------\n");
269 printf("uid.................. %08x\n", uid
);
270 printf("partial key.......... %08x\n", part_key
);
271 printf("nt enc............... %08x\n", nt_enc
);
272 printf("nr enc............... %08x\n", nr_enc
);
273 printf("next encrypted cmd... %s\n", sprint_hex_inrow_ex(enc
, enc_len
, 0));
275 uint64_t t1
= msclock();
277 #if !defined(_WIN32) || !defined(__WIN32__)
278 thread_count
= sysconf(_SC_NPROCESSORS_CONF
);
279 if (thread_count
< 2)
283 printf("\nBruteforce using %d threads to find upper 16bits of key\n", thread_count
);
285 pthread_t threads
[thread_count
];
287 // create a mutex to avoid interlacing print commands from our different threads
288 pthread_mutex_init(&print_lock
, NULL
);
291 for (int i
= 0; i
< thread_count
; ++i
) {
292 struct thread_args
*a
= calloc(1, sizeof(struct thread_args
));
296 a
->part_key
= part_key
;
299 a
->enc_len
= enc_len
;
300 memcpy(a
->enc
, enc
, enc_len
);
301 pthread_create(&threads
[i
], NULL
, brute_thread
, (void *)a
);
304 // wait for threads to terminate:
305 for (int i
= 0; i
< thread_count
; ++i
)
306 pthread_join(threads
[i
], NULL
);
308 if (global_found
== false) {
309 printf("\nFailed to find a key\n\n");
314 printf("execution time " _YELLOW_("%.2f") " sec\n", (float)t1
/ 1000.0);
317 pthread_mutex_destroy(&print_lock
);