From 110dfab668307ba2ed5f320bf6ea6200f9a91a97 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Wed, 6 Nov 2024 16:52:48 +0800 Subject: [PATCH] Improved algorithm for hf iclass legrec Improved algorithm for hf iclass legrec by taking in account the hash0 limitations for the ending bits distributions of each key bite, thus reducing the key entropy and number of required tries from 2^24 to almost 2^19 --- CHANGELOG.md | 1 + armsrc/iclass.c | 47 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6d34e9ab..bc5da24d9 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 improved algorithm for `hf iclass legrec` leveraging reduced entropy from hash0 constraints (@antiklesys) - Fixed `hf iclass configcard` when generating elite or keyroll elite configcards for Rev.C legacy readers (@antiklesys) - Changed `hf mf c*` - now accepts a --gdm flag to write using uscuid/gdm 20/23 alt magic wakeup (@nvx) - Changed `pm3_console()` - Python/Lua/C: replace `passthru` by `capture` and `quiet` (@doegox) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 7a9256323..26f466c95 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -2157,19 +2157,41 @@ out: } } -static void generate_single_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { - uint32_t carry = index; +static void generate_single_key_block_inverted_opt(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { + + uint8_t bits_index = index / 16383; + uint8_t ending_bits[] = { //all possible 70 combinations of 4x0 and 4x1 as key ending bits + 0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, 0x2E, 0x33, + 0x35, 0x36, 0x39, 0x3A, 0x3C, 0x47, 0x4B, 0x4D, 0x4E, 0x53, + 0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A, + 0x6C, 0x71, 0x72, 0x74, 0x78, 0x87, 0x8B, 0x8D, 0x8E, 0x93, + 0x95, 0x96, 0x99, 0x9A, 0x9C, 0xA3, 0xA5, 0xA6, 0xA9, 0xAA, + 0xAC, 0xB1, 0xB2, 0xB4, 0xB8, 0xC3, 0xC5, 0xC6, 0xC9, 0xCA, + 0xCC, 0xD1, 0xD2, 0xD4, 0xD8, 0xE1, 0xE2, 0xE4, 0xE8, 0xF0 + }; + + uint8_t binary_endings[8]; // Array to store binary values for each ending bit + // Extract each bit from the ending_bits[k] and store it in binary_endings + uint8_t ending = ending_bits[bits_index]; + for (int i = 7; i >= 0; i--) { + binary_endings[i] = ending & 1; + ending >>= 1; + } + + uint8_t binary_mids[8]; // Array to store the 2-bit chunks of index + // Iterate over the 16-bit integer and store 2 bits at a time in the result array + for (int i = 0; i < 8; i++) { + // Shift and mask to get 2 bits and store them as an 8-bit value + binary_mids[7 - i] = (index >> (i * 2)) & 0x03; // 0x03 is a mask for 2 bits (binary 11) + } memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE); - for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) { - uint8_t increment_value = carry & 0x07; // Use only the last 3 bits of carry - keyBlock[j] = increment_value; // Set the last 3 bits, assuming first 5 bits are always 0 - - carry >>= 3; // Shift right by 3 bits for the next byte - if (carry == 0) { - // If no more carry, break early to avoid unnecessary loops - break; - } + // Start from the second byte, index 1 as we're never gonna touch the first byte + for (int i = 1; i < PICOPASS_BLOCK_SIZE; i++) { + // Clear the last bit of the current byte (AND with 0xFE) + keyBlock[i] &= 0xF8; + // Set the last bit to the corresponding value from binary_endings (OR with binary_endings[i]) + keyBlock[i] |= ((binary_mids[i] & 0x03) << 1) | (binary_endings[i] & 0x01); } } @@ -2292,8 +2314,9 @@ void iClass_Recover(iclass_recover_req_t *msg) { } } - generate_single_key_block_inverted(zero_key, index, genkeyblock); if (msg->test) { + //Step3 Calculate New Key (Optimised Algo V2) + generate_single_key_block_inverted_opt(zero_key, index, genkeyblock); memcpy(genkeyblock, zero_key, PICOPASS_BLOCK_SIZE); }