mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
Add PAC/Stanley T55XX write support
Add pac_t55xx_writer() for encoding PAC card data into T55XX blocks, along with the T5577_PAC_CONFIG (NRZ/Direct, RF/32, password-protected, 4 data blocks). Wire DATA_CMD_PAC_WRITE_TO_T55XX (3011) through the command processor, dispatch table, and Python client.
This commit is contained in:
@@ -826,6 +826,20 @@ static data_frame_tx_t *cmd_processor_viking_write_to_t55xx(uint16_t cmd, uint16
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
|
||||
static data_frame_tx_t *cmd_processor_pac_write_to_t55xx(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
typedef struct {
|
||||
uint8_t id[LF_PAC_TAG_ID_SIZE];
|
||||
uint8_t new_key[4];
|
||||
uint8_t old_keys[4];
|
||||
} 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_pac_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);
|
||||
}
|
||||
|
||||
#define GENERIC_READ_LEN 800
|
||||
#define GENERIC_READ_TIMEOUT_MS 500
|
||||
static data_frame_tx_t *cmd_processor_generic_read(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
@@ -1828,6 +1842,7 @@ static cmd_data_map_t m_data_cmd_map[] = {
|
||||
{ DATA_CMD_IOPROX_SCAN, before_reader_run, cmd_processor_ioprox_scan, NULL },
|
||||
{ DATA_CMD_IOPROX_WRITE_TO_T55XX, before_reader_run, cmd_processor_ioprox_write_to_t55xx, NULL },
|
||||
{ DATA_CMD_PAC_SCAN, before_reader_run, cmd_processor_pac_scan, NULL },
|
||||
{ DATA_CMD_PAC_WRITE_TO_T55XX, before_reader_run, cmd_processor_pac_write_to_t55xx, NULL },
|
||||
{ DATA_CMD_ADC_GENERIC_READ, before_reader_run, cmd_processor_generic_read, NULL },
|
||||
|
||||
{ DATA_CMD_HF14A_SET_FIELD_ON, before_reader_run, cmd_processor_hf14a_set_field_on, NULL },
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
#define DATA_CMD_HIDPROX_SCAN (3002)
|
||||
#define DATA_CMD_HIDPROX_WRITE_TO_T55XX (3003)
|
||||
#define DATA_CMD_PAC_SCAN (3010)
|
||||
#define DATA_CMD_PAC_WRITE_TO_T55XX (3011)
|
||||
#define DATA_CMD_VIKING_SCAN (3004)
|
||||
#define DATA_CMD_VIKING_WRITE_TO_T55XX (3005)
|
||||
#define DATA_CMD_ADC_GENERIC_READ (3009)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "nordic_common.h"
|
||||
#include "nrf_pwm.h"
|
||||
#include "protocols.h"
|
||||
#include "t55xx.h"
|
||||
#include "tag_base_type.h"
|
||||
|
||||
#define PAC_DATA_SIZE 8 // 8-byte ASCII card ID
|
||||
@@ -333,12 +334,29 @@ static const nrf_pwm_sequence_t *pac_modulator(pac_codec *d, uint8_t *buf) {
|
||||
// Per nRF52840 PS: compare = 0 → pin held LOW; compare >= counter_top → pin held HIGH.
|
||||
// No polarity bits needed — avoids edge-case ambiguity with compare = 0.
|
||||
for (int i = 0; i < PAC_FRAME_BITS; i++) {
|
||||
m_pac_pwm_seq_vals[i].channel_0 = bits[i] ? 32 : 0;
|
||||
m_pac_pwm_seq_vals[i].counter_top = 32;
|
||||
m_pac_pwm_seq_vals[i].channel_0 = bits[i] ? PAC_RF_PER_BIT : 0;
|
||||
m_pac_pwm_seq_vals[i].counter_top = PAC_RF_PER_BIT;
|
||||
}
|
||||
return &m_pac_pwm_seq;
|
||||
}
|
||||
|
||||
#define PAC_T55XX_BLOCK_COUNT 5 // 1 config + 4 data blocks
|
||||
|
||||
uint8_t pac_t55xx_writer(uint8_t *data, uint32_t *blks) {
|
||||
uint8_t bits[PAC_FRAME_BITS];
|
||||
pac_build_bitstream(data, bits);
|
||||
|
||||
blks[0] = T5577_PAC_CONFIG;
|
||||
for (int b = 0; b < 4; b++) {
|
||||
uint32_t word = 0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
word = (word << 1) | bits[b * 32 + i];
|
||||
}
|
||||
blks[b + 1] = word;
|
||||
}
|
||||
return PAC_T55XX_BLOCK_COUNT;
|
||||
}
|
||||
|
||||
const protocol pac = {
|
||||
.tag_type = TAG_TYPE_PAC,
|
||||
.data_size = PAC_DATA_SIZE,
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
#include "protocols.h"
|
||||
|
||||
extern const protocol pac;
|
||||
uint8_t pac_t55xx_writer(uint8_t *data, uint32_t *blks);
|
||||
|
||||
@@ -75,6 +75,12 @@ extern "C" {
|
||||
T5577_PWD | \
|
||||
(2 << T5577_MAXBLOCK_SHIFT))
|
||||
|
||||
#define T5577_PAC_CONFIG ( \
|
||||
T5577_MODULATION_DIRECT | \
|
||||
T5577_BITRATE_RF_32 | \
|
||||
T5577_PWD | \
|
||||
(4 << T5577_MAXBLOCK_SHIFT))
|
||||
|
||||
void t55xx_write_data(uint32_t passwd, uint32_t *blks, uint8_t blk_count);
|
||||
void t55xx_reset_passwd(uint32_t old_passwd, uint32_t new_passwd);
|
||||
|
||||
|
||||
@@ -198,6 +198,13 @@ uint8_t write_viking_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_pac_to_t55xx(uint8_t *data, uint8_t *new_passwd, uint8_t *old_passwds, uint8_t old_passwd_count) {
|
||||
uint32_t blks[7] = {0x00};
|
||||
uint8_t blk_count = pac_t55xx_writer(data, blks);
|
||||
if (blk_count == 0) return STATUS_PAR_ERR;
|
||||
return write_t55xx(blks, blk_count, new_passwd, old_passwds, old_passwd_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the LF card scanning timeout value (in milliseconds).
|
||||
*/
|
||||
|
||||
@@ -20,3 +20,4 @@ uint8_t write_em410x_electra_to_t55xx(uint8_t *uid, uint8_t *newkey, uint8_t *ol
|
||||
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_ioprox_to_t55xx(uint8_t *raw_data, 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);
|
||||
uint8_t write_pac_to_t55xx(uint8_t *data, uint8_t *new_passwd, uint8_t *old_passwds, uint8_t old_passwd_count);
|
||||
|
||||
@@ -592,6 +592,23 @@ class ChameleonCMD:
|
||||
resp.parsed = resp.data[:8]
|
||||
return resp
|
||||
|
||||
@expect_response(Status.LF_TAG_OK)
|
||||
def pac_write_to_t55xx(self, id_bytes: bytes, new_key: bytes = b'\x00\x00\x00\x00', old_keys: list = None):
|
||||
"""
|
||||
Write PAC/Stanley card data to a T55XX tag.
|
||||
|
||||
:param id_bytes: 8-byte ASCII card ID
|
||||
:param new_key: new password (4 bytes)
|
||||
:param old_keys: list of old passwords to try (each 4 bytes)
|
||||
:return:
|
||||
"""
|
||||
if old_keys is None:
|
||||
old_keys = [b'\x00\x00\x00\x00']
|
||||
if len(id_bytes) != 8:
|
||||
raise ValueError("The id bytes length must equal 8")
|
||||
data = struct.pack(f'!8s4s{4*len(old_keys)}s', id_bytes, new_key, b''.join(old_keys))
|
||||
return self.device.send_cmd_sync(Command.PAC_WRITE_TO_T55XX, data)
|
||||
|
||||
@expect_response(Status.LF_TAG_OK)
|
||||
def adc_generic_read(self):
|
||||
"""
|
||||
|
||||
@@ -84,6 +84,7 @@ class Command(enum.IntEnum):
|
||||
VIKING_SCAN = 3004
|
||||
VIKING_WRITE_TO_T55XX = 3005
|
||||
PAC_SCAN = 3010
|
||||
PAC_WRITE_TO_T55XX = 3011
|
||||
ADC_GENERIC_READ = 3009
|
||||
IOPROX_SCAN = 3010
|
||||
IOPROX_WRITE_TO_T55XX = 3011
|
||||
|
||||
Reference in New Issue
Block a user