unleashed-firmware/lib/nfc/protocols/slix.c

413 lines
12 KiB
C
Raw Normal View History

added ISO15693 (NfcV) reading, saving, emulating and revealing from privacy mode (unlock) (#2316) * added support for ISO15693 (NfcV) emulation, added support for reading SLIX tags * SLIX: fixed crash situation when an invalid password was requested * ISO15693: show emulate menu when opening file * rename NfcV emulate scene to match other NfcV names * optimize allocation size for signals * ISO15693: further optimizations of allocation and free code * ISO15693: reduce latency on state machine reset * respond with block security status when option flag is set * increased maximum memory size to match standard added security status handling/load/save added SELECT/QUIET handling more fine grained allocation routines and checks fix memset sizes * added "Listen NfcV Reader" to sniff traffic from reader to card * added correct description to delete menu * also added DSFID/AFI handling and locking * increase sniff log size * scale NfcV frequency a bit, add echo mode, fix signal level at the end * use symbolic modulated/unmodulated GPIO levels * honor AFI field, decrease verbosity and removed debug code * refactor defines for less namespace pollution by using NFCV_ prefixes * correct an oversight that original cards return an generic error when addressing outside block range * use inverse modulation, increasing readable range significantly * rework and better document nfc chip initialization * nfcv code review fixes * Disable accidentally left on signal debug gpio output * Improve NFCV Read/Info GUIs. Authored by @xMasterX, committed by @nvx * Fix crash that occurs when you exit from NFCV emulation and start it again. Authored by @xMasterX, committed by @nvx * Remove delay from emulation loop. This improves compatibility when the reader is Android. * Lib: digital signal debug output pin info Co-authored-by: Tiernan Messmer <tiernan.messmer@gmail.com> Co-authored-by: MX <10697207+xMasterX@users.noreply.github.com> Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
2023-06-08 08:30:53 +03:00
#include <limits.h>
#include "nfcv.h"
#include "slix.h"
#include "nfc_util.h"
#include <furi.h>
#include "furi_hal_nfc.h"
#include <furi_hal_random.h>
#define TAG "SLIX"
static uint32_t slix_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;
}
uint8_t slix_get_ti(FuriHalNfcDevData* nfc_data) {
return (nfc_data->uid[3] >> 3) & 3;
}
bool slix_check_card_type(FuriHalNfcDevData* nfc_data) {
if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) &&
slix_get_ti(nfc_data) == 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) &&
slix_get_ti(nfc_data) == 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_get_random(NfcVData* data) {
uint16_t received = 0;
uint8_t rxBuf[32];
ReturnCode ret = rfalNfcvPollerTransceiveReq(
NFCV_CMD_NXP_GET_RANDOM_NUMBER,
RFAL_NFCV_REQ_FLAG_DEFAULT,
NFCV_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.rand[0] = rxBuf[2];
data->sub_data.slix.rand[1] = rxBuf[1];
}
}
return ret;
}
ReturnCode slix_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.rand[1],
data->sub_data.slix.rand[0],
data->sub_data.slix.rand[1],
data->sub_data.slix.rand[0]};
uint8_t* password = NULL;
switch(password_id) {
case SLIX_PASS_READ:
password = data->sub_data.slix.key_read;
break;
case SLIX_PASS_WRITE:
password = data->sub_data.slix.key_write;
break;
case SLIX_PASS_PRIVACY:
password = data->sub_data.slix.key_privacy;
break;
case SLIX_PASS_DESTROY:
password = data->sub_data.slix.key_destroy;
break;
case SLIX_PASS_EASAFI:
password = data->sub_data.slix.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(
NFCV_CMD_NXP_SET_PASSWORD,
RFAL_NFCV_REQ_FLAG_DATA_RATE,
NFCV_MANUFACTURER_NXP,
NULL,
cmd_set_pass,
sizeof(cmd_set_pass),
rxBuf,
sizeof(rxBuf),
&received);
return ret;
}
bool slix_generic_protocol_filter(
FuriHalNfcTxRxContext* tx_rx,
FuriHalNfcDevData* nfc_data,
void* nfcv_data_in,
uint32_t password_supported) {
furi_assert(tx_rx);
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;
if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER &&
ctx->command != NFCV_CMD_NXP_SET_PASSWORD) {
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"command 0x%02X ignored, privacy mode",
ctx->command);
FURI_LOG_D(TAG, "%s", nfcv_data->last_command);
return true;
}
bool handled = false;
switch(ctx->command) {
case NFCV_CMD_NXP_GET_RANDOM_NUMBER: {
slix->rand[0] = furi_hal_random_get();
slix->rand[1] = furi_hal_random_get();
ctx->response_buffer[0] = NFCV_NOERROR;
ctx->response_buffer[1] = slix->rand[1];
ctx->response_buffer[2] = slix->rand[0];
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 3, ctx->response_flags, ctx->send_time);
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"GET_RANDOM_NUMBER -> 0x%02X%02X",
slix->rand[0],
slix->rand[1]);
handled = true;
break;
}
case NFCV_CMD_NXP_SET_PASSWORD: {
uint8_t password_id = nfcv_data->frame[ctx->payload_offset];
if(!(password_id & password_supported)) {
break;
}
uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1];
uint8_t* rand = slix->rand;
uint8_t* password = NULL;
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;
}
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);
/* if the password is all-zeroes, just accept any password*/
if(!pass_expect || pass_expect == pass_received) {
switch(password_id) {
case SLIX_PASS_READ:
break;
case SLIX_PASS_WRITE:
break;
case SLIX_PASS_PRIVACY:
slix->privacy = false;
nfcv_data->modified = true;
break;
case SLIX_PASS_DESTROY:
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);
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);
}
handled = true;
break;
}
case NFCV_CMD_NXP_ENABLE_PRIVACY: {
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),
"NFCV_CMD_NXP_ENABLE_PRIVACY");
slix->privacy = true;
handled = true;
break;
}
}
return handled;
}
bool slix_l_protocol_filter(
FuriHalNfcTxRxContext* tx_rx,
FuriHalNfcDevData* nfc_data,
void* nfcv_data_in) {
furi_assert(tx_rx);
furi_assert(nfc_data);
furi_assert(nfcv_data_in);
bool handled = false;
/* many SLIX share some of the functions, place that in a generic handler */
if(slix_generic_protocol_filter(
tx_rx,
nfc_data,
nfcv_data_in,
SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI)) {
return true;
}
return handled;
}
void slix_l_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
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");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_l_protocol_filter;
}
bool slix_s_protocol_filter(
FuriHalNfcTxRxContext* tx_rx,
FuriHalNfcDevData* nfc_data,
void* nfcv_data_in) {
furi_assert(tx_rx);
furi_assert(nfc_data);
furi_assert(nfcv_data_in);
bool handled = false;
/* many SLIX share some of the functions, place that in a generic handler */
if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) {
return true;
}
return handled;
}
void slix_s_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
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");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_s_protocol_filter;
}
bool slix_protocol_filter(
FuriHalNfcTxRxContext* tx_rx,
FuriHalNfcDevData* nfc_data,
void* nfcv_data_in) {
furi_assert(tx_rx);
furi_assert(nfc_data);
furi_assert(nfcv_data_in);
bool handled = false;
/* many SLIX share some of the functions, place that in a generic handler */
if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_EASAFI)) {
return true;
}
return handled;
}
void slix_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
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");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_protocol_filter;
}
bool slix2_protocol_filter(
FuriHalNfcTxRxContext* tx_rx,
FuriHalNfcDevData* nfc_data,
void* nfcv_data_in) {
furi_assert(tx_rx);
furi_assert(nfc_data);
furi_assert(nfcv_data_in);
bool handled = false;
/* many SLIX share some of the functions, place that in a generic handler */
if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) {
return true;
}
return handled;
}
void slix2_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
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");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix2_protocol_filter;
}