From 86bac8fe8cdd2ba14a7616b3227cd2af713d4ca0 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Mon, 9 Jun 2025 12:18:34 -0700 Subject: [PATCH 1/7] em4x70 `--par` deprecation: Step 1: client-side always sets `false` --- client/src/cmdlfem4x70.c | 133 +++++++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 46 deletions(-) diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index 69cdcc39a..37ffa865e 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -87,17 +87,17 @@ typedef struct _em4x70_tag_info_t { } em4x70_tag_info_t; typedef struct _em4x70_cmd_input_info_t { - uint8_t use_parity; + uint8_t deprecated_ignored_use_parity; } em4x70_cmd_input_info_t; typedef struct _em4x70_cmd_input_writeblock_t { - uint8_t use_parity; + uint8_t deprecated_ignored_use_parity; uint8_t block; uint8_t value[2]; } em4x70_cmd_input_writeblock_t; typedef struct _em4x70_cmd_input_brute_t { - uint8_t use_parity; + uint8_t deprecated_ignored_use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; uint8_t block; @@ -121,12 +121,12 @@ typedef struct _em4x70_cmd_output_brute_t { } em4x70_cmd_output_brute_t; typedef struct _em4x70_cmd_input_unlock_t { - uint8_t use_parity; + uint8_t deprecated_ignored_use_parity; uint8_t pin[4]; } em4x70_cmd_input_unlock_t; typedef struct _em4x70_cmd_input_auth_t { - uint8_t use_parity; + uint8_t deprecated_ignored_use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; } em4x70_cmd_input_auth_t; @@ -136,12 +136,12 @@ typedef struct _em4x70_cmd_output_auth_t { } em4x70_cmd_output_auth_t; typedef struct _em4x70_cmd_input_setpin_t { - uint8_t use_parity; + uint8_t deprecated_ignored_use_parity; uint8_t pin[4]; } em4x70_cmd_input_setpin_t; typedef struct _em4x70_cmd_input_setkey_t { - uint8_t use_parity; + uint8_t deprecated_ignored_use_parity; ID48LIB_KEY key; } em4x70_cmd_input_setkey_t; @@ -151,7 +151,7 @@ typedef struct _em4x70_cmd_input_recover_t { ID48LIB_NONCE nonce; ID48LIB_FRN frn; ID48LIB_GRN grn; - bool parity; // if true, add parity bit to commands sent to tag + bool deprecated_ignored_use_parity; // if true, add parity bit to commands sent to tag bool verify; // if true, tag must be present } em4x70_cmd_input_recover_t; @@ -164,7 +164,7 @@ typedef struct _em4x70_cmd_output_recover_t { } em4x70_cmd_output_recover_t; typedef struct _em4x70_cmd_input_verify_auth_t { - uint8_t use_parity; + uint8_t deprecated_ignored_use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; ID48LIB_GRN grn; @@ -233,7 +233,7 @@ static int get_em4x70_info(const em4x70_cmd_input_info_t *opts, em4x70_tag_info_ memset(data_out, 0, sizeof(em4x70_tag_info_t)); // TODO: change firmware to use per-cmd structures - em4x70_data_t edata = { .parity = opts->use_parity }; + em4x70_data_t edata = { .parity = false }; clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_INFO, (uint8_t *)&edata, sizeof(em4x70_data_t)); PacketResponseNG resp; @@ -254,7 +254,7 @@ static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t *opts, em4x70_t em4x70_data_t etd = {0}; etd.address = opts->block; etd.word = BYTES2UINT16(opts->value); - etd.parity = opts->use_parity; + etd.parity = false; clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_WRITE, (uint8_t *)&etd, sizeof(etd)); @@ -273,7 +273,7 @@ static int auth_em4x70(const em4x70_cmd_input_auth_t *opts, em4x70_cmd_output_au // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; + etd.parity = false; memcpy(&etd.rnd[0], &opts->rn.rn[0], 7); memcpy(&etd.frnd[0], &opts->frn.frn[0], 4); @@ -298,7 +298,7 @@ static int setkey_em4x70(const em4x70_cmd_input_setkey_t *opts) { // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; + etd.parity = false; memcpy(&etd.crypt_key[0], &opts->key.k[0], 12); clearCommandBuffer(); @@ -315,7 +315,7 @@ static int brute_em4x70(const em4x70_cmd_input_brute_t *opts, em4x70_cmd_output_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; + etd.parity = false; etd.address = opts->block; memcpy(&etd.rnd[0], &opts->rn.rn[0], 7); memcpy(&etd.frnd[0], &opts->frn.frn[0], 4); @@ -366,7 +366,7 @@ static int unlock_em4x70(const em4x70_cmd_input_unlock_t *opts, em4x70_tag_info_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; + etd.parity = false; etd.pin = BYTES2UINT32(opts->pin); clearCommandBuffer(); @@ -386,7 +386,7 @@ static int setpin_em4x70(const em4x70_cmd_input_setpin_t *opts, em4x70_tag_info_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; + etd.parity = false; etd.pin = BYTES2UINT32(opts->pin); clearCommandBuffer(); @@ -429,7 +429,7 @@ static int recover_em4x70(const em4x70_cmd_input_recover_t *opts, em4x70_cmd_out static int verify_auth_em4x70(const em4x70_cmd_input_verify_auth_t *opts) { em4x70_cmd_input_auth_t opts_auth = { - .use_parity = opts->use_parity, + .deprecated_ignored_use_parity = false, .rn = opts->rn, .frn = opts->frn, }; @@ -458,21 +458,25 @@ static int CmdEM4x70Info(const char *Cmd) { " ID48 does not use command parity (default).\n" " V4070 and EM4170 do require parity bit.", "lf em 4x70 info\n" - "lf em 4x70 info --par -> adds parity bit to command\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), + arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_info_t opts = { - .use_parity = arg_get_lit(ctx, 0), + .deprecated_ignored_use_parity = arg_get_lit(ctx, 0), }; CLIParserFree(ctx); + if (opts.deprecated_ignored_use_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + opts.deprecated_ignored_use_parity = false; + } + // Client command line parsing and validation complete ... now use the helper function em4x70_tag_info_t info; int result = get_em4x70_info(&opts, &info); @@ -494,12 +498,11 @@ static int CmdEM4x70Write(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x70 write", "Write EM4x70\n", "lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15\n" - "lf em 4x70 write -b 15 -d c0de --par -> adds parity bit to commands\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), + arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_int1("b", "block", "", "block/word address, dec"), arg_str1("d", "data", "", "data, 2 bytes"), arg_param_end @@ -508,10 +511,11 @@ static int CmdEM4x70Write(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_writeblock_t opts = { - .use_parity = arg_get_lit(ctx, 1), + .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .block = arg_get_int_def(ctx, 2, 1), .value = {0}, // hex value macro exits function, so cannot be initialized here }; + int value_len = 0; CLIGetHexWithReturn(ctx, 3, opts.value, &value_len); CLIParserFree(ctx); @@ -525,6 +529,11 @@ static int CmdEM4x70Write(const char *Cmd) { return PM3_EINVARG; } + if (opts.deprecated_ignored_use_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + opts.deprecated_ignored_use_parity = false; + } + // Client command line parsing and validation complete ... now use the helper function em4x70_tag_info_t info; int result = writeblock_em4x70(&opts, &info); @@ -555,7 +564,7 @@ static int CmdEM4x70Brute(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), + arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_int1("b", "block", "", "block/word address, dec"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), @@ -565,7 +574,7 @@ static int CmdEM4x70Brute(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_brute_t opts = { - .use_parity = arg_get_lit(ctx, 1), + .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .block = arg_get_int_def(ctx, 2, 0), .rn = {{0}}, // hex value macro exits function, so cannot be initialized here .frn = {{0}}, // hex value macro exits function, so cannot be initialized here @@ -594,7 +603,7 @@ static int CmdEM4x70Brute(const char *Cmd) { return PM3_EINVARG; } CLIParserFree(ctx); - + // opts structure takes value in BIG ENDIAN form opts.partial_key_start[0] = (uint8_t)((start_key >> 8) & 0xFF); opts.partial_key_start[1] = (uint8_t)((start_key >> 0) & 0xFF); @@ -609,6 +618,11 @@ static int CmdEM4x70Brute(const char *Cmd) { return PM3_EINVARG; } + if (opts.deprecated_ignored_use_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + opts.deprecated_ignored_use_parity = false; + } + // Client command line parsing and validation complete ... now use the helper function PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("") " to exit"); em4x70_cmd_output_brute_t data; @@ -635,11 +649,10 @@ static int CmdEM4x70Unlock(const char *Cmd) { " AAAAAAAA\n" " 00000000\n", "lf em 4x70 unlock -p 11223344 -> Unlock with PIN\n" - "lf em 4x70 unlock -p 11223344 --par -> Unlock with PIN using parity commands\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), + arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1("p", "pin", "", "pin, 4 bytes"), arg_param_end }; @@ -647,7 +660,7 @@ static int CmdEM4x70Unlock(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_unlock_t opts = { - .use_parity = arg_get_lit(ctx, 1), + .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .pin = {0}, // hex value macro exits function, so cannot be initialized here }; int pin_len = 0; @@ -659,6 +672,11 @@ static int CmdEM4x70Unlock(const char *Cmd) { return PM3_EINVARG; } + if (opts.deprecated_ignored_use_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + opts.deprecated_ignored_use_parity = false; + } + // Client command line parsing and validation complete ... now use the helper function em4x70_tag_info_t info; int result = unlock_em4x70(&opts, &info); @@ -691,7 +709,7 @@ static int CmdEM4x70Auth(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), + arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), arg_param_end @@ -700,7 +718,7 @@ static int CmdEM4x70Auth(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_auth_t opts = { - .use_parity = arg_get_lit(ctx, 1), + .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .rn = {{0}}, // hex value macro exits function, so cannot be initialized here .frn = {{0}}, // hex value macro exits function, so cannot be initialized here }; @@ -719,6 +737,11 @@ static int CmdEM4x70Auth(const char *Cmd) { return PM3_EINVARG; } + if (opts.deprecated_ignored_use_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + opts.deprecated_ignored_use_parity = false; + } + // Client command line parsing and validation complete ... now use the helper function em4x70_cmd_output_auth_t data; int result = auth_em4x70(&opts, &data); @@ -738,18 +761,17 @@ static int CmdEM4x70SetPIN(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x70 setpin", "Write new PIN\n", "lf em 4x70 setpin -p 11223344 -> Write new PIN\n" - "lf em 4x70 setpin -p 11223344 --par -> Write new PIN using parity commands\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), + arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1("p", "pin", "", "pin, 4 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_setpin_t opts = { - .use_parity = arg_get_lit(ctx, 1), + .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .pin = {0}, // hex value macro exits function, so cannot be initialized here }; @@ -762,6 +784,11 @@ static int CmdEM4x70SetPIN(const char *Cmd) { return PM3_EINVARG; } + if (opts.deprecated_ignored_use_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + opts.deprecated_ignored_use_parity = false; + } + // Client command line parsing and validation complete ... now use the helper function em4x70_tag_info_t info; @@ -789,7 +816,7 @@ static int CmdEM4x70SetKey(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), + arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1("k", "key", "", "Key as 12 hex bytes"), arg_param_end }; @@ -797,7 +824,7 @@ static int CmdEM4x70SetKey(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_setkey_t opts = { - .use_parity = arg_get_lit(ctx, 1), + .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .key = {{0}}, // hex value macro exits function, so cannot be initialized here }; int key_len = 12; @@ -808,6 +835,11 @@ static int CmdEM4x70SetKey(const char *Cmd) { return PM3_EINVARG; } + if (opts.deprecated_ignored_use_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + opts.deprecated_ignored_use_parity = false; + } + // Client command line parsing and validation complete ... now use the helper function int result = setkey_em4x70(&opts); @@ -823,7 +855,7 @@ static int CmdEM4x70SetKey(const char *Cmd) { // Now verify authentication using the new key, to ensure it was correctly written em4x70_cmd_input_verify_auth_t opts_v = { - .use_parity = opts.use_parity, + .deprecated_ignored_use_parity = false, //.rn = opts_auth.rn, //.frn = opts_auth.frn, //.grn = {{0}}, @@ -909,7 +941,7 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_ void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), + arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1("k", "key", "", "Key as 6 hex bytes"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), @@ -931,7 +963,11 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_ // if all OK so far, convert to internal data structure if (PM3_SUCCESS == result) { // magic number == index in argtable above. Fragile technique! - out_results->parity = arg_get_lit(ctx, 1); + out_results->deprecated_ignored_use_parity = arg_get_lit(ctx, 1); + if (out_results->deprecated_ignored_use_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + out_results->deprecated_ignored_use_parity = false; + } if (CLIParamHexToBuf(arg_get_str(ctx, 2), &(out_results->key.k[0]), 12, &key_len)) { result = PM3_ESOFT; } @@ -1112,7 +1148,7 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), + arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1(NULL, "rnd", "", "Random 56-bit from known-good authentication"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes from known-good authentication"), arg_str1(NULL, "grn", "", "G(RN) 20-bit as 3 hex bytes from known-good authentication"), @@ -1125,12 +1161,17 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco int rnd_len = 0; // must be 7 bytes hex data int frn_len = 0; // must be 4 bytes hex data int grn_len = 0; // must be 3 bytes hex data - out_results->parity = arg_get_lit(ctx, 1); + out_results->deprecated_ignored_use_parity = arg_get_lit(ctx, 1); CLIGetHexWithReturn(ctx, 2, out_results->nonce.rn, &rnd_len); CLIGetHexWithReturn(ctx, 3, out_results->frn.frn, &frn_len); CLIGetHexWithReturn(ctx, 4, out_results->grn.grn, &grn_len); CLIParserFree(ctx); + if (out_results->deprecated_ignored_use_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + out_results->deprecated_ignored_use_parity = false; + } + if (rnd_len != 7) { PrintAndLogEx(FAILED, "Random number length must be 7 bytes, got %d", rnd_len); result = PM3_EINVARG; @@ -1190,7 +1231,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 auth --rnd %s --frn %s"), rnd_string, frn_string); em4x70_cmd_input_auth_t opts_auth = { - .use_parity = opts.parity, + .deprecated_ignored_use_parity = false, .rn = opts.nonce, .frn = opts.frn, }; @@ -1234,7 +1275,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 write -b %d -d 0000"), block); em4x70_cmd_input_writeblock_t opt_write_zeros = { - .use_parity = opts.parity, + .deprecated_ignored_use_parity = false, .block = block, .value = {0x00, 0x00}, }; @@ -1255,7 +1296,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 brute -b %d --rnd %s --frn %s"), block, rnd_string, frn_string); em4x70_cmd_input_brute_t opts_brute = { - .use_parity = opts.parity, + .deprecated_ignored_use_parity = false, .block = block, .rn = opts.nonce, .frn = opts.frn, @@ -1294,7 +1335,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 write -b %d -d %02X%02X"), block, brute.partial_key[0], brute.partial_key[1]); em4x70_cmd_input_writeblock_t opt_write_zeros2 = { - .use_parity = opts.parity, + .deprecated_ignored_use_parity = false, .block = block, .value = {brute.partial_key[0], brute.partial_key[1]}, }; @@ -1354,7 +1395,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(INFO, "Step 6. Verify which potential key is actually on the tag"); em4x70_cmd_input_verify_auth_t opts_v = { - .use_parity = opts.parity, + .deprecated_ignored_use_parity = false, //.rn = {{0}}, //.frn = {{0}}, //.grn = {{0}}, From 31b1117a519c1772b73aab675f94de1f99017dbc Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Mon, 9 Jun 2025 12:58:50 -0700 Subject: [PATCH 2/7] em4x70 `--par` deprecation: Step 2: arm-side always uses `false` --- armsrc/em4x70.c | 38 +++++++++++++++++++------------------- client/src/cmdlfem4x70.c | 14 +++++++------- include/em4x70.h | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/armsrc/em4x70.c b/armsrc/em4x70.c index 4c2e01ded..0737f16e1 100644 --- a/armsrc/em4x70.c +++ b/armsrc/em4x70.c @@ -45,7 +45,7 @@ #define DPRINTF_EXTENDED(x) do { if ((FORCE_ENABLE_LOGGING) || (g_dbglevel >= DBG_EXTENDED)) { Dbprintf x ; } } while (0); #define DPRINTF_PROLIX(x) do { if ((FORCE_ENABLE_LOGGING) || (g_dbglevel > DBG_EXTENDED)) { Dbprintf x ; } } while (0); // EM4170 requires a parity bit on commands, other variants do not. -static bool g_command_parity = true; +static bool g_deprecated_command_parity = false; static em4x70_tag_t g_tag = { 0 }; @@ -1097,7 +1097,7 @@ static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *respon em4x70_command_bitstream_t auth_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->auth(&auth_cmd, g_command_parity, rnd, frnd); + generator->auth(&auth_cmd, g_deprecated_command_parity, rnd, frnd); bool result = send_bitstream_and_read(&auth_cmd); if (result) { @@ -1185,7 +1185,7 @@ static int bruteforce(const uint8_t address, const uint8_t *rnd, const uint8_t * static int send_pin(const uint32_t pin) { em4x70_command_bitstream_t send_pin_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->pin(&send_pin_cmd, g_command_parity, &g_tag.data[4], pin); + generator->pin(&send_pin_cmd, g_deprecated_command_parity, &g_tag.data[4], pin); bool result = send_bitstream_wait_ack_wait_read(&send_pin_cmd); return result ? PM3_SUCCESS : PM3_ESOFT; @@ -1196,7 +1196,7 @@ static int write(const uint16_t word, const uint8_t address) { em4x70_command_bitstream_t write_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->write(&write_cmd, g_command_parity, word, address); + generator->write(&write_cmd, g_deprecated_command_parity, word, address); bool result = send_bitstream_wait_ack_wait_ack(&write_cmd); if (!result) { @@ -1283,7 +1283,7 @@ static uint8_t encoded_bit_array_to_byte(const uint8_t *bits, int count_of_bits) static bool em4x70_read_id(void) { em4x70_command_bitstream_t read_id_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->id(&read_id_cmd, g_command_parity); + generator->id(&read_id_cmd, g_deprecated_command_parity); bool result = send_bitstream_and_read(&read_id_cmd); if (result) { @@ -1300,7 +1300,7 @@ static bool em4x70_read_id(void) { static bool em4x70_read_um1(void) { em4x70_command_bitstream_t read_um1_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->um1(&read_um1_cmd, g_command_parity); + generator->um1(&read_um1_cmd, g_deprecated_command_parity); bool result = send_bitstream_and_read(&read_um1_cmd); if (result) { @@ -1319,7 +1319,7 @@ static bool em4x70_read_um1(void) { static bool em4x70_read_um2(void) { em4x70_command_bitstream_t read_um2_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->um2(&read_um2_cmd, g_command_parity); + generator->um2(&read_um2_cmd, g_deprecated_command_parity); bool result = send_bitstream_and_read(&read_um2_cmd); if (result) { @@ -1435,7 +1435,7 @@ void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) { bool success_with_UM2 = false; // Support tags with and without command parity bits - g_command_parity = etd->parity; + g_deprecated_command_parity = false; init_tag(); em4x70_setup_read(); @@ -1463,10 +1463,10 @@ void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (g_command_parity) { + if (g_deprecated_command_parity) { DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 write` is non-functional and may corrupt data on the tag.")); // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); // return; @@ -1499,7 +1499,7 @@ void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; init_tag(); em4x70_setup_read(); @@ -1534,10 +1534,10 @@ void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) { uint8_t response[3] = {0}; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (g_command_parity) { + if (g_deprecated_command_parity) { DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 auth` is non-functional.")); // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); // return; @@ -1562,10 +1562,10 @@ void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; uint8_t response[2] = {0}; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (g_command_parity) { + if (g_deprecated_command_parity) { DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 brute` is non-functional and may corrupt data on the tag.")); // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); // return; @@ -1590,10 +1590,10 @@ void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (g_command_parity) { + if (g_deprecated_command_parity) { DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 setpin` is non-functional and may corrupt data on the tag.")); // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); // return; @@ -1639,10 +1639,10 @@ void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (g_command_parity) { + if (g_deprecated_command_parity) { DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 setkey` is non-functional and may corrupt data on the tag.")); // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); // return; diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index 37ffa865e..7cbfdc0cf 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -233,7 +233,7 @@ static int get_em4x70_info(const em4x70_cmd_input_info_t *opts, em4x70_tag_info_ memset(data_out, 0, sizeof(em4x70_tag_info_t)); // TODO: change firmware to use per-cmd structures - em4x70_data_t edata = { .parity = false }; + em4x70_data_t edata = { .deprecated_ignored_use_parity = false }; clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_INFO, (uint8_t *)&edata, sizeof(em4x70_data_t)); PacketResponseNG resp; @@ -254,7 +254,7 @@ static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t *opts, em4x70_t em4x70_data_t etd = {0}; etd.address = opts->block; etd.word = BYTES2UINT16(opts->value); - etd.parity = false; + etd.deprecated_ignored_use_parity = false; clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_WRITE, (uint8_t *)&etd, sizeof(etd)); @@ -273,7 +273,7 @@ static int auth_em4x70(const em4x70_cmd_input_auth_t *opts, em4x70_cmd_output_au // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = false; + etd.deprecated_ignored_use_parity = false; memcpy(&etd.rnd[0], &opts->rn.rn[0], 7); memcpy(&etd.frnd[0], &opts->frn.frn[0], 4); @@ -298,7 +298,7 @@ static int setkey_em4x70(const em4x70_cmd_input_setkey_t *opts) { // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = false; + etd.deprecated_ignored_use_parity = false; memcpy(&etd.crypt_key[0], &opts->key.k[0], 12); clearCommandBuffer(); @@ -315,7 +315,7 @@ static int brute_em4x70(const em4x70_cmd_input_brute_t *opts, em4x70_cmd_output_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = false; + etd.deprecated_ignored_use_parity = false; etd.address = opts->block; memcpy(&etd.rnd[0], &opts->rn.rn[0], 7); memcpy(&etd.frnd[0], &opts->frn.frn[0], 4); @@ -366,7 +366,7 @@ static int unlock_em4x70(const em4x70_cmd_input_unlock_t *opts, em4x70_tag_info_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = false; + etd.deprecated_ignored_use_parity = false; etd.pin = BYTES2UINT32(opts->pin); clearCommandBuffer(); @@ -386,7 +386,7 @@ static int setpin_em4x70(const em4x70_cmd_input_setpin_t *opts, em4x70_tag_info_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = false; + etd.deprecated_ignored_use_parity = false; etd.pin = BYTES2UINT32(opts->pin); clearCommandBuffer(); diff --git a/include/em4x70.h b/include/em4x70.h index 597dbfe26..43678272b 100644 --- a/include/em4x70.h +++ b/include/em4x70.h @@ -38,7 +38,7 @@ /// The only requirement is that this structure remain /// smaller than the NG buffer size (256 bytes). typedef struct { - bool parity; + bool deprecated_ignored_use_parity; // BUGBUG: Deprecated, ignored, but kept for structure size compatibility // Used for writing address uint8_t address; From 4dcf12fd8dc237473af03b417991ba15370152f9 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Mon, 9 Jun 2025 13:32:37 -0700 Subject: [PATCH 3/7] em4x70 `--par` deprecation: Step 3: remove client-only variables, hard-code `false` for client->arm comms --- client/src/cmdlfem4x70.c | 133 +++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 77 deletions(-) diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index 7cbfdc0cf..8ff8cf3ca 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -86,18 +86,12 @@ typedef struct _em4x70_tag_info_t { uint8_t Raw[32]; } em4x70_tag_info_t; -typedef struct _em4x70_cmd_input_info_t { - uint8_t deprecated_ignored_use_parity; -} em4x70_cmd_input_info_t; - typedef struct _em4x70_cmd_input_writeblock_t { - uint8_t deprecated_ignored_use_parity; uint8_t block; uint8_t value[2]; } em4x70_cmd_input_writeblock_t; typedef struct _em4x70_cmd_input_brute_t { - uint8_t deprecated_ignored_use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; uint8_t block; @@ -121,12 +115,10 @@ typedef struct _em4x70_cmd_output_brute_t { } em4x70_cmd_output_brute_t; typedef struct _em4x70_cmd_input_unlock_t { - uint8_t deprecated_ignored_use_parity; uint8_t pin[4]; } em4x70_cmd_input_unlock_t; typedef struct _em4x70_cmd_input_auth_t { - uint8_t deprecated_ignored_use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; } em4x70_cmd_input_auth_t; @@ -136,12 +128,10 @@ typedef struct _em4x70_cmd_output_auth_t { } em4x70_cmd_output_auth_t; typedef struct _em4x70_cmd_input_setpin_t { - uint8_t deprecated_ignored_use_parity; uint8_t pin[4]; } em4x70_cmd_input_setpin_t; typedef struct _em4x70_cmd_input_setkey_t { - uint8_t deprecated_ignored_use_parity; ID48LIB_KEY key; } em4x70_cmd_input_setkey_t; @@ -151,7 +141,6 @@ typedef struct _em4x70_cmd_input_recover_t { ID48LIB_NONCE nonce; ID48LIB_FRN frn; ID48LIB_GRN grn; - bool deprecated_ignored_use_parity; // if true, add parity bit to commands sent to tag bool verify; // if true, tag must be present } em4x70_cmd_input_recover_t; @@ -164,7 +153,6 @@ typedef struct _em4x70_cmd_output_recover_t { } em4x70_cmd_output_recover_t; typedef struct _em4x70_cmd_input_verify_auth_t { - uint8_t deprecated_ignored_use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; ID48LIB_GRN grn; @@ -228,7 +216,7 @@ static void em4x70_print_info_result(const em4x70_tag_info_t *data) { PrintAndLogEx(NORMAL, ""); } -static int get_em4x70_info(const em4x70_cmd_input_info_t *opts, em4x70_tag_info_t *data_out) { +static int get_em4x70_info(em4x70_tag_info_t *data_out) { memset(data_out, 0, sizeof(em4x70_tag_info_t)); @@ -429,7 +417,6 @@ static int recover_em4x70(const em4x70_cmd_input_recover_t *opts, em4x70_cmd_out static int verify_auth_em4x70(const em4x70_cmd_input_verify_auth_t *opts) { em4x70_cmd_input_auth_t opts_auth = { - .deprecated_ignored_use_parity = false, .rn = opts->rn, .frn = opts->frn, }; @@ -467,19 +454,17 @@ static int CmdEM4x70Info(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); - em4x70_cmd_input_info_t opts = { - .deprecated_ignored_use_parity = arg_get_lit(ctx, 0), - }; - CLIParserFree(ctx); - - if (opts.deprecated_ignored_use_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - opts.deprecated_ignored_use_parity = false; + { + bool requested_parity = arg_get_lit(ctx, 0); + if (requested_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + } } + CLIParserFree(ctx); // Client command line parsing and validation complete ... now use the helper function em4x70_tag_info_t info; - int result = get_em4x70_info(&opts, &info); + int result = get_em4x70_info(&info); if (result == PM3_ETIMEOUT) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); @@ -509,9 +494,14 @@ static int CmdEM4x70Write(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); + { + bool requested_parity = arg_get_lit(ctx, 1); + if (requested_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + } + } em4x70_cmd_input_writeblock_t opts = { - .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .block = arg_get_int_def(ctx, 2, 1), .value = {0}, // hex value macro exits function, so cannot be initialized here }; @@ -529,11 +519,6 @@ static int CmdEM4x70Write(const char *Cmd) { return PM3_EINVARG; } - if (opts.deprecated_ignored_use_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - opts.deprecated_ignored_use_parity = false; - } - // Client command line parsing and validation complete ... now use the helper function em4x70_tag_info_t info; int result = writeblock_em4x70(&opts, &info); @@ -572,9 +557,14 @@ static int CmdEM4x70Brute(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + { + bool requested_parity = arg_get_lit(ctx, 1); + if (requested_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + } + } em4x70_cmd_input_brute_t opts = { - .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .block = arg_get_int_def(ctx, 2, 0), .rn = {{0}}, // hex value macro exits function, so cannot be initialized here .frn = {{0}}, // hex value macro exits function, so cannot be initialized here @@ -618,11 +608,6 @@ static int CmdEM4x70Brute(const char *Cmd) { return PM3_EINVARG; } - if (opts.deprecated_ignored_use_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - opts.deprecated_ignored_use_parity = false; - } - // Client command line parsing and validation complete ... now use the helper function PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("") " to exit"); em4x70_cmd_output_brute_t data; @@ -658,9 +643,14 @@ static int CmdEM4x70Unlock(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); + { + bool requested_parity = arg_get_lit(ctx, 1); + if (requested_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + } + } em4x70_cmd_input_unlock_t opts = { - .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .pin = {0}, // hex value macro exits function, so cannot be initialized here }; int pin_len = 0; @@ -671,11 +661,6 @@ static int CmdEM4x70Unlock(const char *Cmd) { PrintAndLogEx(FAILED, "PIN length must be 4 bytes, got %d", pin_len); return PM3_EINVARG; } - - if (opts.deprecated_ignored_use_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - opts.deprecated_ignored_use_parity = false; - } // Client command line parsing and validation complete ... now use the helper function em4x70_tag_info_t info; @@ -716,9 +701,14 @@ static int CmdEM4x70Auth(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); + { + bool requested_parity = arg_get_lit(ctx, 1); + if (requested_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + } + } em4x70_cmd_input_auth_t opts = { - .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .rn = {{0}}, // hex value macro exits function, so cannot be initialized here .frn = {{0}}, // hex value macro exits function, so cannot be initialized here }; @@ -737,11 +727,6 @@ static int CmdEM4x70Auth(const char *Cmd) { return PM3_EINVARG; } - if (opts.deprecated_ignored_use_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - opts.deprecated_ignored_use_parity = false; - } - // Client command line parsing and validation complete ... now use the helper function em4x70_cmd_output_auth_t data; int result = auth_em4x70(&opts, &data); @@ -769,9 +754,14 @@ static int CmdEM4x70SetPIN(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + { + bool requested_parity = arg_get_lit(ctx, 1); + if (requested_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + } + } em4x70_cmd_input_setpin_t opts = { - .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .pin = {0}, // hex value macro exits function, so cannot be initialized here }; @@ -784,11 +774,6 @@ static int CmdEM4x70SetPIN(const char *Cmd) { return PM3_EINVARG; } - if (opts.deprecated_ignored_use_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - opts.deprecated_ignored_use_parity = false; - } - // Client command line parsing and validation complete ... now use the helper function em4x70_tag_info_t info; @@ -822,9 +807,14 @@ static int CmdEM4x70SetKey(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); + { + bool requested_parity = arg_get_lit(ctx, 1); + if (requested_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + } + } em4x70_cmd_input_setkey_t opts = { - .deprecated_ignored_use_parity = arg_get_lit(ctx, 1), .key = {{0}}, // hex value macro exits function, so cannot be initialized here }; int key_len = 12; @@ -834,11 +824,6 @@ static int CmdEM4x70SetKey(const char *Cmd) { PrintAndLogEx(FAILED, "Key length must be 12 bytes, got %d", key_len); return PM3_EINVARG; } - - if (opts.deprecated_ignored_use_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - opts.deprecated_ignored_use_parity = false; - } // Client command line parsing and validation complete ... now use the helper function int result = setkey_em4x70(&opts); @@ -855,7 +840,6 @@ static int CmdEM4x70SetKey(const char *Cmd) { // Now verify authentication using the new key, to ensure it was correctly written em4x70_cmd_input_verify_auth_t opts_v = { - .deprecated_ignored_use_parity = false, //.rn = opts_auth.rn, //.frn = opts_auth.frn, //.grn = {{0}}, @@ -963,10 +947,11 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_ // if all OK so far, convert to internal data structure if (PM3_SUCCESS == result) { // magic number == index in argtable above. Fragile technique! - out_results->deprecated_ignored_use_parity = arg_get_lit(ctx, 1); - if (out_results->deprecated_ignored_use_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - out_results->deprecated_ignored_use_parity = false; + { + bool requested_parity = arg_get_lit(ctx, 1); + if (requested_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + } } if (CLIParamHexToBuf(arg_get_str(ctx, 2), &(out_results->key.k[0]), 12, &key_len)) { result = PM3_ESOFT; @@ -1161,17 +1146,17 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco int rnd_len = 0; // must be 7 bytes hex data int frn_len = 0; // must be 4 bytes hex data int grn_len = 0; // must be 3 bytes hex data - out_results->deprecated_ignored_use_parity = arg_get_lit(ctx, 1); + { + bool requested_parity = arg_get_lit(ctx, 1); + if (requested_parity) { + PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); + } + } CLIGetHexWithReturn(ctx, 2, out_results->nonce.rn, &rnd_len); CLIGetHexWithReturn(ctx, 3, out_results->frn.frn, &frn_len); CLIGetHexWithReturn(ctx, 4, out_results->grn.grn, &grn_len); CLIParserFree(ctx); - if (out_results->deprecated_ignored_use_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - out_results->deprecated_ignored_use_parity = false; - } - if (rnd_len != 7) { PrintAndLogEx(FAILED, "Random number length must be 7 bytes, got %d", rnd_len); result = PM3_EINVARG; @@ -1231,7 +1216,6 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 auth --rnd %s --frn %s"), rnd_string, frn_string); em4x70_cmd_input_auth_t opts_auth = { - .deprecated_ignored_use_parity = false, .rn = opts.nonce, .frn = opts.frn, }; @@ -1275,7 +1259,6 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 write -b %d -d 0000"), block); em4x70_cmd_input_writeblock_t opt_write_zeros = { - .deprecated_ignored_use_parity = false, .block = block, .value = {0x00, 0x00}, }; @@ -1296,7 +1279,6 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 brute -b %d --rnd %s --frn %s"), block, rnd_string, frn_string); em4x70_cmd_input_brute_t opts_brute = { - .deprecated_ignored_use_parity = false, .block = block, .rn = opts.nonce, .frn = opts.frn, @@ -1335,7 +1317,6 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 write -b %d -d %02X%02X"), block, brute.partial_key[0], brute.partial_key[1]); em4x70_cmd_input_writeblock_t opt_write_zeros2 = { - .deprecated_ignored_use_parity = false, .block = block, .value = {brute.partial_key[0], brute.partial_key[1]}, }; @@ -1395,7 +1376,6 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(INFO, "Step 6. Verify which potential key is actually on the tag"); em4x70_cmd_input_verify_auth_t opts_v = { - .deprecated_ignored_use_parity = false, //.rn = {{0}}, //.frn = {{0}}, //.grn = {{0}}, @@ -1595,9 +1575,8 @@ int CmdLFEM4X70(const char *Cmd) { // 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); + int result = get_em4x70_info(&info); if (result == PM3_ETIMEOUT) { // consider removing this output? PrintAndLogEx(WARNING, "timeout while waiting for reply"); From 69a2cc1ff0f78215f2182e8dd37ed7e16fb0e0ff Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Mon, 9 Jun 2025 14:03:05 -0700 Subject: [PATCH 4/7] em4x70 `--par` deprecation: Step 4: remove client references to client->arm field that used to store this --- client/src/cmdlfem4x70.c | 15 +++++---------- include/em4x70.h | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index 8ff8cf3ca..ec7922ff9 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -221,7 +221,7 @@ static int get_em4x70_info(em4x70_tag_info_t *data_out) { memset(data_out, 0, sizeof(em4x70_tag_info_t)); // TODO: change firmware to use per-cmd structures - em4x70_data_t edata = { .deprecated_ignored_use_parity = false }; + em4x70_data_t edata = {0}; clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_INFO, (uint8_t *)&edata, sizeof(em4x70_data_t)); PacketResponseNG resp; @@ -239,10 +239,10 @@ static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t *opts, em4x70_t memset(data_out, 0, sizeof(em4x70_tag_info_t)); // TODO: change firmware to use per-cmd structures - em4x70_data_t etd = {0}; - etd.address = opts->block; - etd.word = BYTES2UINT16(opts->value); - etd.deprecated_ignored_use_parity = false; + em4x70_data_t etd = { + .address = opts->block, + .word = BYTES2UINT16(opts->value), + }; clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_WRITE, (uint8_t *)&etd, sizeof(etd)); @@ -261,7 +261,6 @@ static int auth_em4x70(const em4x70_cmd_input_auth_t *opts, em4x70_cmd_output_au // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.deprecated_ignored_use_parity = false; memcpy(&etd.rnd[0], &opts->rn.rn[0], 7); memcpy(&etd.frnd[0], &opts->frn.frn[0], 4); @@ -286,7 +285,6 @@ static int setkey_em4x70(const em4x70_cmd_input_setkey_t *opts) { // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.deprecated_ignored_use_parity = false; memcpy(&etd.crypt_key[0], &opts->key.k[0], 12); clearCommandBuffer(); @@ -303,7 +301,6 @@ static int brute_em4x70(const em4x70_cmd_input_brute_t *opts, em4x70_cmd_output_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.deprecated_ignored_use_parity = false; etd.address = opts->block; memcpy(&etd.rnd[0], &opts->rn.rn[0], 7); memcpy(&etd.frnd[0], &opts->frn.frn[0], 4); @@ -354,7 +351,6 @@ static int unlock_em4x70(const em4x70_cmd_input_unlock_t *opts, em4x70_tag_info_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.deprecated_ignored_use_parity = false; etd.pin = BYTES2UINT32(opts->pin); clearCommandBuffer(); @@ -374,7 +370,6 @@ static int setpin_em4x70(const em4x70_cmd_input_setpin_t *opts, em4x70_tag_info_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.deprecated_ignored_use_parity = false; etd.pin = BYTES2UINT32(opts->pin); clearCommandBuffer(); diff --git a/include/em4x70.h b/include/em4x70.h index 43678272b..23cb2d8ef 100644 --- a/include/em4x70.h +++ b/include/em4x70.h @@ -38,7 +38,7 @@ /// The only requirement is that this structure remain /// smaller than the NG buffer size (256 bytes). typedef struct { - bool deprecated_ignored_use_parity; // BUGBUG: Deprecated, ignored, but kept for structure size compatibility + bool _ignored__was_use_parity; // BUGBUG: Ignored, but kept for structure size / offset compatibility // Used for writing address uint8_t address; From b5e6d21128be7bdeeb49c9ad11e8af389783d89a Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Mon, 9 Jun 2025 14:54:30 -0700 Subject: [PATCH 5/7] Make it clear that this code was *always* sending a parity bit. All the tags require the parity bit. --- armsrc/em4x70.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/armsrc/em4x70.c b/armsrc/em4x70.c index 0737f16e1..ca56a8c2a 100644 --- a/armsrc/em4x70.c +++ b/armsrc/em4x70.c @@ -905,8 +905,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_id(em4x70_command_bitstream_t bool result = true; memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); out_cmd_bitstream->command = EM4X70_COMMAND_ID; - //uint8_t cmd = with_command_parity ? 0x3u : 0x1u; - uint8_t cmd = 0x3u; + uint8_t cmd = 0x3u; // CMD + Parity bit == 0b001'1 result = result && add_nibble_to_bitstream(&out_cmd_bitstream->to_send, cmd, false); out_cmd_bitstream->to_receive.bitcount = 32; if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { @@ -920,8 +919,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_um1(em4x70_command_bitstream_ bool result = true; memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); out_cmd_bitstream->command = EM4X70_COMMAND_UM1; - //uint8_t cmd = with_command_parity ? 0x5u : 0x2u; - uint8_t cmd = 0x5u; + uint8_t cmd = 0x5u; // CMD + Parity bit == 0b010'1 result = result && add_nibble_to_bitstream(&out_cmd_bitstream->to_send, cmd, false); out_cmd_bitstream->to_receive.bitcount = 32; if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { @@ -935,8 +933,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_um2(em4x70_command_bitstream_ bool result = true; memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); out_cmd_bitstream->command = EM4X70_COMMAND_UM2; - //uint8_t cmd = with_command_parity ? 0xFu : 0x7u; - uint8_t cmd = 0xFu; + uint8_t cmd = 0xFu; // CMD + Parity bit == 0b111'1 result = result && add_nibble_to_bitstream(&out_cmd_bitstream->to_send, cmd, false); out_cmd_bitstream->to_receive.bitcount = 64; if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { @@ -954,8 +951,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_auth(em4x70_command_bitstream em4x70_bitstream_t *s = &out_cmd_bitstream->to_send; - // uint8_t cmd = with_command_parity ? 0x6u : 0x3u; - uint8_t cmd = 0x6u; // HACK - always sent with cmd parity + uint8_t cmd = 0x6u; // CMD + Parity bit == 0b011'0 result = result && add_nibble_to_bitstream(s, cmd, false); // Reader: [RM][0][Command][N55..N0][0000000][f(RN)27..f(RN)0] @@ -1004,8 +1000,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_pin(em4x70_command_bitstream_ out_cmd_bitstream->command = EM4X70_COMMAND_PIN; - //uint8_t cmd = with_command_parity ? 0x9u : 0x4u; - uint8_t cmd = 0x9u; // HACK - always sent with cmd parity, with extra zero bit in RM? + uint8_t cmd = 0x9u; // CMD + Parity bit == 0b100'1 result = result && add_nibble_to_bitstream(s, cmd, false); // Send tag's ID ... indexes 4 .. 35 @@ -1037,8 +1032,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_write(em4x70_command_bitstrea em4x70_bitstream_t *s = &out_cmd_bitstream->to_send; - //uint8_t cmd = with_command_parity ? 0xAu : 0x5u; - uint8_t cmd = 0xAu; // HACK - always sent with cmd parity, with extra zero bit in RM? + uint8_t cmd = 0xAu; // CMD + Parity bit == 0b101'0 result = result && add_nibble_to_bitstream(s, cmd, false); if ((address & 0x0Fu) != address) { From 9b372504531ae3b6847fbeec0a608d7fa2c68c43 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Mon, 9 Jun 2025 15:05:28 -0700 Subject: [PATCH 6/7] em4x70 `--par` deprecation: Step 5: remove client parameters entirely --- client/src/cmdlfem4x70.c | 101 +++++++-------------------------------- 1 file changed, 18 insertions(+), 83 deletions(-) diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index ec7922ff9..6756b6edf 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -444,17 +444,10 @@ static int CmdEM4x70Info(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - { - bool requested_parity = arg_get_lit(ctx, 0); - if (requested_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - } - } CLIParserFree(ctx); // Client command line parsing and validation complete ... now use the helper function @@ -482,27 +475,20 @@ static int CmdEM4x70Write(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_int1("b", "block", "", "block/word address, dec"), arg_str1("d", "data", "", "data, 2 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - { - bool requested_parity = arg_get_lit(ctx, 1); - if (requested_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - } - } em4x70_cmd_input_writeblock_t opts = { - .block = arg_get_int_def(ctx, 2, 1), + .block = arg_get_int_def(ctx, 1, 1), .value = {0}, // hex value macro exits function, so cannot be initialized here }; int value_len = 0; - CLIGetHexWithReturn(ctx, 3, opts.value, &value_len); + CLIGetHexWithReturn(ctx, 2, opts.value, &value_len); CLIParserFree(ctx); if (opts.block >= EM4X70_NUM_BLOCKS) { @@ -544,7 +530,6 @@ static int CmdEM4x70Brute(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_int1("b", "block", "", "block/word address, dec"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), @@ -552,15 +537,9 @@ static int CmdEM4x70Brute(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - { - bool requested_parity = arg_get_lit(ctx, 1); - if (requested_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - } - } em4x70_cmd_input_brute_t opts = { - .block = arg_get_int_def(ctx, 2, 0), + .block = arg_get_int_def(ctx, 1, 0), .rn = {{0}}, // hex value macro exits function, so cannot be initialized here .frn = {{0}}, // hex value macro exits function, so cannot be initialized here .partial_key_start = {0}, // hex value macro exits function, so cannot be initialized here @@ -573,15 +552,15 @@ static int CmdEM4x70Brute(const char *Cmd) { } int rnd_len = 7; - CLIGetHexWithReturn(ctx, 3, opts.rn.rn, &rnd_len); + CLIGetHexWithReturn(ctx, 2, opts.rn.rn, &rnd_len); int frnd_len = 4; - CLIGetHexWithReturn(ctx, 4, opts.frn.frn, &frnd_len); + CLIGetHexWithReturn(ctx, 3, opts.frn.frn, &frnd_len); // would prefer to use above CLIGetHexWithReturn(), but it does not // appear to support optional arguments. uint32_t start_key = 0; - int res = arg_get_u32_hexstr_def_nlen(ctx, 5, 0, &start_key, 2, true); // this stores in NATIVE ENDIAN + int res = arg_get_u32_hexstr_def_nlen(ctx, 4, 0, &start_key, 2, true); // this stores in NATIVE ENDIAN if (res == 2) { PrintAndLogEx(WARNING, "start key parameter must be in range [0, FFFF]"); CLIParserFree(ctx); @@ -632,24 +611,17 @@ static int CmdEM4x70Unlock(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1("p", "pin", "", "pin, 4 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - { - bool requested_parity = arg_get_lit(ctx, 1); - if (requested_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - } - } em4x70_cmd_input_unlock_t opts = { .pin = {0}, // hex value macro exits function, so cannot be initialized here }; int pin_len = 0; - CLIGetHexWithReturn(ctx, 2, opts.pin, &pin_len); + CLIGetHexWithReturn(ctx, 1, opts.pin, &pin_len); CLIParserFree(ctx); if (pin_len != 4) { @@ -689,29 +661,22 @@ static int CmdEM4x70Auth(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - { - bool requested_parity = arg_get_lit(ctx, 1); - if (requested_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - } - } em4x70_cmd_input_auth_t opts = { .rn = {{0}}, // hex value macro exits function, so cannot be initialized here .frn = {{0}}, // hex value macro exits function, so cannot be initialized here }; int rn_len = 7; - CLIGetHexWithReturn(ctx, 2, opts.rn.rn, &rn_len); + CLIGetHexWithReturn(ctx, 1, opts.rn.rn, &rn_len); int frn_len = 4; - CLIGetHexWithReturn(ctx, 3, opts.frn.frn, &frn_len); + CLIGetHexWithReturn(ctx, 2, opts.frn.frn, &frn_len); CLIParserFree(ctx); if (rn_len != 7) { PrintAndLogEx(FAILED, "Random number length must be 7 bytes, got %d", rn_len); @@ -744,24 +709,16 @@ static int CmdEM4x70SetPIN(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1("p", "pin", "", "pin, 4 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - { - bool requested_parity = arg_get_lit(ctx, 1); - if (requested_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - } - } - em4x70_cmd_input_setpin_t opts = { .pin = {0}, // hex value macro exits function, so cannot be initialized here }; int pin_len = 0; - CLIGetHexWithReturn(ctx, 2, opts.pin, &pin_len); + CLIGetHexWithReturn(ctx, 1, opts.pin, &pin_len); CLIParserFree(ctx); if (pin_len != 4) { @@ -796,24 +753,16 @@ static int CmdEM4x70SetKey(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1("k", "key", "", "Key as 12 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - { - bool requested_parity = arg_get_lit(ctx, 1); - if (requested_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - } - } - em4x70_cmd_input_setkey_t opts = { .key = {{0}}, // hex value macro exits function, so cannot be initialized here }; int key_len = 12; - CLIGetHexWithReturn(ctx, 2, opts.key.k, &key_len); + CLIGetHexWithReturn(ctx, 1, opts.key.k, &key_len); CLIParserFree(ctx); if (key_len != 12) { PrintAndLogEx(FAILED, "Key length must be 12 bytes, got %d", key_len); @@ -920,7 +869,6 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_ void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1("k", "key", "", "Key as 6 hex bytes"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), @@ -942,22 +890,16 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_ // if all OK so far, convert to internal data structure if (PM3_SUCCESS == result) { // magic number == index in argtable above. Fragile technique! - { - bool requested_parity = arg_get_lit(ctx, 1); - if (requested_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - } - } - if (CLIParamHexToBuf(arg_get_str(ctx, 2), &(out_results->key.k[0]), 12, &key_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 1), &(out_results->key.k[0]), 12, &key_len)) { result = PM3_ESOFT; } - if (CLIParamHexToBuf(arg_get_str(ctx, 3), &(out_results->nonce.rn[0]), 7, &rnd_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 2), &(out_results->nonce.rn[0]), 7, &rnd_len)) { result = PM3_ESOFT; } - if (CLIParamHexToBuf(arg_get_str(ctx, 4), &(out_results->frn.frn[0]), 4, &frn_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 3), &(out_results->frn.frn[0]), 4, &frn_len)) { result = PM3_ESOFT; } - if (CLIParamHexToBuf(arg_get_str(ctx, 5), &(out_results->grn.grn[0]), 3, &grn_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 4), &(out_results->grn.grn[0]), 3, &grn_len)) { result = PM3_ESOFT; } //out_results->verify = arg_get_lit(ctx, 6); @@ -1128,7 +1070,6 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "DEPRECATED/IGNORED"), arg_str1(NULL, "rnd", "", "Random 56-bit from known-good authentication"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes from known-good authentication"), arg_str1(NULL, "grn", "", "G(RN) 20-bit as 3 hex bytes from known-good authentication"), @@ -1141,15 +1082,9 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco int rnd_len = 0; // must be 7 bytes hex data int frn_len = 0; // must be 4 bytes hex data int grn_len = 0; // must be 3 bytes hex data - { - bool requested_parity = arg_get_lit(ctx, 1); - if (requested_parity) { - PrintAndLogEx(WARNING, "--par option is deprecated and unused (client will reject this option soon)."); - } - } - CLIGetHexWithReturn(ctx, 2, out_results->nonce.rn, &rnd_len); - CLIGetHexWithReturn(ctx, 3, out_results->frn.frn, &frn_len); - CLIGetHexWithReturn(ctx, 4, out_results->grn.grn, &grn_len); + CLIGetHexWithReturn(ctx, 1, out_results->nonce.rn, &rnd_len); + CLIGetHexWithReturn(ctx, 2, out_results->frn.frn, &frn_len); + CLIGetHexWithReturn(ctx, 3, out_results->grn.grn, &grn_len); CLIParserFree(ctx); if (rnd_len != 7) { From af7aa26c143ab38791fc91a50c8f67bf61069204 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Mon, 9 Jun 2025 15:12:13 -0700 Subject: [PATCH 7/7] Update changelog Parity was always used for commands, even when the code seemed to suggest it was optional. This was due to a bug in `LIW` delays sending 1.25 bits too early, coupled with the parity bit happening to align with non-transmission when needed. Parity option was deprecated earlier, and now is fully removed. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5b665025..3facfe8f9 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] +- Removed `--par` from `lf em 4x70` commands. - Changed `hf 14a info` - refactored code to be able to detect card technology across the client easier (@iceman1001) - Changed `hf mf info` - now informs better if a different card technology is detected (@iceman1001) - Changed `hf mf autopwn` - now exits if desfire is detected and limit attacks if mifare plus is detected (@iceman1001)