Added few tools to tackle FM11RF08S cards

This commit is contained in:
Philippe Teuwen 2024-07-30 16:41:00 +02:00
parent dc287c232f
commit 7fec0d693c
7 changed files with 968 additions and 0 deletions

5
.gitignore vendored
View file

@ -82,6 +82,11 @@ tools/cryptorf/cm
tools/cryptorf/sm
tools/cryptorf/sma
tools/cryptorf/sma_multi
tools/mf_fudan_rf08s/rf08s_nested
tools/mf_fudan_rf08s/rf08s_nested_known
tools/mf_fudan_rf08s/rf08s_nested_known_collision
tools/mf_fudan_rf08s/rf08s_nested_known_match
tools/mf_fudan_rf08s/keys*
tools/mf_nonce_brute/mf_nonce_brute
tools/mf_nonce_brute/mf_trace_brute
tools/jtag_openocd/openocd_configuration

View file

@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Added few tools to tackle FM11RF08S cards (@doegox)
- Changed standalone mode HF_MATTYRUN - support more card sizes, user dictionaries, improved emulation (@michaelroland)
- Changed `hf iclass dump --ns` - now supports the nosave flag (@iceman1001)
- Fixed write check in hitag2crack2 buildtables (@mwalker33)

View file

@ -0,0 +1,33 @@
MYSRCPATHS = ../../common ../../common/crapto1
MYSRCS = crypto1.c crapto1.c bucketsort.c
MYINCLUDES = -I../../include -I../../common
MYCFLAGS = -O3
MYDEFS =
BINS = rf08s_nested rf08s_nested_known rf08s_nested_known_collision rf08s_nested_known_match
INSTALLTOOLS = $(BINS)
include ../../Makefile.host
# rf08s_nested.c needs pthread support. Older glibc needs it externally
ifneq ($(SKIPPTHREAD),1)
MYLDLIBS += -lpthread
endif
# checking platform can be done only after Makefile.host
ifneq (,$(findstring MINGW,$(platform)))
# Mingw uses by default Microsoft printf, we want the GNU printf (e.g. for %z)
# and setting _ISOC99_SOURCE sets internally __USE_MINGW_ANSI_STDIO=1
CFLAGS += -D_ISOC99_SOURCE
endif
# macOS doesn't like these compiler params
ifneq ($(platform),Darwin)
MYCFLAGS += --param max-completely-peeled-insns=1000 --param max-completely-peel-times=10000
endif
rf08s_nested : $(OBJDIR)/rf08s_nested.o $(MYOBJS)
rf08s_nested_known : $(OBJDIR)/rf08s_nested_known.o $(MYOBJS)
rf08s_nested_known_collision : $(OBJDIR)/rf08s_nested_known_collision.o $(MYOBJS)
rf08s_nested_known_match : $(OBJDIR)/rf08s_nested_known_match.o $(MYOBJS)

View file

@ -0,0 +1,406 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <pthread.h>
#include "common.h"
#include "crapto1/crapto1.h"
#include "parity.h"
#define NUM_THREADS 20
#define BATCH_SIZE (8192 / NUM_THREADS)
// oversized just in case...
#define KEY_SPACE_SIZE ((1 << 16) * 4)
// we expect intersection to be about 250-500 keys. Oversized just in case...
#define KEY_SPACE_SIZE_STEP2 2000
#define MAX_NR_NONCES 32
typedef struct {
uint32_t ntp;
uint32_t ks1;
} NtpKs1;
typedef struct {
uint32_t authuid;
NtpKs1 *pNK;
uint32_t sizeNK;
uint32_t nt_enc;
uint8_t nt_par_enc;
} NtData;
typedef struct {
NtData NtDataList[MAX_NR_NONCES];
uint32_t nr_nonces;
} NtpKs1List;
// Struct for thread data
typedef struct {
NtpKs1List *pNKL;
uint32_t startPos;
uint32_t endPos;
uint32_t *keyCount[MAX_NR_NONCES];
uint64_t *result_keys[MAX_NR_NONCES];
uint32_t thread_id;
pthread_mutex_t *keyCount_mutex[MAX_NR_NONCES];
uint32_t num_nonces;
} thread_data_t;
static uint32_t hex_to_uint32(const char *hex_str) {
return (uint32_t)strtoul(hex_str, NULL, 16);
}
static int bin_to_uint8_arr(const char *bin_str, uint8_t bit_arr[], uint8_t arr_size) {
if (strlen(bin_str) != arr_size) {
fprintf(stderr, "Error: Binary string (%s) length does not match array size (%i).\n", bin_str, arr_size);
return 1;
}
for (uint8_t i = 0; i < arr_size; i++) {
if (bin_str[i] == '0') {
bit_arr[i] = 0;
} else if (bin_str[i] == '1') {
bit_arr[i] = 1;
} else {
fprintf(stderr, "Error: Invalid character '%c' in binary string.\n", bin_str[i]);
return 1;
}
}
return 0;
}
static uint8_t valid_nonce(uint32_t Nt, uint32_t ks1, uint8_t nt_par_enc) {
return (oddparity8((Nt >> 24) & 0xFF) == (((nt_par_enc >> 3) & 1) ^ BIT(ks1, 16))) &&
(oddparity8((Nt >> 16) & 0xFF) == (((nt_par_enc >> 2) & 1) ^ BIT(ks1, 8))) &&
(oddparity8((Nt >> 8) & 0xFF) == (((nt_par_enc >> 1) & 1) ^ BIT(ks1, 0)));
}
static bool search_match(NtData *pND, NtData *pND0, uint64_t key) {
bool ret = 0;
struct Crypto1State *s;
s = crypto1_create(0);
if (s == NULL) {
fprintf(stderr, "\nMalloc error in search_match!\n");
return 0;
}
crypto1_init(s, key);
uint32_t authuid = pND->authuid;
uint32_t nt_enc = pND->nt_enc;
uint8_t nt_par_enc = pND->nt_par_enc;
uint32_t nt = crypto1_word(s, nt_enc ^ authuid, 1) ^ nt_enc;
// filter: we know nt should be valid, no need to spend time on other ones
if (validate_prng_nonce(nt)) {
// look for same nt
for (uint32_t k = 0; k < pND->sizeNK; k++) {
if (nt == pND->pNK[k].ntp) {
// Possible match
// filter: check the full 4 bits of parity (actually only the last one might be wrong)
uint32_t ks1, ks2;
uint8_t par1, par2, ksp;
par1 = (oddparity8((nt >> 24) & 0xFF) << 3) | (oddparity8((nt >> 16) & 0xFF) << 2) | (oddparity8((nt >> 8) & 0xFF) << 1) | (oddparity8(nt & 0xFF));
ks1 = nt ^ nt_enc;
ks2 = crypto1_word(s, 0, 0);
ksp = (((ks1 >> 16) & 1) << 3) | (((ks1 >> 8) & 1) << 2) | (((ks1 >> 0) & 1) << 1) | ((ks2 >> 24) & 1);
par2 = nt_par_enc ^ ksp;
if (par1 != par2) {
continue;
}
// filter: same check on the full 4 bits of parity of initial nonce
// check is slow so we do it only now
crypto1_init(s, key);
authuid = pND0->authuid;
nt_enc = pND0->nt_enc;
nt_par_enc = pND0->nt_par_enc;
nt = crypto1_word(s, nt_enc ^ authuid, 1) ^ nt_enc;
par1 = (oddparity8((nt >> 24) & 0xFF) << 3) | (oddparity8((nt >> 16) & 0xFF) << 2) | (oddparity8((nt >> 8) & 0xFF) << 1) | (oddparity8(nt & 0xFF));
ks1 = nt ^ nt_enc;
ks2 = crypto1_word(s, 0, 0);
ksp = (((ks1 >> 16) & 1) << 3) | (((ks1 >> 8) & 1) << 2) | (((ks1 >> 0) & 1) << 1) | ((ks2 >> 24) & 1);
par2 = nt_par_enc ^ ksp;
if (par1 != par2) {
continue;
}
k = pND->sizeNK;
ret = 1;
}
}
}
crypto1_destroy(s);
return ret;
}
static void *generate_and_intersect_keys(void *threadarg) {
thread_data_t *data = (thread_data_t *)threadarg;
NtpKs1List *pNKL = data->pNKL;
uint32_t startPos = data->startPos;
uint32_t endPos = data->endPos;
uint32_t thread_id = data->thread_id;
uint32_t num_nonces = data->num_nonces;
struct Crypto1State *revstate, *revstate_start = NULL;
uint64_t lfsr = 0;
uint32_t authuid = pNKL->NtDataList[0].authuid;
for (uint32_t i = startPos; i < endPos; i++) {
uint32_t ntp = pNKL->NtDataList[0].pNK[i].ntp;
uint32_t ks1 = pNKL->NtDataList[0].pNK[i].ks1;
uint32_t nt_probe = ntp ^ authuid;
revstate = lfsr_recovery32(ks1, nt_probe);
if (revstate == NULL) {
fprintf(stderr, "\nMalloc error in generate_and_intersect_keys!\n");
pthread_exit(NULL);
}
if (revstate_start == NULL) {
revstate_start = revstate;
}
uint32_t keyCount0 = 0;
while ((revstate->odd != 0x0) || (revstate->even != 0x0)) {
lfsr_rollback_word(revstate, nt_probe, 0);
crypto1_get_lfsr(revstate, &lfsr);
keyCount0++;
for (uint32_t nonce_index = 1; nonce_index < num_nonces; nonce_index++) {
if (search_match(&pNKL->NtDataList[nonce_index], &pNKL->NtDataList[0], lfsr)) {
pthread_mutex_lock(data->keyCount_mutex[nonce_index]);
data->result_keys[nonce_index][*data->keyCount[nonce_index]] = lfsr;
(*data->keyCount[nonce_index])++;
pthread_mutex_unlock(data->keyCount_mutex[nonce_index]);
if (*data->keyCount[nonce_index] == KEY_SPACE_SIZE_STEP2) {
fprintf(stderr, "No space left on result_keys[%d], abort!\n", nonce_index);
i = endPos;
break;
}
}
}
revstate++;
}
free(revstate_start);
revstate_start = NULL;
pthread_mutex_lock(data->keyCount_mutex[0]);
(*data->keyCount[0]) += keyCount0;
keyCount0 = 0;
pthread_mutex_unlock(data->keyCount_mutex[0]);
if ((i != startPos) && ((i - startPos) % ((endPos - startPos) / 20) == 0)) { // every 5%
printf("\rThread %3i %3i%%", thread_id, (i - startPos) * 100 / (endPos - startPos));
printf(" keys[%d]:%9i", 0, *data->keyCount[0]);
for (uint32_t nonce_index = 1; nonce_index < num_nonces; nonce_index++) {
printf(" keys[%d]:%5i", nonce_index, *data->keyCount[nonce_index]);
}
fflush(stdout);
}
}
pthread_exit(NULL);
}
static uint64_t **unpredictable_nested(NtpKs1List *pNKL, uint32_t keyCounts[]) {
pthread_t threads[NUM_THREADS];
thread_data_t thread_data[NUM_THREADS];
pthread_mutex_t keyCount_mutex[MAX_NR_NONCES];
uint64_t **result_keys = (uint64_t **)malloc(MAX_NR_NONCES * sizeof(uint64_t *));
// no result_keys[0] stored, would be too large
for (uint32_t i = 1; i < MAX_NR_NONCES; i++) {
result_keys[i] = (uint64_t *)malloc(KEY_SPACE_SIZE_STEP2 * sizeof(uint64_t));
keyCounts[i] = 0;
pthread_mutex_init(&keyCount_mutex[i], NULL);
}
uint32_t average = pNKL->NtDataList[0].sizeNK / NUM_THREADS;
uint32_t modulo = pNKL->NtDataList[0].sizeNK % NUM_THREADS;
for (uint32_t t = 0, j = 0; t < NUM_THREADS; t++, j += average) {
thread_data[t].pNKL = pNKL;
thread_data[t].startPos = j;
thread_data[t].endPos = j + average;
for (uint32_t i = 0; i < MAX_NR_NONCES; i++) {
thread_data[t].result_keys[i] = result_keys[i];
thread_data[t].keyCount[i] = &keyCounts[i];
thread_data[t].keyCount_mutex[i] = &keyCount_mutex[i];
}
thread_data[t].thread_id = t;
thread_data[t].num_nonces = pNKL->nr_nonces;
// last thread can decrypt more pNK
if (t == (NUM_THREADS - 1) && modulo > 0) {
thread_data[t].endPos += modulo;
}
pthread_create(&threads[t], NULL, generate_and_intersect_keys, (void *)&thread_data[t]);
}
printf("All threads spawn...\n");
for (uint32_t t = 0; t < NUM_THREADS; t++) {
pthread_join(threads[t], NULL);
}
// no result_keys[0]
for (uint32_t i = 1; i < MAX_NR_NONCES; i++) {
if (keyCounts[i] == 0) {
free(result_keys[i]);
result_keys[i] = NULL;
}
}
return result_keys;
}
// Function to compare keys and keep track of their occurrences
static void analyze_keys(uint64_t **keys, uint32_t keyCounts[MAX_NR_NONCES], uint32_t nr_nonces) {
// Assuming the maximum possible keys
#define MAX_KEYS (MAX_NR_NONCES * KEY_SPACE_SIZE_STEP2)
uint64_t combined_keys[MAX_KEYS] = {0};
uint32_t combined_counts[MAX_KEYS] = {0};
uint32_t combined_length = 0;
printf("Analyzing keys...\n");
for (uint32_t i = 0; i < nr_nonces; i++) {
if (i==0) {
printf("nT(%i): %i key candidates\n", i, keyCounts[i]);
continue;
} else {
printf("nT(%i): %i key candidates matching nT(0)\n", i, keyCounts[i]);
}
for (uint32_t j = 0; j < keyCounts[i]; j++) {
uint64_t key = keys[i][j];
// Check if key is already in combined_keys
bool found = false;
for (uint32_t k = 0; k < combined_length; k++) {
if (combined_keys[k] == key) {
combined_counts[k]++;
found = true;
break;
}
}
// If key not found, add it to combined_keys
if (!found) {
combined_keys[combined_length] = key;
combined_counts[combined_length] = 1;
combined_length++;
}
}
}
for (uint32_t i = 0; i < combined_length; i++) {
if (combined_counts[i] > 1) {
printf("Key %012" PRIx64 " found in %d arrays: 0", combined_keys[i], combined_counts[i]+1);
for (uint32_t ii = 1; ii < nr_nonces; ii++) {
for (uint32_t j = 0; j < keyCounts[ii]; j++) {
if (combined_keys[i] == keys[ii][j]) {
printf(", %2i", ii);
}
}
}
printf("\n");
}
}
}
int main(int argc, char *const argv[]) {
if (argc < 2) {
int cmdlen = strlen(argv[0]);
printf("Usage:\n %s <uid1> <nt_enc1> <nt_par_err1> <uid2> <nt_enc2> <nt_par_err2> ...\n", argv[0]);
printf(" UID placeholder: if uid(n)==uid(n-1) you can use '.' as uid(n+1) placeholder\n");
printf(" parity example: if nt in trace is 7b! fc! 7a! 5b , then nt_enc is 7bfc7a5b and nt_par_err is 1110\n");
printf("Example:\n");
printf(" %*s a13e4902 2e9e49fc 1111 . 7bfc7a5b 1110 a17e4902 50f2abc2 1101\n", cmdlen, argv[0]);
printf(" %*s +uid1 | | +uid2=uid1 | +uid3 | |\n", cmdlen, "");
printf(" %*s +nt_enc1 | +nt_enc2 | +nt_enc3 |\n", cmdlen, "");
printf(" %*s +nt_par_err1 +nt_par_err2 +nt_par_err3\n", cmdlen, "");
return 1;
}
if (argc < 1 + 2 * 3) {
fprintf(stderr, "Too few nonces, abort. Need 2 nonces min.\n");
return 1;
}
if (argc > 1 + MAX_NR_NONCES * 3) {
fprintf(stderr, "Too many nonces, abort. Choose max %i nonces.\n", MAX_NR_NONCES);
return 1;
}
NtpKs1List NKL = {0};
uint64_t **keys = NULL;
uint32_t keyCounts[MAX_NR_NONCES] = {0};
uint32_t authuid = hex_to_uint32(argv[1]);
// process all args.
printf("Generating nonce candidates...\n");
for (uint32_t i = 1; i < argc; i += 3) {
// uid + ntEnc + parEnc
if (strcmp(argv[i], ".") != 0) {
authuid = hex_to_uint32(argv[i]);
}
uint32_t nt_enc = hex_to_uint32(argv[i + 1]);
uint8_t nt_par_err_arr[4];
if (bin_to_uint8_arr(argv[i + 2], nt_par_err_arr, 4)) {
return 1;
}
uint8_t nt_par_enc = ((nt_par_err_arr[0] ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3) |
((nt_par_err_arr[1] ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2) |
((nt_par_err_arr[2] ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1) |
((nt_par_err_arr[3] ^ oddparity8((nt_enc >> 0) & 0xFF)) << 0);
NtData *pNtData = &NKL.NtDataList[NKL.nr_nonces];
// Try to recover the keystream1
uint32_t nttest = prng_successor(1, 16); // a first valid nonce
pNtData->pNK = (NtpKs1 *)malloc(sizeof(NtpKs1) * 8192); // 2**16 filtered with 3 parity bits => 2**13
if (pNtData->pNK == NULL) {
return 1;
}
uint32_t j = 0;
for (uint16_t m = 1; m; m++) {
uint32_t ks1 = nt_enc ^ nttest;
if (valid_nonce(nttest, ks1, nt_par_enc)) {
pNtData->pNK[j].ntp = nttest;
pNtData->pNK[j].ks1 = ks1;
j++;
}
nttest = prng_successor(nttest, 1);
}
printf("uid=%08x nt_enc=%08x nt_par_err=%i%i%i%i nt_par_enc=%i%i%i%i %i/%i: %d\n", authuid, nt_enc,
nt_par_err_arr[0], nt_par_err_arr[1], nt_par_err_arr[2], nt_par_err_arr[3],
(nt_par_enc >> 3)&1, (nt_par_enc >> 2)&1, (nt_par_enc >> 1)&1, nt_par_enc&1,
NKL.nr_nonces + 1, (argc - 1) / 3, j);
pNtData->authuid = authuid;
pNtData->sizeNK = j;
pNtData->nt_enc = nt_enc;
pNtData->nt_par_enc = nt_par_enc;
NKL.nr_nonces++;
}
printf("Finding key candidates...\n");
keys = unpredictable_nested(&NKL, keyCounts);
printf("\n\nFinding phase complete.\n");
for (uint32_t k = 0; k < NKL.nr_nonces; k++)
free(NKL.NtDataList[k].pNK);
analyze_keys(keys, keyCounts, NKL.nr_nonces);
FILE* fptr;
// opening the file in read mode
fptr = fopen("keys.dic", "w");
if (fptr != NULL) {
for (uint32_t i = 1; i < NKL.nr_nonces; i++) {
if (keyCounts[i] > 0) {
for (uint32_t j = 0; j < keyCounts[i]; j++) {
fprintf(fptr, "%012" PRIx64 "\n", keys[i][j]);
}
}
}
fclose(fptr);
} else {
fprintf(stderr, "Warning: Cannot save keys in keys.dic\n");
}
for (uint32_t i = 1; i < NKL.nr_nonces; i++) {
if (keys[i] != NULL) {
free(keys[i]);
}
}
if (keys != NULL) {
free(keys);
}
return 0;
}

View file

@ -0,0 +1,153 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "common.h"
#include "crapto1/crapto1.h"
#include "parity.h"
#define KEY_SPACE_SIZE (1 << 18)
typedef struct {
uint32_t authuid;
uint32_t nt;
uint32_t nt_enc;
uint8_t nt_par_enc;
} NtData;
static uint32_t hex_to_uint32(const char *hex_str) {
return (uint32_t)strtoul(hex_str, NULL, 16);
}
static int bin_to_uint8_arr(const char *bin_str, uint8_t bit_arr[], uint8_t arr_size) {
if (strlen(bin_str) != arr_size) {
fprintf(stderr, "Error: Binary string (%s) length does not match array size (%i).\n", bin_str, arr_size);
return 1;
}
for (uint8_t i = 0; i < arr_size; i++) {
if (bin_str[i] == '0') {
bit_arr[i] = 0;
} else if (bin_str[i] == '1') {
bit_arr[i] = 1;
} else {
fprintf(stderr, "Error: Invalid character '%c' in binary string.\n", bin_str[i]);
return 1;
}
}
return 0;
}
static uint64_t *generate_keys(uint64_t authuid, uint32_t nt, uint32_t nt_enc, uint32_t nt_par_enc, uint32_t *keyCount) {
uint64_t *result_keys = (uint64_t *)malloc(KEY_SPACE_SIZE * sizeof(uint64_t));
if (result_keys == NULL) {
fprintf(stderr, "\nMalloc error in generate_and_intersect_keys!\n");
return NULL;
}
struct Crypto1State *revstate, *revstate_start = NULL, *s = NULL;
uint64_t lfsr = 0;
uint32_t ks1 = nt ^ nt_enc;
revstate = lfsr_recovery32(ks1, nt ^ authuid);
if (revstate == NULL) {
fprintf(stderr, "\nMalloc error in generate_keys!\n");
free(result_keys);
return NULL;
}
if (revstate_start == NULL) {
revstate_start = revstate;
}
s = crypto1_create(0);
if (s == NULL) {
fprintf(stderr, "\nMalloc error in generate_keys!\n");
free(result_keys);
crypto1_destroy(revstate_start);
return 0;
}
while ((revstate->odd != 0x0) || (revstate->even != 0x0)) {
lfsr_rollback_word(revstate, nt ^ authuid, 0);
crypto1_get_lfsr(revstate, &lfsr);
// only filtering possibility: last parity bit ks in ks2
uint32_t ks2;
uint8_t lastpar1, lastpar2, kslastp;
crypto1_init(s, lfsr);
crypto1_word(s, nt ^ authuid, 0);
ks2 = crypto1_word(s, 0, 0);
lastpar1 = oddparity8(nt & 0xFF);
kslastp = (ks2 >> 24) & 1;
lastpar2 = (nt_par_enc & 1) ^ kslastp;
if (lastpar1 == lastpar2) {
result_keys[(*keyCount)++] = lfsr;
if (*keyCount == KEY_SPACE_SIZE) {
fprintf(stderr, "No space left on result_keys, abort! Increase KEY_SPACE_SIZE\n");
break;
}
}
revstate++;
}
crypto1_destroy(s);
crypto1_destroy(revstate_start);
revstate_start = NULL;
return result_keys;
}
int main(int argc, char *const argv[]) {
if (argc != 6) {
int cmdlen = strlen(argv[0]);
printf("Usage:\n %s <uid:hex> <sector:dec> <nt:hex> <nt_enc:hex> <nt_par_err:bin>\n"
" parity example: if for block 63 == sector 15, nt in trace is 7b! fc! 7a! 5b\n"
" then nt_enc is 7bfc7a5b and nt_par_err is 1110\n"
"Example:\n"
" %*s a13e4902 15 d14191b3 2e9e49fc 1111\n"
" %*s +uid +s +nt +nt_enc +nt_par_err\n",
argv[0], cmdlen, argv[0], cmdlen, "");
return 1;
}
uint64_t *keys = NULL;
uint32_t keyCount = 0;
uint32_t authuid = hex_to_uint32(argv[1]);
uint32_t sector = hex_to_uint32(argv[2]);
uint32_t nt = hex_to_uint32(argv[3]);
uint32_t nt_enc = hex_to_uint32(argv[4]);
uint8_t nt_par_err_arr[4];
if (bin_to_uint8_arr(argv[5], nt_par_err_arr, 4)) {
return 1;
}
uint8_t nt_par_enc = ((nt_par_err_arr[0] ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3) |
((nt_par_err_arr[1] ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2) |
((nt_par_err_arr[2] ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1) |
((nt_par_err_arr[3] ^ oddparity8((nt_enc >> 0) & 0xFF)) << 0);
printf("uid=%08x nt=%08x nt_enc=%08x nt_par_err=%i%i%i%i nt_par_enc=%i%i%i%i ks1=%08x\n", authuid, nt, nt_enc,
nt_par_err_arr[0], nt_par_err_arr[1], nt_par_err_arr[2], nt_par_err_arr[3],
(nt_par_enc >> 3)&1, (nt_par_enc >> 2)&1, (nt_par_enc >> 1)&1, nt_par_enc&1,
nt ^ nt_enc);
printf("Finding key candidates...\n");
keys = generate_keys(authuid, nt, nt_enc, nt_par_enc, &keyCount);
printf("Finding phase complete, found %i keys\n", keyCount);
FILE* fptr;
char filename[30];
snprintf(filename, sizeof(filename), "keys_%08x_%02i_%08x.dic", authuid, sector, nt);
fptr = fopen(filename, "w");
if (fptr != NULL) {
if (keyCount > 0) {
for (uint32_t j = 0; j < keyCount; j++) {
fprintf(fptr, "%012" PRIx64 "\n", keys[j]);
}
}
fclose(fptr);
} else {
fprintf(stderr, "Warning: Cannot save keys in %s\n", filename);
}
if (keys != NULL) {
free(keys);
}
return 0;
}

View file

@ -0,0 +1,230 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
uint16_t i_lfsr16[1 << 16] = {0};
uint16_t s_lfsr16[1 << 16] = {0};
static void init_lfsr16_table(void) {
uint16_t x = 1;
for (uint16_t i=1; i; ++i) {
i_lfsr16[(x & 0xff) << 8 | x >> 8] = i;
s_lfsr16[i] = (x & 0xff) << 8 | x >> 8;
x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15;
}
}
// static uint16_t next_lfsr16(uint16_t nonce) {
// return s_lfsr16[(i_lfsr16[nonce]+1) % 65535];
// }
static uint16_t prev_lfsr16(uint16_t nonce) {
return s_lfsr16[(i_lfsr16[nonce]-1) % 65535];
}
static uint16_t compute_seednt16_nt32(uint32_t nt32, uint64_t key) {
uint8_t a[] = {0, 8, 9, 4, 6, 11, 1, 15, 12, 5, 2, 13, 10, 14, 3, 7};
uint8_t b[] = {0, 13, 1, 14, 4, 10, 15, 7, 5, 3, 8, 6, 9, 2, 12, 11};
uint16_t nt = nt32 >> 16;
uint8_t prev = 14;
for (uint8_t i=0; i<prev; i++) {
nt = prev_lfsr16(nt);
}
uint8_t prevoff = 8;
bool odd = 1;
for (uint8_t i=0; i<6*8; i+=8) {
if (odd) {
nt ^= (a[(key >> i) & 0xF]);
nt ^= (b[(key >> i >> 4) & 0xF]) << 4;
} else {
nt ^= (b[(key >> i) & 0xF]);
nt ^= (a[(key >> i >> 4) & 0xF]) << 4;
}
odd ^= 1;
prev += prevoff;
for (uint8_t j=0; j<prevoff; j++) {
nt = prev_lfsr16(nt);
}
}
return nt;
}
int main(int argc, char *const argv[]) {
if (argc != 3) {
printf("Usage:\n %s keys_<uid:08x>_<sector:02>_<nt1:08x>.dic keys_<uid:08x>_<sector:02>_<nt2:08x>.dic\n"
" where both dict files are produced by rf08s_nested_known *for the same UID and same sector*\n",
argv[0]);
return 1;
}
uint32_t uid1, sector1, nt1, uid2, sector2, nt2;
char *filename1 = argv[1], *filename2= argv[2];
int result;
result = sscanf(filename1, "keys_%8x_%2d_%8x.dic", &uid1, &sector1, &nt1);
if (result != 3) {
fprintf(stderr, "Error: Failed to parse the filename %s.\n", filename1);
return 1;
}
result = sscanf(filename2, "keys_%8x_%2d_%8x.dic", &uid2, &sector2, &nt2);
if (result != 3) {
fprintf(stderr, "Error: Failed to parse the filename %s.\n", filename2);
return 1;
}
if (uid1 != uid2) {
fprintf(stderr, "Error: Files must belong to the same UID.\n");
return 1;
}
if (sector1 != sector2) {
fprintf(stderr, "Error: Files must belong to the same sector.\n");
return 1;
}
if (nt1 == nt2) {
fprintf(stderr, "Error: Files must belong to different nonces.\n");
return 1;
}
init_lfsr16_table();
uint32_t keycount1 = 0;
uint64_t* keys1 = NULL;
uint8_t* filter_keys1 = NULL;
uint16_t* seednt1 = NULL;
uint32_t keycount2 = 0;
uint64_t* keys2 = NULL;
uint8_t* filter_keys2 = NULL;
FILE* fptr;
fptr = fopen(filename1, "r");
if (fptr != NULL) {
uint64_t buffer;
while (fscanf(fptr, "%012" PRIx64, &buffer) == 1) {
keycount1++;
}
keys1 = (uint64_t*)malloc(keycount1 * sizeof(uint64_t));
filter_keys1 = (uint8_t*)calloc(keycount1, sizeof(uint8_t));
if ((keys1 == NULL)||(filter_keys1 == NULL)) {
perror("Failed to allocate memory");
fclose(fptr);
goto end;
}
rewind(fptr);
for (uint32_t i = 0; i < keycount1; i++) {
if (fscanf(fptr, "%012" PRIx64, &keys1[i]) != 1) {
perror("Failed to read key");
fclose(fptr);
goto end;
}
}
fclose(fptr);
} else {
fprintf(stderr, "Warning: Cannot open %s\n", filename1);
goto end;
}
fptr = fopen(filename2, "r");
if (fptr != NULL) {
uint64_t buffer;
while (fscanf(fptr, "%012" PRIx64, &buffer) == 1) {
keycount2++;
}
keys2 = (uint64_t*)malloc(keycount2 * sizeof(uint64_t));
filter_keys2 = (uint8_t*)calloc(keycount1, sizeof(uint8_t));
if ((keys2 == NULL)||(filter_keys2 == NULL)) {
perror("Failed to allocate memory");
fclose(fptr);
goto end;
}
rewind(fptr);
for (uint32_t i = 0; i < keycount2; i++) {
if (fscanf(fptr, "%012" PRIx64, &keys2[i]) != 1) {
perror("Failed to read key");
fclose(fptr);
goto end;
}
}
fclose(fptr);
} else {
fprintf(stderr, "Warning: Cannot open %s\n", filename2);
goto end;
}
printf("%s: %i keys loaded\n", filename1, keycount1);
printf("%s: %i keys loaded\n", filename2, keycount2);
seednt1 = (uint16_t*)malloc(keycount1 * sizeof(uint16_t));
if (seednt1 == NULL) {
perror("Failed to allocate memory");
goto end;
}
for (uint32_t i = 0; i < keycount1; i++) {
seednt1[i] = compute_seednt16_nt32(nt1, keys1[i]);
}
for (uint32_t j = 0; j < keycount2; j++) {
uint16_t seednt2 = compute_seednt16_nt32(nt2, keys2[j]);
for (uint32_t i = 0; i < keycount1; i++) {
if (seednt2 == seednt1[i]) {
// printf("MATCH: key1=%012" PRIx64 " key2=%012" PRIx64 "\n", keys1[i], keys2[j]);
filter_keys1[i] = 1;
filter_keys2[j] = 1;
}
}
}
char filter_filename1[40];
uint32_t filter_keycount1 = 0;
snprintf(filter_filename1, sizeof(filter_filename1), "keys_%08x_%02i_%08x_filtered.dic", uid1, sector1, nt1);
fptr = fopen(filter_filename1, "w");
if (fptr != NULL) {
for (uint32_t j = 0; j < keycount1; j++) {
if (filter_keys1[j]) {
filter_keycount1++;
fprintf(fptr, "%012" PRIx64 "\n", keys1[j]);
}
}
fclose(fptr);
} else {
fprintf(stderr, "Warning: Cannot save keys in %s\n", filter_filename1);
}
char filter_filename2[40];
uint32_t filter_keycount2 = 0;
snprintf(filter_filename2, sizeof(filter_filename2), "keys_%08x_%02i_%08x_filtered.dic", uid2, sector2, nt2);
fptr = fopen(filter_filename2, "w");
if (fptr != NULL) {
for (uint32_t j = 0; j < keycount2; j++) {
if (filter_keys2[j]) {
filter_keycount2++;
fprintf(fptr, "%012" PRIx64 "\n", keys2[j]);
}
}
fclose(fptr);
} else {
fprintf(stderr, "Warning: Cannot save keys in %s\n", filter_filename2);
}
printf("%s: %i keys saved\n", filter_filename1, filter_keycount1);
printf("%s: %i keys saved\n", filter_filename2, filter_keycount2);
end:
if (keys1 != NULL)
free(keys1);
if (keys2 != NULL)
free(keys2);
if (filter_keys1 != NULL)
free(filter_keys1);
if (filter_keys2 != NULL)
free(filter_keys2);
if (seednt1 != NULL)
free(seednt1);
return 0;
}

View file

@ -0,0 +1,140 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
static uint32_t hex_to_uint32(const char *hex_str) {
return (uint32_t)strtoul(hex_str, NULL, 16);
}
uint16_t i_lfsr16[1 << 16] = {0};
uint16_t s_lfsr16[1 << 16] = {0};
static void init_lfsr16_table(void) {
uint16_t x = 1;
for (uint16_t i=1; i; ++i) {
i_lfsr16[(x & 0xff) << 8 | x >> 8] = i;
s_lfsr16[i] = (x & 0xff) << 8 | x >> 8;
x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15;
}
}
// static uint16_t next_lfsr16(uint16_t nonce) {
// return s_lfsr16[(i_lfsr16[nonce]+1) % 65535];
// }
static uint16_t prev_lfsr16(uint16_t nonce) {
return s_lfsr16[(i_lfsr16[nonce]-1) % 65535];
}
static uint16_t compute_seednt16_nt32(uint32_t nt32, uint64_t key) {
uint8_t a[] = {0, 8, 9, 4, 6, 11, 1, 15, 12, 5, 2, 13, 10, 14, 3, 7};
uint8_t b[] = {0, 13, 1, 14, 4, 10, 15, 7, 5, 3, 8, 6, 9, 2, 12, 11};
uint16_t nt = nt32 >> 16;
uint8_t prev = 14;
for (uint8_t i=0; i<prev; i++) {
nt = prev_lfsr16(nt);
}
uint8_t prevoff = 8;
bool odd = 1;
for (uint8_t i=0; i<6*8; i+=8) {
if (odd) {
nt ^= (a[(key >> i) & 0xF]);
nt ^= (b[(key >> i >> 4) & 0xF]) << 4;
} else {
nt ^= (b[(key >> i) & 0xF]);
nt ^= (a[(key >> i >> 4) & 0xF]) << 4;
}
odd ^= 1;
prev += prevoff;
for (uint8_t j=0; j<prevoff; j++) {
nt = prev_lfsr16(nt);
}
}
return nt;
}
int main(int argc, char *const argv[]) {
if (argc != 4) {
printf("Usage:\n %s <nt1:08x> <key1:012x> keys_<uid:08x>_<sector:02>_<nt2:08x>.dic\n"
" where dict file is produced by rf08s_nested_known *for the same UID and same sector* as provided nt and key\n",
argv[0]);
return 1;
}
uint32_t nt1 = hex_to_uint32(argv[1]);
uint64_t key1 = 0;
if (sscanf(argv[2], "%012" PRIx64, &key1) != 1) {
fprintf(stderr, "Failed to parse key: %s", argv[2]);
return 1;
}
char *filename= argv[3];
uint32_t uid, sector, nt2;
int result;
result = sscanf(filename, "keys_%8x_%2d_%8x.dic", &uid, &sector, &nt2);
if (result != 3) {
fprintf(stderr, "Error: Failed to parse the filename %s.\n", filename);
return 1;
}
if (nt1 == nt2) {
fprintf(stderr, "Error: File must belong to different nonce.\n");
return 1;
}
init_lfsr16_table();
uint32_t keycount2 = 0;
uint64_t* keys2 = NULL;
FILE* fptr = fopen(filename, "r");
if (fptr != NULL) {
uint64_t buffer;
while (fscanf(fptr, "%012" PRIx64, &buffer) == 1) {
keycount2++;
}
keys2 = (uint64_t*)malloc(keycount2 * sizeof(uint64_t));
if (keys2 == NULL) {
perror("Failed to allocate memory");
fclose(fptr);
goto end;
}
rewind(fptr);
for (uint32_t i = 0; i < keycount2; i++) {
if (fscanf(fptr, "%012" PRIx64, &keys2[i]) != 1) {
perror("Failed to read key");
fclose(fptr);
goto end;
}
}
fclose(fptr);
} else {
fprintf(stderr, "Warning: Cannot open %s\n", filename);
goto end;
}
printf("%s: %i keys loaded\n", filename, keycount2);
uint32_t found = 0;
uint16_t seednt1 = compute_seednt16_nt32(nt1, key1);
for (uint32_t i = 0; i < keycount2; i++) {
if (seednt1 == compute_seednt16_nt32(nt2, keys2[i])) {
printf("MATCH: key2=%012" PRIx64 "\n", keys2[i]);
found++;
}
}
if (!found) {
printf("No key found :(\n");
}
end:
if (keys2 != NULL)
free(keys2);
return 0;
}