From 569d57719d7642db9577a7cb28000fbc09a69949 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Sun, 5 May 2024 15:54:00 -0700 Subject: [PATCH 1/4] Fix firmware return value for em4x70 to always be of type `PM3_*` --- armsrc/em4x70.c | 58 +++++++++++++++++++--------------------- client/src/cmdlfem4x70.c | 58 +++++++++------------------------------- 2 files changed, 40 insertions(+), 76 deletions(-) diff --git a/armsrc/em4x70.c b/armsrc/em4x70.c index 2732c1405..05f08d6bc 100644 --- a/armsrc/em4x70.c +++ b/armsrc/em4x70.c @@ -581,11 +581,6 @@ static bool send_command_and_read(uint8_t command, uint8_t *bytes, size_t length Dbprintf("Invalid data received length: %d, expected %d", len, out_length_bits); return false; } - // TODO: Figure out why getting an extra bit (quite often) here - // e.g., write block and info commands both reach here and output: - // [#] Should have a multiple of 8 bits, was sent 33 - // [#] Should have a multiple of 8 bits, was sent 65 - // Extra bits are currently just dropped, with no ill effect noticed. bits2bytes(bits, len, bytes); return true; } @@ -617,7 +612,6 @@ static bool em4x70_read_um1(void) { } - /** * em4x70_read_um2 * @@ -725,7 +719,7 @@ static int em4x70_receive(uint8_t *bits, size_t length) { void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) { - uint8_t status = 0; + bool success = false; // Support tags with and without command parity bits command_parity = etd->parity; @@ -736,17 +730,17 @@ void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) { // Find the Tag if (get_signalproperties() && find_em4x70_tag()) { // Read ID, UM1 and UM2 - status = em4x70_read_id() && em4x70_read_um1() && em4x70_read_um2(); + success = em4x70_read_id() && em4x70_read_um1() && em4x70_read_um2(); } StopTicks(); lf_finalize(ledcontrol); + int status = success ? PM3_SUCCESS : PM3_ESOFT; reply_ng(CMD_LF_EM4X70_INFO, status, tag.data, sizeof(tag.data)); } void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) { - - uint8_t status = 0; + int status = PM3_ESOFT; command_parity = etd->parity; @@ -757,16 +751,15 @@ void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) { if (get_signalproperties() && find_em4x70_tag()) { // Write - status = write(etd->word, etd->address) == PM3_SUCCESS; + status = write(etd->word, etd->address); - if (status) { + if (status == PM3_SUCCESS) { // Read Tag after writing if (em4x70_read_id()) { em4x70_read_um1(); em4x70_read_um2(); } } - } StopTicks(); @@ -776,7 +769,7 @@ void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) { - uint8_t status = 0; + int status = PM3_ESOFT; command_parity = etd->parity; @@ -790,10 +783,10 @@ void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) { if (em4x70_read_id()) { // Send PIN - status = send_pin(etd->pin) == PM3_SUCCESS; + status = send_pin(etd->pin); // If the write succeeded, read the rest of the tag - if (status) { + if (status == PM3_SUCCESS) { // Read Tag // ID doesn't change em4x70_read_um1(); @@ -809,7 +802,8 @@ void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) { - uint8_t status = 0; + int status = PM3_ESOFT; + uint8_t response[3] = {0}; command_parity = etd->parity; @@ -821,7 +815,7 @@ void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) { if (get_signalproperties() && find_em4x70_tag()) { // Authenticate and get tag response - status = authenticate(etd->rnd, etd->frnd, response) == PM3_SUCCESS; + status = authenticate(etd->rnd, etd->frnd, response); } StopTicks(); @@ -830,7 +824,7 @@ void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) { } void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) { - uint8_t status = 0; + int status = PM3_ESOFT; uint8_t response[2] = {0}; command_parity = etd->parity; @@ -842,7 +836,7 @@ void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) { if (get_signalproperties() && find_em4x70_tag()) { // Bruteforce partial key - status = bruteforce(etd->address, etd->rnd, etd->frnd, etd->start_key, response) == PM3_SUCCESS; + status = bruteforce(etd->address, etd->rnd, etd->frnd, etd->start_key, response); } StopTicks(); @@ -852,7 +846,7 @@ void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) { - uint8_t status = 0; + int status = PM3_ESOFT; command_parity = etd->parity; @@ -865,17 +859,19 @@ void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) { // Read ID (required for send_pin command) if (em4x70_read_id()) { - // Write new PIN - if ((write((etd->pin) & 0xFFFF, EM4X70_PIN_WORD_UPPER) == PM3_SUCCESS) && - (write((etd->pin >> 16) & 0xFFFF, EM4X70_PIN_WORD_LOWER) == PM3_SUCCESS)) { - + // Write the pin + status = write((etd->pin) & 0xFFFF, EM4X70_PIN_WORD_UPPER); + if (status == PM3_SUCCESS) { + status = write((etd->pin >> 16) & 0xFFFF, EM4X70_PIN_WORD_LOWER); + } + if (status == PM3_SUCCESS) { // Now Try to authenticate using the new PIN // Send PIN - status = send_pin(etd->pin) == PM3_SUCCESS; + status = send_pin(etd->pin); // If the write succeeded, read the rest of the tag - if (status) { + if (status == PM3_SUCCESS) { // Read Tag // ID doesn't change em4x70_read_um1(); @@ -892,7 +888,7 @@ void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) { - uint8_t status = 0; + int status = PM3_ESOFT; command_parity = etd->parity; @@ -904,15 +900,15 @@ void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) { // Read ID to ensure we can write to card if (em4x70_read_id()) { - status = 1; + status = PM3_SUCCESS; // Write each crypto block for (int i = 0; i < 6; i++) { uint16_t key_word = (etd->crypt_key[(i * 2) + 1] << 8) + etd->crypt_key[i * 2]; // Write each word, abort if any failure occurs - if (write(key_word, 9 - i) != PM3_SUCCESS) { - status = 0; + status = write(key_word, 9 - i); + if (status != PM3_SUCCESS) { break; } } diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index 72e75f904..da9b985f1 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -225,14 +225,10 @@ static int get_em4x70_info(const em4x70_cmd_input_info_t *opts, em4x70_tag_info_ if (WaitForResponseTimeout(CMD_LF_EM4X70_INFO, &resp, TIMEOUT) == false) { return PM3_ETIMEOUT; } - - //iceman: prefer to have specific return code check. - // like resp.status != PM3_SUCCESS if looking for failure - if (resp.status) { + if (resp.status == PM3_SUCCESS) { memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t)); - return PM3_SUCCESS; } - return PM3_ESOFT; + return resp.status; } static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t *opts, em4x70_tag_info_t *data_out) { @@ -251,14 +247,10 @@ static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t *opts, em4x70_t if (WaitForResponseTimeout(CMD_LF_EM4X70_WRITE, &resp, TIMEOUT) == false) { return PM3_ETIMEOUT; } - - //iceman: prefer to have specific return code check. - // like resp.status != PM3_SUCCESS if looking for failure - if (resp.status) { + if (resp.status == PM3_SUCCESS) { memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t)); - return PM3_SUCCESS; } - return PM3_ESOFT; + return resp.status; } static int auth_em4x70(const em4x70_cmd_input_auth_t *opts, em4x70_cmd_output_auth_t *data_out) { @@ -276,20 +268,15 @@ static int auth_em4x70(const em4x70_cmd_input_auth_t *opts, em4x70_cmd_output_au if (WaitForResponseTimeout(CMD_LF_EM4X70_AUTH, &resp, TIMEOUT) == false) { return PM3_ETIMEOUT; } - - //iceman: prefer to have specific return code check. - // like resp.status != PM3_SUCCESS if looking for failure - if (resp.status) { + if (resp.status == PM3_SUCCESS) { // Response is 20-bit from tag - // HACKHACK -- It appears the byte order differs from what is expected? data_out->grn.grn[0] = resp.data.asBytes[2]; data_out->grn.grn[1] = resp.data.asBytes[1]; data_out->grn.grn[2] = resp.data.asBytes[0]; //memcpy(data_out, &resp.data.asBytes[0], sizeof(ID48LIB_GRN)); - return PM3_SUCCESS; } - return PM3_ESOFT; + return resp.status; } static int setkey_em4x70(const em4x70_cmd_input_setkey_t *opts) { @@ -305,13 +292,7 @@ static int setkey_em4x70(const em4x70_cmd_input_setkey_t *opts) { if (WaitForResponseTimeout(CMD_LF_EM4X70_SETKEY, &resp, TIMEOUT) == false) { return PM3_ETIMEOUT; } - - //iceman: prefer to have specific return code check. - // like resp.status != PM3_SUCCESS if looking for failure - if (resp.status) { - return PM3_SUCCESS; - } - return PM3_ESOFT; + return resp.status; } static int brute_em4x70(const em4x70_cmd_input_brute_t *opts, em4x70_cmd_output_brute_t *data_out) { @@ -346,14 +327,10 @@ static int brute_em4x70(const em4x70_cmd_input_brute_t *opts, em4x70_cmd_output_ } if (WaitForResponseTimeout(CMD_LF_EM4X70_BRUTE, &resp, TIMEOUT)) { - - //iceman: prefer to have specific return code check. - // like resp.status != PM3_SUCCESS if looking for failure - if (resp.status) { + if (resp.status == PM3_SUCCESS) { memcpy(data_out, resp.data.asBytes, sizeof(em4x70_cmd_output_brute_t)); - return PM3_SUCCESS; } - return PM3_ESOFT; + return resp.status; } // NOTE: It takes about 11 seconds per 0x0100 authentication attempts. @@ -383,15 +360,10 @@ static int unlock_em4x70(const em4x70_cmd_input_unlock_t *opts, em4x70_tag_info_ if (WaitForResponseTimeout(CMD_LF_EM4X70_UNLOCK, &resp, TIMEOUT) == false) { return PM3_ETIMEOUT; } - - //iceman: prefer to have specific return code check. - // like resp.status != PM3_SUCCESS if looking for failure - if (resp.status) { + if (resp.status == PM3_SUCCESS) { memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t)); - return PM3_SUCCESS; } - return PM3_ESOFT; - + return resp.status; } static int setpin_em4x70(const em4x70_cmd_input_setpin_t *opts, em4x70_tag_info_t *data_out) { @@ -408,14 +380,10 @@ static int setpin_em4x70(const em4x70_cmd_input_setpin_t *opts, em4x70_tag_info_ if (WaitForResponseTimeout(CMD_LF_EM4X70_SETPIN, &resp, TIMEOUT) == false) { return PM3_ETIMEOUT; } - - //iceman: prefer to have specific return code check. - // like resp.status != PM3_SUCCESS if looking for failure - if (resp.status) { + if (resp.status == PM3_SUCCESS) { memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t)); - return PM3_SUCCESS; } - return PM3_ESOFT; + return resp.status; } static int recover_em4x70(const em4x70_cmd_input_recover_t *opts, em4x70_cmd_output_recover_t *data_out) { From b0b9f4fa4250019e4a42ec0730be2bd569c0a5e8 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Sun, 5 May 2024 15:57:26 -0700 Subject: [PATCH 2/4] Add key that tests autorecovery more quickly This autorecovery test key also has three potential matches for the last phase, thus ensuring authentication test is required to determine which of those keys was actually used. --- client/src/cmdlfem4x70.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index da9b985f1..f1ef08023 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -684,6 +684,7 @@ int CmdEM4x70Auth(const char *Cmd) { " If F(RN) is correct based on the tag key, the tag will give a 20-bit response\n", "lf em 4x70 auth --rnd 45F54ADA252AAC --frn 4866BB70 --> (using pm3 test key)\n" "lf em 4x70 auth --rnd 3FFE1FB6CC513F --frn F355F1A0 --> (using research paper key)\n" + "lf em 4x70 auth --rnd 7D5167003571F8 --frn 982DBCC0 --> (autorecovery test key)\n" ); void *argtable[] = { @@ -781,6 +782,7 @@ int CmdEM4x70SetKey(const char *Cmd) { "Write new 96-bit key to tag\n", "lf em 4x70 setkey -k F32AA98CF5BE4ADFA6D3480B (pm3 test key)\n" "lf em 4x70 setkey -k A090A0A02080000000000000 (research paper key)\n" + "lf em 4x70 setkey -k 022A028C02BE000102030405 (autorecovery test key)\n" ); void *argtable[] = { @@ -1105,6 +1107,7 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco , "lf em 4x70 autorecover --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)\n" "lf em 4x70 autorecover --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)\n" + "lf em 4x70 autorecover --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0 (autorecovery test key)\n" ); void *argtable[] = { From 18cbc7259ca396dea0c99ce9547e0e51370a347c Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Sun, 5 May 2024 16:00:16 -0700 Subject: [PATCH 3/4] prevent sending corrupt data to em4x70 tags --- armsrc/em4x70.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/armsrc/em4x70.c b/armsrc/em4x70.c index 05f08d6bc..d372efe77 100644 --- a/armsrc/em4x70.c +++ b/armsrc/em4x70.c @@ -744,6 +744,13 @@ void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) { command_parity = etd->parity; + // Disable to prevent sending corrupted data to the tag. + if (command_parity) { + Dbprintf("Use of `--par` option with `lf em 4x70 write` is disabled to prevent corrupting tag data"); + reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); + return; + } + init_tag(); em4x70_setup_read(); @@ -808,6 +815,13 @@ void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) { command_parity = etd->parity; + // Disable to prevent sending corrupted data to the tag. + if (command_parity) { + Dbprintf("Use of `--par` option with `lf em 4x70 auth` is disabled to prevent corrupting tag data"); + reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); + return; + } + init_tag(); em4x70_setup_read(); @@ -829,6 +843,13 @@ void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) { command_parity = etd->parity; + // Disable to prevent sending corrupted data to the tag. + if (command_parity) { + Dbprintf("Use of `--par` option with `lf em 4x70 brute` is disabled to prevent corrupting tag data"); + reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); + return; + } + init_tag(); em4x70_setup_read(); @@ -850,6 +871,13 @@ void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) { command_parity = etd->parity; + // Disable to prevent sending corrupted data to the tag. + if (command_parity) { + Dbprintf("Use of `--par` option with `lf em 4x70 setpin` is disabled to prevent corrupting tag data"); + reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); + return; + } + init_tag(); em4x70_setup_read(); @@ -892,6 +920,13 @@ void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) { command_parity = etd->parity; + // Disable to prevent sending corrupted data to the tag. + if (command_parity) { + Dbprintf("Use of `--par` option with `lf em 4x70 setkey` is disabled to prevent corrupting tag data"); + reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); + return; + } + init_tag(); em4x70_setup_read(); From 0de99805cdaab4859a540db51059944b57089289 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Mon, 6 May 2024 09:09:46 -0700 Subject: [PATCH 4/4] Fix broken test --- tools/pm3_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 782c561c9..c10200f88 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -414,7 +414,7 @@ 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 selftest'" "Tests \( ok"; then break; fi + if ! CheckExecute "lf hitag 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