@@ -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-
292288static 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