diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 23ee40f..4e5a491 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -627,12 +627,16 @@ static data_frame_tx_t *cmd_processor_mf1_manipulate_value_block(uint16_t cmd, u } static data_frame_tx_t *cmd_processor_em410x_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { - uint8_t card_buffer[7] = {0x00}; + uint8_t card_buffer[2 + LF_EM410X_ELECTRA_TAG_ID_SIZE] = {0x00}; status = scan_em410x(card_buffer); if (status != STATUS_LF_TAG_OK) { return data_frame_make(cmd, status, 0, NULL); } - return data_frame_make(cmd, STATUS_LF_TAG_OK, sizeof(card_buffer), card_buffer); + + tag_specific_type_t tag_type = (card_buffer[0] << 8) | card_buffer[1]; + uint16_t id_size = (tag_type == TAG_TYPE_EM410X_ELECTRA) ? LF_EM410X_ELECTRA_TAG_ID_SIZE : LF_EM410X_TAG_ID_SIZE; + + return data_frame_make(cmd, STATUS_LF_TAG_OK, 2 + id_size, card_buffer); } static data_frame_tx_t *cmd_processor_em410x_write_to_t55xx(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { @@ -650,6 +654,21 @@ static data_frame_tx_t *cmd_processor_em410x_write_to_t55xx(uint16_t cmd, uint16 return data_frame_make(cmd, status, 0, NULL); } +static data_frame_tx_t *cmd_processor_em410x_electra_write_to_t55xx(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + typedef struct { + uint8_t id[13]; + uint8_t new_key[4]; + uint8_t old_keys[4]; // we can have more than one... struct just to compute offsets with min 1 key + } PACKED payload_t; + payload_t *payload = (payload_t *)data; + if (length < sizeof(payload_t) || (length - offsetof(payload_t, old_keys)) % sizeof(payload->old_keys) != 0) { + return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL); + } + + status = write_em410x_electra_to_t55xx(payload->id, payload->new_key, payload->old_keys, (length - offsetof(payload_t, old_keys)) / sizeof(payload->old_keys)); + return data_frame_make(cmd, status, 0, NULL); +} + static data_frame_tx_t *cmd_processor_hidprox_write_to_t55xx(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { typedef struct { uint8_t id[13]; @@ -830,24 +849,41 @@ static data_frame_tx_t *cmd_processor_wipe_fds(uint16_t cmd, uint16_t status, ui return data_frame_make(cmd, status, 0, NULL); } +static bool get_active_em410x_type(tag_specific_type_t *tag_type_out, uint16_t *id_size_out) { + tag_slot_specific_type_t tag_types; + tag_emulation_get_specific_types_by_slot(tag_emulation_get_slot(), &tag_types); + if (tag_types.tag_lf == TAG_TYPE_EM410X || tag_types.tag_lf == TAG_TYPE_EM410X_ELECTRA) { + *tag_type_out = tag_types.tag_lf; + *id_size_out = (tag_types.tag_lf == TAG_TYPE_EM410X_ELECTRA) ? LF_EM410X_ELECTRA_TAG_ID_SIZE : LF_EM410X_TAG_ID_SIZE; + return true; + } + return false; +} + static data_frame_tx_t *cmd_processor_em410x_set_emu_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { - if (length != LF_EM410X_TAG_ID_SIZE) { + tag_specific_type_t tag_type; + uint16_t id_size; + if (!get_active_em410x_type(&tag_type, &id_size) || length != id_size) { return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL); } - tag_data_buffer_t *buffer = get_buffer_by_tag_type(TAG_TYPE_EM410X); - memcpy(buffer->buffer, data, LF_EM410X_TAG_ID_SIZE); - tag_emulation_load_by_buffer(TAG_TYPE_EM410X, false); + tag_data_buffer_t *buffer = get_buffer_by_tag_type(tag_type); + memcpy(buffer->buffer, data, id_size); + tag_emulation_load_by_buffer(tag_type, false); return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL); } static data_frame_tx_t *cmd_processor_em410x_get_emu_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { - tag_slot_specific_type_t tag_types; - tag_emulation_get_specific_types_by_slot(tag_emulation_get_slot(), &tag_types); - if (tag_types.tag_lf != TAG_TYPE_EM410X) { + tag_specific_type_t tag_type; + uint16_t id_size; + if (!get_active_em410x_type(&tag_type, &id_size)) { return data_frame_make(cmd, STATUS_PAR_ERR, 0, data); // no data in slot, don't send garbage } - tag_data_buffer_t *buffer = get_buffer_by_tag_type(TAG_TYPE_EM410X); - return data_frame_make(cmd, STATUS_SUCCESS, LF_EM410X_TAG_ID_SIZE, buffer->buffer); + tag_data_buffer_t *buffer = get_buffer_by_tag_type(tag_type); + uint8_t resp[2 + LF_EM410X_ELECTRA_TAG_ID_SIZE] = {0x00}; + resp[0] = tag_type >> 8; + resp[1] = tag_type; + memcpy(resp + 2, buffer->buffer, id_size); + return data_frame_make(cmd, STATUS_SUCCESS, 2 + id_size, resp); } static data_frame_tx_t *cmd_processor_hidprox_set_emu_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { @@ -1615,6 +1651,7 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_EM410X_SCAN, before_reader_run, cmd_processor_em410x_scan, NULL }, { DATA_CMD_EM410X_WRITE_TO_T55XX, before_reader_run, cmd_processor_em410x_write_to_t55xx, NULL }, + { DATA_CMD_EM410X_ELECTRA_WRITE_TO_T55XX,before_reader_run, cmd_processor_em410x_electra_write_to_t55xx, NULL }, { DATA_CMD_HIDPROX_SCAN, before_reader_run, cmd_processor_hidprox_scan, NULL }, { DATA_CMD_HIDPROX_WRITE_TO_T55XX, before_reader_run, cmd_processor_hidprox_write_to_t55xx, NULL }, { DATA_CMD_VIKING_SCAN, before_reader_run, cmd_processor_viking_scan, NULL }, diff --git a/firmware/application/src/app_main.c b/firmware/application/src/app_main.c index d6d3388..958638a 100644 --- a/firmware/application/src/app_main.c +++ b/firmware/application/src/app_main.c @@ -648,10 +648,22 @@ static void btn_fn_copy_lf(uint8_t slot, tag_specific_type_t type) { data = id_buffer; break; case TAG_TYPE_EM410X: + case TAG_TYPE_EM410X_ELECTRA: { status = scan_em410x(id_buffer); - size = LF_EM410X_TAG_ID_SIZE; - data = id_buffer + 2; // skip tag type + tag_specific_type_t detected_type = (id_buffer[0] << 8) | id_buffer[1]; + tag_specific_type_t new_type = + detected_type == TAG_TYPE_EM410X_ELECTRA ? TAG_TYPE_EM410X_ELECTRA : TAG_TYPE_EM410X; + + // If we read Electra but the slot was classic (or vice versa), switch slot type automatically. + if (new_type != type) { + tag_emulation_change_type(slot, new_type); + type = new_type; + } + + size = (new_type == TAG_TYPE_EM410X_ELECTRA) ? LF_EM410X_ELECTRA_TAG_ID_SIZE : LF_EM410X_TAG_ID_SIZE; + data = id_buffer + 2; // skip tag type break; + } case TAG_TYPE_VIKING: status = scan_viking(id_buffer); size = LF_VIKING_TAG_ID_SIZE; @@ -1011,4 +1023,4 @@ int main(void) { // Some task process done, we can enter cpu sleep state. sleep_system_run(system_off_enter, nrf_pwr_mgmt_run); } -} \ No newline at end of file +} diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index 1b47183..71ed8f4 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -87,6 +87,7 @@ // #define DATA_CMD_EM410X_SCAN (3000) #define DATA_CMD_EM410X_WRITE_TO_T55XX (3001) +#define DATA_CMD_EM410X_ELECTRA_WRITE_TO_T55XX (3006) #define DATA_CMD_HIDPROX_SCAN (3002) #define DATA_CMD_HIDPROX_WRITE_TO_T55XX (3003) #define DATA_CMD_VIKING_SCAN (3004) diff --git a/firmware/application/src/rfid/nfctag/lf/lf_tag_em.c b/firmware/application/src/rfid/nfctag/lf/lf_tag_em.c index b2ce030..bbe69fa 100644 --- a/firmware/application/src/rfid/nfctag/lf/lf_tag_em.c +++ b/firmware/application/src/rfid/nfctag/lf/lf_tag_em.c @@ -155,6 +155,10 @@ static enum { LF_SENSE_STATE_ENABLE, } m_lf_sense_state = LF_SENSE_STATE_NONE; +static uint16_t lf_em410x_id_size(tag_specific_type_t type) { + return type == TAG_TYPE_EM410X_ELECTRA ? LF_EM410X_ELECTRA_TAG_ID_SIZE : LF_EM410X_TAG_ID_SIZE; +} + /** * @brief switchLfFieldInductionToEnableTheState */ @@ -182,13 +186,14 @@ void lf_tag_125khz_sense_switch(bool enable) { int lf_tag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer) { // ensure buffer size is large enough for specific tag type, // so that tag data (e.g., card numbers) can be converted to corresponding pwm sequence here. - if (type == TAG_TYPE_EM410X && buffer->length >= LF_EM410X_TAG_ID_SIZE) { + if ((type == TAG_TYPE_EM410X || type == TAG_TYPE_EM410X_ELECTRA) && buffer->length >= lf_em410x_id_size(type)) { + const protocol *p = type == TAG_TYPE_EM410X_ELECTRA ? &em410x_electra : &em410x_64; m_tag_type = type; - void *codec = em410x_64.alloc(); - m_pwm_seq = em410x_64.modulator(codec, buffer->buffer); - em410x_64.free(codec); - NRF_LOG_INFO("load lf em410x data finish."); - return LF_EM410X_TAG_ID_SIZE; + void *codec = p->alloc(); + m_pwm_seq = p->modulator(codec, buffer->buffer); + p->free(codec); + NRF_LOG_INFO("load lf em410x%s data finish.", type == TAG_TYPE_EM410X_ELECTRA ? " electra" : ""); + return lf_em410x_id_size(type); } if (type == TAG_TYPE_HID_PROX && buffer->length >= LF_HIDPROX_TAG_ID_SIZE) { @@ -221,7 +226,13 @@ int lf_tag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer) { int lf_tag_em410x_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer) { // Make sure to load this tag before allowing saving // Just save the original card package directly - return m_tag_type == TAG_TYPE_EM410X ? LF_EM410X_TAG_ID_SIZE : 0; + if (m_tag_type == TAG_TYPE_EM410X) { + return LF_EM410X_TAG_ID_SIZE; + } + if (m_tag_type == TAG_TYPE_EM410X_ELECTRA) { + return LF_EM410X_ELECTRA_TAG_ID_SIZE; + } + return 0; } /** @brief Id card deposit card number before callback @@ -252,7 +263,7 @@ bool lf_tag_data_factory(uint8_t slot, tag_specific_type_t tag_type, uint8_t *ta fds_slot_record_map_t map_info; // Get the special card slot FDS record information get_fds_map_by_slot_sense_type_for_dump(slot, sense_type, &map_info); // Call the blocked FDS to write the function, and write the data of the specified field type of the card slot into the Flash - bool ret = fds_write_sync(map_info.id, map_info.key, sizeof(tag_id), (uint8_t *)tag_id); + bool ret = fds_write_sync(map_info.id, map_info.key, length, (uint8_t *)tag_id); if (ret) { NRF_LOG_INFO("Factory slot data success."); } else { @@ -267,9 +278,18 @@ bool lf_tag_data_factory(uint8_t slot, tag_specific_type_t tag_type, uint8_t *ta * @return Whether the format is successful, if the formatting is successful, it will return to True, otherwise False will be returned */ bool lf_tag_em410x_data_factory(uint8_t slot, tag_specific_type_t tag_type) { - // default id, must to align(4), more word... - uint8_t tag_id[5] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88}; - return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id)); + static const uint8_t tag_id_base[LF_EM410X_TAG_ID_SIZE] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88}; + static const uint8_t tag_id_electra[LF_EM410X_ELECTRA_TAG_ID_SIZE] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + switch (tag_type) { + case TAG_TYPE_EM410X_ELECTRA: + return lf_tag_data_factory(slot, tag_type, (uint8_t *)tag_id_electra, sizeof(tag_id_electra)); + case TAG_TYPE_EM410X: + return lf_tag_data_factory(slot, tag_type, (uint8_t *)tag_id_base, sizeof(tag_id_base)); + default: + return false; + } } /** @brief Id card deposit card number before callback diff --git a/firmware/application/src/rfid/nfctag/lf/lf_tag_em.h b/firmware/application/src/rfid/nfctag/lf/lf_tag_em.h index 3dabb30..104f6a5 100644 --- a/firmware/application/src/rfid/nfctag/lf/lf_tag_em.h +++ b/firmware/application/src/rfid/nfctag/lf/lf_tag_em.h @@ -6,6 +6,7 @@ #include "tag_emulation.h" #define LF_EM410X_TAG_ID_SIZE 5 +#define LF_EM410X_ELECTRA_TAG_ID_SIZE 13 #define LF_HIDPROX_TAG_ID_SIZE 13 #define LF_VIKING_TAG_ID_SIZE 4 diff --git a/firmware/application/src/rfid/nfctag/lf/protocols/em410x.c b/firmware/application/src/rfid/nfctag/lf/protocols/em410x.c index cf7d543..c6b1892 100644 --- a/firmware/application/src/rfid/nfctag/lf/protocols/em410x.c +++ b/firmware/application/src/rfid/nfctag/lf/protocols/em410x.c @@ -15,11 +15,17 @@ #define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1) #define EM_RAW_SIZE (64) -#define EM_DATA_SIZE (5) +#define EM_DATA_SIZE_BASE (5) +#define EM_ELECTRA_EPILOGUE_SIZE (8) +#define EM_DATA_SIZE_ELECTRA (EM_DATA_SIZE_BASE + EM_ELECTRA_EPILOGUE_SIZE) +#define EM_DATA_SIZE_MAX (EM_DATA_SIZE_ELECTRA) +#define EM_T55XX_ELECTRA_BLOCK_COUNT (5) #define EM_ROW_COUNT (10) #define EM_COLUMN_COUNT (4) #define EM_HEADER (0x1ff) // 9 bits of 1 +#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL) + #define EM_T55XX_BLOCK_COUNT (3) #define EM_READ_TIME1_BASE (0x40) @@ -33,16 +39,25 @@ #include "nrf_log_default_backends.h" NRF_LOG_MODULE_REGISTER(); -static nrf_pwm_values_wave_form_t m_em410x_pwm_seq_vals[EM_RAW_SIZE] = {}; +static nrf_pwm_values_wave_form_t m_em410x_pwm_seq_vals_base[EM_RAW_SIZE] = {}; +static nrf_pwm_values_wave_form_t m_em410x_pwm_seq_vals_electra[EM_RAW_SIZE * 2] = {}; -nrf_pwm_sequence_t const m_em410x_pwm_seq = { - .values.p_wave_form = m_em410x_pwm_seq_vals, - .length = NRF_PWM_VALUES_LENGTH(m_em410x_pwm_seq_vals), +nrf_pwm_sequence_t const m_em410x_pwm_seq_base = { + .values.p_wave_form = m_em410x_pwm_seq_vals_base, + .length = NRF_PWM_VALUES_LENGTH(m_em410x_pwm_seq_vals_base), + .repeats = 0, + .end_delay = 0, +}; + +nrf_pwm_sequence_t const m_em410x_pwm_seq_electra = { + .values.p_wave_form = m_em410x_pwm_seq_vals_electra, + .length = NRF_PWM_VALUES_LENGTH(m_em410x_pwm_seq_vals_electra), .repeats = 0, .end_delay = 0, }; const protocol *em410x_protocols[] = { + &em410x_electra, &em410x_64, &em410x_32, &em410x_16, @@ -51,9 +66,11 @@ const protocol *em410x_protocols[] = { size_t em410x_protocols_size = ARRAY_SIZE(em410x_protocols); typedef struct { - uint8_t data[EM_DATA_SIZE]; + uint8_t data[EM_DATA_SIZE_MAX]; uint64_t raw; + uint64_t epilogue; uint8_t raw_length; + uint8_t total_length; manchester *modem; } em410x_codec; @@ -80,6 +97,17 @@ uint64_t em410x_raw_data(uint8_t *uid) { return raw; } +uint64_t em410x_raw_epilogue(uint8_t *uid) { + uint64_t raw = 0; + + for (int i = 0; i < EM_ELECTRA_EPILOGUE_SIZE; i++) { + raw <<= 8; + raw |= uid[EM_DATA_SIZE_BASE + i]; + } + + return raw; +} + bool em410x_get_time(uint16_t divisor, uint8_t interval, uint8_t base) { return interval >= (base - EM_READ_JITTER_TIME_BASE) / divisor && interval <= (base + EM_READ_JITTER_TIME_BASE) / divisor; @@ -142,9 +170,11 @@ void em410x_free(em410x_codec *d) { uint8_t *em410x_get_data(em410x_codec *d) { return d->data; }; void em410x_decoder_start(em410x_codec *d, uint8_t format) { - memset(d->data, 0, EM_DATA_SIZE); + memset(d->data, 0, EM_DATA_SIZE_MAX); d->raw = 0; d->raw_length = 0; + d->total_length = 0; + d->epilogue = 0; manchester_reset(d->modem); }; @@ -202,6 +232,8 @@ bool em410x_decoder_feed(em410x_codec *d, uint16_t interval) { if (bitlen == -1) { d->raw = 0; d->raw_length = 0; + d->total_length = 0; + d->epilogue = 0; return false; } for (int i = 0; i < bitlen; i++) { @@ -212,6 +244,79 @@ bool em410x_decoder_feed(em410x_codec *d, uint16_t interval) { return false; }; +void em410x_electra_decoder_start(em410x_codec *d, uint8_t format) { + em410x_decoder_start(d, format); +} + +static bool em410x_electra_decode_feed(em410x_codec *d, bool bit) { + bool carry_bit = (d->epilogue >> 63) & 0x01; + + if (d->total_length < EM_RAW_SIZE + EM_RAW_SIZE) { + d->total_length++; + } + d->raw = (d->raw << 1) | carry_bit; + d->epilogue = (d->epilogue << 1) | (bit ? 1 : 0); + + if (d->total_length < EM_RAW_SIZE + EM_RAW_SIZE) { + return false; + } + + if ((d->raw & EM_ENCODED_DATA_HEADER) != EM_ENCODED_DATA_HEADER) { + return false; + } + + if (d->raw & 0x01) { + return false; + } + + uint8_t pc = 0; + for (int i = 0; i < EM_ROW_COUNT + 1; i++) { + uint8_t row = d->raw >> (EM_RAW_SIZE - 9 - (i + 1) * EM_BITS_PER_ROW_COUNT) & 0x1f; + uint8_t data = (row >> 1) & 0x0f; + pc ^= data; + if (i == 10) { + break; + } + + if (!oddparity8(row)) { // row parity + return false; + } + + if (i % 2) { + d->data[i >> 1] |= data; + } else { + d->data[i >> 1] = data << 4; + } + } + + // if we only saw the same frame twice, treat as standard EM410X + if (d->raw == d->epilogue) { + return false; + } + + for (int i = 0; i < EM_ELECTRA_EPILOGUE_SIZE; i++) { + d->data[EM_DATA_SIZE_BASE + i] = (d->epilogue >> ((EM_ELECTRA_EPILOGUE_SIZE - 1 - i) * 8)) & 0xFF; + } + + return pc == 0x00; +} + +bool em410x_electra_decoder_feed(em410x_codec *d, uint16_t interval) { + bool bits[2] = {0}; + int8_t bitlen = 0; + manchester_feed(d->modem, (uint8_t)interval, bits, &bitlen); + if (bitlen == -1) { + em410x_decoder_start(d, 0); + return false; + } + for (int i = 0; i < bitlen; i++) { + if (em410x_electra_decode_feed(d, bits[i])) { + return true; + } + } + return false; +}; + const nrf_pwm_sequence_t *em410x_modulator(em410x_codec *d, uint8_t *buf) { uint64_t lo = em410x_raw_data(buf); for (int i = 0; i < EM_RAW_SIZE; i++) { @@ -219,16 +324,50 @@ const nrf_pwm_sequence_t *em410x_modulator(em410x_codec *d, uint8_t *buf) { if (IS_SET(lo, EM_RAW_SIZE - i - 1)) { msb = (1 << 15); } - m_em410x_pwm_seq_vals[i].channel_0 = msb | 32; - m_em410x_pwm_seq_vals[i].counter_top = 64; + m_em410x_pwm_seq_vals_base[i].channel_0 = msb | 32; + m_em410x_pwm_seq_vals_base[i].counter_top = 64; } - return &m_em410x_pwm_seq; + return &m_em410x_pwm_seq_base; +}; + +const nrf_pwm_sequence_t *em410x_electra_modulator(em410x_codec *d, uint8_t *buf) { + uint64_t data[] = {em410x_raw_data(buf), em410x_raw_epilogue(buf)}; + uint16_t output_index = 0; + + for (int frame = 0; frame < 2; frame++) { + for (int i = 0; i < EM_RAW_SIZE; i++) { + uint16_t msb = 0x00; + if (IS_SET(data[frame], EM_RAW_SIZE - i - 1)) { + msb = (1 << 15); + } + m_em410x_pwm_seq_vals_electra[output_index].channel_0 = msb | 32; + m_em410x_pwm_seq_vals_electra[output_index].counter_top = 64; + output_index++; + } + } + + return &m_em410x_pwm_seq_electra; +}; + +// EM-Micro, EM410x/64 (std) +const protocol em410x_electra = { + .tag_type = TAG_TYPE_EM410X_ELECTRA, + .data_size = EM_DATA_SIZE_ELECTRA, + .alloc = (codec_alloc)em410x_64_alloc, + .free = (codec_free)em410x_free, + .get_data = (codec_get_data)em410x_get_data, + .modulator = (modulator)em410x_electra_modulator, + .decoder = + { + .start = (decoder_start)em410x_electra_decoder_start, + .feed = (decoder_feed)em410x_electra_decoder_feed, + }, }; // EM-Micro, EM410x/64 (std) const protocol em410x_64 = { .tag_type = TAG_TYPE_EM410X_64, - .data_size = EM_DATA_SIZE, + .data_size = EM_DATA_SIZE_BASE, .alloc = (codec_alloc)em410x_64_alloc, .free = (codec_free)em410x_free, .get_data = (codec_get_data)em410x_get_data, @@ -243,7 +382,7 @@ const protocol em410x_64 = { // EM-Micro, EM410x/32 const protocol em410x_32 = { .tag_type = TAG_TYPE_EM410X_32, - .data_size = EM_DATA_SIZE, + .data_size = EM_DATA_SIZE_BASE, .alloc = (codec_alloc)em410x_32_alloc, .free = (codec_free)em410x_free, .get_data = (codec_get_data)em410x_get_data, @@ -258,7 +397,7 @@ const protocol em410x_32 = { // EM-Micro, EM410x/16 const protocol em410x_16 = { .tag_type = TAG_TYPE_EM410X_16, - .data_size = EM_DATA_SIZE, + .data_size = EM_DATA_SIZE_BASE, .alloc = (codec_alloc)em410x_16_alloc, .free = (codec_free)em410x_free, .get_data = (codec_get_data)em410x_get_data, @@ -277,4 +416,17 @@ uint8_t em410x_t55xx_writer(uint8_t *uid, uint32_t *blks) { blks[1] = raw >> 32; blks[2] = raw & 0xffffffff; return EM_T55XX_BLOCK_COUNT; -} \ No newline at end of file +} + +uint8_t em410x_electra_t55xx_writer(uint8_t *uid, uint32_t *blks) { + uint64_t raw_data = em410x_raw_data(uid); + uint64_t raw_epilogue = em410x_raw_epilogue(uid); + + blks[0] = T5577_EM410X_ELECTRA_CONFIG; + blks[1] = raw_data >> 32; + blks[2] = raw_data & 0xffffffff; + blks[3] = raw_epilogue >> 32; + blks[4] = raw_epilogue & 0xffffffff; + + return EM_T55XX_ELECTRA_BLOCK_COUNT; +} diff --git a/firmware/application/src/rfid/nfctag/lf/protocols/em410x.h b/firmware/application/src/rfid/nfctag/lf/protocols/em410x.h index 074fb3a..f3438f0 100644 --- a/firmware/application/src/rfid/nfctag/lf/protocols/em410x.h +++ b/firmware/application/src/rfid/nfctag/lf/protocols/em410x.h @@ -5,8 +5,10 @@ extern const protocol em410x_64; extern const protocol em410x_32; extern const protocol em410x_16; +extern const protocol em410x_electra; extern const protocol* em410x_protocols[]; extern size_t em410x_protocols_size; -uint8_t em410x_t55xx_writer(uint8_t* uid, uint32_t* blks); \ No newline at end of file +uint8_t em410x_t55xx_writer(uint8_t* uid, uint32_t* blks); +uint8_t em410x_electra_t55xx_writer(uint8_t* uid, uint32_t* blks); diff --git a/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h b/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h index 31f71ae..ba0d84f 100644 --- a/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h +++ b/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h @@ -51,6 +51,12 @@ extern "C" { T5577_PWD | \ (2 << T5577_MAXBLOCK_SHIFT)) +#define T5577_EM410X_ELECTRA_CONFIG ( \ + T5577_BITRATE_RF_64 | \ + T5577_MODULATION_MANCHESTER | \ + T5577_PWD | \ + (4 << T5577_MAXBLOCK_SHIFT)) + #define T5577_HIDPROX_CONFIG ( \ T5577_BITRATE_RF_50 | \ T5577_MODULATION_FSK2a | \ @@ -68,4 +74,4 @@ void t55xx_reset_passwd(uint32_t old_passwd, uint32_t new_passwd); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/firmware/application/src/rfid/nfctag/tag_base_type.h b/firmware/application/src/rfid/nfctag/tag_base_type.h index 19a0a8d..2bd800b 100644 --- a/firmware/application/src/rfid/nfctag/tag_base_type.h +++ b/firmware/application/src/rfid/nfctag/tag_base_type.h @@ -38,6 +38,7 @@ typedef enum { TAG_TYPE_EM410X_16, TAG_TYPE_EM410X_32, TAG_TYPE_EM410X_64, + TAG_TYPE_EM410X_ELECTRA, // FDX-B // securakey // gallagher @@ -106,7 +107,7 @@ typedef enum { } #define TAG_SPECIFIC_TYPE_LF_VALUES \ - TAG_TYPE_EM410X, TAG_TYPE_HID_PROX, TAG_TYPE_VIKING + TAG_TYPE_EM410X, TAG_TYPE_EM410X_ELECTRA, TAG_TYPE_HID_PROX, TAG_TYPE_VIKING #define TAG_SPECIFIC_TYPE_HF_VALUES \ TAG_TYPE_MIFARE_Mini, TAG_TYPE_MIFARE_1024, TAG_TYPE_MIFARE_2048, \ diff --git a/firmware/application/src/rfid/nfctag/tag_emulation.c b/firmware/application/src/rfid/nfctag/tag_emulation.c index 79d7493..fa00dfe 100644 --- a/firmware/application/src/rfid/nfctag/tag_emulation.c +++ b/firmware/application/src/rfid/nfctag/tag_emulation.c @@ -90,6 +90,7 @@ static uint16_t m_slot_config_crc; static tag_base_handler_map_t tag_base_map[] = { // LF tag emulation {TAG_SENSE_LF, TAG_TYPE_EM410X, lf_tag_data_loadcb, lf_tag_em410x_data_savecb, lf_tag_em410x_data_factory, &m_tag_data_lf}, + {TAG_SENSE_LF, TAG_TYPE_EM410X_ELECTRA, lf_tag_data_loadcb, lf_tag_em410x_data_savecb, lf_tag_em410x_data_factory, &m_tag_data_lf}, {TAG_SENSE_LF, TAG_TYPE_HID_PROX, lf_tag_data_loadcb, lf_tag_hidprox_data_savecb, lf_tag_hidprox_data_factory, &m_tag_data_lf}, {TAG_SENSE_LF, TAG_TYPE_VIKING, lf_tag_data_loadcb, lf_tag_viking_data_savecb, lf_tag_viking_data_factory, &m_tag_data_lf}, // MF1 tag emulation diff --git a/firmware/application/src/rfid/reader/lf/lf_reader_main.c b/firmware/application/src/rfid/reader/lf/lf_reader_main.c index 210c10e..f15df3a 100644 --- a/firmware/application/src/rfid/reader/lf/lf_reader_main.c +++ b/firmware/application/src/rfid/reader/lf/lf_reader_main.c @@ -90,6 +90,15 @@ uint8_t write_em410x_to_t55xx(uint8_t *uid, uint8_t *new_passwd, uint8_t *old_pa return write_t55xx(blks, blk_count, new_passwd, old_passwds, old_passwd_count); } +uint8_t write_em410x_electra_to_t55xx(uint8_t *uid, uint8_t *new_passwd, uint8_t *old_passwds, uint8_t old_passwd_count) { + uint32_t blks[7] = {0x00}; + uint8_t blk_count = em410x_electra_t55xx_writer(uid, blks); + if (blk_count == 0) { + return STATUS_PAR_ERR; + } + return write_t55xx(blks, blk_count, new_passwd, old_passwds, old_passwd_count); +} + /** * Write hidprox card data to t55xx */ @@ -124,4 +133,4 @@ uint8_t write_viking_to_t55xx(uint8_t *uid, uint8_t *new_passwd, uint8_t *old_pa /** * Set the LF card scanning timeout value (in milliseconds). */ -void set_scan_tag_timeout(uint32_t ms) { g_timeout_readem_ms = ms; } \ No newline at end of file +void set_scan_tag_timeout(uint32_t ms) { g_timeout_readem_ms = ms; } diff --git a/firmware/application/src/rfid/reader/lf/lf_reader_main.h b/firmware/application/src/rfid/reader/lf/lf_reader_main.h index 82ace89..eba7149 100644 --- a/firmware/application/src/rfid/reader/lf/lf_reader_main.h +++ b/firmware/application/src/rfid/reader/lf/lf_reader_main.h @@ -12,5 +12,6 @@ uint8_t scan_em410x(uint8_t *uid); uint8_t scan_hidprox(uint8_t *uid, uint8_t format_hint); uint8_t scan_viking(uint8_t *uid); uint8_t write_em410x_to_t55xx(uint8_t *uid, uint8_t *newkey, uint8_t *old_keys, uint8_t old_key_count); +uint8_t write_em410x_electra_to_t55xx(uint8_t *uid, uint8_t *newkey, uint8_t *old_keys, uint8_t old_key_count); uint8_t write_hidprox_to_t55xx(uint8_t format, uint32_t fc, uint64_t cn, uint32_t il, uint32_t oem, uint8_t *new_passwd, uint8_t *old_passwds, uint8_t old_passwd_count); uint8_t write_viking_to_t55xx(uint8_t *uid, uint8_t *newkey, uint8_t *old_keys, uint8_t old_key_count); diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index 2f6bcc8..8b6dcf4 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -406,8 +406,8 @@ class LFEMIdArgsUnit(DeviceRequiredUnit): def before_exec(self, args: argparse.Namespace): if not super().before_exec(args): return False - if args.id is None or not re.match(r"^[a-fA-F0-9]{10}$", args.id): - raise ArgsParserError("ID must include 10 HEX symbols") + if args.id is None or not re.match(r"^([a-fA-F0-9]{10}|[a-fA-F0-9]{26})$", args.id): + raise ArgsParserError("ID must include 10 or 26 HEX symbols") return True def args_parser(self) -> ArgumentParserNoExit: @@ -3602,9 +3602,11 @@ class LFEM410xWriteT55xx(LFEMIdArgsUnit, ReaderRequiredUnit): def on_exec(self, args: argparse.Namespace): id_hex = args.id + if len(id_hex) not in (10, 26): + raise ArgsParserError("Writing to T55xx supports 5-byte EM410X (10 hex) or 13-byte Electra (26 hex) IDs.") id_bytes = bytes.fromhex(id_hex) self.cmd.em410x_write_to_t55xx(id_bytes) - print(f" - EM410x ID(10H): {id_hex} write done.") + print(f" - EM410x ID write done: {id_hex}") @lf_hid_prox.command('read') diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index f34889b..3b25658 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -434,7 +434,12 @@ class ChameleonCMD: """ resp = self.device.send_cmd_sync(Command.EM410X_SCAN) if resp.status == Status.LF_TAG_OK: - resp.parsed = struct.unpack('!h5s', resp.data) # tag type + uid + tag_type = struct.unpack('!H', resp.data[:2])[0] + if tag_type == TagSpecificType.EM410X_ELECTRA: + fmt = '!H13s' + else: + fmt = '!H5s' + resp.parsed = struct.unpack(fmt, resp.data[:struct.calcsize(fmt)]) # tag type + uid return resp @expect_response(Status.LF_TAG_OK) @@ -445,10 +450,13 @@ class ChameleonCMD: :param id_bytes: ID card number :return: """ - if len(id_bytes) != 5: - raise ValueError("The id bytes length must equal 5") - data = struct.pack(f'!5s4s{4*len(old_keys)}s', id_bytes, new_key, b''.join(old_keys)) - return self.device.send_cmd_sync(Command.EM410X_WRITE_TO_T55XX, data) + if len(id_bytes) == 5: + data = struct.pack(f'!5s4s{4*len(old_keys)}s', id_bytes, new_key, b''.join(old_keys)) + return self.device.send_cmd_sync(Command.EM410X_WRITE_TO_T55XX, data) + if len(id_bytes) == 13: + data = struct.pack(f'!13s4s{4*len(old_keys)}s', id_bytes, new_key, b''.join(old_keys)) + return self.device.send_cmd_sync(Command.EM410X_ELECTRA_WRITE_TO_T55XX, data) + raise ValueError("The id bytes length must equal 5 (EM410X) or 13 (Electra)") @expect_response(Status.LF_TAG_OK) def hidprox_scan(self, format: int): @@ -591,6 +599,12 @@ class ChameleonCMD: data = struct.pack('!BBB', SlotNumber.to_fw(slot_index), sense_type, enabled) return self.device.send_cmd_sync(Command.SET_SLOT_ENABLE, data) + def _get_active_lf_tag_type(self) -> TagSpecificType: + slotinfo = self.get_slot_info() + active_slot = SlotNumber.from_fw(self.get_active_slot()) + lf_tag_value = slotinfo[active_slot - 1]['lf'] + return TagSpecificType(lf_tag_value) + @expect_response(Status.SUCCESS) def em410x_set_emu_id(self, id: bytes): """ @@ -599,9 +613,18 @@ class ChameleonCMD: :param id_bytes: byte of the card number :return: """ - if len(id) != 5: - raise ValueError("The id bytes length must equal 5") - data = struct.pack('5s', id) + lf_tag_type = self._get_active_lf_tag_type() + if lf_tag_type == TagSpecificType.EM410X_ELECTRA: + expected_len = 13 + elif lf_tag_type == TagSpecificType.EM410X: + expected_len = 5 + else: + raise ValueError(f"Active LF slot type {lf_tag_type} is not EM410X") + + if len(id) != expected_len: + raise ValueError(f"The id bytes length must equal {expected_len}") + + data = struct.pack(f'!{expected_len}s', id) return self.device.send_cmd_sync(Command.EM410X_SET_EMU_ID, data) @expect_response(Status.SUCCESS) @@ -610,7 +633,34 @@ class ChameleonCMD: Get the emulated EM410x card id """ resp = self.device.send_cmd_sync(Command.EM410X_GET_EMU_ID) - resp.parsed = resp.data + if resp.status == Status.SUCCESS: + data = resp.data + id_bytes = data + tag_type = None + + if len(data) >= 2: + try: + candidate = TagSpecificType(int.from_bytes(data[:2], byteorder='big')) + except ValueError: + candidate = None + + if candidate in (TagSpecificType.EM410X, TagSpecificType.EM410X_ELECTRA): + expected_len = 13 if candidate == TagSpecificType.EM410X_ELECTRA else 5 + if len(data) == expected_len + 2: + tag_type = candidate + id_bytes = data[2:2 + expected_len] + + if tag_type is None: + lf_tag_type = self._get_active_lf_tag_type() + if lf_tag_type == TagSpecificType.EM410X_ELECTRA: + expected_len = 13 + elif lf_tag_type == TagSpecificType.EM410X: + expected_len = 5 + else: + expected_len = len(data) + id_bytes = data[:expected_len] + + resp.parsed = id_bytes return resp @expect_response(Status.SUCCESS) diff --git a/software/script/chameleon_enum.py b/software/script/chameleon_enum.py index a66ebca..fefc18e 100644 --- a/software/script/chameleon_enum.py +++ b/software/script/chameleon_enum.py @@ -76,6 +76,7 @@ class Command(enum.IntEnum): EM410X_SCAN = 3000 EM410X_WRITE_TO_T55XX = 3001 + EM410X_ELECTRA_WRITE_TO_T55XX = 3006 HIDPROX_SCAN = 3002 HIDPROX_WRITE_TO_T55XX = 3003 VIKING_SCAN = 3004 @@ -258,6 +259,7 @@ class TagSpecificType(enum.IntEnum): EM410X_16 = 101 EM410X_32 = 102 EM410X_64 = 103 + EM410X_ELECTRA = 104 # FDX-B # securakey # gallagher @@ -349,6 +351,8 @@ class TagSpecificType(enum.IntEnum): return "EM410X/32" elif self == TagSpecificType.EM410X_64: return "EM410X/64" + elif self == TagSpecificType.EM410X_ELECTRA: + return "EM410X Electra" elif self == TagSpecificType.HIDProx: return "HIDProx" elif self == TagSpecificType.Viking: @@ -625,4 +629,3 @@ class HIDFormat(enum.IntEnum): if self in descriptions: return descriptions[self] return "Invalid" -