Add Electra intercom tag support with slot auto switch

This commit is contained in:
Alexandru Mazalu
2026-01-24 14:45:15 +02:00
parent f2bea7d4ea
commit c1c2b66882
15 changed files with 354 additions and 56 deletions
+48 -11
View File
@@ -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 },
+15 -3
View File
@@ -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);
}
}
}
+1
View File
@@ -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)
@@ -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
@@ -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
@@ -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;
}
}
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;
}
@@ -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);
uint8_t em410x_t55xx_writer(uint8_t* uid, uint32_t* blks);
uint8_t em410x_electra_t55xx_writer(uint8_t* uid, uint32_t* blks);
@@ -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
#endif
@@ -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, \
@@ -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
@@ -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; }
void set_scan_tag_timeout(uint32_t ms) { g_timeout_readem_ms = ms; }
@@ -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);
+5 -3
View File
@@ -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')
+59 -9
View File
@@ -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)
+4 -1
View File
@@ -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"