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) {
|
||||
Nfc* nfc = context;
|
||||
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,
|
||||
"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, "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);
|
||||
furi_string_cat_printf(temp_str, "IC Ref: %d\n", nfcv_data->ic_ref);
|
||||
furi_string_cat_printf(temp_str, "Blocks: %d\n", nfcv_data->block_num);
|
||||
furi_string_cat_printf(temp_str, "Blocksize: %d\n", nfcv_data->block_size);
|
||||
|
||||
switch(dev_data->nfcv_data.sub_type) {
|
||||
case NfcVTypePlain:
|
||||
furi_string_cat_printf(temp_str, "Type: Plain\n");
|
||||
break;
|
||||
case NfcVTypeSlix:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX\n");
|
||||
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));
|
||||
nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix, "SLIX");
|
||||
break;
|
||||
case NfcVTypeSlixS:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX-S\n");
|
||||
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));
|
||||
nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixS, "SLIX-S");
|
||||
break;
|
||||
case NfcVTypeSlixL:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX-L\n");
|
||||
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));
|
||||
nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixL, "SLIX-L");
|
||||
break;
|
||||
case NfcVTypeSlix2:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX2\n");
|
||||
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));
|
||||
nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix2, "SLIX2");
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
NfcDeviceData* dev_data = &nfc->dev->dev_data;
|
||||
FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
|
||||
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
|
||||
// Setup view
|
||||
Widget* widget = nfc->widget;
|
||||
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");
|
||||
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++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
||||
}
|
||||
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, "Blocksize: %02X\n", nfcv_data->block_size);
|
||||
furi_string_cat_printf(temp_str, "(see More->Info for details)\n");
|
||||
|
||||
widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
|
||||
@ -51,8 +51,16 @@ struct DigitalSignalInternals {
|
||||
#define T_TIM 1562 /* 15.625 ns *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 */
|
||||
#define SEQUENCE_DMA_RINGBUFFER_SIZE 32
|
||||
#define RINGBUFFER_SIZE 128
|
||||
|
||||
/* maximum number of DigitalSignals in a sequence */
|
||||
#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_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
|
||||
LL_TIM_SetPrescaler(TIM2, 0);
|
||||
LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF);
|
||||
LL_TIM_SetAutoReload(TIM2, SEQ_TIMER_MAX);
|
||||
LL_TIM_SetCounter(TIM2, 0);
|
||||
}
|
||||
|
||||
@ -335,7 +343,7 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) {
|
||||
sequence->bake = false;
|
||||
|
||||
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_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
@ -454,39 +462,23 @@ static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) {
|
||||
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) {
|
||||
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
|
||||
|
||||
if(dma_buffer->dma_active) {
|
||||
uint32_t prev_timer = DWT->CYCCNT;
|
||||
uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
|
||||
do {
|
||||
uint32_t last_pos = dma_buffer->read_pos;
|
||||
|
||||
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) {
|
||||
/* we are finished, when the DMA transferred the SEQ_TIMER_MAX marker */
|
||||
if(TIM2->ARR == SEQ_TIMER_MAX) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(last_pos != dma_buffer->read_pos) { //-V547
|
||||
prev_timer = DWT->CYCCNT;
|
||||
}
|
||||
if(DWT->CYCCNT - prev_timer > wait_ticks) {
|
||||
if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) {
|
||||
dma_buffer->read_pos =
|
||||
RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)",
|
||||
wait_ms,
|
||||
SEQ_LOCK_WAIT_MS,
|
||||
TIM2->ARR,
|
||||
dma_buffer->read_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) {
|
||||
uint32_t prev_timer = DWT->CYCCNT;
|
||||
uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
|
||||
do {
|
||||
uint32_t last_pos = dma_buffer->read_pos;
|
||||
digital_sequence_update_pos(sequence);
|
||||
dma_buffer->read_pos = RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if(last_pos != dma_buffer->read_pos) { //-V547
|
||||
prev_timer = DWT->CYCCNT;
|
||||
}
|
||||
if(DWT->CYCCNT - prev_timer > wait_ticks) {
|
||||
if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"[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,
|
||||
dma_buffer->read_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->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
|
||||
dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF;
|
||||
dma_buffer->write_pos++;
|
||||
dma_buffer->write_pos %= RINGBUFFER_SIZE;
|
||||
dma_buffer->buffer[dma_buffer->write_pos] = SEQ_TIMER_MAX;
|
||||
}
|
||||
|
||||
bool digital_sequence_send(DigitalSequence* sequence) {
|
||||
@ -553,90 +553,97 @@ bool digital_sequence_send(DigitalSequence* sequence) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t remainder = 0;
|
||||
bool traded_first = false;
|
||||
if(!sequence->sequence_used) {
|
||||
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->buffer[0] = 0xFFFFFFFF;
|
||||
dma_buffer->buffer[0] = SEQ_TIMER_MAX;
|
||||
dma_buffer->read_pos = 0;
|
||||
dma_buffer->write_pos = 0;
|
||||
|
||||
for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) {
|
||||
uint8_t signal_index = sequence->sequence[seq_pos];
|
||||
DigitalSignal* sig = sequence->signals[signal_index];
|
||||
bool last_signal = ((seq_pos + 1) == sequence->sequence_used);
|
||||
/* already prepare the current signal pointer */
|
||||
DigitalSignal* sig = sequence->signals[sequence->sequence[0]];
|
||||
DigitalSignal* sig_next = NULL;
|
||||
/* 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 */
|
||||
if(seq_pos == 0) {
|
||||
sequence->gpio_buff = sig->internals->gpio_buff;
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
||||
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++) {
|
||||
if(traded_first) {
|
||||
traded_first = false;
|
||||
continue;
|
||||
}
|
||||
uint32_t pulse_length = 0;
|
||||
bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries);
|
||||
bool last_pulse = ((pulse_pos + 1) >= sig->internals->reload_reg_entries);
|
||||
uint32_t pulse_length = sig->reload_reg_buff[pulse_pos] + trade_for_next;
|
||||
|
||||
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 */
|
||||
if(remainder >= T_TIM_DIV2) {
|
||||
remainder -= T_TIM;
|
||||
pulse_length += 1;
|
||||
}
|
||||
remainder += sig->internals->reload_reg_remainder;
|
||||
|
||||
/* last pulse in that signal and have a next signal? */
|
||||
if(last_pulse) {
|
||||
if((seq_pos + 1) < sequence->sequence_used) {
|
||||
DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]];
|
||||
/* last pulse in current signal and have a next signal? */
|
||||
if(last_pulse && sig_next) {
|
||||
/* when a signal ends with the same level as the next signal begins, let the next signal generate the whole pulse.
|
||||
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 */
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* if they have the same level, pass the duration to the next pulse(s) */
|
||||
if(end_level == sig_next->start_level) {
|
||||
trade_for_next = pulse_length;
|
||||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4);
|
||||
if(!dma_buffer->dma_active) {
|
||||
/* start transmission when buffer was filled enough */
|
||||
bool start_send = sequence->dma_buffer->write_pos >= (RINGBUFFER_SIZE - 2);
|
||||
|
||||
/* or it was the last pulse */
|
||||
if(last_pulse && last_signal) {
|
||||
start_send = true;
|
||||
}
|
||||
/* or it was the last pulse */
|
||||
if(last_pulse && last_signal) {
|
||||
start_send = true;
|
||||
}
|
||||
|
||||
/* start transmission */
|
||||
if(start_send && !dma_buffer->dma_active) {
|
||||
digital_sequence_setup_dma(sequence);
|
||||
digital_signal_setup_timer();
|
||||
/* start transmission */
|
||||
if(start_send) {
|
||||
digital_sequence_setup_dma(sequence);
|
||||
digital_signal_setup_timer();
|
||||
|
||||
/* if the send time is specified, wait till the core timer passed beyond that time */
|
||||
if(sequence->send_time_active) {
|
||||
sequence->send_time_active = false;
|
||||
while(sequence->send_time - DWT->CYCCNT < 0x80000000) {
|
||||
/* if the send time is specified, wait till the core timer passed beyond that time */
|
||||
if(sequence->send_time_active) {
|
||||
sequence->send_time_active = false;
|
||||
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 */
|
||||
digital_sequence_finish(sequence);
|
||||
FURI_CRITICAL_EXIT();
|
||||
digital_sequence_finish(sequence);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -657,178 +657,167 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
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;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
|
||||
do {
|
||||
if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break;
|
||||
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
|
||||
char msg[64];
|
||||
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;
|
||||
|
||||
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;
|
||||
} while(false);
|
||||
|
||||
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;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
memset(data, 0, sizeof(NfcVSlixData));
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
|
||||
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;
|
||||
data->flags = 0;
|
||||
|
||||
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;
|
||||
} 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))
|
||||
break;
|
||||
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;
|
||||
if(!flipper_format_write_hex(
|
||||
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;
|
||||
break;
|
||||
case NfcVTypeSlix:
|
||||
saved = nfc_device_save_slix_data(file, dev);
|
||||
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix, "SLIX");
|
||||
break;
|
||||
case NfcVTypeSlixS:
|
||||
saved = nfc_device_save_slix_s_data(file, dev);
|
||||
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixS, "SLIX-S");
|
||||
break;
|
||||
case NfcVTypeSlixL:
|
||||
saved = nfc_device_save_slix_l_data(file, dev);
|
||||
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixL, "SLIX-L");
|
||||
break;
|
||||
case NfcVTypeSlix2:
|
||||
saved = nfc_device_save_slix2_data(file, dev);
|
||||
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix2, "SLIX2");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -906,23 +896,45 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
uint32_t temp_uint32 = 0;
|
||||
uint8_t temp_value = 0;
|
||||
|
||||
if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break;
|
||||
if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break;
|
||||
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))
|
||||
if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) {
|
||||
FURI_LOG_D(TAG, "Failed reading DSFID");
|
||||
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 */
|
||||
if(flipper_format_key_exist(file, "Security Status")) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
switch(data->sub_type) {
|
||||
@ -930,16 +942,16 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
parsed = true;
|
||||
break;
|
||||
case NfcVTypeSlix:
|
||||
parsed = nfc_device_load_slix_data(file, dev);
|
||||
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix);
|
||||
break;
|
||||
case NfcVTypeSlixS:
|
||||
parsed = nfc_device_load_slix_s_data(file, dev);
|
||||
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixS);
|
||||
break;
|
||||
case NfcVTypeSlixL:
|
||||
parsed = nfc_device_load_slix_l_data(file, dev);
|
||||
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixL);
|
||||
break;
|
||||
case NfcVTypeSlix2:
|
||||
parsed = nfc_device_load_slix2_data(file, dev);
|
||||
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@ -149,12 +149,18 @@ bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* n
|
||||
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)) {
|
||||
FURI_LOG_I(TAG, "NXP SLIX detected");
|
||||
nfcv_data->sub_type = NfcVTypeSlix;
|
||||
} else if(slix2_check_card_type(nfc_data)) {
|
||||
FURI_LOG_I(TAG, "NXP SLIX2 detected");
|
||||
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)) {
|
||||
FURI_LOG_I(TAG, "NXP SLIX-S detected");
|
||||
nfcv_data->sub_type = NfcVTypeSlixS;
|
||||
@ -612,9 +618,34 @@ void nfcv_emu_handle_packet(
|
||||
|
||||
if(ctx->flags & NFCV_REQ_FLAG_AFI) {
|
||||
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 {
|
||||
respond = true;
|
||||
}
|
||||
@ -740,13 +771,19 @@ void nfcv_emu_handle_packet(
|
||||
case NFCV_CMD_READ_MULTI_BLOCK:
|
||||
case NFCV_CMD_READ_BLOCK: {
|
||||
uint8_t block = nfcv_data->frame[ctx->payload_offset];
|
||||
uint8_t blocks = 1;
|
||||
int blocks = 1;
|
||||
|
||||
if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) {
|
||||
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;
|
||||
|
||||
ctx->response_buffer[buffer_pos++] = NFCV_NOERROR;
|
||||
@ -773,10 +810,13 @@ void nfcv_emu_handle_packet(
|
||||
ctx->response_flags,
|
||||
ctx->send_time);
|
||||
} else {
|
||||
ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
|
||||
ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
|
||||
nfcv_emu_send(
|
||||
tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time);
|
||||
/* reply with an error only in addressed or selected mode */
|
||||
if(ctx->addressed || ctx->selected) {
|
||||
ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
|
||||
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);
|
||||
|
||||
|
||||
@ -139,8 +139,10 @@ typedef enum {
|
||||
} NfcVErrorcodes;
|
||||
|
||||
typedef enum {
|
||||
NfcVLockBitDsfid = 1,
|
||||
NfcVLockBitAfi = 2,
|
||||
NfcVLockBitDsfid = 1 << 0,
|
||||
NfcVLockBitAfi = 1 << 1,
|
||||
NfcVLockBitEas = 1 << 2,
|
||||
NfcVLockBitPpl = 1 << 3,
|
||||
} NfcVLockBits;
|
||||
|
||||
typedef enum {
|
||||
@ -168,14 +170,55 @@ typedef enum {
|
||||
NfcVSendFlagsHighRate = 1 << 4
|
||||
} 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 {
|
||||
uint32_t flags;
|
||||
uint8_t key_read[4];
|
||||
uint8_t key_write[4];
|
||||
uint8_t key_privacy[4];
|
||||
uint8_t key_destroy[4];
|
||||
uint8_t key_eas[4];
|
||||
uint8_t rand[2];
|
||||
bool privacy;
|
||||
uint8_t signature[32];
|
||||
/* SLIX2 options */
|
||||
uint8_t pp_pointer;
|
||||
uint8_t pp_condition;
|
||||
} NfcVSlixData;
|
||||
|
||||
typedef union {
|
||||
|
||||
@ -9,6 +9,120 @@
|
||||
|
||||
#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) {
|
||||
uint32_t value = 0;
|
||||
|
||||
@ -137,6 +251,43 @@ ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) {
|
||||
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(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
@ -150,7 +301,8 @@ bool slix_generic_protocol_filter(
|
||||
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
|
||||
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) {
|
||||
snprintf(
|
||||
nfcv_data->last_command,
|
||||
@ -186,66 +338,73 @@ bool slix_generic_protocol_filter(
|
||||
}
|
||||
|
||||
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];
|
||||
/* 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)) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1];
|
||||
/* fetch the last RAND value */
|
||||
uint8_t* rand = slix->rand;
|
||||
uint8_t* password = NULL;
|
||||
|
||||
/* first calc the password that has been sent */
|
||||
uint8_t password_rcv[4];
|
||||
|
||||
switch(password_id) {
|
||||
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;
|
||||
for(int pos = 0; pos < 4; pos++) {
|
||||
password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2];
|
||||
}
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
|
||||
for(int pos = 0; pos < 4; pos++) {
|
||||
password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2];
|
||||
}
|
||||
uint32_t pass_expect = slix_read_be(password, 4);
|
||||
uint32_t pass_received = slix_read_be(password_rcv, 4);
|
||||
/* check if the password is known */
|
||||
bool pass_valid = false;
|
||||
uint32_t pass_expect = 0;
|
||||
|
||||
/* if the password is all-zeroes, just accept any password*/
|
||||
if(!pass_expect || pass_expect == pass_received) {
|
||||
if(slix->flags & flag_set) {
|
||||
/* 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) {
|
||||
case SLIX_PASS_READ:
|
||||
break;
|
||||
case SLIX_PASS_WRITE:
|
||||
break;
|
||||
case SLIX_PASS_PRIVACY:
|
||||
slix->privacy = false;
|
||||
slix->flags &= ~NfcVSlixDataFlagsPrivacy;
|
||||
nfcv_data->modified = true;
|
||||
break;
|
||||
case SLIX_PASS_DESTROY:
|
||||
slix->flags |= NfcVSlixDataFlagsDestroyed;
|
||||
FURI_LOG_D(TAG, "Pooof! Got destroyed");
|
||||
break;
|
||||
case SLIX_PASS_EASAFI:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ctx->response_buffer[0] = NFCV_NOERROR;
|
||||
nfcv_emu_send(
|
||||
tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
|
||||
@ -268,6 +427,49 @@ bool slix_generic_protocol_filter(
|
||||
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: {
|
||||
ctx->response_buffer[0] = NFCV_NOERROR;
|
||||
|
||||
@ -278,7 +480,7 @@ bool slix_generic_protocol_filter(
|
||||
sizeof(nfcv_data->last_command),
|
||||
"NFCV_CMD_NXP_ENABLE_PRIVACY");
|
||||
|
||||
slix->privacy = true;
|
||||
slix->flags |= NfcVSlixDataFlagsPrivacy;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
@ -315,7 +517,10 @@ void slix_l_prepare(NfcVData* nfcv_data) {
|
||||
FURI_LOG_D(
|
||||
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, " 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;
|
||||
ctx->emu_protocol_filter = &slix_l_protocol_filter;
|
||||
@ -345,7 +550,10 @@ void slix_s_prepare(NfcVData* nfcv_data) {
|
||||
FURI_LOG_D(
|
||||
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, " 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;
|
||||
ctx->emu_protocol_filter = &slix_s_protocol_filter;
|
||||
@ -375,7 +583,10 @@ void slix_prepare(NfcVData* nfcv_data) {
|
||||
FURI_LOG_D(
|
||||
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, " 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;
|
||||
ctx->emu_protocol_filter = &slix_protocol_filter;
|
||||
@ -389,6 +600,10 @@ bool slix2_protocol_filter( // -V524
|
||||
furi_assert(nfc_data);
|
||||
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;
|
||||
|
||||
/* many SLIX share some of the functions, place that in a generic handler */
|
||||
@ -396,6 +611,160 @@ bool slix2_protocol_filter( // -V524
|
||||
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;
|
||||
}
|
||||
|
||||
@ -405,7 +774,10 @@ void slix2_prepare(NfcVData* nfcv_data) {
|
||||
FURI_LOG_D(
|
||||
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, " 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;
|
||||
ctx->emu_protocol_filter = &slix2_protocol_filter;
|
||||
|
||||
@ -8,19 +8,35 @@
|
||||
#define NFCV_MANUFACTURER_NXP 0x04
|
||||
|
||||
/* ISO15693-3 CUSTOM NXP COMMANDS */
|
||||
#define NFCV_CMD_NXP_SET_EAS 0xA2
|
||||
#define NFCV_CMD_NXP_RESET_EAS 0xA3
|
||||
#define NFCV_CMD_NXP_LOCK_EAS 0xA4
|
||||
#define NFCV_CMD_NXP_EAS_ALARM 0xA5
|
||||
#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6
|
||||
#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7
|
||||
#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0
|
||||
#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1
|
||||
#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2
|
||||
#define NFCV_CMD_NXP_SET_PASSWORD 0xB3
|
||||
#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4
|
||||
#define NFCV_CMD_NXP_DESTROY 0xB9
|
||||
#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA
|
||||
typedef enum {
|
||||
NFCV_CMD_NXP_SET_EAS = 0xA2,
|
||||
NFCV_CMD_NXP_RESET_EAS = 0xA3,
|
||||
NFCV_CMD_NXP_LOCK_EAS = 0xA4,
|
||||
NFCV_CMD_NXP_EAS_ALARM = 0xA5,
|
||||
NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI = 0xA6,
|
||||
NFCV_CMD_NXP_WRITE_EAS_ID = 0xA7,
|
||||
NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION = 0xAB,
|
||||
NFCV_CMD_NXP_INVENTORY_PAGE_READ = 0xB0,
|
||||
NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST = 0xB1,
|
||||
NFCV_CMD_NXP_GET_RANDOM_NUMBER = 0xB2,
|
||||
NFCV_CMD_NXP_SET_PASSWORD = 0xB3,
|
||||
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 */
|
||||
#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_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_unlock(NfcVData* data, uint32_t password_id);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user