mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-03 04:49:59 -07:00
Revert "Merge branch 'pr/446' into 420"
This reverts commit761dc48b3e, reversing changes made to91f037c63f.
This commit is contained in:
@@ -36,7 +36,7 @@ uint32_t crypto1_filter(uint32_t in) {
|
||||
return FURI_BIT(0xEC57E80A, out);
|
||||
}
|
||||
|
||||
static inline uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) {
|
||||
uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) {
|
||||
furi_assert(crypto1);
|
||||
uint8_t out = crypto1_filter(crypto1->odd);
|
||||
uint32_t feed = out & (!!is_encrypted);
|
||||
@@ -58,15 +58,6 @@ uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) {
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline uint8_t crypto1_byte_inline(Crypto1* crypto1, uint8_t in, int is_encrypted) {
|
||||
furi_assert(crypto1);
|
||||
uint8_t out = 0;
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) {
|
||||
furi_assert(crypto1);
|
||||
uint32_t out = 0;
|
||||
@@ -101,7 +92,7 @@ void crypto1_decrypt(
|
||||
decrypted_data[0] = decrypted_byte;
|
||||
} else {
|
||||
for(size_t i = 0; i < encrypted_data_bits / 8; i++) {
|
||||
decrypted_data[i] = crypto1_byte_inline(crypto, 0, 0) ^ encrypted_data[i];
|
||||
decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +117,7 @@ void crypto1_encrypt(
|
||||
} else {
|
||||
memset(encrypted_parity, 0, plain_data_bits / 8 + 1);
|
||||
for(uint8_t i = 0; i < plain_data_bits / 8; i++) {
|
||||
encrypted_data[i] = crypto1_byte_inline(crypto, keystream ? keystream[i] : 0, 0) ^
|
||||
encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
|
||||
plain_data[i];
|
||||
encrypted_parity[i / 8] |=
|
||||
(((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01)
|
||||
|
||||
@@ -12,7 +12,7 @@ void crypto1_reset(Crypto1* crypto1);
|
||||
|
||||
void crypto1_init(Crypto1* crypto1, uint64_t key);
|
||||
|
||||
//uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted);
|
||||
uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted);
|
||||
|
||||
uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted);
|
||||
|
||||
|
||||
@@ -763,39 +763,26 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
uint8_t plain_data[MF_CLASSIC_MAX_DATA_SIZE];
|
||||
MfClassicKey access_key = MfClassicKeyA;
|
||||
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent;
|
||||
|
||||
FURI_LOG_D(TAG, "Starting mf_classic_emulator");
|
||||
|
||||
// Read command
|
||||
while(!command_processed) {
|
||||
if(!is_encrypted) {
|
||||
crypto1_reset(&emulator->crypto);
|
||||
memcpy(plain_data, tx_rx->rx_data, tx_rx->rx_bits / 8);
|
||||
} else {
|
||||
tx_rx->rx_bits = 0;
|
||||
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Error in tx rx. Tx :%d bits, Rx: %d bits. Received:",
|
||||
"Error in tx rx. Tx :%d bits, Rx: %d bits",
|
||||
tx_rx->tx_bits,
|
||||
tx_rx->rx_bits);
|
||||
|
||||
FURI_LOG_D(TAG,"Sent:");
|
||||
for(int pos = 0; pos < tx_rx->tx_bits/8; pos++) {
|
||||
FURI_LOG_D(TAG," %02X", tx_rx->tx_data[pos]);
|
||||
}
|
||||
FURI_LOG_D(TAG,"Received:");
|
||||
for(int pos = 0; pos < tx_rx->rx_bits/8; pos++) {
|
||||
FURI_LOG_D(TAG," %02X", tx_rx->rx_data[pos]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
|
||||
}
|
||||
|
||||
if(plain_data[0] == 0x50 && plain_data[1] == 0x00) {
|
||||
//furi_hal_nfc_listen_sleep();
|
||||
FURI_LOG_T(TAG, "Halt received");
|
||||
furi_hal_nfc_listen_sleep();
|
||||
command_processed = true;
|
||||
break;
|
||||
} else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) {
|
||||
@@ -812,7 +799,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
access_key = MfClassicKeyB;
|
||||
}
|
||||
|
||||
uint32_t nonce = prng_successor(DWT->CYCCNT, 2) ^ 0xAA;
|
||||
uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA;
|
||||
uint8_t nt[4];
|
||||
uint8_t nt_keystream[4];
|
||||
nfc_util_num2bytes(nonce, 4, nt);
|
||||
@@ -820,15 +807,13 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
crypto1_init(&emulator->crypto, key);
|
||||
if(!is_encrypted) {
|
||||
crypto1_word(&emulator->crypto, emulator->cuid ^ nonce, 0);
|
||||
for(size_t pos = 0; pos < sizeof(nt); pos++) {
|
||||
tx_rx->tx_data[pos] = nt[pos];
|
||||
}
|
||||
memcpy(tx_rx->tx_data, nt, sizeof(nt));
|
||||
tx_rx->tx_parity[0] = 0;
|
||||
for(size_t i = 0; i < sizeof(nt); i++) {
|
||||
tx_rx->tx_parity[0] |= nfc_util_odd_parity8(nt[i]) << (7 - i);
|
||||
}
|
||||
tx_rx->tx_bits = sizeof(nt) * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
} else {
|
||||
crypto1_encrypt(
|
||||
&emulator->crypto,
|
||||
@@ -838,10 +823,10 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
tx_rx->tx_data,
|
||||
tx_rx->tx_parity);
|
||||
tx_rx->tx_bits = sizeof(nt) * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
}
|
||||
if(!furi_hal_nfc_tx_rx(tx_rx, 500)) {
|
||||
FURI_LOG_E(TAG, "Error in NT exchange?");
|
||||
FURI_LOG_E(TAG, "Error in NT exchange");
|
||||
command_processed = true;
|
||||
break;
|
||||
}
|
||||
@@ -854,7 +839,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
|
||||
uint32_t nr = nfc_util_bytes2num(tx_rx->rx_data, 4);
|
||||
uint32_t ar = nfc_util_bytes2num(&tx_rx->rx_data[4], 4);
|
||||
/*
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%08lx key%c block %d nt/nr/ar: %08lx %08lx %08lx",
|
||||
@@ -864,7 +849,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
nonce,
|
||||
nr,
|
||||
ar);
|
||||
*/
|
||||
|
||||
crypto1_word(&emulator->crypto, nr, 1);
|
||||
uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0);
|
||||
if(cardRr != prng_successor(nonce, 64)) {
|
||||
@@ -885,7 +870,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
tx_rx->tx_data,
|
||||
tx_rx->tx_parity);
|
||||
tx_rx->tx_bits = sizeof(responce) * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
|
||||
is_encrypted = true;
|
||||
} else if(is_encrypted && plain_data[0] == 0x30) {
|
||||
@@ -916,7 +901,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
} else {
|
||||
tx_rx->tx_data[0] = nack;
|
||||
}
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
tx_rx->tx_bits = 4;
|
||||
furi_hal_nfc_tx_rx(tx_rx, 300);
|
||||
break;
|
||||
@@ -932,7 +917,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
tx_rx->tx_data,
|
||||
tx_rx->tx_parity);
|
||||
tx_rx->tx_bits = 18 * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
} else if(is_encrypted && plain_data[0] == 0xA0) {
|
||||
uint8_t block = plain_data[1];
|
||||
if(block > mf_classic_get_total_block_num(emulator->data.type)) {
|
||||
@@ -941,7 +926,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
// Send ACK
|
||||
uint8_t ack = 0x0A;
|
||||
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
tx_rx->tx_bits = 4;
|
||||
|
||||
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
|
||||
@@ -976,10 +961,9 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
// Send ACK
|
||||
ack = 0x0A;
|
||||
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
tx_rx->tx_bits = 4;
|
||||
} else {
|
||||
FURI_LOG_T(TAG, "%02X unknown received", plain_data[0]);
|
||||
// Unknown command
|
||||
break;
|
||||
}
|
||||
@@ -993,7 +977,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
} else {
|
||||
tx_rx->tx_data[0] = nack;
|
||||
}
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
tx_rx->tx_bits = 4;
|
||||
furi_hal_nfc_tx_rx(tx_rx, 300);
|
||||
}
|
||||
|
||||
@@ -2,27 +2,19 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
#include <furi_hal_resources.h>
|
||||
|
||||
#define NFCA_CMD_RATS (0xE0U)
|
||||
|
||||
#define NFCA_CRC_INIT (0x6363)
|
||||
|
||||
#define NFCA_F_SIG (13560000.0) /* [Hz] NFC frequency */
|
||||
#define NFCA_F_SUB (NFCA_F_SIG/16) /* [Hz] NFC subcarrier frequency fs/16 (847500 Hz) */
|
||||
#define T_SUB (1000000000000.0f / NFCA_F_SUB) /* [ps] subcarrier period = 1/NFCA_F_SUB (1.18 µs) */
|
||||
#define T_SUB_PHASE (T_SUB/2) /* [ps] a single subcarrier phase (590 µs) */
|
||||
#define NFCA_F_SIG (13560000.0)
|
||||
#define T_SIG 7374 //73.746ns*100
|
||||
#define T_SIG_x8 58992 //T_SIG*8
|
||||
#define T_SIG_x8_x8 471936 //T_SIG*8*8
|
||||
#define T_SIG_x8_x9 530928 //T_SIG*8*9
|
||||
|
||||
#define NFCA_SIGNAL_MAX_EDGES (1350)
|
||||
|
||||
#define SEQ_SOF 0
|
||||
#define SEQ_BIT0 1
|
||||
#define SEQ_BIT1 2
|
||||
#define SEQ_EOF 3
|
||||
#define SEQ_IDLE 4
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t cmd;
|
||||
uint8_t param;
|
||||
@@ -71,69 +63,46 @@ bool nfca_emulation_handler(
|
||||
return sleep;
|
||||
}
|
||||
|
||||
static void nfca_add_bit(DigitalSignal* signal, bool bit) {
|
||||
if(bit) {
|
||||
signal->start_level = true;
|
||||
for(size_t i = 0; i < 7; i++) {
|
||||
signal->edge_timings[i] = T_SIG_x8;
|
||||
}
|
||||
signal->edge_timings[7] = T_SIG_x8_x9;
|
||||
signal->edge_cnt = 8;
|
||||
} else {
|
||||
signal->start_level = false;
|
||||
signal->edge_timings[0] = T_SIG_x8_x8;
|
||||
for(size_t i = 1; i < 9; i++) {
|
||||
signal->edge_timings[i] = T_SIG_x8;
|
||||
}
|
||||
signal->edge_cnt = 9;
|
||||
}
|
||||
}
|
||||
|
||||
static void nfca_add_byte(NfcaSignal* nfca_signal, uint8_t byte, bool parity) {
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
if(byte & (1 << i)) {
|
||||
digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1);
|
||||
digital_signal_append(nfca_signal->tx_signal, nfca_signal->one);
|
||||
} else {
|
||||
digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0);
|
||||
digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero);
|
||||
}
|
||||
}
|
||||
if(parity) {
|
||||
digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1);
|
||||
digital_signal_append(nfca_signal->tx_signal, nfca_signal->one);
|
||||
} else {
|
||||
digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfca_add_modulation(DigitalSignal* signal, size_t phases) {
|
||||
for(size_t i = 0; i < phases; i++) {
|
||||
signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfca_add_silence(DigitalSignal* signal, size_t phases) {
|
||||
bool end_level = signal->start_level ^ ((signal->edge_cnt % 2) == 0);
|
||||
|
||||
if((signal->edge_cnt == 0) || end_level) {
|
||||
signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE);
|
||||
} else {
|
||||
signal->edge_timings[signal->edge_cnt - 1] += DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE);
|
||||
digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero);
|
||||
}
|
||||
}
|
||||
|
||||
NfcaSignal* nfca_signal_alloc() {
|
||||
NfcaSignal* nfca_signal = malloc(sizeof(NfcaSignal));
|
||||
|
||||
/* ISO14443-2 defines 3 sequences for type A communication */
|
||||
nfca_signal->seq_d = digital_signal_alloc(10);
|
||||
nfca_signal->seq_e = digital_signal_alloc(10);
|
||||
nfca_signal->seq_f = digital_signal_alloc(10);
|
||||
|
||||
/* SEQ D has the first half modulated, used as SOF */
|
||||
nfca_signal->seq_d->start_level = true;
|
||||
nfca_add_modulation(nfca_signal->seq_d, 8);
|
||||
nfca_add_silence(nfca_signal->seq_d, 8);
|
||||
|
||||
/* SEQ E has the second half modulated */
|
||||
nfca_signal->seq_e->start_level = false;
|
||||
nfca_add_silence(nfca_signal->seq_e, 8);
|
||||
nfca_add_modulation(nfca_signal->seq_e, 8);
|
||||
|
||||
/* SEQ F is just no modulation, used as EOF */
|
||||
nfca_signal->seq_f->start_level = false;
|
||||
nfca_add_silence(nfca_signal->seq_f, 16);
|
||||
|
||||
nfca_signal->tx_signal = digital_sequence_alloc(NFCA_SIGNAL_MAX_EDGES, &gpio_spi_r_mosi);
|
||||
|
||||
/* we are dealing with shorter sequences, enable bake-before-sending */
|
||||
//nfca_signal->tx_signal->bake = true;
|
||||
|
||||
digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_SOF, nfca_signal->seq_d);
|
||||
digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_BIT0, nfca_signal->seq_e);
|
||||
digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_BIT1, nfca_signal->seq_d);
|
||||
digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_EOF, nfca_signal->seq_f);
|
||||
digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_IDLE, nfca_signal->seq_f);
|
||||
nfca_signal->one = digital_signal_alloc(10);
|
||||
nfca_signal->zero = digital_signal_alloc(10);
|
||||
nfca_add_bit(nfca_signal->one, true);
|
||||
nfca_add_bit(nfca_signal->zero, false);
|
||||
nfca_signal->tx_signal = digital_signal_alloc(NFCA_SIGNAL_MAX_EDGES);
|
||||
|
||||
return nfca_signal;
|
||||
}
|
||||
@@ -141,10 +110,9 @@ NfcaSignal* nfca_signal_alloc() {
|
||||
void nfca_signal_free(NfcaSignal* nfca_signal) {
|
||||
furi_assert(nfca_signal);
|
||||
|
||||
digital_signal_free(nfca_signal->seq_d);
|
||||
digital_signal_free(nfca_signal->seq_e);
|
||||
digital_signal_free(nfca_signal->seq_f);
|
||||
digital_sequence_free(nfca_signal->tx_signal);
|
||||
digital_signal_free(nfca_signal->one);
|
||||
digital_signal_free(nfca_signal->zero);
|
||||
digital_signal_free(nfca_signal->tx_signal);
|
||||
free(nfca_signal);
|
||||
}
|
||||
|
||||
@@ -153,18 +121,17 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u
|
||||
furi_assert(data);
|
||||
furi_assert(parity);
|
||||
|
||||
digital_sequence_clear(nfca_signal->tx_signal);
|
||||
|
||||
/* add some idle bit times before SOF in case the GPIO was active */
|
||||
digital_sequence_add(nfca_signal->tx_signal, SEQ_IDLE);
|
||||
digital_sequence_add(nfca_signal->tx_signal, SEQ_SOF);
|
||||
nfca_signal->tx_signal->edge_cnt = 0;
|
||||
nfca_signal->tx_signal->start_level = true;
|
||||
// Start of frame
|
||||
digital_signal_append(nfca_signal->tx_signal, nfca_signal->one);
|
||||
|
||||
if(bits < 8) {
|
||||
for(size_t i = 0; i < bits; i++) {
|
||||
if(FURI_BIT(data[0], i)) {
|
||||
digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1);
|
||||
digital_signal_append(nfca_signal->tx_signal, nfca_signal->one);
|
||||
} else {
|
||||
digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0);
|
||||
digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -172,6 +139,4 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u
|
||||
nfca_add_byte(nfca_signal, data[i], parity[i / 8] & (1 << (7 - (i & 0x07))));
|
||||
}
|
||||
}
|
||||
|
||||
digital_sequence_add(nfca_signal->tx_signal, SEQ_EOF);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
#include <lib/digital_signal/digital_signal.h>
|
||||
|
||||
typedef struct {
|
||||
DigitalSignal* seq_d; /* sequence D, modulation with subcarrier during first half */
|
||||
DigitalSignal* seq_e; /* sequence E, modulation with subcarrier during second half */
|
||||
DigitalSignal* seq_f; /* sequence F, no modulation at all */
|
||||
DigitalSequence* tx_signal;
|
||||
DigitalSignal* one;
|
||||
DigitalSignal* zero;
|
||||
DigitalSignal* tx_signal;
|
||||
} NfcaSignal;
|
||||
|
||||
uint16_t nfca_get_crc16(uint8_t* buff, uint16_t len);
|
||||
@@ -27,4 +26,3 @@ NfcaSignal* nfca_signal_alloc();
|
||||
void nfca_signal_free(NfcaSignal* nfca_signal);
|
||||
|
||||
void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, uint8_t* parity);
|
||||
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
#include <limits.h>
|
||||
#include <furi.h>
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_nfc.h>
|
||||
#include <furi_hal_spi.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
#include <furi_hal_cortex.h>
|
||||
#include <furi_hal_resources.h>
|
||||
#include <st25r3916.h>
|
||||
#include <st25r3916_irq.h>
|
||||
|
||||
#include "nfca_trans_rx.h"
|
||||
|
||||
#define TAG "NfcA-trans-rx"
|
||||
|
||||
|
||||
void nfca_trans_rx_init(NfcaTransRxState *state) {
|
||||
FURI_LOG_D(TAG, "Starting NfcA transparent rx");
|
||||
|
||||
st25r3916ExecuteCommand(ST25R3916_CMD_STOP);
|
||||
st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3);
|
||||
st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88);
|
||||
st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE);
|
||||
|
||||
furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc);
|
||||
|
||||
/* allocate a 512 edge buffer, more than enough */
|
||||
state->reader_signal = pulse_reader_alloc(&gpio_spi_r_miso, 512);
|
||||
/* timebase shall be 1 ns */
|
||||
pulse_reader_set_timebase(state->reader_signal, PulseReaderUnitNanosecond);
|
||||
|
||||
pulse_reader_start(state->reader_signal);
|
||||
|
||||
/* set start values */
|
||||
state->bits_received = 0;
|
||||
state->have_sof = false;
|
||||
state->valid_frame = false;
|
||||
}
|
||||
|
||||
void nfca_trans_rx_deinit(NfcaTransRxState *state) {
|
||||
furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc);
|
||||
pulse_reader_free(state->reader_signal);
|
||||
}
|
||||
|
||||
void nfca_trans_rx_pause(NfcaTransRxState *state) {
|
||||
pulse_reader_stop(state->reader_signal);
|
||||
}
|
||||
|
||||
void nfca_trans_rx_continue(NfcaTransRxState *state) {
|
||||
pulse_reader_start(state->reader_signal);
|
||||
}
|
||||
|
||||
static void nfca_bit_received(NfcaTransRxState *state, uint8_t bit) {
|
||||
|
||||
/* According to ISO14443-3 short frames have 7 bits and standard 9 bits per byte,
|
||||
where the 9th bit is odd parity. Data is transmitted LSB first. */
|
||||
uint32_t byte_num = (state->bits_received / 9);
|
||||
uint32_t bit_num = (state->bits_received % 9);
|
||||
|
||||
if(byte_num >= NFCA_FRAME_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(bit_num == 8) {
|
||||
uint32_t parity_value = 1 << (state->bits_received / 9);
|
||||
state->parity_bits &= ~parity_value;
|
||||
state->parity_bits |= bit ? parity_value : 0;
|
||||
} else {
|
||||
uint32_t bit_value = 1 << bit_num;
|
||||
state->frame_data[byte_num] &= ~bit_value;
|
||||
state->frame_data[byte_num] |= bit ? bit_value : 0;
|
||||
}
|
||||
|
||||
state->bits_received++;
|
||||
}
|
||||
|
||||
|
||||
bool nfca_trans_rx_loop(NfcaTransRxState *state, uint32_t timeout_ms) {
|
||||
furi_assert(state);
|
||||
|
||||
state->valid_frame = false;
|
||||
state->have_sof = false;
|
||||
state->bits_received = 0;
|
||||
|
||||
bool done = false;
|
||||
|
||||
uint32_t timeout_us = timeout_ms * 1000;
|
||||
|
||||
while(!done) {
|
||||
uint32_t nsec = pulse_reader_receive(state->reader_signal, timeout_us);
|
||||
|
||||
bool eof = state->have_sof && (nsec >= (2 * NFCA_TB));
|
||||
bool lost_pulse = false;
|
||||
|
||||
if(state->have_sof && nsec == PULSE_READER_LOST_EDGE) {
|
||||
nsec = NFCA_T1;
|
||||
lost_pulse = true;
|
||||
} else if(nsec == PULSE_READER_NO_EDGE) {
|
||||
done = true;
|
||||
}
|
||||
|
||||
if(IS_T1(nsec) || eof) {
|
||||
timeout_us = (3 * NFCA_TB) / 1000;
|
||||
if(!state->have_sof) {
|
||||
state->frame_time = -(NFCA_TB - nsec);
|
||||
state->have_sof = true;
|
||||
state->valid_frame = false;
|
||||
state->bits_received = 0;
|
||||
state->debug_pos = 0;
|
||||
if(lost_pulse) {
|
||||
state->frame_time -= nsec;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(state->frame_time > NFCA_TB_MIN) {
|
||||
state->frame_time -= NFCA_TB;
|
||||
nfca_bit_received(state, 0);
|
||||
}
|
||||
|
||||
if(IS_ZERO(state->frame_time)) {
|
||||
state->frame_time = -(NFCA_TB - nsec);
|
||||
nfca_bit_received(state, 0);
|
||||
} else if(IS_TX(state->frame_time)) {
|
||||
state->frame_time = -(NFCA_TX - nsec);
|
||||
nfca_bit_received(state, 1);
|
||||
} else {
|
||||
if(eof) {
|
||||
state->have_sof = false;
|
||||
state->valid_frame = true;
|
||||
done = true;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(!state->have_sof) {
|
||||
if(IS_TB(nsec)) {
|
||||
state->frame_time = 0;
|
||||
state->have_sof = true;
|
||||
state->valid_frame = false;
|
||||
state->bits_received = 0;
|
||||
state->debug_pos = 0;
|
||||
if(lost_pulse) {
|
||||
state->frame_time -= nsec;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
state->frame_time = 0;
|
||||
}
|
||||
} else {
|
||||
state->frame_time += nsec;
|
||||
}
|
||||
}
|
||||
|
||||
if(lost_pulse) {
|
||||
state->frame_time -= nsec;
|
||||
}
|
||||
}
|
||||
|
||||
if(state->valid_frame) {
|
||||
if(state->bits_received > 7) {
|
||||
/* a last 0-bit will look like a missing bit */
|
||||
if((state->bits_received % 9) == 8) {
|
||||
nfca_bit_received(state, 0);
|
||||
state->bits_received++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state->valid_frame;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <lib/digital_signal/digital_signal.h>
|
||||
#include <lib/pulse_reader/pulse_reader.h>
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
#include "nfc_util.h"
|
||||
|
||||
/* assume fc/128 */
|
||||
#define NFCA_FC (13560000.0f) /* MHz */
|
||||
#define NFCA_FC_K ((uint32_t)(NFCA_FC/1000)) /* kHz */
|
||||
#define NFCA_T1 (28.0f / NFCA_FC * 1000000000)
|
||||
#define NFCA_T1_MIN (24.0f / NFCA_FC * 1000000000)
|
||||
#define NFCA_T1_MAX (41.0f / NFCA_FC * 1000000000)
|
||||
#define NFCA_TX (64.0f / NFCA_FC * 1000000000) /* 4.7198 µs */
|
||||
#define NFCA_TX_MIN (0.90f * NFCA_TX)
|
||||
#define NFCA_TX_MAX (1.10f * NFCA_TX)
|
||||
#define NFCA_TB (128.0f / NFCA_FC * 1000000000) /* 9.4395 µs */
|
||||
#define NFCA_TB_MIN (0.80f * NFCA_TB)
|
||||
#define NFCA_TB_MAX (1.20f * NFCA_TB)
|
||||
|
||||
#define IS_T1(x) ((x)>=NFCA_T1_MIN && (x)<=NFCA_T1_MAX)
|
||||
#define IS_TX(x) ((x)>=NFCA_TX_MIN && (x)<=NFCA_TX_MAX)
|
||||
#define IS_TB(x) ((x)>=NFCA_TB_MIN && (x)<=NFCA_TB_MAX)
|
||||
#define IS_ZERO(x) ((x)>=-NFCA_T1_MIN/2 && (x)<=NFCA_T1_MIN/2)
|
||||
|
||||
#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f)
|
||||
#define DIGITAL_SIGNAL_UNIT_US (100000.0f)
|
||||
|
||||
#define NFCA_FRAME_LENGTH 32
|
||||
#define NFCA_DEBUG_LENGTH 128
|
||||
|
||||
|
||||
typedef struct {
|
||||
bool have_sof;
|
||||
bool valid_frame;
|
||||
int32_t frame_time;
|
||||
size_t bits_received;
|
||||
uint8_t frame_data[NFCA_FRAME_LENGTH];
|
||||
uint32_t debug_buffer[NFCA_DEBUG_LENGTH];
|
||||
size_t debug_pos;
|
||||
uint32_t parity_bits;
|
||||
PulseReader *reader_signal;
|
||||
} NfcaTransRxState;
|
||||
|
||||
bool nfca_trans_rx_loop(NfcaTransRxState *state, uint32_t timeout_ms);
|
||||
void nfca_trans_rx_deinit(NfcaTransRxState *state);
|
||||
void nfca_trans_rx_init(NfcaTransRxState *state);
|
||||
|
||||
void nfca_trans_rx_pause(NfcaTransRxState *state);
|
||||
void nfca_trans_rx_continue(NfcaTransRxState *state);
|
||||
@@ -1,747 +0,0 @@
|
||||
#include <limits.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_nfc.h>
|
||||
#include <furi_hal_spi.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
#include <furi_hal_cortex.h>
|
||||
#include <furi_hal_resources.h>
|
||||
#include <st25r3916.h>
|
||||
#include <st25r3916_irq.h>
|
||||
|
||||
#include "nfcv.h"
|
||||
#include "nfc_util.h"
|
||||
#include "slix.h"
|
||||
|
||||
#define TAG "NfcV"
|
||||
|
||||
ReturnCode nfcv_inventory(uint8_t* uid) {
|
||||
uint16_t received = 0;
|
||||
rfalNfcvInventoryRes res;
|
||||
ReturnCode ret = ERR_NONE;
|
||||
|
||||
for(int tries = 0; tries < 5; tries++) {
|
||||
/* TODO: needs proper abstraction via fury_hal(_ll)_* */
|
||||
ret = rfalNfcvPollerInventory(
|
||||
RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received);
|
||||
|
||||
if(ret == ERR_NONE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ret == ERR_NONE) {
|
||||
if(uid != NULL) {
|
||||
memcpy(uid, res.UID, 8);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ReturnCode nfcv_read_blocks(
|
||||
NfcVReader* reader,
|
||||
NfcVData* data) {
|
||||
|
||||
UNUSED(reader);
|
||||
|
||||
uint16_t received = 0;
|
||||
for(size_t block = 0; block < data->block_num; block++) {
|
||||
uint8_t rxBuf[32];
|
||||
FURI_LOG_D(TAG, "Reading block %d/%d", block, (data->block_num - 1));
|
||||
|
||||
ReturnCode ret = ERR_NONE;
|
||||
for(int tries = 0; tries < 5; tries++) {
|
||||
ret = rfalNfcvPollerReadSingleBlock(
|
||||
RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block,
|
||||
rxBuf, sizeof(rxBuf), &received);
|
||||
|
||||
if(ret == ERR_NONE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(ret != ERR_NONE) {
|
||||
FURI_LOG_D(TAG, "failed to read: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
memcpy(&(data->data[block * data->block_size]), &rxBuf[1], data->block_size);
|
||||
FURI_LOG_D(TAG, " %02X %02X %02X %02X",
|
||||
data->data[block * data->block_size + 0], data->data[block * data->block_size + 1],
|
||||
data->data[block * data->block_size + 2], data->data[block * data->block_size + 3]);
|
||||
}
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data) {
|
||||
uint8_t rxBuf[32];
|
||||
uint16_t received = 0;
|
||||
ReturnCode ret = ERR_NONE;
|
||||
|
||||
FURI_LOG_D(TAG, "Read SYSTEM INFORMATION...");
|
||||
|
||||
for(int tries = 0; tries < 5; tries++) {
|
||||
/* TODO: needs proper abstraction via fury_hal(_ll)_* */
|
||||
ret = rfalNfcvPollerGetSystemInformation(
|
||||
RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, rxBuf, sizeof(rxBuf), &received);
|
||||
|
||||
if(ret == ERR_NONE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ret == ERR_NONE) {
|
||||
nfc_data->type = FuriHalNfcTypeV;
|
||||
nfc_data->uid_len = 8;
|
||||
/* UID is stored reversed in this response */
|
||||
for(int pos = 0; pos < nfc_data->uid_len; pos++) {
|
||||
nfc_data->uid[pos] = rxBuf[2 + (7 - pos)];
|
||||
}
|
||||
data->dsfid = rxBuf[10];
|
||||
data->afi = rxBuf[11];
|
||||
data->block_num = rxBuf[12] + 1;
|
||||
data->block_size = rxBuf[13] + 1;
|
||||
data->ic_ref = rxBuf[14];
|
||||
FURI_LOG_D(TAG, " UID: %02X %02X %02X %02X %02X %02X %02X %02X",
|
||||
nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3],
|
||||
nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]);
|
||||
FURI_LOG_D(TAG, " DSFID %d, AFI %d, Blocks %d, Size %d, IC Ref %d", data->dsfid, data->afi, data->block_num, data->block_size, data->ic_ref);
|
||||
return ret;
|
||||
}
|
||||
FURI_LOG_D(TAG, "Failed: %d", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool nfcv_read_card(
|
||||
NfcVReader* reader,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
NfcVData* nfcv_data) {
|
||||
furi_assert(reader);
|
||||
furi_assert(nfc_data);
|
||||
furi_assert(nfcv_data);
|
||||
|
||||
if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(nfcv_read_blocks(reader, nfcv_data) != ERR_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(slix_check_card_type(nfc_data)) {
|
||||
FURI_LOG_I(TAG, "NXP SLIX detected");
|
||||
nfcv_data->type = NfcVTypeSlix;
|
||||
} else if(slix2_check_card_type(nfc_data)) {
|
||||
FURI_LOG_I(TAG, "NXP SLIX2 detected");
|
||||
nfcv_data->type = NfcVTypeSlix2;
|
||||
} else if(slix_s_check_card_type(nfc_data)) {
|
||||
FURI_LOG_I(TAG, "NXP SLIX-S detected");
|
||||
nfcv_data->type = NfcVTypeSlixS;
|
||||
} else if(slix_l_check_card_type(nfc_data)) {
|
||||
FURI_LOG_I(TAG, "NXP SLIX-L detected");
|
||||
nfcv_data->type = NfcVTypeSlixL;
|
||||
} else {
|
||||
nfcv_data->type = NfcVTypePlain;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* emulation part */
|
||||
PulseReader *reader_signal = NULL;
|
||||
|
||||
DigitalSignal* nfcv_resp_pulse_32 = NULL;
|
||||
DigitalSignal* nfcv_resp_unmod = NULL;
|
||||
DigitalSignal* nfcv_resp_one = NULL;
|
||||
DigitalSignal* nfcv_resp_zero = NULL;
|
||||
DigitalSignal* nfcv_resp_sof = NULL;
|
||||
DigitalSignal* nfcv_resp_eof = NULL;
|
||||
DigitalSignal* nfcv_resp_unmod_256 = NULL;
|
||||
DigitalSignal* nfcv_resp_unmod_768 = NULL;
|
||||
|
||||
//const GpioPin* nfcv_out_io = &gpio_ext_pb2;
|
||||
const GpioPin* nfcv_out_io = &gpio_spi_r_mosi;
|
||||
|
||||
DigitalSequence* nfcv_signal = NULL;
|
||||
|
||||
#define SIG_SOF 0
|
||||
#define SIG_BIT0 1
|
||||
#define SIG_BIT1 2
|
||||
#define SIG_EOF 3
|
||||
|
||||
|
||||
void nfcv_crc(uint8_t* data, uint32_t length, uint8_t* out) {
|
||||
uint32_t reg = 0xFFFF;
|
||||
uint32_t i = 0;
|
||||
uint32_t j = 0;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
reg = reg ^ ((uint32_t)data[i]);
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (reg & 0x0001) {
|
||||
reg = (reg >> 1) ^ 0x8408;
|
||||
} else {
|
||||
reg = (reg >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t crc = ~(uint16_t)(reg & 0xffff);
|
||||
|
||||
out[0] = crc & 0xFF;
|
||||
out[1] = crc >> 8;
|
||||
}
|
||||
|
||||
void nfcv_emu_free() {
|
||||
digital_sequence_free(nfcv_signal);
|
||||
digital_signal_free(nfcv_resp_unmod_256);
|
||||
digital_signal_free(nfcv_resp_pulse_32);
|
||||
digital_signal_free(nfcv_resp_one);
|
||||
digital_signal_free(nfcv_resp_zero);
|
||||
digital_signal_free(nfcv_resp_sof);
|
||||
digital_signal_free(nfcv_resp_eof);
|
||||
|
||||
nfcv_signal = NULL;
|
||||
nfcv_resp_unmod_256 = NULL;
|
||||
nfcv_resp_pulse_32 = NULL;
|
||||
nfcv_resp_one = NULL;
|
||||
nfcv_resp_zero = NULL;
|
||||
nfcv_resp_sof = NULL;
|
||||
nfcv_resp_eof = NULL;
|
||||
}
|
||||
|
||||
void nfcv_emu_alloc() {
|
||||
|
||||
if(!nfcv_signal) {
|
||||
/* assuming max frame length is 255 bytes */
|
||||
nfcv_signal = digital_sequence_alloc(8 * 255 + 2, nfcv_out_io);
|
||||
}
|
||||
|
||||
if(!nfcv_resp_unmod_256) {
|
||||
/* unmodulated 256/fc signal as building block */
|
||||
nfcv_resp_unmod_256 = digital_signal_alloc(4);
|
||||
nfcv_resp_unmod_256->start_level = false;
|
||||
nfcv_resp_unmod_256->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S);
|
||||
nfcv_resp_unmod_256->edge_cnt = 1;
|
||||
}
|
||||
if(!nfcv_resp_pulse_32) {
|
||||
/* modulated fc/32 pulse as building block */
|
||||
nfcv_resp_pulse_32 = digital_signal_alloc(4);
|
||||
nfcv_resp_pulse_32->start_level = true;
|
||||
nfcv_resp_pulse_32->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S);
|
||||
nfcv_resp_pulse_32->edge_timings[1] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S);
|
||||
nfcv_resp_pulse_32->edge_cnt = 2;
|
||||
}
|
||||
if(!nfcv_resp_one) {
|
||||
/* logical one: 256/fc unmodulated then 8 pulses fc/32 */
|
||||
nfcv_resp_one = digital_signal_alloc(24);
|
||||
digital_signal_append(nfcv_resp_one, nfcv_resp_unmod_256);
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
digital_signal_append(nfcv_resp_one, nfcv_resp_pulse_32);
|
||||
}
|
||||
}
|
||||
if(!nfcv_resp_zero) {
|
||||
/* logical zero: 8 pulses fc/32 then 256/fc unmodulated */
|
||||
nfcv_resp_zero = digital_signal_alloc(24);
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
digital_signal_append(nfcv_resp_zero, nfcv_resp_pulse_32);
|
||||
}
|
||||
digital_signal_append(nfcv_resp_zero, nfcv_resp_unmod_256);
|
||||
}
|
||||
if(!nfcv_resp_sof) {
|
||||
/* SOF: unmodulated 768/fc, 24 pulses fc/32, logic 1 */
|
||||
nfcv_resp_sof = digital_signal_alloc(128);
|
||||
digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256);
|
||||
digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256);
|
||||
digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256);
|
||||
for(size_t i = 0; i < 24; i++) {
|
||||
digital_signal_append(nfcv_resp_sof, nfcv_resp_pulse_32);
|
||||
}
|
||||
digital_signal_append(nfcv_resp_sof, nfcv_resp_one);
|
||||
}
|
||||
if(!nfcv_resp_eof) {
|
||||
/* EOF: logic 0, 24 pulses fc/32, unmodulated 768/fc */
|
||||
nfcv_resp_eof = digital_signal_alloc(128);
|
||||
digital_signal_append(nfcv_resp_eof, nfcv_resp_zero);
|
||||
for(size_t i = 0; i < 24; i++) {
|
||||
digital_signal_append(nfcv_resp_eof, nfcv_resp_pulse_32);
|
||||
}
|
||||
digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256);
|
||||
digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256);
|
||||
digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256);
|
||||
/* add extra silence */
|
||||
digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256);
|
||||
}
|
||||
|
||||
digital_sequence_set_signal(nfcv_signal, SIG_SOF, nfcv_resp_sof);
|
||||
digital_sequence_set_signal(nfcv_signal, SIG_BIT0, nfcv_resp_zero);
|
||||
digital_sequence_set_signal(nfcv_signal, SIG_BIT1, nfcv_resp_one);
|
||||
digital_sequence_set_signal(nfcv_signal, SIG_EOF, nfcv_resp_eof);
|
||||
}
|
||||
|
||||
|
||||
void nfcv_emu_send_raw(uint8_t* data, uint8_t length) {
|
||||
|
||||
digital_sequence_clear(nfcv_signal);
|
||||
digital_sequence_add(nfcv_signal, SIG_SOF);
|
||||
|
||||
for(int bit_total = 0; bit_total < length * 8; bit_total++) {
|
||||
uint32_t byte_pos = bit_total / 8;
|
||||
uint32_t bit_pos = bit_total % 8;
|
||||
uint8_t bit_val = 0x01 << bit_pos;
|
||||
|
||||
digital_sequence_add(nfcv_signal, (data[byte_pos] & bit_val) ? SIG_BIT1 : SIG_BIT0);
|
||||
}
|
||||
|
||||
digital_sequence_add(nfcv_signal, SIG_EOF);
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
digital_sequence_send(nfcv_signal);
|
||||
FURI_CRITICAL_EXIT();
|
||||
furi_hal_gpio_write(nfcv_out_io, false);
|
||||
}
|
||||
|
||||
void nfcv_emu_send(uint8_t* data, uint8_t length) {
|
||||
uint8_t buffer[64];
|
||||
|
||||
if(length + 2 > (uint8_t)sizeof(buffer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(buffer, data, length);
|
||||
nfcv_crc(buffer, length, &buffer[length]);
|
||||
nfcv_emu_send_raw(buffer, length + 2);
|
||||
}
|
||||
|
||||
|
||||
void nfcv_uidcpy(uint8_t *dst, uint8_t *src) {
|
||||
for(int pos = 0; pos < 8; pos++) {
|
||||
dst[pos] = src[7-pos];
|
||||
}
|
||||
}
|
||||
|
||||
int nfcv_uidcmp(uint8_t *dst, uint8_t *src) {
|
||||
for(int pos = 0; pos < 8; pos++) {
|
||||
if(dst[pos] != src[7-pos]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nfcv_read_le(uint8_t *data, uint32_t length) {
|
||||
uint32_t value = 0;
|
||||
|
||||
for(uint32_t pos = 0; pos < length; pos++) {
|
||||
value |= data[pos] << ((int)pos * 8);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t nfcv_read_be(uint8_t *data, uint32_t length) {
|
||||
uint32_t value = 0;
|
||||
|
||||
for(uint32_t pos = 0; pos < length; pos++) {
|
||||
value <<= 8;
|
||||
value |= data[pos];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint8_t* payload, uint32_t payload_length) {
|
||||
|
||||
if(!payload_length) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t flags = payload[0];
|
||||
uint8_t command = payload[1];
|
||||
bool addressed = !(flags & RFAL_NFCV_REQ_FLAG_INVENTORY) && (flags & RFAL_NFCV_REQ_FLAG_ADDRESS);
|
||||
bool advanced = (command >= 0xA0);
|
||||
uint8_t address_offset = 2 + (advanced ? 1 : 0);
|
||||
uint8_t payload_offset = address_offset + (addressed ? 8 : 0);
|
||||
uint8_t *address = &payload[address_offset];
|
||||
|
||||
if(addressed && nfcv_uidcmp(address, nfc_data->uid)) {
|
||||
FURI_LOG_D(TAG, "addressed command 0x%02X, but not for us:", command);
|
||||
FURI_LOG_D(TAG, " dest: %02X%02X%02X%02X%02X%02X%02X%02X", address[7], address[6], address[5], address[4], address[3], address[2], address[1], address[0]);
|
||||
FURI_LOG_D(TAG, " our UID: %02X%02X%02X%02X%02X%02X%02X%02X", nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t response_buffer[32];
|
||||
|
||||
switch(nfcv_data->type) {
|
||||
case NfcVTypeSlixL:
|
||||
if(nfcv_data->sub_data.slix_l.privacy &&
|
||||
command != ISO15693_CMD_NXP_GET_RANDOM_NUMBER &&
|
||||
command != ISO15693_CMD_NXP_SET_PASSWORD) {
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "command 0x%02X ignored, privacy mode", command);
|
||||
FURI_LOG_D(TAG, "%s", nfcv_data->last_command);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* unfortunately the response is quicker than the original NFC tag which causes frame misses */
|
||||
furi_delay_us(270);
|
||||
|
||||
switch(command) {
|
||||
|
||||
case ISO15693_INVENTORY:
|
||||
{
|
||||
response_buffer[0] = ISO15693_NOERROR;
|
||||
response_buffer[1] = nfcv_data->dsfid;
|
||||
nfcv_uidcpy(&response_buffer[2], nfc_data->uid);
|
||||
|
||||
nfcv_emu_send(response_buffer, 10);
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY");
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_STAYQUIET:
|
||||
{
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "STAYQUIET");
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_LOCKBLOCK:
|
||||
{
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCKBLOCK");
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_READ_MULTI_BLOCK:
|
||||
{
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ_MULTI_BLOCK");
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_WRITE_MULTI_BLOCK:
|
||||
{
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_MULTI_BLOCK");
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_SELECT:
|
||||
{
|
||||
response_buffer[0] = ISO15693_NOERROR;
|
||||
nfcv_emu_send(response_buffer, 1);
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SELECT");
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_READBLOCK:
|
||||
{
|
||||
uint8_t block = payload[payload_offset];
|
||||
|
||||
if(block >= nfcv_data->block_num) {
|
||||
response_buffer[0] = ISO15693_ERROR_BLOCK_WRITE;
|
||||
nfcv_emu_send(response_buffer, 1);
|
||||
} else {
|
||||
response_buffer[0] = ISO15693_NOERROR;
|
||||
memcpy(&response_buffer[1], &nfcv_data->data[nfcv_data->block_size * block], nfcv_data->block_size);
|
||||
nfcv_emu_send(response_buffer, 1 + nfcv_data->block_size);
|
||||
}
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block);
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_WRITEBLOCK:
|
||||
{
|
||||
uint8_t block = payload[payload_offset];
|
||||
uint8_t *data = &payload[payload_offset + 1];
|
||||
|
||||
if(block >= nfcv_data->block_num) {
|
||||
response_buffer[0] = ISO15693_ERROR_BLOCK_WRITE;
|
||||
} else {
|
||||
response_buffer[0] = ISO15693_NOERROR;
|
||||
memcpy(&nfcv_data->data[nfcv_data->block_size * block], &response_buffer[1], nfcv_data->block_size);
|
||||
}
|
||||
nfcv_emu_send(response_buffer, 1);
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_GET_SYSTEM_INFO:
|
||||
{
|
||||
response_buffer[0] = ISO15693_NOERROR;
|
||||
response_buffer[1] = 0x0F;
|
||||
nfcv_uidcpy(&response_buffer[2], nfc_data->uid);
|
||||
response_buffer[10] = nfcv_data->dsfid; /* DSFID */
|
||||
response_buffer[11] = nfcv_data->afi; /* AFI */
|
||||
response_buffer[12] = nfcv_data->block_num - 1; /* number of blocks */
|
||||
response_buffer[13] = nfcv_data->block_size - 1; /* block size */
|
||||
response_buffer[14] = nfcv_data->ic_ref; /* IC reference */
|
||||
|
||||
nfcv_emu_send(response_buffer, 15);
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_CMD_NXP_GET_RANDOM_NUMBER:
|
||||
{
|
||||
nfcv_data->sub_data.slix_l.rand[0] = furi_hal_random_get();
|
||||
nfcv_data->sub_data.slix_l.rand[1] = furi_hal_random_get();
|
||||
|
||||
response_buffer[0] = ISO15693_NOERROR;
|
||||
response_buffer[1] = nfcv_data->sub_data.slix_l.rand[1];
|
||||
response_buffer[2] = nfcv_data->sub_data.slix_l.rand[0];
|
||||
|
||||
nfcv_emu_send(response_buffer, 3);
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command),
|
||||
"GET_RANDOM_NUMBER -> 0x%02X%02X",
|
||||
nfcv_data->sub_data.slix_l.rand[0],
|
||||
nfcv_data->sub_data.slix_l.rand[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_CMD_NXP_SET_PASSWORD:
|
||||
{
|
||||
uint8_t password_id = payload[payload_offset];
|
||||
uint8_t *password_xored = &payload[payload_offset + 1];
|
||||
uint8_t *rand = nfcv_data->sub_data.slix_l.rand;
|
||||
uint8_t *password = NULL;
|
||||
uint8_t password_rcv[4];
|
||||
|
||||
switch(password_id) {
|
||||
case 4:
|
||||
password = nfcv_data->sub_data.slix_l.key_privacy;
|
||||
break;
|
||||
case 8:
|
||||
password = nfcv_data->sub_data.slix_l.key_destroy;
|
||||
break;
|
||||
case 10:
|
||||
password = nfcv_data->sub_data.slix_l.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_expect = nfcv_read_be(password, 4);
|
||||
uint32_t pass_received = nfcv_read_be(password_rcv, 4);
|
||||
|
||||
if(pass_expect == pass_received) {
|
||||
nfcv_data->sub_data.slix_l.privacy = false;
|
||||
response_buffer[0] = ISO15693_NOERROR;
|
||||
nfcv_emu_send(response_buffer, 1);
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X 0x%08lX OK", password_id, pass_received);
|
||||
} else {
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X 0x%08lX/%08lX FAIL", password_id, pass_received, pass_expect);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ISO15693_CMD_NXP_ENABLE_PRIVACY:
|
||||
{
|
||||
response_buffer[0] = ISO15693_NOERROR;
|
||||
|
||||
nfcv_emu_send(response_buffer, 1);
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ISO15693_CMD_NXP_ENABLE_PRIVACY");
|
||||
|
||||
nfcv_data->sub_data.slix_l.privacy = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "unsupported: %02X", command);
|
||||
break;
|
||||
}
|
||||
|
||||
if(strlen(nfcv_data->last_command) > 0) {
|
||||
FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command);
|
||||
}
|
||||
}
|
||||
|
||||
void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
|
||||
nfcv_emu_alloc();
|
||||
rfal_platform_spi_acquire();
|
||||
|
||||
st25r3916ExecuteCommand(ST25R3916_CMD_STOP);
|
||||
st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3);
|
||||
st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88);
|
||||
st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE);
|
||||
|
||||
furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc);
|
||||
|
||||
FURI_LOG_D(TAG, "Starting NfcV emulation");
|
||||
FURI_LOG_D(TAG, " UID: %02X %02X %02X %02X %02X %02X %02X %02X",
|
||||
nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3],
|
||||
nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]);
|
||||
FURI_LOG_D(TAG, " Card type: %d", nfcv_data->type);
|
||||
FURI_LOG_D(TAG, " Privacy pass: 0x%08lX", nfcv_read_be(nfcv_data->sub_data.slix_l.key_privacy, 4));
|
||||
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix_l.privacy ? "ON" : "OFF");
|
||||
|
||||
/* allocate a 512 edge buffer, more than enough */
|
||||
reader_signal = pulse_reader_alloc(&gpio_spi_r_miso, 512);
|
||||
/* timebase shall be 1 ns */
|
||||
pulse_reader_set_timebase(reader_signal, PulseReaderUnitNanosecond);
|
||||
/* and configure to already calculate the number of bits */
|
||||
pulse_reader_set_bittime(reader_signal, PULSE_DURATION_NS);
|
||||
pulse_reader_start(reader_signal);
|
||||
}
|
||||
|
||||
void nfcv_emu_deinit() {
|
||||
furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc);
|
||||
rfal_platform_spi_release();
|
||||
nfcv_emu_free();
|
||||
|
||||
pulse_reader_free(reader_signal);
|
||||
}
|
||||
|
||||
bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms) {
|
||||
|
||||
bool ret = false;
|
||||
uint32_t frame_state = NFCV_FRAME_STATE_SOF1;
|
||||
uint32_t periods_previous = 0;
|
||||
uint8_t frame_payload[128];
|
||||
uint32_t frame_pos = 0;
|
||||
uint32_t byte_value = 0;
|
||||
uint32_t bits_received = 0;
|
||||
char reset_reason[128];
|
||||
bool wait_for_pulse = false;
|
||||
|
||||
while(true) {
|
||||
|
||||
uint32_t periods = pulse_reader_receive(reader_signal, timeout_ms * 1000);
|
||||
|
||||
if(periods == PULSE_READER_NO_EDGE) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(wait_for_pulse) {
|
||||
wait_for_pulse = false;
|
||||
if(periods != 1) {
|
||||
snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected a single low pulse in state %lu, but got %lu", frame_state, periods);
|
||||
frame_state = NFCV_FRAME_STATE_RESET;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(frame_state) {
|
||||
case NFCV_FRAME_STATE_SOF1:
|
||||
if(periods == 1) {
|
||||
frame_state = NFCV_FRAME_STATE_SOF2;
|
||||
} else {
|
||||
frame_state = NFCV_FRAME_STATE_SOF1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case NFCV_FRAME_STATE_SOF2:
|
||||
/* waiting for the second low period, telling us about coding */
|
||||
if(periods == 6) {
|
||||
frame_state = NFCV_FRAME_STATE_CODING_256;
|
||||
periods_previous = 0;
|
||||
wait_for_pulse = true;
|
||||
} else if(periods == 4) {
|
||||
frame_state = NFCV_FRAME_STATE_CODING_4;
|
||||
periods_previous = 2;
|
||||
wait_for_pulse = true;
|
||||
} else {
|
||||
snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 4/6 periods, got %lu", periods);
|
||||
frame_state = NFCV_FRAME_STATE_SOF1;
|
||||
}
|
||||
break;
|
||||
|
||||
case NFCV_FRAME_STATE_CODING_256:
|
||||
if(periods_previous > periods) {
|
||||
snprintf(reset_reason, sizeof(reset_reason), "1oo256: Missing %lu periods from previous symbol, got %lu", periods_previous, periods);
|
||||
frame_state = NFCV_FRAME_STATE_RESET;
|
||||
break;
|
||||
}
|
||||
/* previous symbol left us with some pulse periods */
|
||||
periods -= periods_previous;
|
||||
|
||||
if(periods > 512) {
|
||||
snprintf(reset_reason, sizeof(reset_reason), "1oo256: %lu periods is too much", periods);
|
||||
frame_state = NFCV_FRAME_STATE_RESET;
|
||||
break;
|
||||
}
|
||||
|
||||
if(periods == 2) {
|
||||
frame_state = NFCV_FRAME_STATE_EOF;
|
||||
break;
|
||||
}
|
||||
|
||||
periods_previous = 512 - (periods + 1);
|
||||
byte_value = (periods - 1) / 2;
|
||||
frame_payload[frame_pos++] = (uint8_t)byte_value;
|
||||
|
||||
wait_for_pulse = true;
|
||||
|
||||
break;
|
||||
|
||||
case NFCV_FRAME_STATE_CODING_4:
|
||||
if(periods_previous > periods) {
|
||||
snprintf(reset_reason, sizeof(reset_reason), "1oo4: Missing %lu periods from previous symbol, got %lu", periods_previous, periods);
|
||||
frame_state = NFCV_FRAME_STATE_RESET;
|
||||
break;
|
||||
}
|
||||
|
||||
/* previous symbol left us with some pulse periods */
|
||||
periods -= periods_previous;
|
||||
periods_previous = 0;
|
||||
|
||||
byte_value >>= 2;
|
||||
bits_received += 2;
|
||||
|
||||
if(periods == 1) {
|
||||
byte_value |= 0x00 << 6;
|
||||
periods_previous = 6;
|
||||
} else if(periods == 3) {
|
||||
byte_value |= 0x01 << 6;
|
||||
periods_previous = 4;
|
||||
} else if(periods == 5) {
|
||||
byte_value |= 0x02 << 6;
|
||||
periods_previous = 2;
|
||||
} else if(periods == 7) {
|
||||
byte_value |= 0x03 << 6;
|
||||
periods_previous = 0;
|
||||
} else if(periods == 2) {
|
||||
frame_state = NFCV_FRAME_STATE_EOF;
|
||||
break;
|
||||
} else {
|
||||
snprintf(reset_reason, sizeof(reset_reason), "1oo4: Expected 1/3/5/7 low pulses, but got %lu", periods);
|
||||
frame_state = NFCV_FRAME_STATE_RESET;
|
||||
break;
|
||||
}
|
||||
|
||||
if(bits_received >= 8) {
|
||||
frame_payload[frame_pos++] = (uint8_t)byte_value;
|
||||
bits_received = 0;
|
||||
}
|
||||
wait_for_pulse = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* post-state-machine cleanup and reset */
|
||||
if(frame_state == NFCV_FRAME_STATE_RESET) {
|
||||
frame_state = NFCV_FRAME_STATE_SOF1;
|
||||
|
||||
FURI_LOG_D(TAG, "Resetting state machine, reason: '%s'", reset_reason);
|
||||
} else if(frame_state == NFCV_FRAME_STATE_EOF) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(frame_state == NFCV_FRAME_STATE_EOF) {
|
||||
/* we know that this code uses TIM2, so stop pulse reader */
|
||||
pulse_reader_stop(reader_signal);
|
||||
nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos);
|
||||
pulse_reader_start(reader_signal);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <lib/digital_signal/digital_signal.h>
|
||||
#include <lib/pulse_reader/pulse_reader.h>
|
||||
#include "nfc_util.h"
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
|
||||
|
||||
|
||||
#define NFCV_FC (13560000.0f) /* MHz */
|
||||
#define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC/32) / 2.0f) /* 1.1799 µs */
|
||||
#define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */
|
||||
|
||||
#define PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) /* ns */
|
||||
|
||||
#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f)
|
||||
#define DIGITAL_SIGNAL_UNIT_US (100000.0f)
|
||||
|
||||
#define NFCV_TOTAL_BLOCKS_MAX 256
|
||||
#define NFCV_BLOCK_SIZE 4
|
||||
#define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE*NFCV_TOTAL_BLOCKS_MAX)
|
||||
|
||||
|
||||
#define NFCV_FRAME_STATE_SOF1 0
|
||||
#define NFCV_FRAME_STATE_SOF2 1
|
||||
#define NFCV_FRAME_STATE_CODING_4 2
|
||||
#define NFCV_FRAME_STATE_CODING_256 3
|
||||
#define NFCV_FRAME_STATE_EOF 4
|
||||
#define NFCV_FRAME_STATE_RESET 5
|
||||
|
||||
/* */
|
||||
#define ISO15693_INVENTORY 0x01
|
||||
#define ISO15693_STAYQUIET 0x02
|
||||
#define ISO15693_READBLOCK 0x20
|
||||
#define ISO15693_WRITEBLOCK 0x21
|
||||
#define ISO15693_LOCKBLOCK 0x22
|
||||
#define ISO15693_READ_MULTI_BLOCK 0x23
|
||||
#define ISO15693_WRITE_MULTI_BLOCK 0x24
|
||||
#define ISO15693_SELECT 0x25
|
||||
#define ISO15693_RESET_TO_READY 0x26
|
||||
#define ISO15693_WRITE_AFI 0x27
|
||||
#define ISO15693_LOCK_AFI 0x28
|
||||
#define ISO15693_WRITE_DSFID 0x29
|
||||
#define ISO15693_LOCK_DSFID 0x2A
|
||||
#define ISO15693_GET_SYSTEM_INFO 0x2B
|
||||
#define ISO15693_READ_MULTI_SECSTATUS 0x2C
|
||||
|
||||
// ISO15693 MANUFACTURER CODES
|
||||
#define ISO15693_MANUFACTURER_NXP 0x04
|
||||
|
||||
// ISO15693-3 CUSTOM NXP COMMANDS
|
||||
#define ISO15693_CMD_NXP_SET_EAS 0xA2
|
||||
#define ISO15693_CMD_NXP_RESET_EAS 0xA3
|
||||
#define ISO15693_CMD_NXP_LOCK_EAS 0xA4
|
||||
#define ISO15693_CMD_NXP_EAS_ALARM 0xA5
|
||||
#define ISO15693_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6
|
||||
#define ISO15693_CMD_NXP_WRITE_EAS_ID 0xA7
|
||||
#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ 0xB0
|
||||
#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1
|
||||
#define ISO15693_CMD_NXP_GET_RANDOM_NUMBER 0xB2
|
||||
#define ISO15693_CMD_NXP_SET_PASSWORD 0xB3
|
||||
#define ISO15693_CMD_NXP_WRITE_PASSWORD 0xB4
|
||||
#define ISO15693_CMD_NXP_DESTROY 0xB9
|
||||
#define ISO15693_CMD_NXP_ENABLE_PRIVACY 0xBA
|
||||
|
||||
// ISO15693 RESPONSE ERROR CODES
|
||||
#define ISO15693_NOERROR 0x00
|
||||
#define ISO15693_ERROR_CMD_NOT_SUP 0x01 // Command not supported
|
||||
#define ISO15693_ERROR_CMD_NOT_REC 0x02 // Command not recognized (eg. parameter error)
|
||||
#define ISO15693_ERROR_CMD_OPTION 0x03 // Command option not supported
|
||||
#define ISO15693_ERROR_GENERIC 0x0F // No additional Info about this error
|
||||
#define ISO15693_ERROR_BLOCK_UNAVAILABLE 0x10
|
||||
#define ISO15693_ERROR_BLOCK_LOCKED_ALREADY 0x11 // cannot lock again
|
||||
#define ISO15693_ERROR_BLOCK_LOCKED 0x12 // cannot be changed
|
||||
#define ISO15693_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful
|
||||
#define ISO15693_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful
|
||||
|
||||
|
||||
typedef enum {
|
||||
NfcVAuthMethodManual,
|
||||
NfcVAuthMethodTonieBox,
|
||||
} NfcVAuthMethod;
|
||||
|
||||
typedef enum {
|
||||
NfcVTypePlain = 0,
|
||||
NfcVTypeSlix = 1,
|
||||
NfcVTypeSlixS = 2,
|
||||
NfcVTypeSlixL = 3,
|
||||
NfcVTypeSlix2 = 4,
|
||||
} NfcVType;
|
||||
|
||||
typedef struct {
|
||||
uint8_t key_eas[4];
|
||||
uint8_t rand[2];
|
||||
} NfcVSlixData;
|
||||
|
||||
typedef struct {
|
||||
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;
|
||||
} NfcVSlix2Data;
|
||||
|
||||
typedef struct {
|
||||
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;
|
||||
} NfcVSlixSData;
|
||||
|
||||
typedef struct {
|
||||
uint8_t key_privacy[4];
|
||||
uint8_t key_destroy[4];
|
||||
uint8_t key_eas[4];
|
||||
uint8_t rand[2];
|
||||
bool privacy;
|
||||
} NfcVSlixLData;
|
||||
|
||||
typedef union {
|
||||
NfcVSlixData slix;
|
||||
NfcVSlix2Data slix2;
|
||||
NfcVSlixSData slix_s;
|
||||
NfcVSlixLData slix_l;
|
||||
} NfcVSubtypeData;
|
||||
|
||||
typedef struct {
|
||||
/* common ISO15693 fields */
|
||||
uint8_t dsfid;
|
||||
uint8_t afi;
|
||||
uint8_t ic_ref;
|
||||
uint16_t block_num;
|
||||
uint8_t block_size;
|
||||
uint8_t data[NFCV_MAX_DUMP_SIZE];
|
||||
|
||||
/* specfic variant infos */
|
||||
NfcVType type;
|
||||
NfcVSubtypeData sub_data;
|
||||
|
||||
/* runtime data */
|
||||
char last_command[128];
|
||||
char error[32];
|
||||
NfcVAuthMethod auth_method;
|
||||
bool auth_success;
|
||||
} NfcVData;
|
||||
|
||||
typedef struct {
|
||||
uint16_t blocks_to_read;
|
||||
int16_t blocks_read;
|
||||
} NfcVReader;
|
||||
|
||||
ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data);
|
||||
ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data);
|
||||
ReturnCode nfcv_inventory(uint8_t* uid);
|
||||
bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data);
|
||||
|
||||
void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
|
||||
void nfcv_emu_deinit();
|
||||
bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms);
|
||||
@@ -1,125 +0,0 @@
|
||||
|
||||
#include <limits.h>
|
||||
#include "nfcv.h"
|
||||
#include "slix.h"
|
||||
#include "nfc_util.h"
|
||||
#include <furi.h>
|
||||
#include "furi_hal_nfc.h"
|
||||
|
||||
|
||||
bool slix_check_card_type(FuriHalNfcDevData* nfc_data) {
|
||||
if((nfc_data->uid[0] == 0xE0)
|
||||
&& (nfc_data->uid[1] == 0x04)
|
||||
&& (nfc_data->uid[2] == 0x01)
|
||||
&& (((nfc_data->uid[3] >> 4) & 3) == 2)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool slix2_check_card_type(FuriHalNfcDevData* nfc_data) {
|
||||
if((nfc_data->uid[0] == 0xE0)
|
||||
&& (nfc_data->uid[1] == 0x04)
|
||||
&& (nfc_data->uid[2] == 0x01)
|
||||
&& (((nfc_data->uid[3] >> 4) & 3) == 1)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data) {
|
||||
if((nfc_data->uid[0] == 0xE0)
|
||||
&& (nfc_data->uid[1] == 0x04)
|
||||
&& (nfc_data->uid[2] == 0x02)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data) {
|
||||
if((nfc_data->uid[0] == 0xE0)
|
||||
&& (nfc_data->uid[1] == 0x04)
|
||||
&& (nfc_data->uid[2] == 0x03)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
ReturnCode slix_l_get_random(NfcVData* data) {
|
||||
uint16_t received = 0;
|
||||
uint8_t rxBuf[32];
|
||||
|
||||
ReturnCode ret = rfalNfcvPollerTransceiveReq(
|
||||
ISO15693_CMD_NXP_GET_RANDOM_NUMBER,
|
||||
RFAL_NFCV_REQ_FLAG_DEFAULT,
|
||||
ISO15693_MANUFACTURER_NXP,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
rxBuf,
|
||||
sizeof(rxBuf),
|
||||
&received);
|
||||
|
||||
if(ret == ERR_NONE) {
|
||||
if(received != 3) {
|
||||
return ERR_PROTO;
|
||||
}
|
||||
if(data != NULL) {
|
||||
data->sub_data.slix_l.rand[0] = rxBuf[2];
|
||||
data->sub_data.slix_l.rand[1] = rxBuf[1];
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ReturnCode slix_l_unlock(NfcVData* data, uint32_t password_id) {
|
||||
furi_assert(rand);
|
||||
|
||||
uint16_t received = 0;
|
||||
uint8_t rxBuf[32];
|
||||
uint8_t cmd_set_pass[] = {
|
||||
password_id,
|
||||
data->sub_data.slix_l.rand[1],
|
||||
data->sub_data.slix_l.rand[0],
|
||||
data->sub_data.slix_l.rand[1],
|
||||
data->sub_data.slix_l.rand[0]
|
||||
};
|
||||
uint8_t *password = NULL;
|
||||
|
||||
switch(password_id) {
|
||||
case 4:
|
||||
password = data->sub_data.slix_l.key_privacy;
|
||||
break;
|
||||
case 8:
|
||||
password = data->sub_data.slix_l.key_destroy;
|
||||
break;
|
||||
case 10:
|
||||
password = data->sub_data.slix_l.key_eas;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!password) {
|
||||
return ERR_NOTSUPP;
|
||||
}
|
||||
|
||||
for(int pos = 0; pos < 4; pos++) {
|
||||
cmd_set_pass[1 + pos] ^= password[3 - pos];
|
||||
}
|
||||
|
||||
ReturnCode ret = rfalNfcvPollerTransceiveReq(
|
||||
ISO15693_CMD_NXP_SET_PASSWORD,
|
||||
RFAL_NFCV_REQ_FLAG_DATA_RATE,
|
||||
ISO15693_MANUFACTURER_NXP,
|
||||
NULL,
|
||||
cmd_set_pass,
|
||||
sizeof(cmd_set_pass),
|
||||
rxBuf,
|
||||
sizeof(rxBuf),
|
||||
&received);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "nfc_util.h"
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
#define ISO15693_CMD_NXP_GET_RANDOM_NUMBER 0xB2
|
||||
#define ISO15693_CMD_NXP_SET_PASSWORD 0xB3
|
||||
#define ISO15693_MANUFACTURER_NXP 0x04
|
||||
|
||||
|
||||
bool slix_check_card_type(FuriHalNfcDevData* nfc_data);
|
||||
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 slix_l_get_random(NfcVData* data);
|
||||
ReturnCode slix_l_unlock(NfcVData* data, uint32_t password_id);
|
||||
|
||||
Reference in New Issue
Block a user