From 0d7a0db3035397779fee0f624e423bb44d5bf7be Mon Sep 17 00:00:00 2001 From: Aram Date: Tue, 12 Apr 2016 20:55:22 +0200 Subject: [PATCH] first commit --- 0xcafec0de.bin | Bin 0 -> 45006 bytes Makefile | 18 ++ README.md | 38 +++ crypto1_bs.c | 94 ++++++++ crypto1_bs.h | 99 ++++++++ crypto1_bs_crack.c | 196 ++++++++++++++++ crypto1_bs_crack.h | 21 ++ pwpiwi_proxmark3_hard_nested.patch | 358 +++++++++++++++++++++++++++++ solve_bs.c | 85 +++++++ solve_piwi.c | 85 +++++++ solve_piwi_bs.c | 110 +++++++++ 11 files changed, 1104 insertions(+) create mode 100644 0xcafec0de.bin create mode 100644 Makefile create mode 100644 README.md create mode 100644 crypto1_bs.c create mode 100644 crypto1_bs.h create mode 100644 crypto1_bs_crack.c create mode 100644 crypto1_bs_crack.h create mode 100644 pwpiwi_proxmark3_hard_nested.patch create mode 100644 solve_bs.c create mode 100644 solve_piwi.c create mode 100644 solve_piwi_bs.c diff --git a/0xcafec0de.bin b/0xcafec0de.bin new file mode 100644 index 0000000000000000000000000000000000000000..8139d18000dd43fb74b20dc5de878161d61c4a5a GIT binary patch literal 45006 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�@Zxky1#)~0!k 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 literal 0 HcmV?d00001 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..534bf8a --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +all: + gcc -O3 craptev1-v1.0/solve.c -fPIC -shared -o solve.so + gcc -O3 -mpopcnt -std=c99 solve_bs.c crypto1_bs.c crypto1_bs_crack.c -Icraptev1-v1.0 craptev1-v1.0/craptev1.c crapto1-v3.3/crapto1.c ./solve.so -o solve_bs -lpthread + gcc -O3 -mpopcnt -std=c99 solve_piwi_bs.c crypto1_bs.c crypto1_bs_crack.c -Icraptev1-v1.0 craptev1-v1.0/craptev1.c crapto1-v3.3/crapto1.c -o solve_piwi_bs -lpthread + gcc -O3 -mpopcnt solve_piwi.c -I craptev1-v1.0 craptev1-v1.0/craptev1.c -o solve_piwi -lpthread + +clean: + rm solve.so solve_bs solve_piwi_bs solve_piwi + +get_craptev1: + wget http://crapto1.netgarage.org/craptev1-v1.0.tar.xz + tar Jxvf craptev1-v1.0.tar.xz + +get_crapto1: + wget http://crapto1.netgarage.org/crapto1-v3.3.tar.xz + mkdir crapto1-v3.3 + tar Jxvf crapto1-v3.3.tar.xz -C crapto1-v3.3 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..f03ab3b --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +Bitsliced Crypto-1 brute-forcer +=============================== + +A pure C(99) implementation of the [Crypto-1 cipher](https://en.wikipedia.org/wiki/Crypto-1) using the method of [bitslicing](https://en.wikipedia.org/wiki/Bit_slicing), which uses GNU vector extensions to be portable across SSE/AVX/AVX2 supporting architectures while offering the highest amount of possible parallelism. + + +Background +---------- + +I wrote this as a patch for [piwi's imlementation](https://github.com/pwpiwi/proxmark3/tree/hard_nested/) of the research documented in [Ciphertext-only cryptanalysis on Hardened Mifare Classic cards](http://www.cs.ru.nl/~rverdult/Ciphertext-only_Cryptanalysis_on_Hardened_Mifare_Classic_Cards-CCS_2015.pdf) after reading (most of) the paper, while it was still under [active development](http://www.proxmark.org/forum/viewtopic.php?id=2120). +The final patch is included as `pwpiwi_proxmark3_hard_nested.patch`. + +Later on, another implementation of the same attack surfaced, [CraptEV1](http://crapto1.netgarage.org/). +I managed to gather some great tricks from that code, which unfortunately is off-line now (and has a license forbidding redistribution). +This also allowed me to compare my Crypto-1 implementation to a finished brute-forcer, and eventually I managed to significantly beat CraptEV1's (great) performance. + +Tools +----- + +If you want to use the following stand-alone binaries, you will need the original CraptEV1 / Crapto1 source packages. +For convenience, and because redistribution of CraptEV1 is not allowed, I've added make targets `get_craptev1` and `get_crapto1` to fetch and extract these packages to the current working directory. +I have included a conversion of the test file `0xcafec0de.txt` included in the CraptEV1 package to the binary format used by the `proxmark3/hard_nested` branch. + +`solve_bs` is analogous to CraptEV1 `solve` and works on .txt files using the bitsliced crypto-1 cracker + + $ ./solve_bs craptev1-v1.0/0xcafec0de.txt 0xcafec0de + +`solve_piwi` uses CraptEV1 on .bin files as gathered by piwi's PM3 code + + $ ./solve_piwi 0xcafec0de.bin + +`solve_piwi_bs` does the same but uses the bitsliced cracker + + $ ./solve_piwi_bs 0xcafec0de.bin + + +Special thanks to Carlo Meijer, Roel Verdult, piwi and bla. + diff --git a/crypto1_bs.c b/crypto1_bs.c new file mode 100644 index 0000000..cc68246 --- /dev/null +++ b/crypto1_bs.c @@ -0,0 +1,94 @@ +// Bit-sliced Crypto-1 implementation (C) 2015 by Aram Verstegen +// The cipher states are stored with the least significant bit first, hence all bit indexes are reversed here + +#include "crypto1_bs.h" + +// The following functions use this global or thread-local state +// It is sized to fit exactly KEYSTREAM_SIZE more states next to the initial state +__thread bitslice_t states[KEYSTREAM_SIZE+STATE_SIZE]; +__thread bitslice_t * restrict state_p; + +void crypto1_bs_init(){ + // initialize constant one and zero bit vectors + memset(bs_ones.bytes, 0xff, VECTOR_SIZE); + memset(bs_zeroes.bytes, 0x00, VECTOR_SIZE); +} + +// The following functions have side effects on 48 bitslices at the state_p pointer +// use the crypto1_bs_rewind_* macros to (re-)initialize them as needed + +inline const bitslice_value_t crypto1_bs_bit(const bitslice_value_t input, const bool is_encrypted){ + bitslice_value_t feedback = (state_p[47- 0].value ^ state_p[47- 5].value ^ state_p[47- 9].value ^ + state_p[47-10].value ^ state_p[47-12].value ^ state_p[47-14].value ^ + state_p[47-15].value ^ state_p[47-17].value ^ state_p[47-19].value ^ + state_p[47-24].value ^ state_p[47-25].value ^ state_p[47-27].value ^ + state_p[47-29].value ^ state_p[47-35].value ^ state_p[47-39].value ^ + state_p[47-41].value ^ state_p[47-42].value ^ state_p[47-43].value); + const bitslice_value_t ks_bits = crypto1_bs_f20(state_p); + if(is_encrypted){ + feedback ^= ks_bits; + } + state_p--; + state_p[0].value = feedback ^ input; + return ks_bits; +} + +inline const bitslice_value_t crypto1_bs_lfsr_rollback(const bitslice_value_t input, const bool is_encrypted){ + bitslice_value_t feedout = state_p[0].value; + state_p++; + const bitslice_value_t ks_bits = crypto1_bs_f20(state_p); + if(is_encrypted){ + feedout ^= ks_bits; + } + const bitslice_value_t feedback = (feedout ^ state_p[47- 5].value ^ state_p[47- 9].value ^ + state_p[47-10].value ^ state_p[47-12].value ^ state_p[47-14].value ^ + state_p[47-15].value ^ state_p[47-17].value ^ state_p[47-19].value ^ + state_p[47-24].value ^ state_p[47-25].value ^ state_p[47-27].value ^ + state_p[47-29].value ^ state_p[47-35].value ^ state_p[47-39].value ^ + state_p[47-41].value ^ state_p[47-42].value ^ state_p[47-43].value); + state_p[47].value = feedback ^ input; + return ks_bits; +} + +// side-effect free from here on +// note that bytes are sliced and unsliced with reversed endianness +inline void crypto1_bs_convert_states(bitslice_t bitsliced_states[], state_t regular_states[]){ + size_t bit_idx = 0, slice_idx = 0; + state_t values[MAX_BITSLICES]; + for(slice_idx = 0; slice_idx < MAX_BITSLICES; slice_idx++){ + for(bit_idx = 0; bit_idx < STATE_SIZE; bit_idx++){ + bool bit = get_vector_bit(slice_idx, bitsliced_states[bit_idx]); + values[slice_idx].value <<= 1; + values[slice_idx].value |= bit; + } + // swap endianness + values[slice_idx].value = rev_state_t(values[slice_idx].value); + // roll off unused bits + values[slice_idx].value >>= ((sizeof(state_t)*8)-STATE_SIZE); + } + memcpy(regular_states, values, sizeof(values)); +} + +// bitslice a value +void crypto1_bs_bitslice_value32(uint32_t value, bitslice_t bitsliced_value[], size_t bit_len){ + // load nonce bytes with unswapped endianness + size_t bit_idx; + for(bit_idx = 0; bit_idx < bit_len; bit_idx++){ + bool bit = get_bit(bit_len-1-bit_idx, rev32(value)); + if(bit){ + bitsliced_value[bit_idx].value = bs_ones.value; + } else { + bitsliced_value[bit_idx].value = bs_zeroes.value; + } + } +} + +void crypto1_bs_print_states(bitslice_t bitsliced_states[]){ + size_t slice_idx = 0; + state_t values[MAX_BITSLICES]; + crypto1_bs_convert_states(bitsliced_states, values); + for(slice_idx = 0; slice_idx < MAX_BITSLICES; slice_idx++){ + printf("State %03lu: %012lx\n", slice_idx, values[slice_idx].value); + } +} + diff --git a/crypto1_bs.h b/crypto1_bs.h new file mode 100644 index 0000000..8f33274 --- /dev/null +++ b/crypto1_bs.h @@ -0,0 +1,99 @@ +#ifndef _CRYPTO1_BS_H +#define _CRYPTO1_BS_H +#include +#include +#include +#include +#include +#include + +// bitslice type +// while AVX supports 256 bit vector floating point operations, we need integer operations for boolean logic +// same for AVX2 and 512 bit vectors +// using larger vectors works but seems to generate more register pressure +#if defined(__AVX2__) +#define MAX_BITSLICES 256 +#elif defined(__AVX__) +#define MAX_BITSLICES 128 +#elif defined(__SSE2__) +#define MAX_BITSLICES 128 +#else +#define MAX_BITSLICES 64 +#endif + +#define VECTOR_SIZE (MAX_BITSLICES/8) +typedef unsigned int __attribute__((aligned(VECTOR_SIZE))) __attribute__((vector_size(VECTOR_SIZE))) bitslice_value_t; +typedef union { + bitslice_value_t value; + uint64_t bytes64[MAX_BITSLICES/64]; + uint8_t bytes[MAX_BITSLICES/8]; +} bitslice_t; + +// filter function (f20) +// sourced from ``Wirelessly Pickpocketing a Mifare Classic Card'' by Flavio Garcia, Peter van Rossum, Roel Verdult and Ronny Wichers Schreur +#define f20a(a,b,c,d) (((a|b)^(a&d))^(c&((a^b)|d))) +#define f20b(a,b,c,d) (((a&b)|c)^((a^b)&(c|d))) +#define f20c(a,b,c,d,e) ((a|((b|e)&(d^e)))^((a^(b&d))&((c^d)|(b&e)))) + +#define crypto1_bs_f20(s) \ +f20c(f20a((s[47- 9].value), (s[47-11].value), (s[47-13].value), (s[47-15].value)), \ + f20b((s[47-17].value), (s[47-19].value), (s[47-21].value), (s[47-23].value)), \ + f20b((s[47-25].value), (s[47-27].value), (s[47-29].value), (s[47-31].value)), \ + f20a((s[47-33].value), (s[47-35].value), (s[47-37].value), (s[47-39].value)), \ + f20b((s[47-41].value), (s[47-43].value), (s[47-45].value), (s[47-47].value))) + +// bit indexing +#define get_bit(n, word) ((word >> (n)) & 1) +#define get_vector_bit(slice, value) get_bit(slice&0x3f, value.bytes64[slice>>6]) + +// constant ones/zeroes +bitslice_t bs_ones; +bitslice_t bs_zeroes; + +// size of crypto-1 state +#define STATE_SIZE 48 +// size of nonce to be decrypted +#define KEYSTREAM_SIZE 32 +// size of first uid^nonce byte to be rolled back to the initial key +#define ROLLBACK_SIZE 8 +// number of nonces required to test to cover entire 48-bit state +// I would have said it's 12... but bla goes with 100, so I do too +#define NONCE_TESTS 100 + +// state pointer management +extern __thread bitslice_t states[KEYSTREAM_SIZE+STATE_SIZE]; +extern __thread bitslice_t * restrict state_p; + +// rewind to the point a0, at which KEYSTREAM_SIZE more bits can be generated +#define crypto1_bs_rewind_a0() (state_p = &states[KEYSTREAM_SIZE]) + +// bitsliced bytewise parity +#define bitsliced_byte_parity(n) (n[0].value ^ n[1].value ^ n[2].value ^ n[3].value ^ n[4].value ^ n[5].value ^ n[6].value ^ n[7].value) + +// 48-bit crypto-1 states are normally represented using 64-bit values +typedef union { + uint64_t value; + uint8_t bytes[8]; +} state_t; + +// endianness conversion +#define rev32(word) (((word & 0xff) << 24) | (((word >> 8) & 0xff) << 16) | (((word >> 16) & 0xff) << 8) | (((word >> 24) & 0xff))) +#define rev64(x) (rev32(x)<<32|(rev32((x>>32)))) +#define rev_state_t rev64 + +// crypto-1 functions +const bitslice_value_t crypto1_bs_bit(const bitslice_value_t input, const bool is_encrypted); +const bitslice_value_t crypto1_bs_lfsr_rollback(const bitslice_value_t input, const bool is_encrypted); + +// initialization functions +void crypto1_bs_init(); + +// conversion functions +void crypto1_bs_bitslice_value32(uint32_t value, bitslice_t bitsliced_value[], size_t bit_len); +void crypto1_bs_convert_states(bitslice_t bitsliced_states[], state_t regular_states[]); + +// debug print +void crypto1_bs_print_states(bitslice_t *bitsliced_states); + +#endif // _CRYPTO1_BS_H + diff --git a/crypto1_bs_crack.c b/crypto1_bs_crack.c new file mode 100644 index 0000000..50db40e --- /dev/null +++ b/crypto1_bs_crack.c @@ -0,0 +1,196 @@ +#include +#include "crypto1_bs_crack.h" + +inline uint64_t crack_states_bitsliced(uint32_t **task){ + // the idea to roll back the half-states before combining them was suggested/explained to me by bla + // first we pre-bitslice all the even state bits and roll them back, then bitslice the odd bits and combine the two in the inner loop + uint64_t key = -1; +#ifdef EXACT_COUNT + size_t bucket_states_tested = 0; + size_t bucket_size[(task[4]-task[3])/MAX_BITSLICES]; +#else + const size_t bucket_states_tested = (task[4]-task[3])*(task[2]-task[1]); +#endif + // bitslice all the even states + bitslice_t * restrict bitsliced_even_states[(task[4]-task[3])/MAX_BITSLICES]; + size_t bitsliced_blocks = 0; + for(uint32_t const * restrict p_even = task[3]; p_even < task[4]; p_even+=MAX_BITSLICES){ + bitslice_t * restrict lstate_p = memalign(sizeof(bitslice_t), (STATE_SIZE+ROLLBACK_SIZE)*sizeof(bitslice_t)); + memset(lstate_p, 0x0, (STATE_SIZE)*sizeof(bitslice_t)); + // bitslice even half-states + const size_t max_slices = (task[4]-p_even) < MAX_BITSLICES ? task[4]-p_even : MAX_BITSLICES; +#ifdef EXACT_COUNT + bucket_size[bitsliced_blocks] = max_slices; +#endif + for(size_t slice_idx = 0; slice_idx < max_slices; ++slice_idx){ + // set even bits + uint32_t e = *(p_even+slice_idx); + for(size_t bit_idx = 1; bit_idx < STATE_SIZE; bit_idx+=2, e >>= 1){ + if(e&1){ + lstate_p[bit_idx].bytes64[slice_idx>>6] |= 1ull << (slice_idx&63); + } + } + } + // compute the rollback bits + for(size_t rollback = 0; rollback < ROLLBACK_SIZE; ++rollback){ + // inlined crypto1_bs_lfsr_rollback + const bitslice_value_t feedout = lstate_p[0].value; + ++lstate_p; + const bitslice_value_t ks_bits = crypto1_bs_f20(lstate_p); + const bitslice_value_t feedback = (feedout ^ ks_bits ^ lstate_p[47- 5].value ^ lstate_p[47- 9].value ^ + lstate_p[47-10].value ^ lstate_p[47-12].value ^ lstate_p[47-14].value ^ + lstate_p[47-15].value ^ lstate_p[47-17].value ^ lstate_p[47-19].value ^ + lstate_p[47-24].value ^ lstate_p[47-25].value ^ lstate_p[47-27].value ^ + lstate_p[47-29].value ^ lstate_p[47-35].value ^ lstate_p[47-39].value ^ + lstate_p[47-41].value ^ lstate_p[47-42].value ^ lstate_p[47-43].value); + lstate_p[47].value = feedback ^ bitsliced_rollback_byte[rollback].value; + } + bitsliced_even_states[bitsliced_blocks++] = lstate_p; + } + // bitslice every odd state to every block of even half-states with half-finished rollback + for(uint32_t const * restrict p_odd = task[1]; p_odd < task[2]; ++p_odd){ + // early abort + if(keys_found){ + goto out; + } + + // set the odd bits and compute rollback + uint64_t o = (uint64_t) *p_odd; + lfsr_rollback_byte(&o, 0, 1); + // pre-compute part of the odd feedback bits (minus rollback) + bool odd_feedback_bit = parity(o&0x9ce5c); + + crypto1_bs_rewind_a0(); + // set odd bits + for(size_t state_idx = 0; state_idx < (STATE_SIZE-ROLLBACK_SIZE); o >>= 1, state_idx+=2){ + if(o & 1){ + state_p[state_idx] = bs_ones; + } else { + state_p[state_idx] = bs_zeroes; + } + } + const bitslice_value_t odd_feedback = odd_feedback_bit ? bs_ones.value : bs_zeroes.value; + + // set even and rollback bits + for(size_t block_idx = 0; block_idx < bitsliced_blocks; ++block_idx){ + const bitslice_t const * restrict bitsliced_even_state = bitsliced_even_states[block_idx]; + size_t state_idx; + // set even bits + for(state_idx = 0; state_idx < (STATE_SIZE-ROLLBACK_SIZE); state_idx+=2){ + state_p[1+state_idx] = bitsliced_even_state[1+state_idx]; + } + // set rollback bits + uint64_t lo = o; + for(; state_idx < STATE_SIZE; lo >>= 1, state_idx+=2){ + // set the odd bits and take in the odd rollback bits from the even states + if(lo & 1){ + state_p[state_idx].value = ~bitsliced_even_state[state_idx].value; + } else { + state_p[state_idx] = bitsliced_even_state[state_idx]; + } + + // set the even bits and take in the even rollback bits from the odd states + if((lo >> 32) & 1){ + state_p[1+state_idx].value = ~bitsliced_even_state[1+state_idx].value; + } else { + state_p[1+state_idx] = bitsliced_even_state[1+state_idx]; + } + } + +#ifdef EXACT_COUNT + bucket_states_tested += bucket_size[block_idx]; +#endif + // pre-compute first keystream and feedback bit vectors + const bitslice_value_t ksb = crypto1_bs_f20(state_p); + const bitslice_value_t fbb = (odd_feedback ^ state_p[47- 0].value ^ state_p[47- 5].value ^ // take in the even and rollback bits + state_p[47-10].value ^ state_p[47-12].value ^ state_p[47-14].value ^ + state_p[47-24].value ^ state_p[47-42].value); + + // vector to contain test results (1 = passed, 0 = failed) + bitslice_t results = bs_ones; + + for(size_t tests = 0; tests < NONCE_TESTS; ++tests){ + size_t parity_bit_idx = 0; + bitslice_value_t fb_bits = fbb; + bitslice_value_t ks_bits = ksb; + state_p = &states[KEYSTREAM_SIZE-1]; + bitslice_value_t parity_bit_vector = bs_zeroes.value; + + // highest bit is transmitted/received first + for(int32_t ks_idx = KEYSTREAM_SIZE-1; ks_idx >= 0; --ks_idx, --state_p){ + // decrypt nonce bits + const bitslice_value_t encrypted_nonce_bit_vector = bitsliced_encrypted_nonces[tests][ks_idx].value; + const bitslice_value_t decrypted_nonce_bit_vector = (encrypted_nonce_bit_vector ^ ks_bits); + + // compute real parity bits on the fly + parity_bit_vector ^= decrypted_nonce_bit_vector; + + // update state + state_p[0].value = (fb_bits ^ decrypted_nonce_bit_vector); + + // compute next keystream bit + ks_bits = crypto1_bs_f20(state_p); + + // for each byte: + if((ks_idx&7) == 0){ + // get encrypted parity bits + const bitslice_value_t encrypted_parity_bit_vector = bitsliced_encrypted_parity_bits[tests][parity_bit_idx++].value; + + // decrypt parity bits + const bitslice_value_t decrypted_parity_bit_vector = (encrypted_parity_bit_vector ^ ks_bits); + + // compare actual parity bits with decrypted parity bits and take count in results vector + results.value &= (parity_bit_vector ^ decrypted_parity_bit_vector); + + // make sure we still have a match in our set + // if(memcmp(&results, &bs_zeroes, sizeof(bitslice_t)) == 0){ + + // this is much faster on my gcc, because somehow a memcmp needlessly spills/fills all the xmm registers to/from the stack - ??? + // the short-circuiting also helps + if(results.bytes64[0] == 0 +#if MAX_BITSLICES > 64 + && results.bytes64[1] == 0 +#endif +#if MAX_BITSLICES > 128 + && results.bytes64[2] == 0 + && results.bytes64[3] == 0 +#endif + ){ + goto stop_tests; + } + // this is about as fast but less portable (requires -std=gnu99) + // asm goto ("ptest %1, %0\n\t" + // "jz %l2" :: "xm" (results.value), "xm" (bs_ones.value) : "cc" : stop_tests); + parity_bit_vector = bs_zeroes.value; + } + // compute next feedback bit vector + fb_bits = (state_p[47- 0].value ^ state_p[47- 5].value ^ state_p[47- 9].value ^ + state_p[47-10].value ^ state_p[47-12].value ^ state_p[47-14].value ^ + state_p[47-15].value ^ state_p[47-17].value ^ state_p[47-19].value ^ + state_p[47-24].value ^ state_p[47-25].value ^ state_p[47-27].value ^ + state_p[47-29].value ^ state_p[47-35].value ^ state_p[47-39].value ^ + state_p[47-41].value ^ state_p[47-42].value ^ state_p[47-43].value); + } + } + // all nonce tests were successful: we've found the key in this block! + state_t keys[MAX_BITSLICES]; + crypto1_bs_convert_states(&states[KEYSTREAM_SIZE], keys); + for(size_t results_idx = 0; results_idx < MAX_BITSLICES; ++results_idx){ + if(get_vector_bit(results_idx, results)){ + key = keys[results_idx].value; + goto out; + } + } +stop_tests: + // prepare to set new states + crypto1_bs_rewind_a0(); + continue; + } + } +out: + for(size_t block_idx = 0; block_idx < bitsliced_blocks; ++block_idx){ + free(bitsliced_even_states[block_idx]-ROLLBACK_SIZE); + } + __sync_fetch_and_add(&total_states_tested, bucket_states_tested); + return key; +} diff --git a/crypto1_bs_crack.h b/crypto1_bs_crack.h new file mode 100644 index 0000000..f4867a7 --- /dev/null +++ b/crypto1_bs_crack.h @@ -0,0 +1,21 @@ +#ifndef _CRYPTO1_BS_CRACK_H +#define _CRYPTO1_BS_CRACK_H +#include +#include "crypto1_bs.h" +#include "craptev1.h" +uint64_t crack_states_bitsliced(uint32_t **task); +size_t keys_found; +size_t total_states_tested; +size_t total_states; + +// linked from crapto1.c file +extern uint8_t lfsr_rollback_byte(uint64_t* s, uint32_t in, int fb); + +#define EXACT_COUNT + +// arrays of bitsliced states with identical values in all slices +bitslice_t bitsliced_encrypted_nonces[NONCE_TESTS][STATE_SIZE]; +bitslice_t bitsliced_encrypted_parity_bits[NONCE_TESTS][STATE_SIZE]; +bitslice_t bitsliced_rollback_byte[ROLLBACK_SIZE]; + +#endif // _CRYPTO1_BS_CRACK_H diff --git a/pwpiwi_proxmark3_hard_nested.patch b/pwpiwi_proxmark3_hard_nested.patch new file mode 100644 index 0000000..2db24fc --- /dev/null +++ b/pwpiwi_proxmark3_hard_nested.patch @@ -0,0 +1,358 @@ +diff --git a/client/Makefile b/client/Makefile +index 91e595d..dc3557f 100644 +--- a/client/Makefile ++++ b/client/Makefile +@@ -107,6 +107,7 @@ CMDSRCS = nonce2key/crapto1.c\ + aes.c\ + protocols.c\ + sha1.c\ ++ crypto1_bs.c \ + + ZLIBSRCS = deflate.c adler32.c trees.c zutil.c inflate.c inffast.c inftrees.c + ZLIB_FLAGS = -DZ_SOLO -DZ_PREFIX -DNO_GZIP -DZLIB_PM3_TUNED +diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c +index b3893ea..4a0bd38 100644 +--- a/client/cmdhfmfhard.c ++++ b/client/cmdhfmfhard.c +@@ -20,18 +20,21 @@ + #include + #include + #include ++#include ++#include + #include "proxmark3.h" + #include "cmdmain.h" + #include "ui.h" + #include "util.h" + #include "nonce2key/crapto1.h" + #include "parity.h" ++#include "crypto1_bs.h" + + // uint32_t test_state_odd = 0; + // uint32_t test_state_even = 0; + + #define CONFIDENCE_THRESHOLD 0.95 // Collect nonces until we are certain enough that the following brute force is successfull +-#define GOOD_BYTES_REQUIRED 30 ++#define GOOD_BYTES_REQUIRED 28 + + + static const float p_K[257] = { // the probability that a random nonce has a Sum Property == K +@@ -88,6 +91,8 @@ typedef struct noncelist { + } noncelist_t; + + ++static size_t nonces_to_bruteforce = 0; ++static noncelistentry_t *brute_force_nonces[256]; + static uint32_t cuid; + static noncelist_t nonces[256]; + static uint8_t best_first_bytes[256]; +@@ -169,6 +174,11 @@ static int add_nonce(uint32_t nonce_enc, uint8_t par_enc) + p2->nonce_enc = nonce_enc; + p2->par_enc = par_enc; + ++ if(nonces_to_bruteforce < 256){ ++ brute_force_nonces[nonces_to_bruteforce] = p2; ++ nonces_to_bruteforce++; ++ } ++ + nonces[first_byte].num++; + nonces[first_byte].Sum += evenparity32((nonce_enc & 0x00ff0000) | (par_enc & 0x04)); + nonces[first_byte].updated = true; // indicates that we need to recalculate the Sum(a8) probability for this first byte +@@ -1376,19 +1386,293 @@ static void free_statelist_cache(void) + } + } + ++size_t keys_found = 0; ++size_t bucket_count = 0; ++statelist_t* buckets[128]; ++size_t total_states_tested = 0; ++size_t thread_count = 4; ++ ++// these bitsliced states will hold identical states in all slices ++bitslice_t bitsliced_rollback_byte[ROLLBACK_SIZE]; ++ ++// arrays of bitsliced states with identical values in all slices ++bitslice_t bitsliced_encrypted_nonces[NONCE_TESTS][STATE_SIZE]; ++bitslice_t bitsliced_encrypted_parity_bits[NONCE_TESTS][ROLLBACK_SIZE]; ++ ++#define EXACT_COUNT ++ ++static const uint64_t crack_states_bitsliced(statelist_t *p){ ++ // the idea to roll back the half-states before combining them was suggested/explained to me by bla ++ // first we pre-bitslice all the even state bits and roll them back, then bitslice the odd bits and combine the two in the inner loop ++ uint64_t key = -1; ++#ifdef EXACT_COUNT ++ size_t bucket_states_tested = 0; ++ size_t bucket_size[p->len[EVEN_STATE]/MAX_BITSLICES]; ++#else ++ const size_t bucket_states_tested = (p->len[EVEN_STATE])*(p->len[ODD_STATE]); ++#endif ++ bitslice_t *bitsliced_even_states[p->len[EVEN_STATE]/MAX_BITSLICES]; ++ size_t bitsliced_blocks = 0; ++ uint32_t const * restrict even_end = p->states[EVEN_STATE]+p->len[EVEN_STATE]; ++ for(uint32_t * restrict p_even = p->states[EVEN_STATE]; p_even < even_end; p_even+=MAX_BITSLICES){ ++ bitslice_t * restrict lstate_p = memalign(sizeof(bitslice_t), (STATE_SIZE+ROLLBACK_SIZE)*sizeof(bitslice_t)); ++ memset(lstate_p+1, 0x0, (STATE_SIZE-1)*sizeof(bitslice_t)); // zero even bits ++ // bitslice even half-states ++ const size_t max_slices = (even_end-p_even) < MAX_BITSLICES ? even_end-p_even : MAX_BITSLICES; ++#ifdef EXACT_COUNT ++ bucket_size[bitsliced_blocks] = max_slices; ++#endif ++ for(size_t slice_idx = 0; slice_idx < max_slices; ++slice_idx){ ++ uint32_t e = *(p_even+slice_idx); ++ for(size_t bit_idx = 1; bit_idx < STATE_SIZE; bit_idx+=2, e >>= 1){ ++ // set even bits ++ if(e&1){ ++ lstate_p[bit_idx].bytes64[slice_idx>>6] |= 1ull << (slice_idx&63); ++ } ++ } ++ } ++ // compute the rollback bits ++ for(size_t rollback = 0; rollback < ROLLBACK_SIZE; ++rollback){ ++ // inlined crypto1_bs_lfsr_rollback ++ const bitslice_value_t feedout = lstate_p[0].value; ++ ++lstate_p; ++ const bitslice_value_t ks_bits = crypto1_bs_f20(lstate_p); ++ const bitslice_value_t feedback = (feedout ^ ks_bits ^ lstate_p[47- 5].value ^ lstate_p[47- 9].value ^ ++ lstate_p[47-10].value ^ lstate_p[47-12].value ^ lstate_p[47-14].value ^ ++ lstate_p[47-15].value ^ lstate_p[47-17].value ^ lstate_p[47-19].value ^ ++ lstate_p[47-24].value ^ lstate_p[47-25].value ^ lstate_p[47-27].value ^ ++ lstate_p[47-29].value ^ lstate_p[47-35].value ^ lstate_p[47-39].value ^ ++ lstate_p[47-41].value ^ lstate_p[47-42].value ^ lstate_p[47-43].value); ++ lstate_p[47].value = feedback ^ bitsliced_rollback_byte[rollback].value; ++ } ++ bitsliced_even_states[bitsliced_blocks++] = lstate_p; ++ } ++ for(uint32_t const * restrict p_odd = p->states[ODD_STATE]; p_odd < p->states[ODD_STATE]+p->len[ODD_STATE]; ++p_odd){ ++ // early abort ++ if(keys_found){ ++ goto out; ++ } ++ ++ // set the odd bits and compute rollback ++ uint64_t o = (uint64_t) *p_odd; ++ lfsr_rollback_byte((struct Crypto1State*) &o, 0, 1); ++ // pre-compute part of the odd feedback bits (minus rollback) ++ bool odd_feedback_bit = parity(o&0x9ce5c); ++ ++ crypto1_bs_rewind_a0(); ++ // set odd bits ++ for(size_t state_idx = 0; state_idx < STATE_SIZE-ROLLBACK_SIZE; o >>= 1, state_idx+=2){ ++ if(o & 1){ ++ state_p[state_idx] = bs_ones; ++ } else { ++ state_p[state_idx] = bs_zeroes; ++ } ++ } ++ const bitslice_value_t odd_feedback = odd_feedback_bit ? bs_ones.value : bs_zeroes.value; ++ ++ for(size_t block_idx = 0; block_idx < bitsliced_blocks; ++block_idx){ ++ const bitslice_t const * restrict bitsliced_even_state = bitsliced_even_states[block_idx]; ++ size_t state_idx; ++ // set even bits ++ for(state_idx = 0; state_idx < STATE_SIZE-ROLLBACK_SIZE; state_idx+=2){ ++ state_p[1+state_idx] = bitsliced_even_state[1+state_idx]; ++ } ++ // set rollback bits ++ uint64_t lo = o; ++ for(; state_idx < STATE_SIZE; lo >>= 1, state_idx+=2){ ++ // set the odd bits and take in the odd rollback bits from the even states ++ if(lo & 1){ ++ state_p[state_idx].value = ~bitsliced_even_state[state_idx].value; ++ } else { ++ state_p[state_idx] = bitsliced_even_state[state_idx]; ++ } ++ ++ // set the even bits and take in the even rollback bits from the odd states ++ if((lo >> 32) & 1){ ++ state_p[1+state_idx].value = ~bitsliced_even_state[1+state_idx].value; ++ } else { ++ state_p[1+state_idx] = bitsliced_even_state[1+state_idx]; ++ } ++ } ++ ++#ifdef EXACT_COUNT ++ bucket_states_tested += bucket_size[block_idx]; ++#endif ++ // pre-compute first keystream and feedback bit vectors ++ const bitslice_value_t ksb = crypto1_bs_f20(state_p); ++ const bitslice_value_t fbb = (odd_feedback ^ state_p[47- 0].value ^ state_p[47- 5].value ^ // take in the even and rollback bits ++ state_p[47-10].value ^ state_p[47-12].value ^ state_p[47-14].value ^ ++ state_p[47-24].value ^ state_p[47-42].value); ++ ++ // test keys ++ bitslice_t results = bs_ones; ++ ++ for(size_t tests = 0; tests < NONCE_TESTS; ++tests){ ++ size_t parity_bit_idx = 0; ++ bitslice_value_t fb_bits = fbb; ++ bitslice_value_t ks_bits = ksb; ++ state_p = &states[KEYSTREAM_SIZE-1]; ++ bitslice_value_t parity_bit_vector = bs_zeroes.value; ++ ++ // highest bit is transmitted/received first ++ for(int32_t ks_idx = KEYSTREAM_SIZE-1; ks_idx >= 0; --ks_idx, --state_p){ ++ // decrypt nonce bits ++ const bitslice_value_t encrypted_nonce_bit_vector = bitsliced_encrypted_nonces[tests][ks_idx].value; ++ const bitslice_value_t decrypted_nonce_bit_vector = (encrypted_nonce_bit_vector ^ ks_bits); ++ ++ // compute real parity bits on the fly ++ parity_bit_vector ^= decrypted_nonce_bit_vector; ++ ++ // update state ++ state_p[0].value = (fb_bits ^ decrypted_nonce_bit_vector); ++ ++ // compute next keystream bit ++ ks_bits = crypto1_bs_f20(state_p); ++ ++ // for each byte: ++ if((ks_idx&7) == 0){ ++ // get encrypted parity bits ++ const bitslice_value_t encrypted_parity_bit_vector = bitsliced_encrypted_parity_bits[tests][parity_bit_idx++].value; ++ ++ // decrypt parity bits ++ const bitslice_value_t decrypted_parity_bit_vector = (encrypted_parity_bit_vector ^ ks_bits); ++ ++ // compare actual parity bits with decrypted parity bits and take count in results vector ++ results.value &= (parity_bit_vector ^ decrypted_parity_bit_vector); ++ ++ // make sure we still have a match in our set ++ // if(memcmp(&results, &bs_zeroes, sizeof(bitslice_t)) == 0){ ++ ++ // this is much faster on my gcc, because somehow a memcmp needlessly spills/fills all the xmm registers to/from the stack - ??? ++ // the short-circuiting also helps ++ if(results.bytes64[0] == 0 ++#if MAX_BITSLICES > 64 ++ && results.bytes64[1] == 0 ++#endif ++#if MAX_BITSLICES > 128 ++ && results.bytes64[2] == 0 ++ && results.bytes64[3] == 0 ++#endif ++ ){ ++ goto stop_tests; ++ } ++ // this is about as fast but less portable (requires -std=gnu99) ++ // asm goto ("ptest %1, %0\n\t" ++ // "jz %l2" :: "xm" (results.value), "xm" (bs_ones.value) : "cc" : stop_tests); ++ parity_bit_vector = bs_zeroes.value; ++ } ++ // compute next feedback bit vector ++ fb_bits = (state_p[47- 0].value ^ state_p[47- 5].value ^ state_p[47- 9].value ^ ++ state_p[47-10].value ^ state_p[47-12].value ^ state_p[47-14].value ^ ++ state_p[47-15].value ^ state_p[47-17].value ^ state_p[47-19].value ^ ++ state_p[47-24].value ^ state_p[47-25].value ^ state_p[47-27].value ^ ++ state_p[47-29].value ^ state_p[47-35].value ^ state_p[47-39].value ^ ++ state_p[47-41].value ^ state_p[47-42].value ^ state_p[47-43].value); ++ } ++ } ++ // all nonce tests were successful: we've found the key in this block! ++ state_t keys[MAX_BITSLICES]; ++ crypto1_bs_convert_states(&states[KEYSTREAM_SIZE], keys); ++ for(size_t results_idx = 0; results_idx < MAX_BITSLICES; ++results_idx){ ++ if(get_vector_bit(results_idx, results)){ ++ key = keys[results_idx].value; ++ goto out; ++ } ++ } ++stop_tests: ++ // prepare to set new states ++ crypto1_bs_rewind_a0(); ++ continue; ++ } ++ } ++out: ++ for(size_t block_idx = 0; block_idx < bitsliced_blocks; ++block_idx){ ++ free(bitsliced_even_states[block_idx]-ROLLBACK_SIZE); ++ } ++ __sync_fetch_and_add(&total_states_tested, bucket_states_tested); ++ return key; ++} ++ ++static void* crack_states_thread(void* x){ ++ const size_t thread_id = (size_t)x; ++ size_t current_bucket = thread_id; ++ while(current_bucket < bucket_count){ ++ statelist_t * bucket = buckets[current_bucket]; ++ if(bucket){ ++ const uint64_t key = crack_states_bitsliced(bucket); ++ if(key != -1){ ++ printf("Found key: %012lx\n", key); ++ __sync_fetch_and_add(&keys_found, 1); ++ break; ++ } else if(keys_found){ ++ break; ++ } else { ++ printf("Cracking... %6.02f%%\n", (100.0*total_states_tested/(maximum_states))); ++ } ++ } ++ current_bucket += thread_count; ++ } ++ return NULL; ++} + +-static void brute_force(void) ++void brute_force(void) + { + if (known_target_key != -1) { + PrintAndLog("Looking for known target key in remaining key space..."); + TestIfKeyExists(known_target_key); + } else { +- PrintAndLog("Brute Force phase is not implemented."); ++ PrintAndLog("Brute force phase starting."); ++ time_t start, end; ++ time(&start); ++ keys_found = 0; ++ ++ crypto1_bs_init(); ++ ++ PrintAndLog("Using %u-bit bitslices", MAX_BITSLICES); ++ PrintAndLog("Bitslicing best_first_byte^uid[3] (rollback byte): %02x...", best_first_bytes[0]^(cuid>>24)); ++ // convert to 32 bit little-endian ++ crypto1_bs_bitslice_value32(rev32((best_first_bytes[0]^(cuid>>24))), bitsliced_rollback_byte, 8); ++ ++ PrintAndLog("Bitslicing nonces..."); ++ for(size_t tests = 0; tests < NONCE_TESTS; tests++){ ++ uint32_t test_nonce = brute_force_nonces[tests]->nonce_enc; ++ uint8_t test_parity = brute_force_nonces[tests]->par_enc; ++ // pre-xor the uid into the decrypted nonces, and also pre-xor the cuid parity into the encrypted parity bits - otherwise an exta xor is required in the decryption routine ++ crypto1_bs_bitslice_value32(cuid^test_nonce, bitsliced_encrypted_nonces[tests], 32); ++ // convert to 32 bit little-endian ++ crypto1_bs_bitslice_value32(rev32( ~(test_parity ^ ~(parity(cuid>>24 & 0xff)<<3 | parity(cuid>>16 & 0xff)<<2 | parity(cuid>>8 & 0xff)<<1 | parity(cuid&0xff)))), bitsliced_encrypted_parity_bits[tests], 4); ++ } ++ total_states_tested = 0; ++ ++ // count number of states to go ++ bucket_count = 0; ++ for (statelist_t *p = candidates; p != NULL; p = p->next) { ++ buckets[bucket_count] = p; ++ bucket_count++; ++ } ++ ++ // enumerate states using all hardware threads, each thread handles one bucket ++ PrintAndLog("Starting %u cracking threads to search %u buckets containing a total of %lu states...", thread_count, bucket_count, maximum_states); ++ pthread_t threads[thread_count]; ++ thread_count = sysconf(_SC_NPROCESSORS_CONF); ++ for(size_t i = 0; i < thread_count; i++){ ++ pthread_create(&threads[i], NULL, crack_states_thread, (void*) i); ++ } ++ for(size_t i = 0; i < thread_count; i++){ ++ pthread_join(threads[i], 0); ++ } ++ ++ time(&end); ++ unsigned long elapsed_time = difftime(end, start); ++ PrintAndLog("Tested %lu states, found %u keys after %u seconds", total_states_tested, keys_found, elapsed_time); ++ if(!keys_found){ ++ assert(total_states_tested == maximum_states); ++ } ++ // reset this counter for the next call ++ nonces_to_bruteforce = 0; + } +- + } + +- + int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow, int tests) + { + // initialize Random number generator diff --git a/solve_bs.c b/solve_bs.c new file mode 100644 index 0000000..bffaabc --- /dev/null +++ b/solve_bs.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include +#include +#include "craptev1.h" +#include "crypto1_bs.h" +#include "crypto1_bs_crack.h" + +// linked from .so / .c files by bla +extern uint64_t *readnonces(char* fname); + +uint32_t **space; +size_t thread_count; + +void* crack_states_thread(void* x){ + const size_t thread_id = (size_t)x; + int j; + for(j = thread_id; space[j * 5]; j += thread_count) { + const uint64_t key = crack_states_bitsliced(space + j * 5); + if(key != -1){ + printf("Found key: %012lx\n", key); + __sync_fetch_and_add(&keys_found, 1); + break; + } else if(keys_found){ + break; + } else { + printf("Cracking... %6.02f%%\n", (100.0*total_states_tested/(total_states))); + } + } + return NULL; +} + +int main(int argc, char* argv[]){ + if(argc != 3){ + printf("Usage: %s \n", argv[0]); + return -1; + } + uint64_t *nonces = readnonces(argv[1]); + uint32_t uid = strtoul(argv[2], NULL, 16); + space = craptev1_get_space(nonces, 95, uid); + total_states = craptev1_sizeof_space(space); + + thread_count = get_nprocs_conf(); + pthread_t threads[thread_count]; + size_t i; + + printf("Initializing BS crypto-1\n"); + crypto1_bs_init(); + printf("Using %u-bit bitslices\n", MAX_BITSLICES); + + uint8_t rollback_byte = **space; + printf("Bitslicing rollback byte: %02x...\n", rollback_byte); + // convert to 32 bit little-endian + crypto1_bs_bitslice_value32(rev32((rollback_byte)), bitsliced_rollback_byte, 8); + + printf("Bitslicing nonces...\n"); + for(size_t tests = 0; tests < NONCE_TESTS; tests++){ + // pre-xor the uid into the decrypted nonces, and also pre-xor the uid parity into the encrypted parity bits - otherwise an exta xor is required in the decryption routine + uint32_t test_nonce = uid^rev32(nonces[tests]); + uint32_t test_parity = (nonces[tests]>>32)^rev32(uid); + test_parity = ((parity(test_parity >> 24 & 0xff) & 1) | (parity(test_parity>>16 & 0xff) & 1)<<1 | (parity(test_parity>>8 & 0xff) & 1)<<2 | (parity(test_parity & 0xff) & 1) << 3); + crypto1_bs_bitslice_value32(test_nonce, bitsliced_encrypted_nonces[tests], 32); + // convert to 32 bit little-endian + crypto1_bs_bitslice_value32(~(test_parity)<<24, bitsliced_encrypted_parity_bits[tests], 4); + } + + total_states_tested = 0; + keys_found = 0; + + printf("Starting %lu threads to test %lu states\n", thread_count, total_states); + for(i = 0; i < thread_count; i++){ + pthread_create(&threads[i], NULL, crack_states_thread, (void*) i); + } + for(i = 0; i < thread_count; i++){ + pthread_join(threads[i], 0); + } + printf("Tested %lu states\n", total_states_tested); + + craptev1_destroy_space(space); + return 0; +} + diff --git a/solve_piwi.c b/solve_piwi.c new file mode 100644 index 0000000..ea21c3b --- /dev/null +++ b/solve_piwi.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include +#include "craptev1.h" + +#define rev32(word) (((word & 0xff) << 24) | (((word >> 8) & 0xff) << 16) | (((word >> 16) & 0xff) << 8) | (((word >> 24) & 0xff))) + +uint64_t split(uint8_t p){ + return (((p & 0x8) >>3 )| ((p & 0x4) >> 2) << 8 | ((p & 0x2) >> 1) << 16 | (p & 0x1) << 24 ); +} + +uint32_t uid; +uint64_t *readnonces(char* fname){ + int i; + FILE *f = fopen(fname, "r"); + uint64_t *nonces = malloc(sizeof (uint64_t) << 24); + if(fread(&uid, 1, 4, f)){ + uid = rev32(uid); + } + fseek(f, 6, SEEK_SET); + i = 0; + while(!feof(f)){ + uint32_t nt_enc1, nt_enc2; + uint8_t par_enc; + if(fread(&nt_enc1, 1, 4, f) && fread(&nt_enc2, 1, 4, f) && fread(&par_enc, 1, 1, f)){ + nonces[i ] = split(~(par_enc >> 4)) << 32 | nt_enc1; + nonces[i+1] = split(~(par_enc & 0xff)) << 32 | nt_enc2; + i += 2; + } + } + nonces[i] = -1; + fclose(f); + return nonces; +} + +uint32_t **space; +size_t thread_count; +size_t states_tested = 0; +size_t total_states; +size_t keys_found = 0; + +void* crack_states_thread(void* x){ + const size_t thread_id = (size_t)x; + int j; + for(j = thread_id; space[j * 5]; j += thread_count) { + uint64_t key = craptev1_search_partition(space + j * 5); + states_tested = total_states - craptev1_sizeof_space(space+j*5); + printf("Cracking... %6.02f%%\n", (100.0*states_tested/(total_states))); + if(key != -1){ + printf("Found key: %012lx\n", key); + exit(0); + } + } + return NULL; +} + +int main(int argc, char* argv[]){ + if(argc != 2){ + printf("Usage: %s \n", argv[0]); + return -1; + } + uint64_t *nonces = readnonces(argv[1]); + space = craptev1_get_space(nonces, 95, uid); + total_states = craptev1_sizeof_space(space); + + thread_count = get_nprocs_conf(); + pthread_t threads[thread_count]; + printf("Starting %lu threads to test %lu states\n", thread_count, total_states); + size_t i; + states_tested = 0; + for(i = 0; i < thread_count; i++){ + pthread_create(&threads[i], NULL, crack_states_thread, (void*) i); + } + for(i = 0; i < thread_count; i++){ + pthread_join(threads[i], 0); + } + printf("Tested %lu states\n", states_tested); + + craptev1_destroy_space(space); + return 0; +} + diff --git a/solve_piwi_bs.c b/solve_piwi_bs.c new file mode 100644 index 0000000..aea81b3 --- /dev/null +++ b/solve_piwi_bs.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include "craptev1.h" +#include "crypto1_bs.h" +#include "crypto1_bs_crack.h" + +uint64_t split(uint8_t p){ + return (((p & 0x8) >>3 )| ((p & 0x4) >> 2) << 8 | ((p & 0x2) >> 1) << 16 | (p & 0x1) << 24 ); +} + +uint32_t uid; +uint64_t *readnonces(char* fname){ + int i; + FILE *f = fopen(fname, "r"); + uint64_t *nonces = malloc(sizeof (uint64_t) << 24); + if(fread(&uid, 1, 4, f)){ + uid = rev32(uid); + } + fseek(f, 6, SEEK_SET); + i = 0; + while(!feof(f)){ + uint32_t nt_enc1, nt_enc2; + uint8_t par_enc; + if(fread(&nt_enc1, 1, 4, f) && fread(&nt_enc2, 1, 4, f) && fread(&par_enc, 1, 1, f)){ + nonces[i ] = split(~(par_enc >> 4)) << 32 | nt_enc1; + nonces[i+1] = split(~(par_enc & 0xff)) << 32 | nt_enc2; + i += 2; + } + } + nonces[i] = -1; + fclose(f); + return nonces; +} + +uint32_t **space; +size_t thread_count; + +void* crack_states_thread(void* x){ + const size_t thread_id = (size_t)x; + int j; + for(j = thread_id; space[j * 5]; j += thread_count) { + const uint64_t key = crack_states_bitsliced(space + j * 5); + if(key != -1){ + printf("Found key: %012lx\n", key); + __sync_fetch_and_add(&keys_found, 1); + break; + } else if(keys_found){ + break; + } else { + printf("Cracking... %6.02f%%\n", (100.0*total_states_tested/(total_states))); + } + } + return NULL; +} + +int main(int argc, char* argv[]){ + if(argc != 2){ + printf("Usage: %s \n", argv[0]); + return -1; + } + uint64_t *nonces = readnonces(argv[1]); + space = craptev1_get_space(nonces, 95, uid); + total_states = craptev1_sizeof_space(space); + + thread_count = get_nprocs_conf(); + pthread_t threads[thread_count]; + size_t i; + + printf("Initializing BS crypto-1\n"); + crypto1_bs_init(); + printf("Using %u-bit bitslices\n", MAX_BITSLICES); + + uint8_t rollback_byte = **space; + printf("Bitslicing rollback byte: %02x...\n", rollback_byte); + // convert to 32 bit little-endian + crypto1_bs_bitslice_value32(rev32((rollback_byte)), bitsliced_rollback_byte, 8); + + printf("Bitslicing nonces...\n"); + for(size_t tests = 0; tests < NONCE_TESTS; tests++){ + // pre-xor the uid into the decrypted nonces, and also pre-xor the uid parity into the encrypted parity bits - otherwise an exta xor is required in the decryption routine + uint32_t test_nonce = uid^rev32(nonces[tests]); + uint32_t test_parity = (nonces[tests]>>32)^rev32(uid); + test_parity = ((parity(test_parity >> 24 & 0xff) & 1) | (parity(test_parity>>16 & 0xff) & 1)<<1 | (parity(test_parity>>8 & 0xff) & 1)<<2 | (parity(test_parity &0xff) & 1) << 3); + crypto1_bs_bitslice_value32(test_nonce, bitsliced_encrypted_nonces[tests], 32); + // convert to 32 bit little-endian + crypto1_bs_bitslice_value32(~(test_parity)<<24, bitsliced_encrypted_parity_bits[tests], 4); + } + + total_states_tested = 0; + keys_found = 0; + + printf("Starting %lu threads to test %lu states\n", thread_count, total_states); + for(i = 0; i < thread_count; i++){ + pthread_create(&threads[i], NULL, crack_states_thread, (void*) i); + } + for(i = 0; i < thread_count; i++){ + pthread_join(threads[i], 0); + } + printf("Tested %lu states\n", total_states_tested); + + craptev1_destroy_space(space); + return 0; +} + +