mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 05:43:48 -07:00
Merge pull request #2385 from henrygab/em4x70_add_tests
add `lf em 4x70 calc` and self-tests
This commit is contained in:
commit
2b276cae1a
5 changed files with 237 additions and 118 deletions
|
@ -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 `lf em 4x70 calc` - calculate `frn`/`grn` for a given `key` + `rnd`
|
||||
- Fixed `hf 15 dump` memory leaks (@jlitewski)
|
||||
- Changed `hf search` - topaz is detect before ISO14443a and commented out WIP ICT code path (@iceman1001)
|
||||
- Fixed `hf search` - where felica reader now doesnt timeout and give wrong response (@iceman1001)
|
||||
|
|
144
armsrc/em4x70.c
144
armsrc/em4x70.c
|
@ -30,43 +30,50 @@ static em4x70_tag_t tag = { 0 };
|
|||
// EM4170 requires a parity bit on commands, other variants do not.
|
||||
static bool command_parity = true;
|
||||
|
||||
// Conversion from Ticks to RF periods
|
||||
// 1 us = 1.5 ticks
|
||||
// 1RF Period = 8us = 12 Ticks
|
||||
#define TICKS_PER_FC 12
|
||||
|
||||
// Chip timing from datasheet
|
||||
// Converted into Ticks for timing functions
|
||||
#define EM4X70_T_TAG_QUARTER_PERIOD (8 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_HALF_PERIOD (16 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_THREE_QUARTER_PERIOD (24 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_FULL_PERIOD (32 * TICKS_PER_FC) // 1 Bit Period
|
||||
#define EM4X70_T_TAG_TWA (128 * TICKS_PER_FC) // Write Access Time
|
||||
#define EM4X70_T_TAG_DIV (224 * TICKS_PER_FC) // Divergency Time
|
||||
#define EM4X70_T_TAG_AUTH (4224 * TICKS_PER_FC) // Authentication Time
|
||||
#define EM4X70_T_TAG_WEE (3072 * TICKS_PER_FC) // EEPROM write Time
|
||||
#define EM4X70_T_TAG_TWALB (672 * TICKS_PER_FC) // Write Access Time of Lock Bits
|
||||
#define EM4X70_T_TAG_BITMOD (4 * TICKS_PER_FC) // Initial time to stop modulation when sending 0
|
||||
#define EM4X70_T_TAG_TOLERANCE (8 * TICKS_PER_FC) // Tolerance in RF periods for receive/LIW
|
||||
#if 1 // Calculation of ticks for timing functions
|
||||
// Conversion from Ticks to RF periods
|
||||
// 1 us = 1.5 ticks
|
||||
// 1RF Period = 8us = 12 Ticks
|
||||
#define TICKS_PER_FC 12
|
||||
|
||||
#define EM4X70_T_TAG_TIMEOUT (4 * EM4X70_T_TAG_FULL_PERIOD) // Timeout if we ever get a pulse longer than this
|
||||
#define EM4X70_T_WAITING_FOR_LIW 50 // Pulses to wait for listen window
|
||||
#define EM4X70_T_READ_HEADER_LEN 16 // Read header length (16 bit periods)
|
||||
// Chip timing from datasheet
|
||||
// Converted into Ticks for timing functions
|
||||
#define EM4X70_T_TAG_QUARTER_PERIOD (8 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_HALF_PERIOD (16 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_THREE_QUARTER_PERIOD (24 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_FULL_PERIOD (32 * TICKS_PER_FC) // 1 Bit Period
|
||||
#define EM4X70_T_TAG_TWA (128 * TICKS_PER_FC) // Write Access Time
|
||||
#define EM4X70_T_TAG_DIV (224 * TICKS_PER_FC) // Divergency Time
|
||||
#define EM4X70_T_TAG_AUTH (4224 * TICKS_PER_FC) // Authentication Time
|
||||
#define EM4X70_T_TAG_WEE (3072 * TICKS_PER_FC) // EEPROM write Time
|
||||
#define EM4X70_T_TAG_TWALB (672 * TICKS_PER_FC) // Write Access Time of Lock Bits
|
||||
#define EM4X70_T_TAG_BITMOD (4 * TICKS_PER_FC) // Initial time to stop modulation when sending 0
|
||||
#define EM4X70_T_TAG_TOLERANCE (8 * TICKS_PER_FC) // Tolerance in RF periods for receive/LIW
|
||||
|
||||
#define EM4X70_COMMAND_RETRIES 5 // Attempts to send/read command
|
||||
#define EM4X70_MAX_RECEIVE_LENGTH 96 // Maximum bits to expect from any command
|
||||
#define EM4X70_T_TAG_TIMEOUT (4 * EM4X70_T_TAG_FULL_PERIOD) // Timeout if we ever get a pulse longer than this
|
||||
#define EM4X70_T_WAITING_FOR_LIW 50 // Pulses to wait for listen window
|
||||
#define EM4X70_T_READ_HEADER_LEN 16 // Read header length (16 bit periods)
|
||||
|
||||
/**
|
||||
* These IDs are from the EM4170 datasheet
|
||||
* Some versions of the chip require a
|
||||
* (even) parity bit, others do not
|
||||
*/
|
||||
#define EM4X70_COMMAND_ID 0x01
|
||||
#define EM4X70_COMMAND_UM1 0x02
|
||||
#define EM4X70_COMMAND_AUTH 0x03
|
||||
#define EM4X70_COMMAND_PIN 0x04
|
||||
#define EM4X70_COMMAND_WRITE 0x05
|
||||
#define EM4X70_COMMAND_UM2 0x07
|
||||
#define EM4X70_COMMAND_RETRIES 5 // Attempts to send/read command
|
||||
#define EM4X70_MAX_RECEIVE_LENGTH 96 // Maximum bits to expect from any command
|
||||
#endif // Calculation of ticks for timing functions
|
||||
|
||||
#if 1 // EM4x70 Command IDs
|
||||
/**
|
||||
* These IDs are from the EM4170 datasheet.
|
||||
* Some versions of the chip require a
|
||||
* (even) parity bit, others do not.
|
||||
* The command is thus stored only in the
|
||||
* three least significant bits (mask 0x07).
|
||||
*/
|
||||
#define EM4X70_COMMAND_ID 0x01
|
||||
#define EM4X70_COMMAND_UM1 0x02
|
||||
#define EM4X70_COMMAND_AUTH 0x03
|
||||
#define EM4X70_COMMAND_PIN 0x04
|
||||
#define EM4X70_COMMAND_WRITE 0x05
|
||||
#define EM4X70_COMMAND_UM2 0x07
|
||||
#endif // EM4x70 Command IDs
|
||||
|
||||
// Constants used to determine high/low state of signal
|
||||
#define EM4X70_NOISE_THRESHOLD 13 // May depend on noise in environment
|
||||
|
@ -80,9 +87,9 @@ static bool command_parity = true;
|
|||
#define IS_TIMEOUT(timeout_ticks) (GetTicks() > timeout_ticks)
|
||||
#define TICKS_ELAPSED(start_ticks) (GetTicks() - start_ticks)
|
||||
|
||||
static uint8_t bits2byte(const uint8_t *bits, int length);
|
||||
static void bits2bytes(const uint8_t *bits, int length, uint8_t *out);
|
||||
static int em4x70_receive(uint8_t *bits, size_t length);
|
||||
static uint8_t encoded_bit_array_to_byte(const uint8_t *bits, int count_of_bits);
|
||||
static void encoded_bit_array_to_bytes(const uint8_t *bits, int count_of_bits, uint8_t *out);
|
||||
static int em4x70_receive(uint8_t *bits, size_t maximum_bits_to_read);
|
||||
static bool find_listen_window(bool command);
|
||||
|
||||
static void init_tag(void) {
|
||||
|
@ -207,9 +214,10 @@ static uint32_t get_pulse_length(edge_detection_t edge) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool check_pulse_length(uint32_t pl, uint32_t length) {
|
||||
// check if pulse length <pl> corresponds to given length <length>
|
||||
return ((pl >= (length - EM4X70_T_TAG_TOLERANCE)) && (pl <= (length + EM4X70_T_TAG_TOLERANCE)));
|
||||
static bool check_pulse_length(uint32_t pulse_tick_length, uint32_t target_tick_length) {
|
||||
// check if pulse tick length corresponds to target length (+/- tolerance)
|
||||
return ((pulse_tick_length >= (target_tick_length - EM4X70_T_TAG_TOLERANCE)) &&
|
||||
(pulse_tick_length <= (target_tick_length + EM4X70_T_TAG_TOLERANCE)));
|
||||
}
|
||||
|
||||
static void em4x70_send_bit(bool bit) {
|
||||
|
@ -301,7 +309,7 @@ static bool check_ack(void) {
|
|||
// ACK 64 + 64
|
||||
// NAK 64 + 48
|
||||
if (check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD) &&
|
||||
check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD)) {
|
||||
check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD)) {
|
||||
// ACK
|
||||
return true;
|
||||
}
|
||||
|
@ -344,7 +352,11 @@ static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *respon
|
|||
if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Auth failed");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
bits2bytes(grnd, 24, response);
|
||||
// although only received 20 bits
|
||||
// ask for 24 bits converted because
|
||||
// this utility function requires
|
||||
// decoding in multiples of 8 bits
|
||||
encoded_bit_array_to_bytes(grnd, 24, response);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -455,12 +467,12 @@ static int send_pin(const uint32_t pin) {
|
|||
WaitTicks(EM4X70_T_TAG_WEE);
|
||||
// <-- Receive header + ID
|
||||
uint8_t tag_id[EM4X70_MAX_RECEIVE_LENGTH];
|
||||
int num = em4x70_receive(tag_id, 32);
|
||||
if (num < 32) {
|
||||
int count_of_bits_received = em4x70_receive(tag_id, 32);
|
||||
if (count_of_bits_received < 32) {
|
||||
Dbprintf("Invalid ID Received");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
bits2bytes(tag_id, num, &tag.data[4]);
|
||||
encoded_bit_array_to_bytes(tag_id, count_of_bits_received, &tag.data[4]);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
@ -537,36 +549,38 @@ static bool find_listen_window(bool command) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static void bits2bytes(const uint8_t *bits, int length, uint8_t *out) {
|
||||
// *bits == array of bytes, each byte storing a single bit.
|
||||
// *out == array of bytes, storing converted bits --> bytes.
|
||||
//
|
||||
// [in, bcount(count_of_bits) ] const uint8_t *bits
|
||||
// [out, bcount(count_of_bits/8)] uint8_t *out
|
||||
static void encoded_bit_array_to_bytes(const uint8_t *bits, int count_of_bits, uint8_t *out) {
|
||||
|
||||
if (length % 8 != 0) {
|
||||
Dbprintf("Should have a multiple of 8 bits, was sent %d", length);
|
||||
if (count_of_bits % 8 != 0) {
|
||||
Dbprintf("Should have a multiple of 8 bits, was sent %d", count_of_bits);
|
||||
}
|
||||
|
||||
int num_bytes = length / 8; // We should have a multiple of 8 here
|
||||
int num_bytes = count_of_bits / 8; // We should have a multiple of 8 here
|
||||
|
||||
for (int i = 1; i <= num_bytes; i++) {
|
||||
out[num_bytes - i] = bits2byte(bits, 8);
|
||||
out[num_bytes - i] = encoded_bit_array_to_byte(bits, 8);
|
||||
bits += 8;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t bits2byte(const uint8_t *bits, int length) {
|
||||
static uint8_t encoded_bit_array_to_byte(const uint8_t *bits, int count_of_bits) {
|
||||
|
||||
// converts <length> separate bits into a single "byte"
|
||||
// converts <count_of_bits> separate bits into a single "byte"
|
||||
uint8_t byte = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
for (int i = 0; i < count_of_bits; i++) {
|
||||
byte <<= 1;
|
||||
byte |= bits[i];
|
||||
|
||||
if (i != length - 1)
|
||||
byte <<= 1;
|
||||
}
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
static bool send_command_and_read(uint8_t command, uint8_t *bytes, size_t length) {
|
||||
static bool send_command_and_read(uint8_t command, uint8_t *bytes, size_t expected_byte_count) {
|
||||
|
||||
int retries = EM4X70_COMMAND_RETRIES;
|
||||
while (retries) {
|
||||
|
@ -574,14 +588,14 @@ static bool send_command_and_read(uint8_t command, uint8_t *bytes, size_t length
|
|||
|
||||
if (find_listen_window(true)) {
|
||||
uint8_t bits[EM4X70_MAX_RECEIVE_LENGTH] = {0};
|
||||
size_t out_length_bits = length * 8;
|
||||
size_t out_length_bits = expected_byte_count * 8;
|
||||
em4x70_send_nibble(command, command_parity);
|
||||
int len = em4x70_receive(bits, out_length_bits);
|
||||
if (len < out_length_bits) {
|
||||
Dbprintf("Invalid data received length: %d, expected %d", len, out_length_bits);
|
||||
return false;
|
||||
}
|
||||
bits2bytes(bits, len, bytes);
|
||||
encoded_bit_array_to_bytes(bits, len, bytes);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -629,7 +643,7 @@ static bool find_em4x70_tag(void) {
|
|||
return find_listen_window(false);
|
||||
}
|
||||
|
||||
static int em4x70_receive(uint8_t *bits, size_t length) {
|
||||
static int em4x70_receive(uint8_t *bits, size_t maximum_bits_to_read) {
|
||||
|
||||
uint32_t pl;
|
||||
int bit_pos = 0;
|
||||
|
@ -667,7 +681,7 @@ static int em4x70_receive(uint8_t *bits, size_t length) {
|
|||
|
||||
// identify remaining bits based on pulse lengths
|
||||
// between listen windows only pulse lengths of 1, 1.5 and 2 are possible
|
||||
while (bit_pos < length) {
|
||||
while (bit_pos < maximum_bits_to_read) {
|
||||
|
||||
pl = get_pulse_length(edge);
|
||||
|
||||
|
@ -681,13 +695,13 @@ static int em4x70_receive(uint8_t *bits, size_t length) {
|
|||
// pulse length 1.5 -> 2 bits + flip edge detection
|
||||
if (edge == FALLING_EDGE) {
|
||||
bits[bit_pos++] = 0;
|
||||
if (bit_pos < length) {
|
||||
if (bit_pos < maximum_bits_to_read) {
|
||||
bits[bit_pos++] = 0;
|
||||
}
|
||||
edge = RISING_EDGE;
|
||||
} else {
|
||||
bits[bit_pos++] = 1;
|
||||
if (bit_pos < length) {
|
||||
if (bit_pos < maximum_bits_to_read) {
|
||||
bits[bit_pos++] = 1;
|
||||
}
|
||||
edge = FALLING_EDGE;
|
||||
|
@ -698,12 +712,12 @@ static int em4x70_receive(uint8_t *bits, size_t length) {
|
|||
// pulse length of 2 -> two bits
|
||||
if (edge == FALLING_EDGE) {
|
||||
bits[bit_pos++] = 0;
|
||||
if (bit_pos < length) {
|
||||
if (bit_pos < maximum_bits_to_read) {
|
||||
bits[bit_pos++] = 1;
|
||||
}
|
||||
} else {
|
||||
bits[bit_pos++] = 1;
|
||||
if (bit_pos < length) {
|
||||
if (bit_pos < maximum_bits_to_read) {
|
||||
bits[bit_pos++] = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,7 +164,15 @@ typedef struct _em4x70_cmd_input_verify_auth_t {
|
|||
ID48LIB_GRN grn;
|
||||
} em4x70_cmd_input_verify_auth_t;
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
typedef struct _em4x70_cmd_input_calculate_t {
|
||||
ID48LIB_KEY key;
|
||||
ID48LIB_NONCE rn;
|
||||
} em4x70_cmd_input_calculate_t;
|
||||
typedef struct _em4x70_cmd_output_calculate_t {
|
||||
ID48LIB_FRN frn;
|
||||
ID48LIB_GRN grn;
|
||||
} em4x70_cmd_output_calculate_t;
|
||||
|
||||
|
||||
static void fill_buffer_prng_bytes(void *buffer, size_t byte_count) {
|
||||
if (byte_count == 0) {
|
||||
|
@ -431,23 +439,8 @@ static int verify_auth_em4x70(const em4x70_cmd_input_verify_auth_t *opts) {
|
|||
return result;
|
||||
}
|
||||
|
||||
// used by `lf search` and `search`, this is a quick test for EM4x70 tag
|
||||
// In alignment with other tags implementations, this also dumps basic information
|
||||
// about the tag, if one is found.
|
||||
// Use helper function `get_em4x70_info()` if wanting to limit / avoid output.
|
||||
bool detect_4x70_block(void) {
|
||||
em4x70_tag_info_t info;
|
||||
em4x70_cmd_input_info_t opts = { 0 };
|
||||
|
||||
int result = get_em4x70_info(&opts, &info);
|
||||
|
||||
if (result == PM3_ETIMEOUT) { // consider removing this output?
|
||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
||||
}
|
||||
return result == PM3_SUCCESS;
|
||||
}
|
||||
|
||||
int CmdEM4x70Info(const char *Cmd) {
|
||||
static int CmdEM4x70Info(const char *Cmd) {
|
||||
|
||||
// invoke reading of a EM4x70 tag which has to be on the antenna because
|
||||
// decoding is done by the device (not on client side)
|
||||
|
@ -487,7 +480,7 @@ int CmdEM4x70Info(const char *Cmd) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int CmdEM4x70Write(const char *Cmd) {
|
||||
static int CmdEM4x70Write(const char *Cmd) {
|
||||
|
||||
// write one block/word (16 bits) to the tag at given block address (0-15)
|
||||
CLIParserContext *ctx;
|
||||
|
@ -539,7 +532,7 @@ int CmdEM4x70Write(const char *Cmd) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int CmdEM4x70Brute(const char *Cmd) {
|
||||
static int CmdEM4x70Brute(const char *Cmd) {
|
||||
|
||||
// From paper "Dismantling Megamos Crypto", Roel Verdult, Flavio D. Garcia and Barıs¸ Ege.
|
||||
// Partial Key-Update Attack (optimized version)
|
||||
|
@ -549,8 +542,10 @@ int CmdEM4x70Brute(const char *Cmd) {
|
|||
"This attack does NOT write anything to the tag.\n"
|
||||
"Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'.\n"
|
||||
"After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'\n",
|
||||
"lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 --> bruteforcing key bits k95...k80\n"
|
||||
);
|
||||
"lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 --> bruteforcing key bits k95...k80 (pm3 test key)\n"
|
||||
"lf em 4x70 brute -b 8 --rnd 3FFE1FB6CC513F --frn F355F1A0 --> bruteforcing key bits k79...k64 (research paper key)\n"
|
||||
"lf em 4x70 brute -b 7 --rnd 7D5167003571F8 --frn 982DBCC0 --> bruteforcing key bits k63...k48 (autorecovery test key)\n"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_lit0(NULL, "par", "Add parity bit when sending commands"),
|
||||
|
@ -623,7 +618,7 @@ int CmdEM4x70Brute(const char *Cmd) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int CmdEM4x70Unlock(const char *Cmd) {
|
||||
static int CmdEM4x70Unlock(const char *Cmd) {
|
||||
|
||||
// send pin code to device, unlocking it for writing
|
||||
CLIParserContext *ctx;
|
||||
|
@ -671,7 +666,7 @@ int CmdEM4x70Unlock(const char *Cmd) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int CmdEM4x70Auth(const char *Cmd) {
|
||||
static int CmdEM4x70Auth(const char *Cmd) {
|
||||
|
||||
// Authenticate transponder
|
||||
// Send 56-bit random number + pre-computed f(rnd, k) to transponder.
|
||||
|
@ -731,7 +726,7 @@ int CmdEM4x70Auth(const char *Cmd) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int CmdEM4x70SetPIN(const char *Cmd) {
|
||||
static int CmdEM4x70SetPIN(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "lf em 4x70 setpin",
|
||||
"Write new PIN\n",
|
||||
|
@ -776,7 +771,7 @@ int CmdEM4x70SetPIN(const char *Cmd) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int CmdEM4x70SetKey(const char *Cmd) {
|
||||
static int CmdEM4x70SetKey(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "lf em 4x70 setkey",
|
||||
"Write new 96-bit key to tag\n",
|
||||
|
@ -902,6 +897,7 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_
|
|||
,
|
||||
"lf em 4x70 recover --key F32AA98CF5BE --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)\n"
|
||||
"lf em 4x70 recover --key A090A0A02080 --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)\n"
|
||||
"lf em 4x70 recover --key 022A028C02BE --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0 (autorecovery test key)\n"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
|
@ -973,7 +969,7 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_
|
|||
return result;
|
||||
}
|
||||
|
||||
int CmdEM4x70Recover(const char *Cmd) {
|
||||
static int CmdEM4x70Recover(const char *Cmd) {
|
||||
// From paper "Dismantling Megamos Crypto", Roel Verdult, Flavio D. Garcia and Barıs¸ Ege.
|
||||
// Partial Key-Update Attack -- final 48 bits (after optimized version gets k95..k48)
|
||||
em4x70_recovery_data_t recover_ctx = {0};
|
||||
|
@ -1442,6 +1438,97 @@ static int CmdEM4x70AutoRecover(const char *Cmd) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static int CmdEM4x70Calc_ParseArgs(const char *Cmd, em4x70_cmd_input_calculate_t *out_results) {
|
||||
|
||||
memset(out_results, 0, sizeof(em4x70_cmd_input_calculate_t));
|
||||
|
||||
int result = PM3_SUCCESS;
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(
|
||||
&ctx,
|
||||
"lf em 4x70 calc",
|
||||
"Calculates both the reader and tag challenge for a user-provided key and rnd.\n"
|
||||
,
|
||||
"lf em 4x70 calc --key F32AA98CF5BE4ADFA6D3480B --rnd 45F54ADA252AAC (pm3 test key)\n" // --frn 4866BB70 --grn 9BD180
|
||||
"lf em 4x70 calc --key A090A0A02080000000000000 --rnd 3FFE1FB6CC513F (research paper key)\n" // --frn F355F1A0 --grn 609D60
|
||||
"lf em 4x70 calc --key 022A028C02BE000102030405 --rnd 7D5167003571F8 (autorecovery test key)\n" // --frn 982DBCC0 --grn 36C0E0
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1(NULL, "key", "<hex>", "Key 96-bit as 12 hex bytes"),
|
||||
arg_str1(NULL, "rnd", "<hex>", "56-bit random value sent to tag for authentication"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
int key_len = 0; // must be 12 bytes hex data
|
||||
int rnd_len = 0; // must be 7 bytes hex data
|
||||
|
||||
// These macros hide early function return on error ... including free'ing ctx.
|
||||
CLIGetHexWithReturn(ctx, 1, out_results->key.k, &key_len);
|
||||
CLIGetHexWithReturn(ctx, 2, out_results->rn.rn, &rnd_len);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (key_len != 12) {
|
||||
PrintAndLogEx(FAILED, "Key length must be 12 bytes, got %d", key_len);
|
||||
result = PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (rnd_len != 7) {
|
||||
PrintAndLogEx(FAILED, "Random number length must be 7 bytes, got %d", rnd_len);
|
||||
result = PM3_EINVARG;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int CmdEM4x70Calc(const char *Cmd) {
|
||||
em4x70_cmd_input_calculate_t opts = {0};
|
||||
em4x70_cmd_output_calculate_t data = {0};
|
||||
|
||||
// 0. Parse the command line
|
||||
int result = CmdEM4x70Calc_ParseArgs(Cmd, &opts);
|
||||
if (PM3_SUCCESS != result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// There are no failure paths. All inputs are valid, and ID48LIB doesn't add any failure paths.
|
||||
id48lib_generator(&opts.key, &opts.rn, &data.frn, &data.grn);
|
||||
|
||||
char key_string[24 + 1] = {0};
|
||||
char rnd_string[14 + 1] = {0};
|
||||
char frn_string[ 8 + 1] = {0};
|
||||
char grn_string[ 6 + 1] = {0};
|
||||
if (true) {
|
||||
snprintf(
|
||||
key_string, 25,
|
||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
opts.key.k[ 0], opts.key.k[ 1], opts.key.k[ 2], opts.key.k[ 3],
|
||||
opts.key.k[ 4], opts.key.k[ 5], opts.key.k[ 6], opts.key.k[ 7],
|
||||
opts.key.k[ 8], opts.key.k[ 9], opts.key.k[10], opts.key.k[11]
|
||||
);
|
||||
snprintf(
|
||||
rnd_string, 15,
|
||||
"%02X%02X%02X%02X%02X%02X%02X",
|
||||
opts.rn.rn[0], opts.rn.rn[1], opts.rn.rn[2], opts.rn.rn[3], opts.rn.rn[4], opts.rn.rn[5], opts.rn.rn[6]
|
||||
);
|
||||
snprintf(
|
||||
frn_string, 9,
|
||||
"%02X%02X%02X%02X",
|
||||
data.frn.frn[0], data.frn.frn[1], data.frn.frn[2], data.frn.frn[3]
|
||||
);
|
||||
snprintf(
|
||||
grn_string, 7,
|
||||
"%02X%02X%02X",
|
||||
data.grn.grn[0], data.grn.grn[1], data.grn.grn[2]
|
||||
);
|
||||
}
|
||||
PrintAndLogEx(SUCCESS, "KEY: %s RND: %s FRN: " _GREEN_("%s") " GRN: " _GREEN_("%s"), key_string, rnd_string, frn_string, grn_string);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// Must be declared to be used in the table,
|
||||
// but cannot be defined yet because it uses the table.
|
||||
static int CmdHelp(const char *Cmd);
|
||||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
|
@ -1452,6 +1539,7 @@ static command_t CommandTable[] = {
|
|||
{"auth", CmdEM4x70Auth, IfPm3EM4x70, "Authenticate EM4x70"},
|
||||
{"setpin", CmdEM4x70SetPIN, IfPm3EM4x70, "Write PIN"},
|
||||
{"setkey", CmdEM4x70SetKey, IfPm3EM4x70, "Write key"},
|
||||
{"calc", CmdEM4x70Calc, AlwaysAvailable, "Calculate EM4x70 challenge and response"},
|
||||
{"recover", CmdEM4x70Recover, AlwaysAvailable, "Recover remaining key from partial key"},
|
||||
{"autorecover", CmdEM4x70AutoRecover, IfPm3EM4x70, "Recover entire key from writable tag"},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
|
@ -1463,7 +1551,27 @@ static int CmdHelp(const char *Cmd) {
|
|||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Only two functions need to be non-static:
|
||||
// * CmdLFEM4X70()
|
||||
// * detect_4x70_block()
|
||||
int CmdLFEM4X70(const char *Cmd) {
|
||||
clearCommandBuffer();
|
||||
return CmdsParse(CommandTable, Cmd);
|
||||
}
|
||||
|
||||
// used by `lf search` and `search`, this is a quick test for EM4x70 tag
|
||||
// In alignment with other tags implementations, this also dumps basic information
|
||||
// about the tag, if one is found.
|
||||
// Use helper function `get_em4x70_info()` if wanting to limit / avoid output.
|
||||
bool detect_4x70_block(void) {
|
||||
em4x70_tag_info_t info;
|
||||
em4x70_cmd_input_info_t opts = { 0 };
|
||||
|
||||
int result = get_em4x70_info(&opts, &info);
|
||||
|
||||
if (result == PM3_ETIMEOUT) { // consider removing this output?
|
||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
||||
}
|
||||
return result == PM3_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -24,14 +24,6 @@
|
|||
#define TIMEOUT 2000
|
||||
|
||||
int CmdLFEM4X70(const char *Cmd);
|
||||
int CmdEM4x70Info(const char *Cmd);
|
||||
int CmdEM4x70Write(const char *Cmd);
|
||||
int CmdEM4x70Brute(const char *Cmd);
|
||||
int CmdEM4x70Unlock(const char *Cmd);
|
||||
int CmdEM4x70Auth(const char *Cmd);
|
||||
int CmdEM4x70SetPIN(const char *Cmd);
|
||||
int CmdEM4x70SetKey(const char *Cmd);
|
||||
int CmdEM4x70Recover(const char *Cmd);
|
||||
|
||||
// for `lf search`:
|
||||
bool detect_4x70_block(void);
|
||||
|
|
|
@ -414,26 +414,30 @@ while true; do
|
|||
if ! CheckExecute "nfc decode test - signature" "$CLIENTBIN -c 'nfc decode -d 03FF010194113870696C65742E65653A656B616172743A3266195F26063132303832325904202020205F28033233335F2701316E1B5A13333038363439303039303030323636343030355304EBF2CE704103000000AC536967010200803A2448FCA7D354A654A81BD021150D1A152D1DF4D7A55D2B771F12F094EAB6E5E10F2617A2F8DAD4FD38AFF8EA39B71C19BD42618CDA86EE7E144636C8E0E7CFC4096E19C3680E09C78A0CDBC05DA2D698E551D5D709717655E56FE3676880B897D2C70DF5F06ECE07C71435255144F8EE41AF110E7B180DA0E6C22FB8FDEF61800025687474703A2F2F70696C65742E65652F6372742F33303836343930302D303030312E637274FE'" "30864900-0001.crt"; then break; fi
|
||||
|
||||
echo -e "\n${C_BLUE}Testing LF:${C_NC}"
|
||||
if ! CheckExecute "lf hitag2 test" "$CLIENTBIN -c 'lf hitag test'" "Tests \( ok"; then break; fi
|
||||
if ! CheckExecute "lf cotag demod test" "$CLIENTBIN -c 'data load -f traces/lf_cotag_220_8331.pm3; data norm; data cthreshold -u 50 -d -20; data envelope; data raw --ar -c 272; lf cotag demod'" \
|
||||
if ! CheckExecute "lf hitag2 test" "$CLIENTBIN -c 'lf hitag test'" "Tests \( ok"; then break; fi
|
||||
if ! CheckExecute "lf cotag demod test" "$CLIENTBIN -c 'data load -f traces/lf_cotag_220_8331.pm3; data norm; data cthreshold -u 50 -d -20; data envelope; data raw --ar -c 272; lf cotag demod'" \
|
||||
"COTAG Found: FC 220, CN: 8331 Raw: FFB841170363FFFE00001E7F00000000"; then break; fi
|
||||
if ! CheckExecute "lf AWID test" "$CLIENTBIN -c 'data load -f traces/lf_AWID-15-259.pm3;lf search -1'" "AWID ID found"; then break; fi
|
||||
if ! CheckExecute "lf EM410x test" "$CLIENTBIN -c 'data load -f traces/lf_EM4102-1.pm3;lf search -1'" "EM410x ID found"; then break; fi
|
||||
if ! CheckExecute "lf EM4x05 test" "$CLIENTBIN -c 'data load -f traces/lf_EM4x05.pm3;lf search -1'" "FDX-B ID found"; then break; fi
|
||||
if ! CheckExecute "lf FDX-A FECAVA test" "$CLIENTBIN -c 'data load -f traces/lf_EM4305_fdxa_destron.pm3;lf search -1'" "FDX-A FECAVA Destron ID found"; then break; fi
|
||||
if ! CheckExecute "lf FDX-B test" "$CLIENTBIN -c 'data load -f traces/lf_HomeAgain1600.pm3;lf search -1'" "FDX-B ID found"; then break; fi
|
||||
if ! CheckExecute "lf FDX/BioThermo test" "$CLIENTBIN -c 'data load -f traces/lf_FDXB_Bio-Thermo.pm3; lf fdxb demod'" "95.2 F / 35.1 C"; then break; fi
|
||||
if ! CheckExecute "lf GPROXII test" "$CLIENTBIN -c 'data load -f traces/lf_GProx_36_30_14489.pm3; lf search -1'" "Guardall G-Prox II ID found"; then break; fi
|
||||
if ! CheckExecute "lf HID Prox test" "$CLIENTBIN -c 'data load -f traces/lf_HID-proxCardII-05512-11432784-1.pm3;lf search -1'" "HID Prox ID found"; then break; fi
|
||||
if ! CheckExecute "lf IDTECK test" "$CLIENTBIN -c 'data load -f traces/lf_IDTECK_4944544BAC40E069.pm3; lf search -1'" "Idteck ID found"; then break; fi
|
||||
if ! CheckExecute "lf INDALA test" "$CLIENTBIN -c 'data load -f traces/lf_Indala-504278295.pm3;lf search -1'" "Indala ID found"; then break; fi
|
||||
if ! CheckExecute "lf KERI test" "$CLIENTBIN -c 'data load -f traces/lf_Keri.pm3;lf search -1'" "Pyramid ID found"; then break; fi
|
||||
if ! CheckExecute "lf NEXWATCH test" "$CLIENTBIN -c 'data load -f traces/lf_NEXWATCH_Quadrakey-521512301.pm3;lf search -1 '" "NexWatch ID found"; then break; fi
|
||||
if ! CheckExecute "lf SECURAKEY test" "$CLIENTBIN -c 'data load -f traces/lf_NEXWATCH_Securakey-64169.pm3;lf search -1 '" "Securakey ID found"; then break; fi
|
||||
if ! CheckExecute "lf PAC test" "$CLIENTBIN -c 'data load -f traces/lf_PAC-8E4C058E.pm3;lf search -1'" "PAC/Stanley ID found"; then break; fi
|
||||
if ! CheckExecute "lf PARADOX test" "$CLIENTBIN -c 'data load -f traces/lf_Paradox-96_40426-APJN08.pm3;lf search -1'" "Paradox ID found"; then break; fi
|
||||
if ! CheckExecute "lf VIKING test" "$CLIENTBIN -c 'data load -f traces/lf_Transit999-best.pm3;lf search -1'" "Viking ID found"; then break; fi
|
||||
if ! CheckExecute "lf VISA2000 test" "$CLIENTBIN -c 'data load -f traces/lf_VISA2000.pm3;lf search -1'" "Visa2000 ID found"; then break; fi
|
||||
if ! CheckExecute "lf AWID test" "$CLIENTBIN -c 'data load -f traces/lf_AWID-15-259.pm3;lf search -1'" "AWID ID found"; then break; fi
|
||||
if ! CheckExecute "lf EM410x test" "$CLIENTBIN -c 'data load -f traces/lf_EM4102-1.pm3;lf search -1'" "EM410x ID found"; then break; fi
|
||||
if ! CheckExecute "lf EM4x05 test" "$CLIENTBIN -c 'data load -f traces/lf_EM4x05.pm3;lf search -1'" "FDX-B ID found"; then break; fi
|
||||
if ! CheckExecute "lf EM4x70 calc test" "$CLIENTBIN -c 'lf em 4x70 calc --key F32AA98CF5BE4ADFA6D3480B --rnd 45F54ADA252AAC'" "FRN: 4866BB70 GRN: 9BD180"; then break; fi
|
||||
if ! CheckExecute "lf EM4x70 recover test 1/3" "$CLIENTBIN -c 'lf em 4x70 recover --key 022A028C02BE --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0'" "022a028c02be000102030405"; then break; fi
|
||||
if ! CheckExecute "lf EM4x70 recover test 2/3" "$CLIENTBIN -c 'lf em 4x70 recover --key 022A028C02BE --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0'" "022a028c02be366866191b60"; then break; fi
|
||||
if ! CheckExecute "lf EM4x70 recover test 3/3" "$CLIENTBIN -c 'lf em 4x70 recover --key 022A028C02BE --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0'" "022a028c02bef1e352c2718d"; then break; fi
|
||||
if ! CheckExecute "lf FDX-A FECAVA test" "$CLIENTBIN -c 'data load -f traces/lf_EM4305_fdxa_destron.pm3;lf search -1'" "FDX-A FECAVA Destron ID found"; then break; fi
|
||||
if ! CheckExecute "lf FDX-B test" "$CLIENTBIN -c 'data load -f traces/lf_HomeAgain1600.pm3;lf search -1'" "FDX-B ID found"; then break; fi
|
||||
if ! CheckExecute "lf FDX/BioThermo test" "$CLIENTBIN -c 'data load -f traces/lf_FDXB_Bio-Thermo.pm3; lf fdxb demod'" "95.2 F / 35.1 C"; then break; fi
|
||||
if ! CheckExecute "lf GPROXII test" "$CLIENTBIN -c 'data load -f traces/lf_GProx_36_30_14489.pm3; lf search -1'" "Guardall G-Prox II ID found"; then break; fi
|
||||
if ! CheckExecute "lf HID Prox test" "$CLIENTBIN -c 'data load -f traces/lf_HID-proxCardII-05512-11432784-1.pm3;lf search -1'" "HID Prox ID found"; then break; fi
|
||||
if ! CheckExecute "lf IDTECK test" "$CLIENTBIN -c 'data load -f traces/lf_IDTECK_4944544BAC40E069.pm3; lf search -1'" "Idteck ID found"; then break; fi
|
||||
if ! CheckExecute "lf INDALA test" "$CLIENTBIN -c 'data load -f traces/lf_Indala-504278295.pm3;lf search -1'" "Indala ID found"; then break; fi
|
||||
if ! CheckExecute "lf KERI test" "$CLIENTBIN -c 'data load -f traces/lf_Keri.pm3;lf search -1'" "Pyramid ID found"; then break; fi
|
||||
if ! CheckExecute "lf NEXWATCH test" "$CLIENTBIN -c 'data load -f traces/lf_NEXWATCH_Quadrakey-521512301.pm3;lf search -1 '" "NexWatch ID found"; then break; fi
|
||||
if ! CheckExecute "lf SECURAKEY test" "$CLIENTBIN -c 'data load -f traces/lf_NEXWATCH_Securakey-64169.pm3;lf search -1 '" "Securakey ID found"; then break; fi
|
||||
if ! CheckExecute "lf PAC test" "$CLIENTBIN -c 'data load -f traces/lf_PAC-8E4C058E.pm3;lf search -1'" "PAC/Stanley ID found"; then break; fi
|
||||
if ! CheckExecute "lf PARADOX test" "$CLIENTBIN -c 'data load -f traces/lf_Paradox-96_40426-APJN08.pm3;lf search -1'" "Paradox ID found"; then break; fi
|
||||
if ! CheckExecute "lf VIKING test" "$CLIENTBIN -c 'data load -f traces/lf_Transit999-best.pm3;lf search -1'" "Viking ID found"; then break; fi
|
||||
if ! CheckExecute "lf VISA2000 test" "$CLIENTBIN -c 'data load -f traces/lf_VISA2000.pm3;lf search -1'" "Visa2000 ID found"; then break; fi
|
||||
|
||||
if ! CheckExecute slow "lf T55 awid 26 test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_awid_26.pm3; lf search -1'" "AWID ID found"; then break; fi
|
||||
if ! CheckExecute slow "lf T55 awid 26 test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_awid_26.pm3; lf awid demod'" \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue