mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
Merge pull request #3204 from Antiklesys/master
Huge hf iclass legbrute performance improvements (4x)
This commit is contained in:
@@ -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]
|
||||
- Optimized `hf iclass legbrute` throughput: replaced recursive `suc()`/`output()` cipher functions with iterative loops, added 256-entry LUT for the `select()` function eliminating redundant bit arithmetic and halving key lookups per state step, switched successor state to in-place pointer update removing per-call struct copies, added `doMAC_brute()` with byte-wise LSB-first processing and direct output bit packing eliminating all bitstream struct overhead and output reversal calls per key candidate, and replaced per-iteration 64-bit modulo progress check with a countdown counter (@antiklesys)
|
||||
- Improved `hf iclass legbrute`: fixed multithreaded key-range partitioning so threads cover non-overlapping slices of the 40-bit keyspace, added ETA display, keyboard abort with resume hint, `_Atomic` correctness for shared state, `pthread_create` error handling, and thread count capped at available CPUs (@antiklesys)
|
||||
- Added wildcard support to `hf secc sim` payloads (@antiklesys)
|
||||
- Added `hf secc` to build a base for simulating basic function of iclass SE config cards (@antiklesys)
|
||||
|
||||
+3
-2
@@ -3187,12 +3187,13 @@ fast_restore:
|
||||
Dbhexdump(8, fast_restore_key, false);
|
||||
Dbprintf(_RED_("Attempted to restore original key for %3d times and failed. Stopping. Card is likely unusable."), revert_retries);
|
||||
}
|
||||
if (recovered) {
|
||||
if (recovered && reverted) {
|
||||
goto restore;
|
||||
} else {
|
||||
} else if (revert_retries >= 7) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
goto out;
|
||||
|
||||
restore:
|
||||
;// empty statement for compilation
|
||||
|
||||
+24
-24
@@ -4338,7 +4338,7 @@ static int CmdHFiClass_BlackTears(const char *Cmd) {
|
||||
arg_int0(NULL, "ki", "<dec>", "Key index to select key from memory 'hf iclass managekeys'"),
|
||||
arg_lit0(NULL, "credit", "key is assumed to be the credit key"),
|
||||
arg_int0("s", NULL, "<dec>", "tearoff delay start (in us) must be between 1 and 43000 (43ms). Precision is about 1/3 us"),
|
||||
arg_int0("i", NULL, "<dec>", "tearoff delay increment (in us) - default 10"),
|
||||
arg_int0("i", NULL, "<dec>", "tearoff delay increment (in us) - default 5"),
|
||||
arg_int0("e", NULL, "<dec>", "tearoff delay end (in us) must be a higher value than the start delay"),
|
||||
arg_str0("o", "otp", "<hex>", "Custom OTP value as 2 hex bytes"),
|
||||
arg_lit0(NULL, "dns", "Do not stabilize the bits, and return the raw dump of the block after tearoff"),
|
||||
@@ -4510,27 +4510,16 @@ static int CmdHFiClass_BlackTears(const char *Cmd) {
|
||||
uint8_t data_read_orig[8] = {0};
|
||||
uint8_t ff_data[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
bool first_read = false;
|
||||
bool reread = false;
|
||||
bool erase_phase = false;
|
||||
|
||||
read_auth = false;
|
||||
|
||||
int blockno = 1;
|
||||
|
||||
|
||||
int res_orig = iclass_read_block_ex(key, blockno, keyType, elite, rawkey, use_replay, verbose, read_auth, shallow_mod, data_read_orig, false, false);
|
||||
while (reread) {
|
||||
if (res_orig == PM3_SUCCESS && !reread) {
|
||||
if (memcmp(data_read_orig, zeros, 8) == 0) {
|
||||
reread = true;
|
||||
} else {
|
||||
reread = false;
|
||||
}
|
||||
} else if (res_orig == PM3_SUCCESS && reread) {
|
||||
reread = false;
|
||||
if (blockno == 2 && memcmp(data_read_orig, zeros, 8) == 0) {
|
||||
reread = true;
|
||||
}
|
||||
}
|
||||
if (res_orig == PM3_SUCCESS && memcmp(data_read_orig, zeros, 8) == 0) {
|
||||
// zeros may be a transient read artifact - read once more to confirm
|
||||
res_orig = iclass_read_block_ex(key, blockno, keyType, elite, rawkey, use_replay, verbose, read_auth, shallow_mod, data_read_orig, false, false);
|
||||
}
|
||||
|
||||
uint8_t data[8] = { 0 }; // tearoff payload
|
||||
@@ -4604,7 +4593,7 @@ static int CmdHFiClass_BlackTears(const char *Cmd) {
|
||||
// read the data back
|
||||
uint8_t data_read[8] = {0};
|
||||
first_read = false;
|
||||
reread = false;
|
||||
bool reread = false;
|
||||
bool decrease = false;
|
||||
int readcount = 0;
|
||||
while (first_read == false) {
|
||||
@@ -4631,6 +4620,9 @@ static int CmdHFiClass_BlackTears(const char *Cmd) {
|
||||
reread = false;
|
||||
} else if (res != PM3_SUCCESS) {
|
||||
decrease = true;
|
||||
if (readcount == 100) {
|
||||
PrintAndLogEx(WARNING, "\nCard not responding after %d attempts, press " _GREEN_("<Enter>") " to abort", readcount);
|
||||
}
|
||||
}
|
||||
|
||||
readcount++;
|
||||
@@ -4688,7 +4680,7 @@ static int CmdHFiClass_BlackTears(const char *Cmd) {
|
||||
bool goto_out = false;
|
||||
|
||||
// App limit became SMALLER :(
|
||||
if (data_read[0] > data_read_orig[0]) {
|
||||
if (data_read[0] < data_read_orig[0]) {
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(SUCCESS, "Application limit changed, from "_YELLOW_("%u")" to "_YELLOW_("%u"), data_read_orig[0], data_read[0]);
|
||||
@@ -4772,7 +4764,7 @@ static int CmdHFiClass_BlackTears(const char *Cmd) {
|
||||
msleep(tearoff_sleep);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
out:
|
||||
|
||||
@@ -6111,13 +6103,14 @@ static void *brute_thread(void *args_void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t progress_countdown = 1000000;
|
||||
while (index < args->index_end && !*(args->found) && !*(args->aborted)) {
|
||||
|
||||
generate_key_block_inverted(args->startingKey, index, div_key);
|
||||
doMAC(args->CCNR1, div_key, mac);
|
||||
doMAC_brute(args->CCNR1, div_key, mac);
|
||||
|
||||
if (memcmp(mac, args->MAC_TAG1, 4) == 0) {
|
||||
doMAC(args->CCNR2, div_key, verification_mac);
|
||||
doMAC_brute(args->CCNR2, div_key, verification_mac);
|
||||
if (memcmp(verification_mac, args->MAC_TAG2, 4) == 0) {
|
||||
pthread_mutex_lock(args->log_lock);
|
||||
if (!*(args->found)) {
|
||||
@@ -6134,7 +6127,8 @@ static void *brute_thread(void *args_void) {
|
||||
}
|
||||
|
||||
uint64_t thread_progress = index - args->index_start;
|
||||
if (thread_progress % 1000000 == 0 && !*(args->found)) {
|
||||
if (--progress_countdown == 0 && !*(args->found)) {
|
||||
progress_countdown = 1000000;
|
||||
|
||||
if (args->thread_id == 0) {
|
||||
uint64_t keyspace = (uint64_t)1 << 40;
|
||||
@@ -6481,7 +6475,7 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf iclass legrec",
|
||||
"Attempts to recover the diversified key of a specific iCLASS card. This may take several days.\n"
|
||||
"The card must remain be on the PM3 antenna during the whole process.\n"
|
||||
"The card must remain on the PM3 antenna during the whole process.\n"
|
||||
_RED_(" ! Warning ! ") _WHITE_(" This process may brick the card! ") _RED_(" ! Warning ! "),
|
||||
"hf iclass legrec --macs 0000000089cb984b\n"
|
||||
"hf iclass legrec --macs 0000000089cb984b --index 0 --loop 100 --notest"
|
||||
@@ -6489,7 +6483,7 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) {
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1(NULL, "macs", "<hex>", "AA1 Authentication MACs"),
|
||||
arg_str0(NULL, "macs", "<hex>", "AA1 Authentication MACs"),
|
||||
arg_int0(NULL, "index", "<dec>", "Where to start from to retrieve the key (def: 0)"),
|
||||
arg_int0(NULL, "loop", "<dec>", "The number of key retrieval cycles to perform, max 10000 (def 100)"),
|
||||
arg_lit0(NULL, "debug", "Re-enables tracing for debugging. Limits cycles to 1"),
|
||||
@@ -6523,6 +6517,12 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) {
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
if (macs_len == 0) {
|
||||
PrintAndLogEx(ERR, "Missing required argument: --macs");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (no_test) {
|
||||
test = false;
|
||||
}
|
||||
|
||||
+94
-135
@@ -59,132 +59,57 @@ typedef struct {
|
||||
uint16_t t;
|
||||
} State_t;
|
||||
|
||||
/**
|
||||
* Definition 2. The feedback function for the top register T : F 16/2 → F 2
|
||||
* is defined as
|
||||
* T (x 0 x 1 . . . . . . x 15 ) = x 0 ⊕ x 1 ⊕ x 5 ⊕ x 7 ⊕ x 10 ⊕ x 11 ⊕ x 14 ⊕ x 15 .
|
||||
**/
|
||||
static bool T(State_t state) {
|
||||
/*
|
||||
bool x0 = state.t & 0x8000;
|
||||
bool x1 = state.t & 0x4000;
|
||||
bool x5 = state.t & 0x0400;
|
||||
bool x7 = state.t & 0x0100;
|
||||
bool x10 = state.t & 0x0020;
|
||||
bool x11 = state.t & 0x0010;
|
||||
bool x14 = state.t & 0x0002;
|
||||
bool x15 = state.t & 0x0001;
|
||||
return x0 ^ x1 ^ x5 ^ x7 ^ x10 ^ x11 ^ x14 ^ x15;
|
||||
*/
|
||||
#define _x0 ((state.t & 0x8000) >> 15 )
|
||||
#define _x1 ((state.t & 0x4000) >> 14 )
|
||||
#define _x5 ((state.t & 0x0400) >> 10 )
|
||||
#define _x7 ((state.t & 0x0100) >> 8 )
|
||||
#define _x10 ((state.t & 0x0020) >> 5 )
|
||||
#define _x11 ((state.t & 0x0010) >> 4 )
|
||||
#define _x14 ((state.t & 0x0002) >> 1 )
|
||||
#define _x15 (state.t & 0x0001)
|
||||
return (_x0) ^ (_x1) ^ (_x5) ^ (_x7) ^ (_x10) ^ (_x11) ^ (_x14) ^ (_x15);
|
||||
}
|
||||
/**
|
||||
* Similarly, the feedback function for the bottom register B : F 8/2 → F 2 is defined as
|
||||
* B(x 0 x 1 . . . x 7 ) = x 1 ⊕ x 2 ⊕ x 3 ⊕ x 7 .
|
||||
**/
|
||||
/*static bool B(State_t state) {
|
||||
bool x1 = state.b & 0x40;
|
||||
bool x2 = state.b & 0x20;
|
||||
bool x3 = state.b & 0x10;
|
||||
bool x7 = state.b & 0x01;
|
||||
return x1 ^ x2 ^ x3 ^ x7;
|
||||
}
|
||||
*/
|
||||
#define B(x) (((x.b & 0x40) >> 6) ^ ((x.b & 0x20) >> 5) ^ ((x.b & 0x10) >> 4) ^ (x.b & 0x01))
|
||||
|
||||
// 12 3456
|
||||
// 0100 0000
|
||||
// Precomputed lookup table for the r-dependent part of select(x, y, r).
|
||||
// z0 (bit2) depends only on r. z1 (bit1) = LUT_bit1 ^ x ^ y. z2 (bit0) = LUT_bit0 ^ x.
|
||||
// Generated from the _select formula; x and y are folded in at call time.
|
||||
static const uint8_t opt_select_LUT[256] = {
|
||||
00, 03, 02, 01, 02, 03, 00, 01, 04, 07, 07, 04, 06, 07, 05, 04,
|
||||
01, 02, 03, 00, 02, 03, 00, 01, 05, 06, 06, 05, 06, 07, 05, 04,
|
||||
06, 05, 04, 07, 04, 05, 06, 07, 06, 05, 05, 06, 04, 05, 07, 06,
|
||||
07, 04, 05, 06, 04, 05, 06, 07, 07, 04, 04, 07, 04, 05, 07, 06,
|
||||
06, 05, 04, 07, 04, 05, 06, 07, 02, 01, 01, 02, 00, 01, 03, 02,
|
||||
03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06,
|
||||
00, 03, 02, 01, 02, 03, 00, 01, 00, 03, 03, 00, 02, 03, 01, 00,
|
||||
05, 06, 07, 04, 06, 07, 04, 05, 05, 06, 06, 05, 06, 07, 05, 04,
|
||||
02, 01, 00, 03, 00, 01, 02, 03, 06, 05, 05, 06, 04, 05, 07, 06,
|
||||
03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06,
|
||||
02, 01, 00, 03, 00, 01, 02, 03, 02, 01, 01, 02, 00, 01, 03, 02,
|
||||
03, 00, 01, 02, 00, 01, 02, 03, 03, 00, 00, 03, 00, 01, 03, 02,
|
||||
04, 07, 06, 05, 06, 07, 04, 05, 00, 03, 03, 00, 02, 03, 01, 00,
|
||||
01, 02, 03, 00, 02, 03, 00, 01, 05, 06, 06, 05, 06, 07, 05, 04,
|
||||
04, 07, 06, 05, 06, 07, 04, 05, 04, 07, 07, 04, 06, 07, 05, 04,
|
||||
01, 02, 03, 00, 02, 03, 00, 01, 01, 02, 02, 01, 02, 03, 01, 00,
|
||||
};
|
||||
|
||||
/**
|
||||
* Definition 3 (Selection function). The selection function select : F 2 × F 2 ×
|
||||
* F 8/2 → F 3/2 is defined as select(x, y, r) = z 0 z 1 z 2 where
|
||||
* z 0 = (r 0 ∧ r 2 ) ⊕ (r 1 ∧ r 3 ) ⊕ (r 2 ∨ r 4 )
|
||||
* z 1 = (r 0 ∨ r 2 ) ⊕ (r 5 ∨ r 7 ) ⊕ r 1 ⊕ r 6 ⊕ x ⊕ y
|
||||
* z 2 = (r 3 ∧ r 5 ) ⊕ (r 4 ∧ r 6 ) ⊕ r 7 ⊕ x
|
||||
* Definition 4 (Successor state). Optimized in-place version.
|
||||
* T(t) computed via parallel XOR reduction on the masked t register.
|
||||
* B(b) computed via parallel XOR reduction on the b register.
|
||||
* select(x,y,r) resolved via opt_select_LUT with a single key lookup.
|
||||
**/
|
||||
static uint8_t _select(bool x, bool y, uint8_t r) {
|
||||
#define _r0 ((r >> 7) & 0x01)
|
||||
#define _r1 ((r >> 6) & 0x01)
|
||||
#define _r2 ((r >> 5) & 0x01)
|
||||
#define _r3 ((r >> 4) & 0x01)
|
||||
#define _r4 ((r >> 3) & 0x01)
|
||||
#define _r5 ((r >> 2) & 0x01)
|
||||
#define _r6 ((r >> 1) & 0x01)
|
||||
#define _r7 (r & 0x01)
|
||||
static void successor(const uint8_t *k, State_t *s, uint8_t y) {
|
||||
// T(t) = x0^x1^x5^x7^x10^x11^x14^x15, mask selects those bits
|
||||
uint16_t Tt = s->t & 0xc533;
|
||||
Tt ^= Tt >> 1;
|
||||
Tt ^= Tt >> 4;
|
||||
Tt ^= Tt >> 10;
|
||||
Tt ^= Tt >> 8;
|
||||
// bit0 of Tt is now T(t)
|
||||
|
||||
#define _z0 ( (_r0 & _r2) ^ ( _r1 & (!_r3)) ^ (_r2 | _r4) )
|
||||
#define _z1 ( (_r0 | _r2) ^ ( _r5 | _r7) ^_r1 ^ _r6 ^ (x) ^ (y) )
|
||||
#define _z2 ( (_r3 & (!_r5)) ^ (_r4 & _r6) ^ _r7 ^ (x) )
|
||||
s->t = (s->t >> 1) | ((Tt ^ (s->r >> 7) ^ (s->r >> 3)) << 15);
|
||||
|
||||
/*
|
||||
uint8_t r0 = r >> 7 & 0x1;
|
||||
uint8_t r1 = r >> 6 & 0x1;
|
||||
uint8_t r2 = r >> 5 & 0x1;
|
||||
uint8_t r3 = r >> 4 & 0x1;
|
||||
uint8_t r4 = r >> 3 & 0x1;
|
||||
uint8_t r5 = r >> 2 & 0x1;
|
||||
uint8_t r6 = r >> 1 & 0x1;
|
||||
uint8_t r7 = r & 0x1;
|
||||
// B(b) = b1^b2^b3^b7; bit0 of opt_B = B(b) after the XOR reduction
|
||||
uint8_t opt_B = s->b ^ (s->b >> 6) ^ (s->b >> 5) ^ (s->b >> 4);
|
||||
s->b = (s->b >> 1) | ((opt_B ^ s->r) << 7);
|
||||
|
||||
bool z0 = (r0 & r2) ^ (r1 & (!r3)) ^ (r2 | r4);
|
||||
bool z1 = (r0 | r2) ^ (r5 | r7) ^ r1 ^ r6 ^ x ^ y;
|
||||
bool z2 = (r3 & (!r5)) ^ (r4 & r6) ^ r7 ^ x;
|
||||
// select via LUT: z0 from LUT directly, z1/z2 fold in Tt and y
|
||||
uint8_t sel = opt_select_LUT[s->r] & 0x04;
|
||||
sel |= (opt_select_LUT[s->r] ^ ((Tt ^ y) << 1)) & 0x02;
|
||||
sel |= (opt_select_LUT[s->r] ^ Tt) & 0x01;
|
||||
|
||||
// The three bitz z0.. z1 are packed into a uint8_t:
|
||||
// 00000ZZZ
|
||||
//Return value is a uint8_t
|
||||
return ((z0 << 2) & 4) | ((z1 << 1) & 2) | (z2 & 1);
|
||||
*/
|
||||
return ((_z0 << 2) & 4) | ((_z1 << 1) & 2) | (_z2 & 1);
|
||||
|
||||
/*
|
||||
uint8_t retval = 0;
|
||||
retval |= (z0 << 2) & 4;
|
||||
retval |= (z1 << 1) & 2;
|
||||
retval |= (z2) & 1;
|
||||
|
||||
// Return value 0 <= retval <= 7
|
||||
return retval;
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition 4 (Successor state). Let s = l, r, t, b be a cipher state, k ∈ (F 82 ) 8
|
||||
* be a key and y ∈ F 2 be the input bit. Then, the successor cipher state s ′ =
|
||||
* l ′ , r ′ , t ′ , b ′ is defined as
|
||||
* t ′ := (T (t) ⊕ r 0 ⊕ r 4 )t 0 . . . t 14 l ′ := (k [select(T (t),y,r)] ⊕ b ′ ) ⊞ l ⊞ r
|
||||
* b ′ := (B(b) ⊕ r 7 )b 0 . . . b 6 r ′ := (k [select(T (t),y,r)] ⊕ b ′ ) ⊞ l
|
||||
*
|
||||
* @param s - state
|
||||
* @param k - array containing 8 bytes
|
||||
**/
|
||||
static State_t successor(const uint8_t *k, State_t s, bool y) {
|
||||
bool r0 = s.r >> 7 & 0x1;
|
||||
bool r4 = s.r >> 3 & 0x1;
|
||||
bool r7 = s.r & 0x1;
|
||||
|
||||
State_t successor = {0, 0, 0, 0};
|
||||
|
||||
successor.t = s.t >> 1;
|
||||
successor.t |= ((T(s)) ^ (r0) ^ (r4)) << 15;
|
||||
|
||||
successor.b = s.b >> 1;
|
||||
successor.b |= ((B(s)) ^ (r7)) << 7;
|
||||
|
||||
bool Tt = T(s);
|
||||
|
||||
successor.l = ((k[_select(Tt, y, s.r)] ^ successor.b) + s.l + s.r) & 0xFF;
|
||||
successor.r = ((k[_select(Tt, y, s.r)] ^ successor.b) + s.l) & 0xFF;
|
||||
|
||||
return successor;
|
||||
uint8_t r = s->r;
|
||||
s->r = (k[sel] ^ s->b) + s->l;
|
||||
s->l = s->r + r;
|
||||
}
|
||||
/**
|
||||
* We define the successor function suc which takes a key k ∈ (F 82 ) 8 , a state s and
|
||||
@@ -193,11 +118,9 @@ static State_t successor(const uint8_t *k, State_t s, bool y) {
|
||||
* @param k - array containing 8 bytes
|
||||
**/
|
||||
static State_t suc(uint8_t *k, State_t s, BitstreamIn_t *bitstream) {
|
||||
if (bitsLeft(bitstream) == 0) {
|
||||
return s;
|
||||
}
|
||||
bool lastbit = tailBit(bitstream);
|
||||
return successor(k, suc(k, s, bitstream), lastbit);
|
||||
while (bitsLeft(bitstream) > 0)
|
||||
successor(k, &s, headBit(bitstream));
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,15 +131,11 @@ static State_t suc(uint8_t *k, State_t s, BitstreamIn_t *bitstream) {
|
||||
* output(k, s, x 0 . . . x n ) = output(s) · output(k, s ′ , x 1 . . . x n )
|
||||
* where s ′ = suc(k, s, x 0 ).
|
||||
**/
|
||||
static void output(uint8_t *k, State_t s, BitstreamIn_t *in, BitstreamOut_t *out) {
|
||||
if (bitsLeft(in) == 0) {
|
||||
return;
|
||||
static void output(uint8_t *k, State_t s, BitstreamIn_t *in, BitstreamOut_t *out) {
|
||||
while (bitsLeft(in) > 0) {
|
||||
pushBit(out, (s.r >> 2) & 1);
|
||||
successor(k, &s, headBit(in));
|
||||
}
|
||||
pushBit(out, (s.r >> 2) & 1);
|
||||
//Remove first bit
|
||||
uint8_t x0 = headBit(in);
|
||||
State_t ss = successor(k, s, x0);
|
||||
output(k, ss, in, out);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,21 +162,61 @@ static void MAC(uint8_t *k, BitstreamIn_t input, BitstreamOut_t out) {
|
||||
|
||||
void doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) {
|
||||
uint8_t cc_nr[13] = { 0 };
|
||||
uint8_t div_key[8];
|
||||
|
||||
memcpy(cc_nr, cc_nr_p, 12);
|
||||
memcpy(div_key, div_key_p, 8);
|
||||
|
||||
reverse_arraybytes(cc_nr, 12);
|
||||
BitstreamIn_t bitstream = {cc_nr, 12 * 8, 0};
|
||||
uint8_t dest [] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
BitstreamOut_t out = { dest, sizeof(dest) * 8, 0 };
|
||||
MAC(div_key, bitstream, out);
|
||||
MAC(div_key_p, bitstream, out);
|
||||
//The output MAC must also be reversed
|
||||
reverse_arraybytes(dest, sizeof(dest));
|
||||
memcpy(mac, dest, 4);
|
||||
}
|
||||
|
||||
// Feeds `length` raw bytes into cipher state s, one bit at a time LSB-first.
|
||||
// Equivalent to reflect8-then-MSB-first used by doMAC, with no intermediate buffer.
|
||||
static void suc_bytes(const uint8_t *k, State_t *s, const uint8_t *in, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
uint8_t b = in[i];
|
||||
successor(k, s, b); b >>= 1;
|
||||
successor(k, s, b); b >>= 1;
|
||||
successor(k, s, b); b >>= 1;
|
||||
successor(k, s, b); b >>= 1;
|
||||
successor(k, s, b); b >>= 1;
|
||||
successor(k, s, b); b >>= 1;
|
||||
successor(k, s, b); b >>= 1;
|
||||
successor(k, s, b);
|
||||
}
|
||||
}
|
||||
|
||||
// Collects nbytes of cipher output into `out`, packing bits LSB-first per byte.
|
||||
// Equivalent to output()+reflect8 used by doMAC, with no intermediate buffer or reversal.
|
||||
static void output_bytes(const uint8_t *k, State_t *s, uint8_t *out, int nbytes) {
|
||||
for (int i = 0; i < nbytes; i++) {
|
||||
uint8_t bout = 0;
|
||||
bout |= (s->r & 0x4) >> 2; successor(k, s, 0);
|
||||
bout |= (s->r & 0x4) >> 1; successor(k, s, 0);
|
||||
bout |= (s->r & 0x4); successor(k, s, 0);
|
||||
bout |= (s->r & 0x4) << 1; successor(k, s, 0);
|
||||
bout |= (s->r & 0x4) << 2; successor(k, s, 0);
|
||||
bout |= (s->r & 0x4) << 3; successor(k, s, 0);
|
||||
bout |= (s->r & 0x4) << 4; successor(k, s, 0);
|
||||
bout |= (s->r & 0x4) << 5; successor(k, s, 0);
|
||||
out[i] = bout;
|
||||
}
|
||||
}
|
||||
|
||||
// doMAC variant for the brute-force hot loop: takes raw (non-reflected) cc_nr and
|
||||
// produces the same MAC as doMAC with no intermediate buffers, no reversal calls,
|
||||
// and no bitstream overhead.
|
||||
void doMAC_brute(const uint8_t *cc_nr, const uint8_t *div_key, uint8_t mac[4]) {
|
||||
State_t s = init(div_key);
|
||||
suc_bytes(div_key, &s, cc_nr, 12);
|
||||
output_bytes(div_key, &s, mac, 4);
|
||||
}
|
||||
|
||||
void doMAC_N(uint8_t *address_data_p, uint8_t address_data_size, uint8_t *div_key_p, uint8_t mac[4]) {
|
||||
uint8_t *address_data;
|
||||
uint8_t div_key[8];
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "pm3_cmd.h"
|
||||
|
||||
void doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]);
|
||||
void doMAC_brute(const uint8_t *cc_nr, const uint8_t *div_key, uint8_t mac[4]);
|
||||
void doMAC_N(uint8_t *address_data_p, uint8_t address_data_size, uint8_t *div_key_p, uint8_t mac[4]);
|
||||
|
||||
#ifndef ON_DEVICE
|
||||
|
||||
@@ -327,6 +327,24 @@ static void *bf_thread(void *thread_arg) {
|
||||
memcpy(bytes_to_recover, targ->bytes_to_recover, sizeof(bytes_to_recover));
|
||||
memcpy(keytable, targ->keytable, sizeof(keytable));
|
||||
|
||||
// Precompute which key_sel positions are fixed (already-cracked) vs. brute-forced.
|
||||
// sel_brute_idx[i] == 0xFF: key_sel[i] is constant for this entire brute-force run.
|
||||
// sel_brute_idx[i] == j: key_sel[i] = (brute >> (j*8)) & 0xFF each iteration.
|
||||
uint8_t sel_brute_idx[8];
|
||||
uint8_t key_sel[8];
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
sel_brute_idx[i] = 0xFF;
|
||||
for (uint8_t j = 0; j < numbytes_to_recover; j++) {
|
||||
if (key_index[i] == bytes_to_recover[j]) {
|
||||
sel_brute_idx[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sel_brute_idx[i] == 0xFF) {
|
||||
key_sel[i] = keytable[key_index[i]] & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
while (!(brute & endmask)) {
|
||||
|
||||
int found = __atomic_load_n(&loclass_found, __ATOMIC_SEQ_CST);
|
||||
@@ -335,24 +353,13 @@ static void *bf_thread(void *thread_arg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//Update the keytable with the brute-values
|
||||
for (uint8_t i = 0; i < numbytes_to_recover; i++) {
|
||||
keytable[bytes_to_recover[i]] &= 0xFF00;
|
||||
keytable[bytes_to_recover[i]] |= (brute >> (i * 8) & 0xFF);
|
||||
// Update only the bruted positions of key_sel directly from brute
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
if (sel_brute_idx[i] != 0xFF) {
|
||||
key_sel[i] = (brute >> (sel_brute_idx[i] * 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t key_sel[8] = {0};
|
||||
|
||||
// Piece together the key
|
||||
key_sel[0] = keytable[key_index[0]] & 0xFF;
|
||||
key_sel[1] = keytable[key_index[1]] & 0xFF;
|
||||
key_sel[2] = keytable[key_index[2]] & 0xFF;
|
||||
key_sel[3] = keytable[key_index[3]] & 0xFF;
|
||||
key_sel[4] = keytable[key_index[4]] & 0xFF;
|
||||
key_sel[5] = keytable[key_index[5]] & 0xFF;
|
||||
key_sel[6] = keytable[key_index[6]] & 0xFF;
|
||||
key_sel[7] = keytable[key_index[7]] & 0xFF;
|
||||
|
||||
// Permute from iclass format to standard format
|
||||
|
||||
uint8_t key_sel_p[8] = {0};
|
||||
@@ -364,7 +371,7 @@ static void *bf_thread(void *thread_arg) {
|
||||
|
||||
// Calc mac
|
||||
uint8_t calculated_MAC[4] = {0};
|
||||
doMAC(cc_nr, div_key, calculated_MAC);
|
||||
doMAC_brute(cc_nr, div_key, calculated_MAC);
|
||||
|
||||
// success
|
||||
if (memcmp(calculated_MAC, mac, 4) == 0) {
|
||||
@@ -375,8 +382,8 @@ static void *bf_thread(void *thread_arg) {
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0 ; i < numbytes_to_recover; i++) {
|
||||
r->values[i] = keytable[bytes_to_recover[i]] & 0xFF;
|
||||
for (uint8_t i = 0 ; i < numbytes_to_recover && i < sizeof(r->values); i++) {
|
||||
r->values[i] = (brute >> (i * 8)) & 0xFF;
|
||||
}
|
||||
__atomic_store_n(&loclass_found, targ->thread_idx, __ATOMIC_SEQ_CST);
|
||||
pthread_exit((void *)r);
|
||||
|
||||
Reference in New Issue
Block a user