SLIX2 emulation support / practical use for Dymo printers (#2783)
* improve digital_signal for longer packets, also clean up code * added SLIX2 specific features like signature and unknown keys (for issue #2781), added WRITE_PASSWORD handling * fix NfcV AFI selection * when NFCV_CMD_READ_MULTI_BLOCK reads beyond memory end, return the maximum possible block's content * added SLIX2 reading * fix NXP SYSTEMINFO response check size * capture the first received password if none was set before * clear stored data before reading SLIX details renamed slix2_dump functions to slix2_read * display card block size values as decimal Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									d1c27b6457
								
							
						
					
					
						commit
						c10c45616d
					
				| @ -7,6 +7,83 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void nfc_scene_slix_build_string( | ||||||
|  |     FuriString* temp_str, | ||||||
|  |     NfcVData* nfcv_data, | ||||||
|  |     SlixTypeFeatures features, | ||||||
|  |     const char* type) { | ||||||
|  |     furi_string_cat_printf(temp_str, "Type: %s\n", type); | ||||||
|  |     furi_string_cat_printf(temp_str, "Keys:\n"); | ||||||
|  |     if(features & SlixFeatureRead) { | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, | ||||||
|  |             " Read     %08llX%s\n", | ||||||
|  |             nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4), | ||||||
|  |             (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyRead) ? "" : " (unset)"); | ||||||
|  |     } | ||||||
|  |     if(features & SlixFeatureWrite) { | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, | ||||||
|  |             " Write    %08llX%s\n", | ||||||
|  |             nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4), | ||||||
|  |             (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyWrite) ? "" : " (unset)"); | ||||||
|  |     } | ||||||
|  |     if(features & SlixFeaturePrivacy) { | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, | ||||||
|  |             " Privacy  %08llX%s\n", | ||||||
|  |             nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4), | ||||||
|  |             (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyPrivacy) ? "" : " (unset)"); | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, | ||||||
|  |             " Privacy mode %s\n", | ||||||
|  |             (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ENABLED" : "DISABLED"); | ||||||
|  |     } | ||||||
|  |     if(features & SlixFeatureDestroy) { | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, | ||||||
|  |             " Destroy  %08llX%s\n", | ||||||
|  |             nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4), | ||||||
|  |             (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyDestroy) ? "" : " (unset)"); | ||||||
|  |     } | ||||||
|  |     if(features & SlixFeatureEas) { | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, | ||||||
|  |             " EAS      %08llX%s\n", | ||||||
|  |             nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4), | ||||||
|  |             (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyEas) ? "" : " (unset)"); | ||||||
|  |     } | ||||||
|  |     if(features & SlixFeatureSignature) { | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, | ||||||
|  |             "Signature %08llX...\n", | ||||||
|  |             nfc_util_bytes2num(nfcv_data->sub_data.slix.signature, 4)); | ||||||
|  |     } | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         temp_str, | ||||||
|  |         "DSFID: %02X %s\n", | ||||||
|  |         nfcv_data->dsfid, | ||||||
|  |         (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         temp_str, | ||||||
|  |         "AFI: %02X %s\n", | ||||||
|  |         nfcv_data->afi, | ||||||
|  |         (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         temp_str, | ||||||
|  |         "EAS: %s\n", | ||||||
|  |         (nfcv_data->security_status[0] & NfcVLockBitEas) ? "locked" : "not locked"); | ||||||
|  | 
 | ||||||
|  |     if(features & SlixFeatureProtection) { | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, | ||||||
|  |             "PPL: %s\n", | ||||||
|  |             (nfcv_data->security_status[0] & NfcVLockBitPpl) ? "locked" : "not locked"); | ||||||
|  |         furi_string_cat_printf(temp_str, "Prot.ptr %02X\n", nfcv_data->sub_data.slix.pp_pointer); | ||||||
|  |         furi_string_cat_printf(temp_str, "Prot.con %02X\n", nfcv_data->sub_data.slix.pp_condition); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void nfc_scene_nfc_data_info_on_enter(void* context) { | void nfc_scene_nfc_data_info_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     Widget* widget = nfc->widget; |     Widget* widget = nfc->widget; | ||||||
| @ -76,95 +153,25 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { | |||||||
|         } |         } | ||||||
|         furi_string_cat_printf(temp_str, "\n"); |         furi_string_cat_printf(temp_str, "\n"); | ||||||
| 
 | 
 | ||||||
|         furi_string_cat_printf( |         furi_string_cat_printf(temp_str, "IC Ref: %d\n", nfcv_data->ic_ref); | ||||||
|             temp_str, |         furi_string_cat_printf(temp_str, "Blocks: %d\n", nfcv_data->block_num); | ||||||
|             "DSFID: %02X %s\n", |         furi_string_cat_printf(temp_str, "Blocksize: %d\n", nfcv_data->block_size); | ||||||
|             nfcv_data->dsfid, |  | ||||||
|             (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); |  | ||||||
|         furi_string_cat_printf( |  | ||||||
|             temp_str, |  | ||||||
|             "AFI: %02X %s\n", |  | ||||||
|             nfcv_data->afi, |  | ||||||
|             (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); |  | ||||||
|         furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); |  | ||||||
|         furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); |  | ||||||
|         furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); |  | ||||||
| 
 | 
 | ||||||
|         switch(dev_data->nfcv_data.sub_type) { |         switch(dev_data->nfcv_data.sub_type) { | ||||||
|         case NfcVTypePlain: |         case NfcVTypePlain: | ||||||
|             furi_string_cat_printf(temp_str, "Type: Plain\n"); |             furi_string_cat_printf(temp_str, "Type: Plain\n"); | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlix: |         case NfcVTypeSlix: | ||||||
|             furi_string_cat_printf(temp_str, "Type: SLIX\n"); |             nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix, "SLIX"); | ||||||
|             furi_string_cat_printf(temp_str, "Keys:\n"); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " EAS      %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); |  | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlixS: |         case NfcVTypeSlixS: | ||||||
|             furi_string_cat_printf(temp_str, "Type: SLIX-S\n"); |             nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixS, "SLIX-S"); | ||||||
|             furi_string_cat_printf(temp_str, "Keys:\n"); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " Read     %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " Write    %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " Privacy  %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " Destroy  %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " EAS      %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); |  | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlixL: |         case NfcVTypeSlixL: | ||||||
|             furi_string_cat_printf(temp_str, "Type: SLIX-L\n"); |             nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixL, "SLIX-L"); | ||||||
|             furi_string_cat_printf(temp_str, "Keys:\n"); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " Privacy  %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " Destroy  %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " EAS      %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); |  | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlix2: |         case NfcVTypeSlix2: | ||||||
|             furi_string_cat_printf(temp_str, "Type: SLIX2\n"); |             nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix2, "SLIX2"); | ||||||
|             furi_string_cat_printf(temp_str, "Keys:\n"); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " Read     %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " Write    %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " Privacy  %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " Destroy  %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); |  | ||||||
|             furi_string_cat_printf( |  | ||||||
|                 temp_str, |  | ||||||
|                 " EAS      %08llX\n", |  | ||||||
|                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); |  | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); |             furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); | ||||||
|  | |||||||
| @ -16,7 +16,6 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) { | |||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     NfcDeviceData* dev_data = &nfc->dev->dev_data; |     NfcDeviceData* dev_data = &nfc->dev->dev_data; | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; |     FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; | ||||||
|     NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; |  | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|     Widget* widget = nfc->widget; |     Widget* widget = nfc->widget; | ||||||
|     widget_add_button_element( |     widget_add_button_element( | ||||||
| @ -46,13 +45,12 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) { | |||||||
|         furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); |         furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     furi_string_cat_printf(temp_str, "UID:"); |     furi_string_cat_printf(temp_str, "UID:\n"); | ||||||
|     for(size_t i = 0; i < nfc_data->uid_len; i++) { |     for(size_t i = 0; i < nfc_data->uid_len; i++) { | ||||||
|         furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); |         furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); | ||||||
|     } |     } | ||||||
|     furi_string_cat_printf(temp_str, "\n"); |     furi_string_cat_printf(temp_str, "\n"); | ||||||
|     furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); |     furi_string_cat_printf(temp_str, "(see More->Info for details)\n"); | ||||||
|     furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); |  | ||||||
| 
 | 
 | ||||||
|     widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); |     widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); | ||||||
|     furi_string_free(temp_str); |     furi_string_free(temp_str); | ||||||
|  | |||||||
| @ -51,8 +51,16 @@ struct DigitalSignalInternals { | |||||||
| #define T_TIM 1562 /* 15.625 ns *100 */ | #define T_TIM 1562 /* 15.625 ns *100 */ | ||||||
| #define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ | #define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ | ||||||
| 
 | 
 | ||||||
|  | /* end marker in DMA ringbuffer, will get written into timer register at the end */ | ||||||
|  | #define SEQ_TIMER_MAX 0xFFFFFFFF | ||||||
|  | 
 | ||||||
|  | /* time to wait in loops before returning */ | ||||||
|  | #define SEQ_LOCK_WAIT_MS 10UL | ||||||
|  | #define SEQ_LOCK_WAIT_TICKS (SEQ_LOCK_WAIT_MS * 1000 * 64) | ||||||
|  | 
 | ||||||
| /* maximum entry count of the sequence dma ring buffer */ | /* maximum entry count of the sequence dma ring buffer */ | ||||||
| #define SEQUENCE_DMA_RINGBUFFER_SIZE 32 | #define RINGBUFFER_SIZE 128 | ||||||
|  | 
 | ||||||
| /* maximum number of DigitalSignals in a sequence */ | /* maximum number of DigitalSignals in a sequence */ | ||||||
| #define SEQUENCE_SIGNALS_SIZE 32 | #define SEQUENCE_SIGNALS_SIZE 32 | ||||||
| /*
 | /*
 | ||||||
| @ -252,7 +260,7 @@ static void digital_signal_setup_timer() { | |||||||
|     LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); |     LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); | ||||||
|     LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); |     LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); | ||||||
|     LL_TIM_SetPrescaler(TIM2, 0); |     LL_TIM_SetPrescaler(TIM2, 0); | ||||||
|     LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); |     LL_TIM_SetAutoReload(TIM2, SEQ_TIMER_MAX); | ||||||
|     LL_TIM_SetCounter(TIM2, 0); |     LL_TIM_SetCounter(TIM2, 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -335,7 +343,7 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { | |||||||
|     sequence->bake = false; |     sequence->bake = false; | ||||||
| 
 | 
 | ||||||
|     sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer)); |     sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer)); | ||||||
|     sequence->dma_buffer->size = SEQUENCE_DMA_RINGBUFFER_SIZE; |     sequence->dma_buffer->size = RINGBUFFER_SIZE; | ||||||
|     sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t)); |     sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t)); | ||||||
| 
 | 
 | ||||||
|     sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; |     sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; | ||||||
| @ -454,39 +462,23 @@ static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void digital_sequence_update_pos(DigitalSequence* sequence) { |  | ||||||
|     struct ReloadBuffer* dma_buffer = sequence->dma_buffer; |  | ||||||
| 
 |  | ||||||
|     dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const uint32_t wait_ms = 10; |  | ||||||
| static const uint32_t wait_ticks = wait_ms * 1000 * 64; |  | ||||||
| 
 |  | ||||||
| static void digital_sequence_finish(DigitalSequence* sequence) { | static void digital_sequence_finish(DigitalSequence* sequence) { | ||||||
|     struct ReloadBuffer* dma_buffer = sequence->dma_buffer; |     struct ReloadBuffer* dma_buffer = sequence->dma_buffer; | ||||||
| 
 | 
 | ||||||
|     if(dma_buffer->dma_active) { |     if(dma_buffer->dma_active) { | ||||||
|         uint32_t prev_timer = DWT->CYCCNT; |         uint32_t prev_timer = DWT->CYCCNT; | ||||||
|         uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; |  | ||||||
|         do { |         do { | ||||||
|             uint32_t last_pos = dma_buffer->read_pos; |             /* we are finished, when the DMA transferred the SEQ_TIMER_MAX marker */ | ||||||
| 
 |             if(TIM2->ARR == SEQ_TIMER_MAX) { | ||||||
|             digital_sequence_update_pos(sequence); |  | ||||||
| 
 |  | ||||||
|             /* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */ |  | ||||||
|             if(dma_buffer->read_pos == end_pos) { |  | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 |             if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) { | ||||||
|             if(last_pos != dma_buffer->read_pos) { //-V547
 |                 dma_buffer->read_pos = | ||||||
|                 prev_timer = DWT->CYCCNT; |                     RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); | ||||||
|             } |  | ||||||
|             if(DWT->CYCCNT - prev_timer > wait_ticks) { |  | ||||||
|                 FURI_LOG_D( |                 FURI_LOG_D( | ||||||
|                     TAG, |                     TAG, | ||||||
|                     "[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)", |                     "[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)", | ||||||
|                     wait_ms, |                     SEQ_LOCK_WAIT_MS, | ||||||
|                     TIM2->ARR, |                     TIM2->ARR, | ||||||
|                     dma_buffer->read_pos, |                     dma_buffer->read_pos, | ||||||
|                     dma_buffer->write_pos); |                     dma_buffer->write_pos); | ||||||
| @ -504,23 +496,30 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len | |||||||
| 
 | 
 | ||||||
|     if(dma_buffer->dma_active) { |     if(dma_buffer->dma_active) { | ||||||
|         uint32_t prev_timer = DWT->CYCCNT; |         uint32_t prev_timer = DWT->CYCCNT; | ||||||
|         uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; |  | ||||||
|         do { |         do { | ||||||
|             uint32_t last_pos = dma_buffer->read_pos; |             dma_buffer->read_pos = RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); | ||||||
|             digital_sequence_update_pos(sequence); |  | ||||||
| 
 | 
 | ||||||
|             if(dma_buffer->read_pos != end_pos) { |             uint32_t free = | ||||||
|  |                 (RINGBUFFER_SIZE + dma_buffer->read_pos - dma_buffer->write_pos) % RINGBUFFER_SIZE; | ||||||
|  | 
 | ||||||
|  |             if(free > 2) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if(last_pos != dma_buffer->read_pos) { //-V547
 |             if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) { | ||||||
|                 prev_timer = DWT->CYCCNT; |  | ||||||
|             } |  | ||||||
|             if(DWT->CYCCNT - prev_timer > wait_ticks) { |  | ||||||
|                 FURI_LOG_D( |                 FURI_LOG_D( | ||||||
|                     TAG, |                     TAG, | ||||||
|                     "[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)", |                     "[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)", | ||||||
|                     wait_ms, |                     SEQ_LOCK_WAIT_MS, | ||||||
|  |                     TIM2->ARR, | ||||||
|  |                     dma_buffer->read_pos, | ||||||
|  |                     dma_buffer->write_pos); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             if(TIM2->ARR == SEQ_TIMER_MAX) { | ||||||
|  |                 FURI_LOG_D( | ||||||
|  |                     TAG, | ||||||
|  |                     "[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)", | ||||||
|                     TIM2->ARR, |                     TIM2->ARR, | ||||||
|                     dma_buffer->read_pos, |                     dma_buffer->read_pos, | ||||||
|                     dma_buffer->write_pos); |                     dma_buffer->write_pos); | ||||||
| @ -530,8 +529,9 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     dma_buffer->buffer[dma_buffer->write_pos] = length; |     dma_buffer->buffer[dma_buffer->write_pos] = length; | ||||||
|     dma_buffer->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; |     dma_buffer->write_pos++; | ||||||
|     dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF; |     dma_buffer->write_pos %= RINGBUFFER_SIZE; | ||||||
|  |     dma_buffer->buffer[dma_buffer->write_pos] = SEQ_TIMER_MAX; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool digital_sequence_send(DigitalSequence* sequence) { | bool digital_sequence_send(DigitalSequence* sequence) { | ||||||
| @ -553,90 +553,97 @@ bool digital_sequence_send(DigitalSequence* sequence) { | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     int32_t remainder = 0; |     if(!sequence->sequence_used) { | ||||||
|     bool traded_first = false; |         return false; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     FURI_CRITICAL_ENTER(); |     int32_t remainder = 0; | ||||||
|  |     uint32_t trade_for_next = 0; | ||||||
|  |     uint32_t seq_pos_next = 1; | ||||||
| 
 | 
 | ||||||
|     dma_buffer->dma_active = false; |     dma_buffer->dma_active = false; | ||||||
|     dma_buffer->buffer[0] = 0xFFFFFFFF; |     dma_buffer->buffer[0] = SEQ_TIMER_MAX; | ||||||
|     dma_buffer->read_pos = 0; |     dma_buffer->read_pos = 0; | ||||||
|     dma_buffer->write_pos = 0; |     dma_buffer->write_pos = 0; | ||||||
| 
 | 
 | ||||||
|     for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) { |     /* already prepare the current signal pointer */ | ||||||
|         uint8_t signal_index = sequence->sequence[seq_pos]; |     DigitalSignal* sig = sequence->signals[sequence->sequence[0]]; | ||||||
|         DigitalSignal* sig = sequence->signals[signal_index]; |     DigitalSignal* sig_next = NULL; | ||||||
|         bool last_signal = ((seq_pos + 1) == sequence->sequence_used); |     /* re-use the GPIO buffer from the first signal */ | ||||||
|  |     sequence->gpio_buff = sig->internals->gpio_buff; | ||||||
| 
 | 
 | ||||||
|         /* all signals are prepared and we can re-use the GPIO buffer from the fist signal */ |     FURI_CRITICAL_ENTER(); | ||||||
|         if(seq_pos == 0) { | 
 | ||||||
|             sequence->gpio_buff = sig->internals->gpio_buff; |     while(sig) { | ||||||
|  |         bool last_signal = (seq_pos_next >= sequence->sequence_used); | ||||||
|  | 
 | ||||||
|  |         if(!last_signal) { | ||||||
|  |             sig_next = sequence->signals[sequence->sequence[seq_pos_next++]]; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) { |         for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) { | ||||||
|             if(traded_first) { |             bool last_pulse = ((pulse_pos + 1) >= sig->internals->reload_reg_entries); | ||||||
|                 traded_first = false; |             uint32_t pulse_length = sig->reload_reg_buff[pulse_pos] + trade_for_next; | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|             uint32_t pulse_length = 0; |  | ||||||
|             bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries); |  | ||||||
| 
 | 
 | ||||||
|             pulse_length = sig->reload_reg_buff[pulse_pos]; |             trade_for_next = 0; | ||||||
| 
 | 
 | ||||||
|             /* when we are too late more than half a tick, make the first edge temporarily longer */ |             /* when we are too late more than half a tick, make the first edge temporarily longer */ | ||||||
|             if(remainder >= T_TIM_DIV2) { |             if(remainder >= T_TIM_DIV2) { | ||||||
|                 remainder -= T_TIM; |                 remainder -= T_TIM; | ||||||
|                 pulse_length += 1; |                 pulse_length += 1; | ||||||
|             } |             } | ||||||
|             remainder += sig->internals->reload_reg_remainder; |  | ||||||
| 
 | 
 | ||||||
|             /* last pulse in that signal and have a next signal? */ |             /* last pulse in current signal and have a next signal? */ | ||||||
|             if(last_pulse) { |             if(last_pulse && sig_next) { | ||||||
|                 if((seq_pos + 1) < sequence->sequence_used) { |                 /* when a signal ends with the same level as the next signal begins, let the next signal generate the whole pulse.
 | ||||||
|                     DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]]; |                    beware, we do not want the level after the last edge, but the last level before that edge */ | ||||||
|  |                 bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); | ||||||
| 
 | 
 | ||||||
|                     /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */ |                 /* if they have the same level, pass the duration to the next pulse(s) */ | ||||||
|                     /* beware, we do not want the level after the last edge, but the last level before that edge */ |                 if(end_level == sig_next->start_level) { | ||||||
|                     bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); |                     trade_for_next = pulse_length; | ||||||
| 
 |  | ||||||
|                     /* take from the next, add it to the current if they have the same level */ |  | ||||||
|                     if(end_level == sig_next->start_level) { |  | ||||||
|                         pulse_length += sig_next->reload_reg_buff[0]; |  | ||||||
|                         traded_first = true; |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             digital_sequence_queue_pulse(sequence, pulse_length); |             /* if it was decided, that the next signal's first pulse shall also handle our "length", then do not queue here */ | ||||||
|  |             if(!trade_for_next) { | ||||||
|  |                 digital_sequence_queue_pulse(sequence, pulse_length); | ||||||
| 
 | 
 | ||||||
|             /* start transmission when buffer was filled enough */ |                 if(!dma_buffer->dma_active) { | ||||||
|             bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4); |                     /* start transmission when buffer was filled enough */ | ||||||
|  |                     bool start_send = sequence->dma_buffer->write_pos >= (RINGBUFFER_SIZE - 2); | ||||||
| 
 | 
 | ||||||
|             /* or it was the last pulse */ |                     /* or it was the last pulse */ | ||||||
|             if(last_pulse && last_signal) { |                     if(last_pulse && last_signal) { | ||||||
|                 start_send = true; |                         start_send = true; | ||||||
|             } |                     } | ||||||
| 
 | 
 | ||||||
|             /* start transmission */ |                     /* start transmission */ | ||||||
|             if(start_send && !dma_buffer->dma_active) { |                     if(start_send) { | ||||||
|                 digital_sequence_setup_dma(sequence); |                         digital_sequence_setup_dma(sequence); | ||||||
|                 digital_signal_setup_timer(); |                         digital_signal_setup_timer(); | ||||||
| 
 | 
 | ||||||
|                 /* if the send time is specified, wait till the core timer passed beyond that time */ |                         /* if the send time is specified, wait till the core timer passed beyond that time */ | ||||||
|                 if(sequence->send_time_active) { |                         if(sequence->send_time_active) { | ||||||
|                     sequence->send_time_active = false; |                             sequence->send_time_active = false; | ||||||
|                     while(sequence->send_time - DWT->CYCCNT < 0x80000000) { |                             while(sequence->send_time - DWT->CYCCNT < 0x80000000) { | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         digital_signal_start_timer(); | ||||||
|  |                         dma_buffer->dma_active = true; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 digital_signal_start_timer(); |  | ||||||
|                 dma_buffer->dma_active = true; |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         remainder += sig->internals->reload_reg_remainder; | ||||||
|  |         sig = sig_next; | ||||||
|  |         sig_next = NULL; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* wait until last dma transaction was finished */ |     /* wait until last dma transaction was finished */ | ||||||
|     digital_sequence_finish(sequence); |  | ||||||
|     FURI_CRITICAL_EXIT(); |     FURI_CRITICAL_EXIT(); | ||||||
|  |     digital_sequence_finish(sequence); | ||||||
| 
 | 
 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  | |||||||
| @ -657,178 +657,167 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { | |||||||
|     return parsed; |     return parsed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) { | static bool nfc_device_save_slix_data( | ||||||
|  |     FlipperFormat* file, | ||||||
|  |     NfcDevice* dev, | ||||||
|  |     SlixTypeFeatures features, | ||||||
|  |     const char* type) { | ||||||
|     bool saved = false; |     bool saved = false; | ||||||
|     NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; |     NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break; |         char msg[64]; | ||||||
|         if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) |         snprintf(msg, sizeof(msg), "%s specific data", type); | ||||||
|  |         if(!flipper_format_write_comment_cstr(file, msg)) break; | ||||||
|  |         if(!flipper_format_write_comment_cstr( | ||||||
|  |                file, "Passwords are optional. If password is omitted, any password is accepted")) | ||||||
|             break; |             break; | ||||||
|  | 
 | ||||||
|  |         if(features & SlixFeatureRead) { | ||||||
|  |             if(data->flags & NfcVSlixDataFlagsHasKeyRead) { | ||||||
|  |                 if(!flipper_format_write_hex( | ||||||
|  |                        file, "Password Read", data->key_read, sizeof(data->key_read))) | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeatureWrite) { | ||||||
|  |             if(data->flags & NfcVSlixDataFlagsHasKeyWrite) { | ||||||
|  |                 if(!flipper_format_write_hex( | ||||||
|  |                        file, "Password Write", data->key_write, sizeof(data->key_write))) | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeaturePrivacy) { | ||||||
|  |             if(data->flags & NfcVSlixDataFlagsHasKeyPrivacy) { | ||||||
|  |                 if(!flipper_format_write_hex( | ||||||
|  |                        file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeatureDestroy) { | ||||||
|  |             if(data->flags & NfcVSlixDataFlagsHasKeyDestroy) { | ||||||
|  |                 if(!flipper_format_write_hex( | ||||||
|  |                        file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeatureEas) { | ||||||
|  |             if(data->flags & NfcVSlixDataFlagsHasKeyEas) { | ||||||
|  |                 if(!flipper_format_write_hex( | ||||||
|  |                        file, "Password EAS", data->key_eas, sizeof(data->key_eas))) | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeatureSignature) { | ||||||
|  |             if(!flipper_format_write_comment_cstr( | ||||||
|  |                    file, | ||||||
|  |                    "This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.")) | ||||||
|  |                 break; | ||||||
|  |             if(!flipper_format_write_hex( | ||||||
|  |                    file, "Signature", data->signature, sizeof(data->signature))) | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeaturePrivacy) { | ||||||
|  |             bool privacy = (data->flags & NfcVSlixDataFlagsPrivacy) ? true : false; | ||||||
|  |             if(!flipper_format_write_bool(file, "Privacy Mode", &privacy, 1)) break; | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeatureProtection) { | ||||||
|  |             if(!flipper_format_write_comment_cstr(file, "Protection pointer configuration")) break; | ||||||
|  |             if(!flipper_format_write_hex(file, "Protection pointer", &data->pp_pointer, 1)) break; | ||||||
|  |             if(!flipper_format_write_hex(file, "Protection condition", &data->pp_condition, 1)) | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|         saved = true; |         saved = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|     return saved; |     return saved; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) { | bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev, SlixTypeFeatures features) { | ||||||
|     bool parsed = false; |     bool parsed = false; | ||||||
|     NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; |     NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; | ||||||
|     memset(data, 0, sizeof(NfcVSlixData)); |     memset(data, 0, sizeof(NfcVSlixData)); | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) |         data->flags = 0; | ||||||
|             break; |  | ||||||
| 
 |  | ||||||
|         parsed = true; |  | ||||||
|     } while(false); |  | ||||||
| 
 |  | ||||||
|     return parsed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) { |  | ||||||
|     bool saved = false; |  | ||||||
|     NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break; |  | ||||||
|         if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex( |  | ||||||
|                file, "Password Write", data->key_write, sizeof(data->key_write))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex( |  | ||||||
|                file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex( |  | ||||||
|                file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; |  | ||||||
|         saved = true; |  | ||||||
|     } while(false); |  | ||||||
| 
 |  | ||||||
|     return saved; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) { |  | ||||||
|     bool parsed = false; |  | ||||||
|     NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; |  | ||||||
|     memset(data, 0, sizeof(NfcVSlixData)); |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_hex( |  | ||||||
|                file, "Password Write", data->key_write, sizeof(data->key_write))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_hex( |  | ||||||
|                file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_hex( |  | ||||||
|                file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; |  | ||||||
| 
 |  | ||||||
|         parsed = true; |  | ||||||
|     } while(false); |  | ||||||
| 
 |  | ||||||
|     return parsed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { |  | ||||||
|     bool saved = false; |  | ||||||
|     NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; |  | ||||||
|         if(!flipper_format_write_hex( |  | ||||||
|                file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex( |  | ||||||
|                file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; |  | ||||||
|         saved = true; |  | ||||||
|     } while(false); |  | ||||||
| 
 |  | ||||||
|     return saved; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { |  | ||||||
|     bool parsed = false; |  | ||||||
|     NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; |  | ||||||
|     memset(data, 0, sizeof(NfcVSlixData)); |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         if(!flipper_format_read_hex( |  | ||||||
|                file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_hex( |  | ||||||
|                file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; |  | ||||||
| 
 |  | ||||||
|         parsed = true; |  | ||||||
|     } while(false); |  | ||||||
| 
 |  | ||||||
|     return parsed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { |  | ||||||
|     bool saved = false; |  | ||||||
|     NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break; |  | ||||||
|         if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex( |  | ||||||
|                file, "Password Write", data->key_write, sizeof(data->key_write))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex( |  | ||||||
|                file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex( |  | ||||||
|                file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; |  | ||||||
|         saved = true; |  | ||||||
|     } while(false); |  | ||||||
| 
 |  | ||||||
|     return saved; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { // -V524
 |  | ||||||
|     bool parsed = false; |  | ||||||
|     NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; |  | ||||||
|     memset(data, 0, sizeof(NfcVSlixData)); |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_hex( |  | ||||||
|                file, "Password Write", data->key_write, sizeof(data->key_write))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_hex( |  | ||||||
|                file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_hex( |  | ||||||
|                file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) |  | ||||||
|             break; |  | ||||||
|         if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; |  | ||||||
| 
 | 
 | ||||||
|  |         if(features & SlixFeatureRead) { | ||||||
|  |             if(flipper_format_key_exist(file, "Password Read")) { | ||||||
|  |                 if(!flipper_format_read_hex( | ||||||
|  |                        file, "Password Read", data->key_read, sizeof(data->key_read))) { | ||||||
|  |                     FURI_LOG_D(TAG, "Failed reading Password Read"); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 data->flags |= NfcVSlixDataFlagsHasKeyRead; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeatureWrite) { | ||||||
|  |             if(flipper_format_key_exist(file, "Password Write")) { | ||||||
|  |                 if(!flipper_format_read_hex( | ||||||
|  |                        file, "Password Write", data->key_write, sizeof(data->key_write))) { | ||||||
|  |                     FURI_LOG_D(TAG, "Failed reading Password Write"); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 data->flags |= NfcVSlixDataFlagsHasKeyWrite; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeaturePrivacy) { | ||||||
|  |             if(flipper_format_key_exist(file, "Password Privacy")) { | ||||||
|  |                 if(!flipper_format_read_hex( | ||||||
|  |                        file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) { | ||||||
|  |                     FURI_LOG_D(TAG, "Failed reading Password Privacy"); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 data->flags |= NfcVSlixDataFlagsHasKeyPrivacy; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeatureDestroy) { | ||||||
|  |             if(flipper_format_key_exist(file, "Password Destroy")) { | ||||||
|  |                 if(!flipper_format_read_hex( | ||||||
|  |                        file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) { | ||||||
|  |                     FURI_LOG_D(TAG, "Failed reading Password Destroy"); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 data->flags |= NfcVSlixDataFlagsHasKeyDestroy; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeatureEas) { | ||||||
|  |             if(flipper_format_key_exist(file, "Password EAS")) { | ||||||
|  |                 if(!flipper_format_read_hex( | ||||||
|  |                        file, "Password EAS", data->key_eas, sizeof(data->key_eas))) { | ||||||
|  |                     FURI_LOG_D(TAG, "Failed reading Password EAS"); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 data->flags |= NfcVSlixDataFlagsHasKeyEas; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeatureSignature) { | ||||||
|  |             if(!flipper_format_read_hex( | ||||||
|  |                    file, "Signature", data->signature, sizeof(data->signature))) { | ||||||
|  |                 FURI_LOG_D(TAG, "Failed reading Signature"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeaturePrivacy) { | ||||||
|  |             bool privacy; | ||||||
|  |             if(!flipper_format_read_bool(file, "Privacy Mode", &privacy, 1)) { | ||||||
|  |                 FURI_LOG_D(TAG, "Failed reading Privacy Mode"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             if(privacy) { | ||||||
|  |                 data->flags |= NfcVSlixDataFlagsPrivacy; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(features & SlixFeatureProtection) { | ||||||
|  |             if(!flipper_format_read_hex(file, "Protection pointer", &(data->pp_pointer), 1)) { | ||||||
|  |                 FURI_LOG_D(TAG, "Failed reading Protection pointer"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             if(!flipper_format_read_hex(file, "Protection condition", &(data->pp_condition), 1)) { | ||||||
|  |                 FURI_LOG_D(TAG, "Failed reading Protection condition"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|         parsed = true; |         parsed = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
| @ -859,7 +848,8 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { | |||||||
|                file, "Data Content", data->data, data->block_num * data->block_size)) |                file, "Data Content", data->data, data->block_num * data->block_size)) | ||||||
|             break; |             break; | ||||||
|         if(!flipper_format_write_comment_cstr( |         if(!flipper_format_write_comment_cstr( | ||||||
|                file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info")) |                file, | ||||||
|  |                "First byte: DSFID (0x01) / AFI (0x02) / EAS (0x04) / PPL (0x08) lock info, others: block lock info")) | ||||||
|             break; |             break; | ||||||
|         if(!flipper_format_write_hex( |         if(!flipper_format_write_hex( | ||||||
|                file, "Security Status", data->security_status, 1 + data->block_num)) |                file, "Security Status", data->security_status, 1 + data->block_num)) | ||||||
| @ -877,16 +867,16 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { | |||||||
|             saved = true; |             saved = true; | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlix: |         case NfcVTypeSlix: | ||||||
|             saved = nfc_device_save_slix_data(file, dev); |             saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix, "SLIX"); | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlixS: |         case NfcVTypeSlixS: | ||||||
|             saved = nfc_device_save_slix_s_data(file, dev); |             saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixS, "SLIX-S"); | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlixL: |         case NfcVTypeSlixL: | ||||||
|             saved = nfc_device_save_slix_l_data(file, dev); |             saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixL, "SLIX-L"); | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlix2: |         case NfcVTypeSlix2: | ||||||
|             saved = nfc_device_save_slix2_data(file, dev); |             saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix2, "SLIX2"); | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
| @ -906,23 +896,45 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { | |||||||
|         uint32_t temp_uint32 = 0; |         uint32_t temp_uint32 = 0; | ||||||
|         uint8_t temp_value = 0; |         uint8_t temp_value = 0; | ||||||
| 
 | 
 | ||||||
|         if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; |         if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) { | ||||||
|         if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; |             FURI_LOG_D(TAG, "Failed reading DSFID"); | ||||||
|         if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; |  | ||||||
|         if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break; |  | ||||||
|         data->block_num = temp_uint32; |  | ||||||
|         if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; |  | ||||||
|         if(!flipper_format_read_hex( |  | ||||||
|                file, "Data Content", data->data, data->block_num * data->block_size)) |  | ||||||
|             break; |             break; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) { | ||||||
|  |             FURI_LOG_D(TAG, "Failed reading AFI"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) { | ||||||
|  |             FURI_LOG_D(TAG, "Failed reading IC Reference"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) { | ||||||
|  |             FURI_LOG_D(TAG, "Failed reading Block Count"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         data->block_num = temp_uint32; | ||||||
|  |         if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) { | ||||||
|  |             FURI_LOG_D(TAG, "Failed reading Block Size"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_read_hex( | ||||||
|  |                file, "Data Content", data->data, data->block_num * data->block_size)) { | ||||||
|  |             FURI_LOG_D(TAG, "Failed reading Data Content"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         /* optional, as added later */ |         /* optional, as added later */ | ||||||
|         if(flipper_format_key_exist(file, "Security Status")) { |         if(flipper_format_key_exist(file, "Security Status")) { | ||||||
|             if(!flipper_format_read_hex( |             if(!flipper_format_read_hex( | ||||||
|                    file, "Security Status", data->security_status, 1 + data->block_num)) |                    file, "Security Status", data->security_status, 1 + data->block_num)) { | ||||||
|  |                 FURI_LOG_D(TAG, "Failed reading Security Status"); | ||||||
|                 break; |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) { | ||||||
|  |             FURI_LOG_D(TAG, "Failed reading Subtype"); | ||||||
|  |             break; | ||||||
|         } |         } | ||||||
|         if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; |  | ||||||
|         data->sub_type = temp_value; |         data->sub_type = temp_value; | ||||||
| 
 | 
 | ||||||
|         switch(data->sub_type) { |         switch(data->sub_type) { | ||||||
| @ -930,16 +942,16 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { | |||||||
|             parsed = true; |             parsed = true; | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlix: |         case NfcVTypeSlix: | ||||||
|             parsed = nfc_device_load_slix_data(file, dev); |             parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix); | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlixS: |         case NfcVTypeSlixS: | ||||||
|             parsed = nfc_device_load_slix_s_data(file, dev); |             parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixS); | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlixL: |         case NfcVTypeSlixL: | ||||||
|             parsed = nfc_device_load_slix_l_data(file, dev); |             parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixL); | ||||||
|             break; |             break; | ||||||
|         case NfcVTypeSlix2: |         case NfcVTypeSlix2: | ||||||
|             parsed = nfc_device_load_slix2_data(file, dev); |             parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix2); | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -149,12 +149,18 @@ bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* n | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /* clear all know sub type data before reading them */ | ||||||
|  |     memset(&nfcv_data->sub_data, 0x00, sizeof(nfcv_data->sub_data)); | ||||||
|  | 
 | ||||||
|     if(slix_check_card_type(nfc_data)) { |     if(slix_check_card_type(nfc_data)) { | ||||||
|         FURI_LOG_I(TAG, "NXP SLIX detected"); |         FURI_LOG_I(TAG, "NXP SLIX detected"); | ||||||
|         nfcv_data->sub_type = NfcVTypeSlix; |         nfcv_data->sub_type = NfcVTypeSlix; | ||||||
|     } else if(slix2_check_card_type(nfc_data)) { |     } else if(slix2_check_card_type(nfc_data)) { | ||||||
|         FURI_LOG_I(TAG, "NXP SLIX2 detected"); |         FURI_LOG_I(TAG, "NXP SLIX2 detected"); | ||||||
|         nfcv_data->sub_type = NfcVTypeSlix2; |         nfcv_data->sub_type = NfcVTypeSlix2; | ||||||
|  |         if(slix2_read_custom(nfc_data, nfcv_data) != ERR_NONE) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|     } else if(slix_s_check_card_type(nfc_data)) { |     } else if(slix_s_check_card_type(nfc_data)) { | ||||||
|         FURI_LOG_I(TAG, "NXP SLIX-S detected"); |         FURI_LOG_I(TAG, "NXP SLIX-S detected"); | ||||||
|         nfcv_data->sub_type = NfcVTypeSlixS; |         nfcv_data->sub_type = NfcVTypeSlixS; | ||||||
| @ -612,9 +618,34 @@ void nfcv_emu_handle_packet( | |||||||
| 
 | 
 | ||||||
|         if(ctx->flags & NFCV_REQ_FLAG_AFI) { |         if(ctx->flags & NFCV_REQ_FLAG_AFI) { | ||||||
|             uint8_t afi = nfcv_data->frame[ctx->payload_offset]; |             uint8_t afi = nfcv_data->frame[ctx->payload_offset]; | ||||||
|             if(afi == nfcv_data->afi) { | 
 | ||||||
|                 respond = true; |             uint8_t family = (afi & 0xF0); | ||||||
|  |             uint8_t subfamily = (afi & 0x0F); | ||||||
|  | 
 | ||||||
|  |             if(family) { | ||||||
|  |                 if(subfamily) { | ||||||
|  |                     /* selected family and subfamily only */ | ||||||
|  |                     if(afi == nfcv_data->afi) { | ||||||
|  |                         respond = true; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     /* selected family, any subfamily */ | ||||||
|  |                     if(family == (nfcv_data->afi & 0xf0)) { | ||||||
|  |                         respond = true; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 if(subfamily) { | ||||||
|  |                     /* proprietary subfamily only */ | ||||||
|  |                     if(afi == nfcv_data->afi) { | ||||||
|  |                         respond = true; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     /* all families and subfamilies */ | ||||||
|  |                     respond = true; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|         } else { |         } else { | ||||||
|             respond = true; |             respond = true; | ||||||
|         } |         } | ||||||
| @ -740,13 +771,19 @@ void nfcv_emu_handle_packet( | |||||||
|     case NFCV_CMD_READ_MULTI_BLOCK: |     case NFCV_CMD_READ_MULTI_BLOCK: | ||||||
|     case NFCV_CMD_READ_BLOCK: { |     case NFCV_CMD_READ_BLOCK: { | ||||||
|         uint8_t block = nfcv_data->frame[ctx->payload_offset]; |         uint8_t block = nfcv_data->frame[ctx->payload_offset]; | ||||||
|         uint8_t blocks = 1; |         int blocks = 1; | ||||||
| 
 | 
 | ||||||
|         if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { |         if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { | ||||||
|             blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; |             blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(block + blocks <= nfcv_data->block_num) { |         /* limit the maximum block count, underflow accepted */ | ||||||
|  |         if(block + blocks > nfcv_data->block_num) { | ||||||
|  |             blocks = nfcv_data->block_num - block; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* only respond with the valid blocks, if there are any */ | ||||||
|  |         if(blocks > 0) { | ||||||
|             uint8_t buffer_pos = 0; |             uint8_t buffer_pos = 0; | ||||||
| 
 | 
 | ||||||
|             ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; |             ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; | ||||||
| @ -773,10 +810,13 @@ void nfcv_emu_handle_packet( | |||||||
|                 ctx->response_flags, |                 ctx->response_flags, | ||||||
|                 ctx->send_time); |                 ctx->send_time); | ||||||
|         } else { |         } else { | ||||||
|             ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; |             /* reply with an error only in addressed or selected mode */ | ||||||
|             ctx->response_buffer[1] = NFCV_ERROR_GENERIC; |             if(ctx->addressed || ctx->selected) { | ||||||
|             nfcv_emu_send( |                 ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; | ||||||
|                 tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); |                 ctx->response_buffer[1] = NFCV_ERROR_GENERIC; | ||||||
|  |                 nfcv_emu_send( | ||||||
|  |                     tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); |         snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -139,8 +139,10 @@ typedef enum { | |||||||
| } NfcVErrorcodes; | } NfcVErrorcodes; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     NfcVLockBitDsfid = 1, |     NfcVLockBitDsfid = 1 << 0, | ||||||
|     NfcVLockBitAfi = 2, |     NfcVLockBitAfi = 1 << 1, | ||||||
|  |     NfcVLockBitEas = 1 << 2, | ||||||
|  |     NfcVLockBitPpl = 1 << 3, | ||||||
| } NfcVLockBits; | } NfcVLockBits; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -168,14 +170,55 @@ typedef enum { | |||||||
|     NfcVSendFlagsHighRate = 1 << 4 |     NfcVSendFlagsHighRate = 1 << 4 | ||||||
| } NfcVSendFlags; | } NfcVSendFlags; | ||||||
| 
 | 
 | ||||||
|  | /* SLIX specific config flags */ | ||||||
|  | typedef enum { | ||||||
|  |     NfcVSlixDataFlagsNone = 0, | ||||||
|  |     NfcVSlixDataFlagsHasKeyRead = 1 << 0, | ||||||
|  |     NfcVSlixDataFlagsHasKeyWrite = 1 << 1, | ||||||
|  |     NfcVSlixDataFlagsHasKeyPrivacy = 1 << 2, | ||||||
|  |     NfcVSlixDataFlagsHasKeyDestroy = 1 << 3, | ||||||
|  |     NfcVSlixDataFlagsHasKeyEas = 1 << 4, | ||||||
|  |     NfcVSlixDataFlagsValidKeyRead = 1 << 8, | ||||||
|  |     NfcVSlixDataFlagsValidKeyWrite = 1 << 9, | ||||||
|  |     NfcVSlixDataFlagsValidKeyPrivacy = 1 << 10, | ||||||
|  |     NfcVSlixDataFlagsValidKeyDestroy = 1 << 11, | ||||||
|  |     NfcVSlixDataFlagsValidKeyEas = 1 << 12, | ||||||
|  |     NfcVSlixDataFlagsPrivacy = 1 << 16, | ||||||
|  |     NfcVSlixDataFlagsDestroyed = 1 << 17 | ||||||
|  | } NfcVSlixDataFlags; | ||||||
|  | 
 | ||||||
|  | /* abstract the file read/write operations for all SLIX types to reduce duplicated code */ | ||||||
|  | typedef enum { | ||||||
|  |     SlixFeatureRead = 1 << 0, | ||||||
|  |     SlixFeatureWrite = 1 << 1, | ||||||
|  |     SlixFeaturePrivacy = 1 << 2, | ||||||
|  |     SlixFeatureDestroy = 1 << 3, | ||||||
|  |     SlixFeatureEas = 1 << 4, | ||||||
|  |     SlixFeatureSignature = 1 << 5, | ||||||
|  |     SlixFeatureProtection = 1 << 6, | ||||||
|  | 
 | ||||||
|  |     SlixFeatureSlix = SlixFeatureEas, | ||||||
|  |     SlixFeatureSlixS = | ||||||
|  |         (SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy | | ||||||
|  |          SlixFeatureEas), | ||||||
|  |     SlixFeatureSlixL = (SlixFeaturePrivacy | SlixFeatureDestroy | SlixFeatureEas), | ||||||
|  |     SlixFeatureSlix2 = | ||||||
|  |         (SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy | | ||||||
|  |          SlixFeatureEas | SlixFeatureSignature | SlixFeatureProtection), | ||||||
|  | } SlixTypeFeatures; | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  |     uint32_t flags; | ||||||
|     uint8_t key_read[4]; |     uint8_t key_read[4]; | ||||||
|     uint8_t key_write[4]; |     uint8_t key_write[4]; | ||||||
|     uint8_t key_privacy[4]; |     uint8_t key_privacy[4]; | ||||||
|     uint8_t key_destroy[4]; |     uint8_t key_destroy[4]; | ||||||
|     uint8_t key_eas[4]; |     uint8_t key_eas[4]; | ||||||
|     uint8_t rand[2]; |     uint8_t rand[2]; | ||||||
|     bool privacy; |     uint8_t signature[32]; | ||||||
|  |     /* SLIX2 options */ | ||||||
|  |     uint8_t pp_pointer; | ||||||
|  |     uint8_t pp_condition; | ||||||
| } NfcVSlixData; | } NfcVSlixData; | ||||||
| 
 | 
 | ||||||
| typedef union { | typedef union { | ||||||
|  | |||||||
| @ -9,6 +9,120 @@ | |||||||
| 
 | 
 | ||||||
| #define TAG "SLIX" | #define TAG "SLIX" | ||||||
| 
 | 
 | ||||||
|  | ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { | ||||||
|  |     furi_assert(nfc_data); | ||||||
|  |     furi_assert(nfcv_data); | ||||||
|  | 
 | ||||||
|  |     uint8_t rxBuf[32]; | ||||||
|  |     uint16_t received = 0; | ||||||
|  |     ReturnCode ret = ERR_NONE; | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Read NXP SYSTEM INFORMATION..."); | ||||||
|  | 
 | ||||||
|  |     for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { | ||||||
|  |         uint8_t cmd[] = {}; | ||||||
|  |         uint8_t uid[NFCV_UID_LENGTH]; | ||||||
|  | 
 | ||||||
|  |         /* UID is stored reversed in requests */ | ||||||
|  |         for(int pos = 0; pos < nfc_data->uid_len; pos++) { | ||||||
|  |             uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ReturnCode ret = rfalNfcvPollerTransceiveReq( | ||||||
|  |             NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION, | ||||||
|  |             RFAL_NFCV_REQ_FLAG_DEFAULT, | ||||||
|  |             NFCV_MANUFACTURER_NXP, | ||||||
|  |             uid, | ||||||
|  |             cmd, | ||||||
|  |             sizeof(cmd), | ||||||
|  |             rxBuf, | ||||||
|  |             sizeof(rxBuf), | ||||||
|  |             &received); | ||||||
|  | 
 | ||||||
|  |         if(ret == ERR_NONE) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(ret != ERR_NONE || received != 8) { //-V560
 | ||||||
|  |         FURI_LOG_D(TAG, "Failed: %d, %d", ret, received); | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |     FURI_LOG_D(TAG, "Success..."); | ||||||
|  | 
 | ||||||
|  |     NfcVSlixData* slix = &nfcv_data->sub_data.slix; | ||||||
|  |     slix->pp_pointer = rxBuf[1]; | ||||||
|  |     slix->pp_condition = rxBuf[2]; | ||||||
|  | 
 | ||||||
|  |     /* convert NXP's to our internal lock bits format */ | ||||||
|  |     nfcv_data->security_status[0] = 0; | ||||||
|  |     nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitDsfid) ? NfcVLockBitDsfid : 0; | ||||||
|  |     nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitAfi) ? NfcVLockBitAfi : 0; | ||||||
|  |     nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitEas) ? NfcVLockBitEas : 0; | ||||||
|  |     nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitPpl) ? NfcVLockBitPpl : 0; | ||||||
|  | 
 | ||||||
|  |     return ERR_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { | ||||||
|  |     furi_assert(nfc_data); | ||||||
|  |     furi_assert(nfcv_data); | ||||||
|  | 
 | ||||||
|  |     uint8_t rxBuf[64]; | ||||||
|  |     uint16_t received = 0; | ||||||
|  |     ReturnCode ret = ERR_NONE; | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Read SIGNATURE..."); | ||||||
|  | 
 | ||||||
|  |     for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { | ||||||
|  |         uint8_t cmd[] = {}; | ||||||
|  |         uint8_t uid[NFCV_UID_LENGTH]; | ||||||
|  | 
 | ||||||
|  |         /* UID is stored reversed in requests */ | ||||||
|  |         for(int pos = 0; pos < nfc_data->uid_len; pos++) { | ||||||
|  |             uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ReturnCode ret = rfalNfcvPollerTransceiveReq( | ||||||
|  |             NFCV_CMD_NXP_READ_SIGNATURE, | ||||||
|  |             RFAL_NFCV_REQ_FLAG_DEFAULT, | ||||||
|  |             NFCV_MANUFACTURER_NXP, | ||||||
|  |             uid, | ||||||
|  |             cmd, | ||||||
|  |             sizeof(cmd), | ||||||
|  |             rxBuf, | ||||||
|  |             sizeof(rxBuf), | ||||||
|  |             &received); | ||||||
|  | 
 | ||||||
|  |         if(ret == ERR_NONE) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(ret != ERR_NONE || received != 33) { //-V560
 | ||||||
|  |         FURI_LOG_D(TAG, "Failed: %d, %d", ret, received); | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |     FURI_LOG_D(TAG, "Success..."); | ||||||
|  | 
 | ||||||
|  |     NfcVSlixData* slix = &nfcv_data->sub_data.slix; | ||||||
|  |     memcpy(slix->signature, &rxBuf[1], 32); | ||||||
|  | 
 | ||||||
|  |     return ERR_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { | ||||||
|  |     ReturnCode ret = ERR_NONE; | ||||||
|  | 
 | ||||||
|  |     ret = slix2_read_nxp_sysinfo(nfc_data, nfcv_data); | ||||||
|  |     if(ret != ERR_NONE) { | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |     ret = slix2_read_signature(nfc_data, nfcv_data); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static uint32_t slix_read_be(uint8_t* data, uint32_t length) { | static uint32_t slix_read_be(uint8_t* data, uint32_t length) { | ||||||
|     uint32_t value = 0; |     uint32_t value = 0; | ||||||
| 
 | 
 | ||||||
| @ -137,6 +251,43 @@ ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) { | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void slix_generic_pass_infos( | ||||||
|  |     uint8_t password_id, | ||||||
|  |     NfcVSlixData* slix, | ||||||
|  |     uint8_t** password, | ||||||
|  |     uint32_t* flag_valid, | ||||||
|  |     uint32_t* flag_set) { | ||||||
|  |     switch(password_id) { | ||||||
|  |     case SLIX_PASS_READ: | ||||||
|  |         *password = slix->key_read; | ||||||
|  |         *flag_valid = NfcVSlixDataFlagsValidKeyRead; | ||||||
|  |         *flag_set = NfcVSlixDataFlagsHasKeyRead; | ||||||
|  |         break; | ||||||
|  |     case SLIX_PASS_WRITE: | ||||||
|  |         *password = slix->key_write; | ||||||
|  |         *flag_valid = NfcVSlixDataFlagsValidKeyWrite; | ||||||
|  |         *flag_set = NfcVSlixDataFlagsHasKeyWrite; | ||||||
|  |         break; | ||||||
|  |     case SLIX_PASS_PRIVACY: | ||||||
|  |         *password = slix->key_privacy; | ||||||
|  |         *flag_valid = NfcVSlixDataFlagsValidKeyPrivacy; | ||||||
|  |         *flag_set = NfcVSlixDataFlagsHasKeyPrivacy; | ||||||
|  |         break; | ||||||
|  |     case SLIX_PASS_DESTROY: | ||||||
|  |         *password = slix->key_destroy; | ||||||
|  |         *flag_valid = NfcVSlixDataFlagsValidKeyDestroy; | ||||||
|  |         *flag_set = NfcVSlixDataFlagsHasKeyDestroy; | ||||||
|  |         break; | ||||||
|  |     case SLIX_PASS_EASAFI: | ||||||
|  |         *password = slix->key_eas; | ||||||
|  |         *flag_valid = NfcVSlixDataFlagsValidKeyEas; | ||||||
|  |         *flag_set = NfcVSlixDataFlagsHasKeyEas; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool slix_generic_protocol_filter( | bool slix_generic_protocol_filter( | ||||||
|     FuriHalNfcTxRxContext* tx_rx, |     FuriHalNfcTxRxContext* tx_rx, | ||||||
|     FuriHalNfcDevData* nfc_data, |     FuriHalNfcDevData* nfc_data, | ||||||
| @ -150,7 +301,8 @@ bool slix_generic_protocol_filter( | |||||||
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; |     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; | ||||||
|     NfcVSlixData* slix = &nfcv_data->sub_data.slix; |     NfcVSlixData* slix = &nfcv_data->sub_data.slix; | ||||||
| 
 | 
 | ||||||
|     if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && |     if((slix->flags & NfcVSlixDataFlagsPrivacy) && | ||||||
|  |        ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && | ||||||
|        ctx->command != NFCV_CMD_NXP_SET_PASSWORD) { |        ctx->command != NFCV_CMD_NXP_SET_PASSWORD) { | ||||||
|         snprintf( |         snprintf( | ||||||
|             nfcv_data->last_command, |             nfcv_data->last_command, | ||||||
| @ -186,66 +338,73 @@ bool slix_generic_protocol_filter( | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     case NFCV_CMD_NXP_SET_PASSWORD: { |     case NFCV_CMD_NXP_SET_PASSWORD: { | ||||||
|  |         /* the password to be set is the first parameter */ | ||||||
|         uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; |         uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; | ||||||
|  |         /* right after that is the XORed password */ | ||||||
|  |         uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; | ||||||
| 
 | 
 | ||||||
|  |         /* only handle if the password type is supported */ | ||||||
|         if(!(password_id & password_supported)) { |         if(!(password_id & password_supported)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; |         /* fetch the last RAND value */ | ||||||
|         uint8_t* rand = slix->rand; |         uint8_t* rand = slix->rand; | ||||||
|         uint8_t* password = NULL; | 
 | ||||||
|  |         /* first calc the password that has been sent */ | ||||||
|         uint8_t password_rcv[4]; |         uint8_t password_rcv[4]; | ||||||
| 
 |         for(int pos = 0; pos < 4; pos++) { | ||||||
|         switch(password_id) { |             password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; | ||||||
|         case SLIX_PASS_READ: |  | ||||||
|             password = slix->key_read; |  | ||||||
|             break; |  | ||||||
|         case SLIX_PASS_WRITE: |  | ||||||
|             password = slix->key_write; |  | ||||||
|             break; |  | ||||||
|         case SLIX_PASS_PRIVACY: |  | ||||||
|             password = slix->key_privacy; |  | ||||||
|             break; |  | ||||||
|         case SLIX_PASS_DESTROY: |  | ||||||
|             password = slix->key_destroy; |  | ||||||
|             break; |  | ||||||
|         case SLIX_PASS_EASAFI: |  | ||||||
|             password = slix->key_eas; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |         } | ||||||
|  |         uint32_t pass_received = slix_read_be(password_rcv, 4); | ||||||
| 
 | 
 | ||||||
|  |         /* then determine the password type (or even update if not set yet) */ | ||||||
|  |         uint8_t* password = NULL; | ||||||
|  |         uint32_t flag_valid = 0; | ||||||
|  |         uint32_t flag_set = 0; | ||||||
|  | 
 | ||||||
|  |         slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set); | ||||||
|  | 
 | ||||||
|  |         /* when the password is not supported, return silently */ | ||||||
|         if(!password) { |         if(!password) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         for(int pos = 0; pos < 4; pos++) { |         /* check if the password is known */ | ||||||
|             password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; |         bool pass_valid = false; | ||||||
|         } |         uint32_t pass_expect = 0; | ||||||
|         uint32_t pass_expect = slix_read_be(password, 4); |  | ||||||
|         uint32_t pass_received = slix_read_be(password_rcv, 4); |  | ||||||
| 
 | 
 | ||||||
|         /* if the password is all-zeroes, just accept any password*/ |         if(slix->flags & flag_set) { | ||||||
|         if(!pass_expect || pass_expect == pass_received) { |             /* if so, fetch the stored password and compare */ | ||||||
|  |             pass_expect = slix_read_be(password, 4); | ||||||
|  |             pass_valid = (pass_expect == pass_received); | ||||||
|  |         } else { | ||||||
|  |             /* if not known, just accept it and store that password */ | ||||||
|  |             memcpy(password, password_rcv, 4); | ||||||
|  |             nfcv_data->modified = true; | ||||||
|  |             slix->flags |= flag_set; | ||||||
|  | 
 | ||||||
|  |             pass_valid = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* if the pass was valid or accepted for other reasons, continue */ | ||||||
|  |         if(pass_valid) { | ||||||
|  |             slix->flags |= flag_valid; | ||||||
|  | 
 | ||||||
|  |             /* handle actions when a correct password was given, aside of setting the flag */ | ||||||
|             switch(password_id) { |             switch(password_id) { | ||||||
|             case SLIX_PASS_READ: |  | ||||||
|                 break; |  | ||||||
|             case SLIX_PASS_WRITE: |  | ||||||
|                 break; |  | ||||||
|             case SLIX_PASS_PRIVACY: |             case SLIX_PASS_PRIVACY: | ||||||
|                 slix->privacy = false; |                 slix->flags &= ~NfcVSlixDataFlagsPrivacy; | ||||||
|                 nfcv_data->modified = true; |                 nfcv_data->modified = true; | ||||||
|                 break; |                 break; | ||||||
|             case SLIX_PASS_DESTROY: |             case SLIX_PASS_DESTROY: | ||||||
|  |                 slix->flags |= NfcVSlixDataFlagsDestroyed; | ||||||
|                 FURI_LOG_D(TAG, "Pooof! Got destroyed"); |                 FURI_LOG_D(TAG, "Pooof! Got destroyed"); | ||||||
|                 break; |                 break; | ||||||
|             case SLIX_PASS_EASAFI: |  | ||||||
|                 break; |  | ||||||
|             default: |             default: | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|             ctx->response_buffer[0] = NFCV_NOERROR; |             ctx->response_buffer[0] = NFCV_NOERROR; | ||||||
|             nfcv_emu_send( |             nfcv_emu_send( | ||||||
|                 tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); |                 tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); | ||||||
| @ -268,6 +427,49 @@ bool slix_generic_protocol_filter( | |||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     case NFCV_CMD_NXP_WRITE_PASSWORD: { | ||||||
|  |         uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; | ||||||
|  | 
 | ||||||
|  |         if(!(password_id & password_supported)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         uint8_t* new_password = &nfcv_data->frame[ctx->payload_offset + 1]; | ||||||
|  |         uint8_t* password = NULL; | ||||||
|  |         uint32_t flag_valid = 0; | ||||||
|  |         uint32_t flag_set = 0; | ||||||
|  | 
 | ||||||
|  |         slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set); | ||||||
|  | 
 | ||||||
|  |         /* when the password is not supported, return silently */ | ||||||
|  |         if(!password) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool pass_valid = (slix->flags & flag_valid); | ||||||
|  |         if(!(slix->flags & flag_set)) { | ||||||
|  |             pass_valid = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(pass_valid) { | ||||||
|  |             slix->flags |= flag_valid; | ||||||
|  |             slix->flags |= flag_set; | ||||||
|  | 
 | ||||||
|  |             memcpy(password, new_password, 4); | ||||||
|  | 
 | ||||||
|  |             ctx->response_buffer[0] = NFCV_NOERROR; | ||||||
|  |             nfcv_emu_send( | ||||||
|  |                 tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); | ||||||
|  |             snprintf( | ||||||
|  |                 nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD OK"); | ||||||
|  |         } else { | ||||||
|  |             snprintf( | ||||||
|  |                 nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD FAIL"); | ||||||
|  |         } | ||||||
|  |         handled = true; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     case NFCV_CMD_NXP_ENABLE_PRIVACY: { |     case NFCV_CMD_NXP_ENABLE_PRIVACY: { | ||||||
|         ctx->response_buffer[0] = NFCV_NOERROR; |         ctx->response_buffer[0] = NFCV_NOERROR; | ||||||
| 
 | 
 | ||||||
| @ -278,7 +480,7 @@ bool slix_generic_protocol_filter( | |||||||
|             sizeof(nfcv_data->last_command), |             sizeof(nfcv_data->last_command), | ||||||
|             "NFCV_CMD_NXP_ENABLE_PRIVACY"); |             "NFCV_CMD_NXP_ENABLE_PRIVACY"); | ||||||
| 
 | 
 | ||||||
|         slix->privacy = true; |         slix->flags |= NfcVSlixDataFlagsPrivacy; | ||||||
|         handled = true; |         handled = true; | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| @ -315,7 +517,10 @@ void slix_l_prepare(NfcVData* nfcv_data) { | |||||||
|     FURI_LOG_D( |     FURI_LOG_D( | ||||||
|         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); |         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); | ||||||
|     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); |     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); | ||||||
|     FURI_LOG_D(TAG, "  Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); |     FURI_LOG_D( | ||||||
|  |         TAG, | ||||||
|  |         "  Privacy mode: %s", | ||||||
|  |         (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); | ||||||
| 
 | 
 | ||||||
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; |     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; | ||||||
|     ctx->emu_protocol_filter = &slix_l_protocol_filter; |     ctx->emu_protocol_filter = &slix_l_protocol_filter; | ||||||
| @ -345,7 +550,10 @@ void slix_s_prepare(NfcVData* nfcv_data) { | |||||||
|     FURI_LOG_D( |     FURI_LOG_D( | ||||||
|         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); |         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); | ||||||
|     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); |     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); | ||||||
|     FURI_LOG_D(TAG, "  Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); |     FURI_LOG_D( | ||||||
|  |         TAG, | ||||||
|  |         "  Privacy mode: %s", | ||||||
|  |         (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); | ||||||
| 
 | 
 | ||||||
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; |     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; | ||||||
|     ctx->emu_protocol_filter = &slix_s_protocol_filter; |     ctx->emu_protocol_filter = &slix_s_protocol_filter; | ||||||
| @ -375,7 +583,10 @@ void slix_prepare(NfcVData* nfcv_data) { | |||||||
|     FURI_LOG_D( |     FURI_LOG_D( | ||||||
|         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); |         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); | ||||||
|     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); |     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); | ||||||
|     FURI_LOG_D(TAG, "  Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); |     FURI_LOG_D( | ||||||
|  |         TAG, | ||||||
|  |         "  Privacy mode: %s", | ||||||
|  |         (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); | ||||||
| 
 | 
 | ||||||
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; |     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; | ||||||
|     ctx->emu_protocol_filter = &slix_protocol_filter; |     ctx->emu_protocol_filter = &slix_protocol_filter; | ||||||
| @ -389,6 +600,10 @@ bool slix2_protocol_filter( // -V524 | |||||||
|     furi_assert(nfc_data); |     furi_assert(nfc_data); | ||||||
|     furi_assert(nfcv_data_in); |     furi_assert(nfcv_data_in); | ||||||
| 
 | 
 | ||||||
|  |     NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; | ||||||
|  |     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; | ||||||
|  |     NfcVSlixData* slix = &nfcv_data->sub_data.slix; | ||||||
|  | 
 | ||||||
|     bool handled = false; |     bool handled = false; | ||||||
| 
 | 
 | ||||||
|     /* many SLIX share some of the functions, place that in a generic handler */ |     /* many SLIX share some of the functions, place that in a generic handler */ | ||||||
| @ -396,6 +611,160 @@ bool slix2_protocol_filter( // -V524 | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     switch(ctx->command) { | ||||||
|  |     /* override WRITE BLOCK for block 79 (16 bit counter)  */ | ||||||
|  |     case NFCV_CMD_WRITE_BLOCK: | ||||||
|  |     case NFCV_CMD_WRITE_MULTI_BLOCK: { | ||||||
|  |         uint8_t resp_len = 1; | ||||||
|  |         uint8_t blocks = 1; | ||||||
|  |         uint8_t block = nfcv_data->frame[ctx->payload_offset]; | ||||||
|  |         uint8_t data_pos = ctx->payload_offset + 1; | ||||||
|  | 
 | ||||||
|  |         if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { | ||||||
|  |             blocks = nfcv_data->frame[data_pos] + 1; | ||||||
|  |             data_pos++; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         uint8_t* data = &nfcv_data->frame[data_pos]; | ||||||
|  |         uint32_t data_len = nfcv_data->block_size * blocks; | ||||||
|  | 
 | ||||||
|  |         if((block + blocks) <= nfcv_data->block_num && | ||||||
|  |            (data_pos + data_len + 2) == nfcv_data->frame_length) { | ||||||
|  |             ctx->response_buffer[0] = NFCV_NOERROR; | ||||||
|  | 
 | ||||||
|  |             for(int block_num = block; block_num < block + blocks; block_num++) { | ||||||
|  |                 /* special case, 16-bit counter */ | ||||||
|  |                 if(block_num == 79) { | ||||||
|  |                     uint32_t dest; | ||||||
|  |                     uint32_t ctr_old; | ||||||
|  | 
 | ||||||
|  |                     memcpy(&dest, &nfcv_data->frame[data_pos], 4); | ||||||
|  |                     memcpy(&ctr_old, &nfcv_data->data[nfcv_data->block_size * block_num], 4); | ||||||
|  | 
 | ||||||
|  |                     uint32_t ctr_new = ctr_old; | ||||||
|  |                     bool allowed = true; | ||||||
|  | 
 | ||||||
|  |                     /* increment counter */ | ||||||
|  |                     if(dest == 1) { | ||||||
|  |                         ctr_new = (ctr_old & 0xFFFF0000) | ((ctr_old + 1) & 0xFFFF); | ||||||
|  | 
 | ||||||
|  |                         /* protection flag set? */ | ||||||
|  |                         if(ctr_old & 0x01000000) { //-V1051
 | ||||||
|  |                             allowed = nfcv_data->sub_data.slix.flags & | ||||||
|  |                                       NfcVSlixDataFlagsValidKeyRead; | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         ctr_new = dest; | ||||||
|  |                         allowed = nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsValidKeyWrite; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if(allowed) { | ||||||
|  |                         memcpy( //-V1086
 | ||||||
|  |                             &nfcv_data->data[nfcv_data->block_size * block_num], | ||||||
|  |                             &ctr_new, | ||||||
|  |                             4); | ||||||
|  |                     } else { | ||||||
|  |                         /* incorrect read or write password */ | ||||||
|  |                         ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; | ||||||
|  |                         ctx->response_buffer[1] = NFCV_ERROR_GENERIC; | ||||||
|  |                         resp_len = 2; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     memcpy( | ||||||
|  |                         &nfcv_data->data[nfcv_data->block_size * block_num], | ||||||
|  |                         &nfcv_data->frame[data_pos], | ||||||
|  |                         nfcv_data->block_size); | ||||||
|  |                 } | ||||||
|  |                 data_pos += nfcv_data->block_size; | ||||||
|  |             } | ||||||
|  |             nfcv_data->modified = true; | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |             ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; | ||||||
|  |             ctx->response_buffer[1] = NFCV_ERROR_GENERIC; | ||||||
|  |             resp_len = 2; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool respond = (ctx->response_buffer[0] == NFCV_NOERROR) || | ||||||
|  |                        (ctx->addressed || ctx->selected); | ||||||
|  | 
 | ||||||
|  |         if(respond) { | ||||||
|  |             nfcv_emu_send( | ||||||
|  |                 tx_rx, | ||||||
|  |                 nfcv_data, | ||||||
|  |                 ctx->response_buffer, | ||||||
|  |                 resp_len, | ||||||
|  |                 ctx->response_flags, | ||||||
|  |                 ctx->send_time); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { | ||||||
|  |             snprintf( | ||||||
|  |                 nfcv_data->last_command, | ||||||
|  |                 sizeof(nfcv_data->last_command), | ||||||
|  |                 "WRITE MULTI BLOCK %d, %d blocks", | ||||||
|  |                 block, | ||||||
|  |                 blocks); | ||||||
|  |         } else { | ||||||
|  |             snprintf( | ||||||
|  |                 nfcv_data->last_command, | ||||||
|  |                 sizeof(nfcv_data->last_command), | ||||||
|  |                 "WRITE BLOCK %d <- %02X %02X %02X %02X", | ||||||
|  |                 block, | ||||||
|  |                 data[0], | ||||||
|  |                 data[1], | ||||||
|  |                 data[2], | ||||||
|  |                 data[3]); | ||||||
|  |         } | ||||||
|  |         handled = true; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case NFCV_CMD_NXP_READ_SIGNATURE: { | ||||||
|  |         uint32_t len = 0; | ||||||
|  |         ctx->response_buffer[len++] = NFCV_NOERROR; | ||||||
|  |         memcpy(&ctx->response_buffer[len], slix->signature, sizeof(slix->signature)); | ||||||
|  |         len += sizeof(slix->signature); | ||||||
|  | 
 | ||||||
|  |         nfcv_emu_send( | ||||||
|  |             tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time); | ||||||
|  |         snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ_SIGNATURE"); | ||||||
|  | 
 | ||||||
|  |         handled = true; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION: { | ||||||
|  |         uint32_t len = 0; | ||||||
|  |         uint8_t lock_bits = 0; | ||||||
|  | 
 | ||||||
|  |         /* convert our internal lock bits format into NXP's */ | ||||||
|  |         lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? SlixLockBitDsfid : 0; | ||||||
|  |         lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitAfi) ? SlixLockBitAfi : 0; | ||||||
|  |         lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitEas) ? SlixLockBitEas : 0; | ||||||
|  |         lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitPpl) ? SlixLockBitPpl : 0; | ||||||
|  | 
 | ||||||
|  |         ctx->response_buffer[len++] = NFCV_NOERROR; | ||||||
|  |         ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_pointer; | ||||||
|  |         ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_condition; | ||||||
|  |         ctx->response_buffer[len++] = lock_bits; | ||||||
|  |         ctx->response_buffer[len++] = 0x7F; /* features LSB */ | ||||||
|  |         ctx->response_buffer[len++] = 0x35; /* features */ | ||||||
|  |         ctx->response_buffer[len++] = 0; /* features */ | ||||||
|  |         ctx->response_buffer[len++] = 0; /* features MSB */ | ||||||
|  | 
 | ||||||
|  |         nfcv_emu_send( | ||||||
|  |             tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time); | ||||||
|  |         snprintf( | ||||||
|  |             nfcv_data->last_command, | ||||||
|  |             sizeof(nfcv_data->last_command), | ||||||
|  |             "GET_NXP_SYSTEM_INFORMATION"); | ||||||
|  | 
 | ||||||
|  |         handled = true; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return handled; |     return handled; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -405,7 +774,10 @@ void slix2_prepare(NfcVData* nfcv_data) { | |||||||
|     FURI_LOG_D( |     FURI_LOG_D( | ||||||
|         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); |         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); | ||||||
|     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); |     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); | ||||||
|     FURI_LOG_D(TAG, "  Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); |     FURI_LOG_D( | ||||||
|  |         TAG, | ||||||
|  |         "  Privacy mode: %s", | ||||||
|  |         (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); | ||||||
| 
 | 
 | ||||||
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; |     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; | ||||||
|     ctx->emu_protocol_filter = &slix2_protocol_filter; |     ctx->emu_protocol_filter = &slix2_protocol_filter; | ||||||
|  | |||||||
| @ -8,19 +8,35 @@ | |||||||
| #define NFCV_MANUFACTURER_NXP 0x04 | #define NFCV_MANUFACTURER_NXP 0x04 | ||||||
| 
 | 
 | ||||||
| /* ISO15693-3 CUSTOM NXP COMMANDS */ | /* ISO15693-3 CUSTOM NXP COMMANDS */ | ||||||
| #define NFCV_CMD_NXP_SET_EAS 0xA2 | typedef enum { | ||||||
| #define NFCV_CMD_NXP_RESET_EAS 0xA3 |     NFCV_CMD_NXP_SET_EAS = 0xA2, | ||||||
| #define NFCV_CMD_NXP_LOCK_EAS 0xA4 |     NFCV_CMD_NXP_RESET_EAS = 0xA3, | ||||||
| #define NFCV_CMD_NXP_EAS_ALARM 0xA5 |     NFCV_CMD_NXP_LOCK_EAS = 0xA4, | ||||||
| #define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 |     NFCV_CMD_NXP_EAS_ALARM = 0xA5, | ||||||
| #define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7 |     NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI = 0xA6, | ||||||
| #define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0 |     NFCV_CMD_NXP_WRITE_EAS_ID = 0xA7, | ||||||
| #define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 |     NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION = 0xAB, | ||||||
| #define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2 |     NFCV_CMD_NXP_INVENTORY_PAGE_READ = 0xB0, | ||||||
| #define NFCV_CMD_NXP_SET_PASSWORD 0xB3 |     NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST = 0xB1, | ||||||
| #define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4 |     NFCV_CMD_NXP_GET_RANDOM_NUMBER = 0xB2, | ||||||
| #define NFCV_CMD_NXP_DESTROY 0xB9 |     NFCV_CMD_NXP_SET_PASSWORD = 0xB3, | ||||||
| #define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA |     NFCV_CMD_NXP_WRITE_PASSWORD = 0xB4, | ||||||
|  |     NFCV_CMD_NXP_64_BIT_PASSWORD_PROTECTION = 0xB5, | ||||||
|  |     NFCV_CMD_NXP_PROTECT_PAGE = 0xB6, | ||||||
|  |     NFCV_CMD_NXP_LOCK_PAGE_PROTECTION_CONDITION = 0xB7, | ||||||
|  |     NFCV_CMD_NXP_DESTROY = 0xB9, | ||||||
|  |     NFCV_CMD_NXP_ENABLE_PRIVACY = 0xBA, | ||||||
|  |     NFCV_CMD_NXP_STAY_QUIET_PERSISTENT = 0xBC, | ||||||
|  |     NFCV_CMD_NXP_READ_SIGNATURE = 0xBD | ||||||
|  | } SlixCommands; | ||||||
|  | 
 | ||||||
|  | /* lock bit bits used in SLIX's NXP SYSTEM INFORMATION response */ | ||||||
|  | typedef enum { | ||||||
|  |     SlixLockBitAfi = 1 << 0, | ||||||
|  |     SlixLockBitEas = 1 << 1, | ||||||
|  |     SlixLockBitDsfid = 1 << 2, | ||||||
|  |     SlixLockBitPpl = 1 << 3, | ||||||
|  | } SlixLockBits; | ||||||
| 
 | 
 | ||||||
| /* available passwords */ | /* available passwords */ | ||||||
| #define SLIX_PASS_READ 0x01 | #define SLIX_PASS_READ 0x01 | ||||||
| @ -37,6 +53,10 @@ bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); | |||||||
| bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); | bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); | ||||||
| bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); | bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); | ||||||
| 
 | 
 | ||||||
|  | ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); | ||||||
|  | ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); | ||||||
|  | ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); | ||||||
|  | 
 | ||||||
| ReturnCode slix_get_random(NfcVData* data); | ReturnCode slix_get_random(NfcVData* data); | ||||||
| ReturnCode slix_unlock(NfcVData* data, uint32_t password_id); | ReturnCode slix_unlock(NfcVData* data, uint32_t password_id); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 g3gg0.de
						g3gg0.de