mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
fix hf ntag424 changefs SDM offset serialization bug
The changefs command was using memcpy on a fixed struct layout to serialize SDM settings, which caused SDM offset fields to be written incorrectly. The struct's sdm_data[8][3] array was copied contiguously, but the NTAG 424 DNA specification (AN12196) requires certain offset fields to be conditionally omitted based on the SDM configuration. This resulted in: - Plain mode: MAC offsets not written correctly - PICC mode: MAC offsets not written correctly - Both modes producing cmac=0000000000000000 The fix replaces the memcpy-based approach with dynamic payload building that conditionally includes only the required SDM offset fields in the correct order: - UIDOffset (only if UID option + plain mode sdmMetaRead=0xE) - SDMReadCtrOffset (only if counter option + plain mode) - PICCDataOffset (only if encrypted PICC mode sdmMetaRead<=0x04) - SDMMACInputOffset (only if file read enabled) - SDMEncOffset + SDMEncLength (only if encrypted file data enabled) - SDMMACOffset (only if file read enabled) - SDMReadCtrLimit (only if limit option set)
This commit is contained in:
+89
-16
@@ -285,10 +285,6 @@ static int ntag424_calc_file_settings_size(const ntag424_file_settings_t *settin
|
||||
return size;
|
||||
}
|
||||
|
||||
static int ntag424_calc_file_write_settings_size(const ntag424_file_settings_t *settings) {
|
||||
return ntag424_calc_file_settings_size(settings) - 4;
|
||||
}
|
||||
|
||||
static void ntag424_calc_send_iv(ntag424_session_keys_t *session_keys, uint8_t *out_ivc) {
|
||||
uint8_t iv_clear[] = { 0xa5, 0x5a,
|
||||
session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3],
|
||||
@@ -480,21 +476,99 @@ static int ntag424_get_file_settings(uint8_t fileno, ntag424_file_settings_t *se
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// Build the file settings command buffer dynamically.
|
||||
// This correctly handles conditional SDM offset fields per AN12196 specification.
|
||||
// Returns the number of bytes written to cmd_buffer (excluding fileno byte).
|
||||
static size_t ntag424_build_file_settings_cmd(const ntag424_file_settings_t *settings, uint8_t *cmd_buffer) {
|
||||
size_t offset = 0;
|
||||
int sdm_data_idx = 0;
|
||||
|
||||
// File options and access rights (always present)
|
||||
cmd_buffer[offset++] = settings->options;
|
||||
cmd_buffer[offset++] = settings->access[0];
|
||||
cmd_buffer[offset++] = settings->access[1];
|
||||
|
||||
// SDM settings are only present if SDM is enabled
|
||||
if (settings->options & FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING) {
|
||||
// SDM options and access rights
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_options;
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_access[0];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_access[1];
|
||||
|
||||
uint8_t sdm_options = settings->optional_sdm_settings.sdm_options;
|
||||
uint8_t sdm_meta_read = ntag424_file_settings_get_sdm_meta_read(settings);
|
||||
uint8_t sdm_file_read = ntag424_file_settings_get_sdm_file_read(settings);
|
||||
|
||||
// UIDOffset: only in plain mode (sdmMetaRead == 0xE) with UID option set
|
||||
if ((sdm_options & FILE_SETTINGS_SDM_OPTIONS_UID) && sdm_meta_read == 0x0e) {
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
|
||||
sdm_data_idx++;
|
||||
}
|
||||
|
||||
// SDMReadCtrOffset: only in plain mode (sdmMetaRead == 0xE) with counter option set
|
||||
if ((sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER) && sdm_meta_read == 0x0e) {
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
|
||||
sdm_data_idx++;
|
||||
}
|
||||
|
||||
// PICCDataOffset: only in encrypted mode (sdmMetaRead <= 0x04)
|
||||
if (sdm_meta_read <= 0x04) {
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
|
||||
sdm_data_idx++;
|
||||
}
|
||||
|
||||
// SDMMACInputOffset: when file read is enabled (sdmFileRead != 0x0F)
|
||||
if (sdm_file_read != 0x0f) {
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
|
||||
sdm_data_idx++;
|
||||
|
||||
// SDMEncOffset and SDMEncLength: only when encrypted file data is enabled
|
||||
if (sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA) {
|
||||
// SDMEncOffset
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
|
||||
sdm_data_idx++;
|
||||
// SDMEncLength
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
|
||||
sdm_data_idx++;
|
||||
}
|
||||
|
||||
// SDMMACOffset: always included when file read is enabled
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
|
||||
sdm_data_idx++;
|
||||
}
|
||||
|
||||
// SDMReadCtrLimit: when counter limit option is set
|
||||
if (sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER_LIMIT) {
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
|
||||
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
|
||||
sdm_data_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int ntag424_write_file_settings(uint8_t fileno, const ntag424_file_settings_t *settings, ntag424_session_keys_t *session_keys) {
|
||||
|
||||
// ------- Convert file settings to the format for writing
|
||||
file_settings_write_t write_settings = {
|
||||
.options = settings->options,
|
||||
.access[0] = settings->access[0],
|
||||
.access[1] = settings->access[1],
|
||||
.optional_sdm_settings = settings->optional_sdm_settings,
|
||||
};
|
||||
|
||||
size_t settings_size = ntag424_calc_file_write_settings_size(settings);
|
||||
|
||||
// Build the command buffer dynamically based on which SDM fields are required
|
||||
uint8_t cmd_buffer[256];
|
||||
cmd_buffer[0] = fileno;
|
||||
memcpy(&cmd_buffer[1], &write_settings, settings_size);
|
||||
size_t settings_size = ntag424_build_file_settings_cmd(settings, &cmd_buffer[1]);
|
||||
|
||||
APDU_t apdu = {
|
||||
.cla = 0x90,
|
||||
@@ -503,7 +577,6 @@ static int ntag424_write_file_settings(uint8_t fileno, const ntag424_file_settin
|
||||
.data = cmd_buffer
|
||||
};
|
||||
|
||||
|
||||
// ------- Actually send the APDU
|
||||
int response_length = 8 + 2;
|
||||
uint8_t response[response_length];
|
||||
|
||||
Reference in New Issue
Block a user