Skip to content

Commit 001a80d

Browse files
authored
Merge pull request #3083 from RashitKhamidullin/fix/ntag424-sdm-offset-serialization
fix `hf ntag424 changefs` SDM offset serialization bug
2 parents 3cc841b + 129d9e9 commit 001a80d

File tree

1 file changed

+88
-15
lines changed

1 file changed

+88
-15
lines changed

client/src/cmdhfntag424.c

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -285,10 +285,6 @@ static int ntag424_calc_file_settings_size(const ntag424_file_settings_t *settin
285285
return size;
286286
}
287287

288-
static int ntag424_calc_file_write_settings_size(const ntag424_file_settings_t *settings) {
289-
return ntag424_calc_file_settings_size(settings) - 4;
290-
}
291-
292288
static void ntag424_calc_send_iv(ntag424_session_keys_t *session_keys, uint8_t *out_ivc) {
293289
uint8_t iv_clear[] = { 0xa5, 0x5a,
294290
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
480476
return PM3_SUCCESS;
481477
}
482478

483-
static int ntag424_write_file_settings(uint8_t fileno, const ntag424_file_settings_t *settings, ntag424_session_keys_t *session_keys) {
479+
// Build the file settings command buffer dynamically.
480+
// This correctly handles conditional SDM offset fields per AN12196 specification.
481+
// Returns the number of bytes written to cmd_buffer (excluding fileno byte).
482+
static size_t ntag424_build_file_settings_cmd(const ntag424_file_settings_t *settings, uint8_t *cmd_buffer) {
483+
size_t offset = 0;
484+
int sdm_data_idx = 0;
484485

485-
// ------- Convert file settings to the format for writing
486-
file_settings_write_t write_settings = {
487-
.options = settings->options,
488-
.access[0] = settings->access[0],
489-
.access[1] = settings->access[1],
490-
.optional_sdm_settings = settings->optional_sdm_settings,
491-
};
486+
// File options and access rights (always present)
487+
cmd_buffer[offset++] = settings->options;
488+
cmd_buffer[offset++] = settings->access[0];
489+
cmd_buffer[offset++] = settings->access[1];
490+
491+
// SDM settings are only present if SDM is enabled
492+
if (settings->options & FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING) {
493+
// SDM options and access rights
494+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_options;
495+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_access[0];
496+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_access[1];
497+
498+
uint8_t sdm_options = settings->optional_sdm_settings.sdm_options;
499+
uint8_t sdm_meta_read = ntag424_file_settings_get_sdm_meta_read(settings);
500+
uint8_t sdm_file_read = ntag424_file_settings_get_sdm_file_read(settings);
501+
502+
// UIDOffset: only in plain mode (sdmMetaRead == 0xE) with UID option set
503+
if ((sdm_options & FILE_SETTINGS_SDM_OPTIONS_UID) && sdm_meta_read == 0x0e) {
504+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
505+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
506+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
507+
sdm_data_idx++;
508+
}
509+
510+
// SDMReadCtrOffset: only in plain mode (sdmMetaRead == 0xE) with counter option set
511+
if ((sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER) && sdm_meta_read == 0x0e) {
512+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
513+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
514+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
515+
sdm_data_idx++;
516+
}
517+
518+
// PICCDataOffset: only in encrypted mode (sdmMetaRead <= 0x04)
519+
if (sdm_meta_read <= 0x04) {
520+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
521+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
522+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
523+
sdm_data_idx++;
524+
}
492525

493-
size_t settings_size = ntag424_calc_file_write_settings_size(settings);
526+
// SDMMACInputOffset: when file read is enabled (sdmFileRead != 0x0F)
527+
if (sdm_file_read != 0x0f) {
528+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
529+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
530+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
531+
sdm_data_idx++;
532+
533+
// SDMEncOffset and SDMEncLength: only when encrypted file data is enabled
534+
if (sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA) {
535+
// SDMEncOffset
536+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
537+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
538+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
539+
sdm_data_idx++;
540+
// SDMEncLength
541+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
542+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
543+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
544+
sdm_data_idx++;
545+
}
546+
547+
// SDMMACOffset: always included when file read is enabled
548+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
549+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
550+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
551+
sdm_data_idx++;
552+
}
553+
554+
// SDMReadCtrLimit: when counter limit option is set
555+
if (sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER_LIMIT) {
556+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][0];
557+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][1];
558+
cmd_buffer[offset++] = settings->optional_sdm_settings.sdm_data[sdm_data_idx][2];
559+
sdm_data_idx++;
560+
}
561+
}
494562

563+
return offset;
564+
}
565+
566+
static int ntag424_write_file_settings(uint8_t fileno, const ntag424_file_settings_t *settings, ntag424_session_keys_t *session_keys) {
567+
568+
// Build the command buffer dynamically based on which SDM fields are required
495569
uint8_t cmd_buffer[256];
496570
cmd_buffer[0] = fileno;
497-
memcpy(&cmd_buffer[1], &write_settings, settings_size);
571+
size_t settings_size = ntag424_build_file_settings_cmd(settings, &cmd_buffer[1]);
498572

499573
APDU_t apdu = {
500574
.cla = 0x90,
@@ -503,7 +577,6 @@ static int ntag424_write_file_settings(uint8_t fileno, const ntag424_file_settin
503577
.data = cmd_buffer
504578
};
505579

506-
507580
// ------- Actually send the APDU
508581
int response_length = 8 + 2;
509582
uint8_t response[response_length];

0 commit comments

Comments
 (0)