7 #include "nested_util.h"
8 #include "crapto1/crapto1.h"
11 #define AEND "\x1b[0m"
12 #define _RED_(s) "\x1b[31m" s AEND
13 #define _GREEN_(s) "\x1b[32m" s AEND
14 #define _YELLOW_(s) "\x1b[33m" s AEND
15 #define _CYAN_(s) "\x1b[36m" s AEND
19 struct Crypto1State
*slhead
;
23 struct Crypto1State
*sltail
;
35 inline static int compare_uint64(const void *a
, const void *b
) {
36 if (*(uint64_t *)b
== *(uint64_t *)a
) return 0;
37 if (*(uint64_t *)b
< * (uint64_t *)a
) return 1;
41 // Compare 16 Bits out of cryptostate
42 inline static int compare16Bits(const void *a
, const void *b
) {
43 if ((*(uint64_t *)b
& 0x00ff000000ff0000) == (*(uint64_t *)a
& 0x00ff000000ff0000)) return 0;
44 if ((*(uint64_t *)b
& 0x00ff000000ff0000) > (*(uint64_t *)a
& 0x00ff000000ff0000)) return 1;
48 // create the intersection (common members) of two sorted lists. Lists are terminated by -1. Result will be in list1. Number of elements is returned.
49 static uint32_t intersection(uint64_t *listA
, uint64_t *listB
) {
50 if (listA
== NULL
|| listB
== NULL
)
53 uint64_t *p1
, *p2
, *p3
;
57 while (*p1
!= UINT64_C(-1) && *p2
!= UINT64_C(-1)) {
58 if (compare_uint64(p1
, p2
) == 0) {
62 while (compare_uint64(p1
, p2
) < 0) ++p1
;
63 while (compare_uint64(p1
, p2
) > 0) ++p2
;
70 // wrapper function for multi-threaded lfsr_recovery32
72 #ifdef __has_attribute
73 #if __has_attribute(force_align_arg_pointer)
74 __attribute__((force_align_arg_pointer
))
77 *nested_worker_thread(void *arg
) {
78 struct Crypto1State
*p1
;
79 StateList_t
*statelist
= arg
;
80 statelist
->head
.slhead
= lfsr_recovery32(statelist
->ks1
, statelist
->nt_enc
^ statelist
->uid
);
82 for (p1
= statelist
->head
.slhead
; p1
->odd
| p1
->even
; p1
++) {};
84 statelist
->len
= p1
- statelist
->head
.slhead
;
85 statelist
->tail
.sltail
= --p1
;
87 qsort(statelist
->head
.slhead
, statelist
->len
, sizeof(uint64_t), compare16Bits
);
89 return statelist
->head
.slhead
;
92 static void pm3_staticnested(uint32_t uid
, uint32_t nt1
, uint32_t ks1
, uint32_t nt2
, uint32_t ks2
) {
94 StateList_t statelists
[2];
95 struct Crypto1State
*p1
, * p2
, * p3
, * p4
;
97 for (uint8_t i
= 0; i
< 2; i
++) {
98 statelists
[i
].uid
= uid
;
101 statelists
[0].nt_enc
= nt1
;
102 statelists
[0].ks1
= ks1
;
103 statelists
[1].nt_enc
= nt2
;
104 statelists
[1].ks1
= ks2
;
107 pthread_t thread_id
[2];
109 // create and run worker threads
110 for (uint8_t i
= 0; i
< 2; i
++) {
111 pthread_create(thread_id
+ i
, NULL
, nested_worker_thread
, &statelists
[i
]);
114 // wait for threads to terminate:
115 for (uint8_t i
= 0; i
< 2; i
++) {
116 pthread_join(thread_id
[i
], (void *)&statelists
[i
].head
.slhead
);
119 // the first 16 Bits of the cryptostate already contain part of our key.
120 // Create the intersection of the two lists based on these 16 Bits and
121 // roll back the cryptostate
122 p1
= p3
= statelists
[0].head
.slhead
;
123 p2
= p4
= statelists
[1].head
.slhead
;
125 while (p1
<= statelists
[0].tail
.sltail
&& p2
<= statelists
[1].tail
.sltail
) {
126 if (compare16Bits(p1
, p2
) == 0) {
128 struct Crypto1State savestate
;
131 while (compare16Bits(p1
, &savestate
) == 0 && p1
<= statelists
[0].tail
.sltail
) {
133 lfsr_rollback_word(p3
, statelists
[0].nt_enc
^ statelists
[0].uid
, 0);
139 while (compare16Bits(p2
, &savestate
) == 0 && p2
<= statelists
[1].tail
.sltail
) {
141 lfsr_rollback_word(p4
, statelists
[1].nt_enc
^ statelists
[1].uid
, 0);
146 while (compare16Bits(p1
, p2
) == -1) p1
++;
147 while (compare16Bits(p1
, p2
) == 1) p2
++;
155 statelists
[0].len
= p3
- statelists
[0].head
.slhead
;
156 statelists
[1].len
= p4
- statelists
[1].head
.slhead
;
157 statelists
[0].tail
.sltail
= --p3
;
158 statelists
[1].tail
.sltail
= --p4
;
160 // the statelists now contain possible keys. The key we are searching for must be in the
161 // intersection of both lists
162 qsort(statelists
[0].head
.keyhead
, statelists
[0].len
, sizeof(uint64_t), compare_uint64
);
163 qsort(statelists
[1].head
.keyhead
, statelists
[1].len
, sizeof(uint64_t), compare_uint64
);
164 // Create the intersection
165 statelists
[0].len
= intersection(statelists
[0].head
.keyhead
, statelists
[1].head
.keyhead
);
167 uint32_t keycnt
= statelists
[0].len
;
169 printf("PM3 Static nested --> Found " _YELLOW_("%u") " key candidates\n", keycnt
);
170 for (uint32_t k
= 0; k
< keycnt
; k
++) {
172 crypto1_get_lfsr(statelists
[0].head
.slhead
+ k
, &key64
);
173 printf("[ %u ] " _GREEN_("%012" PRIx64
) "\n", k
+ 1, key64
);
178 static int usage(void) {
180 printf("\nProgram tries to recover keys from static encrypted nested MFC cards\n");
181 printf("using two different implementations, Chameleon Ultra (CU) and Proxmark3.\n");
182 printf("It uses the nonce, keystream sent from pm3 device to client.\n");
183 printf("ie: NOT the CU data which is data in the trace.\n");
185 printf("syntax: staticnested <uid> <nt1> <ks1> <nt2> <ks2>\n\n");
186 printf("samples:\n");
188 printf(" ./staticnested 461dce03 7eef3586 ffb02eda 322bc14d ffc875ca\n");
189 printf(" ./staticnested 461dce03 7eef3586 1fb6b496 322bc14d 1f4eebdd\n");
190 printf(" ./staticnested 461dce03 7eef3586 7fa28c7e 322bc14d 7f62b3d6\n");
195 int main(int argc
, char *const argv
[]) {
197 printf("\nMIFARE Classic static nested key recovery\n\n");
199 if (argc
< 5) return usage();
202 NtpKs1
*pNK
= calloc(2, sizeof(NtpKs1
));
209 sscanf(argv
[1], "%x", &uid
);
210 sscanf(argv
[2], "%x", &pNK
[0].ntp
);
211 sscanf(argv
[3], "%x", &pNK
[0].ks1
);
212 sscanf(argv
[4], "%x", &pNK
[1].ntp
);
213 sscanf(argv
[5], "%x", &pNK
[1].ks1
);
215 printf("uid... %08x\n", uid
);
216 printf("nt1... %08x\n", pNK
[0].ntp
);
217 printf("ks1... %08x\n", pNK
[0].ks1
);
218 printf("nt2... %08x\n", pNK
[1].ntp
);
219 printf("ks2... %08x\n", pNK
[1].ks1
);
222 printf("Recovery...\n");
224 uint32_t key_count
= 0;
225 uint64_t *keys
= nested(pNK
, 2, uid
, &key_count
);
228 printf("Ultra Static nested --> Found " _YELLOW_("%u") " key candidates\n", key_count
);
229 for (uint32_t k
= 0; k
< key_count
; k
++) {
230 printf("[ %u ] " _GREEN_("%012" PRIx64
) "\n", k
+ 1, keys
[k
]);
234 pm3_staticnested(uid
, pNK
[0].ntp
, pNK
[0].ks1
, pNK
[1].ntp
, pNK
[1].ks1
);