mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-14 19:53:35 -07:00
u2f over nfc
https://github.com/flipperdevices/flipperzero-firmware/pull/1755 from @devsnek may need more testing
This commit is contained in:
@@ -67,7 +67,7 @@ static void nfc_cli_emulate(Cli* cli, FuriString* args) {
|
||||
};
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 100)) {
|
||||
if (furi_hal_nfc_listen(¶ms, false, 100)) {
|
||||
printf("Reader detected\r\n");
|
||||
furi_hal_nfc_sleep();
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ void u2f_scene_main_on_enter(void* context) {
|
||||
if(app->u2f_ready == true) {
|
||||
u2f_set_event_callback(app->u2f_instance, u2f_scene_main_event_callback, app);
|
||||
app->u2f_hid = u2f_hid_start(app->u2f_instance);
|
||||
app->u2f_nfc = u2f_nfc_start(app->u2f_instance);
|
||||
u2f_view_set_ok_callback(app->u2f_view, u2f_scene_main_ok_callback, app);
|
||||
} else {
|
||||
u2f_free(app->u2f_instance);
|
||||
@@ -117,6 +118,7 @@ void u2f_scene_main_on_exit(void* context) {
|
||||
furi_timer_free(app->timer);
|
||||
if(app->u2f_ready == true) {
|
||||
u2f_hid_stop(app->u2f_hid);
|
||||
u2f_nfc_stop(app->u2f_nfc);
|
||||
u2f_free(app->u2f_instance);
|
||||
}
|
||||
}
|
||||
|
||||
+84
-43
@@ -16,6 +16,7 @@
|
||||
#define U2F_CMD_REGISTER 0x01
|
||||
#define U2F_CMD_AUTHENTICATE 0x02
|
||||
#define U2F_CMD_VERSION 0x03
|
||||
#define U2F_CMD_APPLET_SELECTION 0xA4
|
||||
|
||||
typedef enum {
|
||||
U2fCheckOnly = 0x07, // "check-only" - only check key handle, don't send auth response
|
||||
@@ -37,11 +38,6 @@ typedef struct {
|
||||
} __attribute__((packed)) U2fKeyHandle;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t len[3];
|
||||
uint8_t challenge[32];
|
||||
uint8_t app_id[32];
|
||||
} __attribute__((packed)) U2fRegisterReq;
|
||||
@@ -54,11 +50,6 @@ typedef struct {
|
||||
} __attribute__((packed)) U2fRegisterResp;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t len[3];
|
||||
uint8_t challenge[32];
|
||||
uint8_t app_id[32];
|
||||
U2fKeyHandle key_handle;
|
||||
@@ -72,10 +63,14 @@ typedef struct {
|
||||
|
||||
static const uint8_t ver_str[] = {"U2F_V2"};
|
||||
|
||||
// NFC applet selection fields
|
||||
static const uint8_t rid_ac_ax[] = { 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01 };
|
||||
|
||||
static const uint8_t state_no_error[] = {0x90, 0x00};
|
||||
static const uint8_t state_not_supported[] = {0x6D, 0x00};
|
||||
static const uint8_t state_user_missing[] = {0x69, 0x85};
|
||||
static const uint8_t state_wrong_data[] = {0x6A, 0x80};
|
||||
static const uint8_t state_app_not_found[] = { 0x6A, 0x82 };
|
||||
|
||||
struct U2fData {
|
||||
uint8_t device_key[32];
|
||||
@@ -88,6 +83,17 @@ struct U2fData {
|
||||
void* context;
|
||||
};
|
||||
|
||||
static void* apdu_command_data(U2fApduCommand* cmd) {
|
||||
// Short encoding is a single byte.
|
||||
// Extended length encoding is 0 byte followed by MSB and LSB bytes.
|
||||
if (cmd->len[0] == 0) {
|
||||
return cmd->len + 3;
|
||||
}
|
||||
else {
|
||||
return cmd->len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int u2f_uecc_random(uint8_t* dest, unsigned size) {
|
||||
furi_hal_random_fill_buf(dest, size);
|
||||
return 1;
|
||||
@@ -178,9 +184,10 @@ static uint8_t u2f_der_encode_signature(uint8_t* der, uint8_t* sig) {
|
||||
return len;
|
||||
}
|
||||
|
||||
static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) {
|
||||
U2fRegisterReq* req = (U2fRegisterReq*)buf;
|
||||
U2fRegisterResp* resp = (U2fRegisterResp*)buf;
|
||||
static uint16_t u2f_register(U2fData* U2F, const uint8_t* in_buf, uint8_t* out_buf) {
|
||||
U2fApduCommand* cmd = (U2fApduCommand*)in_buf;
|
||||
U2fRegisterReq* req = apdu_command_data(cmd);
|
||||
U2fRegisterResp* resp = (U2fRegisterResp*)out_buf;
|
||||
U2fKeyHandle handle;
|
||||
uint8_t private[32];
|
||||
U2fPubKey pub_key;
|
||||
@@ -190,13 +197,13 @@ static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) {
|
||||
if(u2f_data_check(false) == false) {
|
||||
U2F->ready = false;
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context);
|
||||
memcpy(&buf[0], state_not_supported, 2);
|
||||
memcpy(&out_buf[0], state_not_supported, 2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyRegister, U2F->context);
|
||||
if(U2F->user_present == false) {
|
||||
memcpy(&buf[0], state_user_missing, 2);
|
||||
memcpy(&out_buf[0], state_user_missing, 2);
|
||||
return 2;
|
||||
}
|
||||
U2F->user_present = false;
|
||||
@@ -247,9 +254,10 @@ static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) {
|
||||
return (sizeof(U2fRegisterResp) + cert_len + signature_len + 2);
|
||||
}
|
||||
|
||||
static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
|
||||
U2fAuthReq* req = (U2fAuthReq*)buf;
|
||||
U2fAuthResp* resp = (U2fAuthResp*)buf;
|
||||
static uint16_t u2f_authenticate(U2fData* U2F, const uint8_t* in_buf, uint8_t* out_buf) {
|
||||
U2fApduCommand* cmd = (U2fApduCommand*)in_buf;
|
||||
U2fAuthReq* req = apdu_command_data(cmd);
|
||||
U2fAuthResp* resp = (U2fAuthResp*)out_buf;
|
||||
uint8_t priv_key[32];
|
||||
uint8_t mac_control[32];
|
||||
hmac_sha256_context hmac_ctx;
|
||||
@@ -262,7 +270,7 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
|
||||
if(u2f_data_check(false) == false) {
|
||||
U2F->ready = false;
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context);
|
||||
memcpy(&buf[0], state_not_supported, 2);
|
||||
memcpy(&out_buf[0], state_not_supported, 2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -270,8 +278,8 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
|
||||
if(U2F->user_present == true) {
|
||||
flags |= 1;
|
||||
} else {
|
||||
if(req->p1 == U2fEnforce) {
|
||||
memcpy(&buf[0], state_user_missing, 2);
|
||||
if (cmd->p1 == U2fEnforce) {
|
||||
memcpy(&out_buf[0], state_user_missing, 2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
@@ -302,12 +310,12 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
|
||||
|
||||
if(memcmp(req->key_handle.hash, mac_control, 32) != 0) {
|
||||
FURI_LOG_W(TAG, "Wrong handle!");
|
||||
memcpy(&buf[0], state_wrong_data, 2);
|
||||
memcpy(&out_buf[0], state_wrong_data, 2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(req->p1 == U2fCheckOnly) { // Check-only: don't need to send full response
|
||||
memcpy(&buf[0], state_user_missing, 2);
|
||||
if (cmd->p1 == U2fCheckOnly) { // Check-only: don't need to send full response
|
||||
memcpy(&out_buf[0], state_user_missing, 2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -327,36 +335,69 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
|
||||
return (sizeof(U2fAuthResp) + signature_len + 2);
|
||||
}
|
||||
|
||||
uint16_t u2f_msg_parse(U2fData* U2F, uint8_t* buf, uint16_t len) {
|
||||
uint16_t u2f_applet_selection(const uint8_t* in_buf, uint8_t* out_buf) {
|
||||
U2fApduCommand* cmd = (U2fApduCommand*)in_buf;
|
||||
uint8_t* data = apdu_command_data(cmd);
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"len=%d %02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
cmd->len[0],
|
||||
data[0],
|
||||
data[1],
|
||||
data[2],
|
||||
data[3],
|
||||
data[4],
|
||||
data[5],
|
||||
data[6],
|
||||
data[7]);
|
||||
|
||||
if (cmd->len[0] != 8 || memcmp(rid_ac_ax, data, 8) != 0) {
|
||||
memcpy(&out_buf[0], state_app_not_found, 2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
memcpy(&out_buf[0], ver_str, 6);
|
||||
memcpy(&out_buf[6], state_no_error, 2);
|
||||
return 8;
|
||||
}
|
||||
|
||||
uint16_t u2f_msg_parse(U2fData* U2F, const uint8_t* in_buf, uint16_t in_len, uint8_t* out_buf) {
|
||||
furi_assert(U2F);
|
||||
if(!U2F->ready) return 0;
|
||||
if((buf[0] != 0x00) && (len < 5)) return 0;
|
||||
if(buf[1] == U2F_CMD_REGISTER) { // Register request
|
||||
return u2f_register(U2F, buf);
|
||||
|
||||
} else if(buf[1] == U2F_CMD_AUTHENTICATE) { // Authenticate request
|
||||
return u2f_authenticate(U2F, buf);
|
||||
|
||||
} else if(buf[1] == U2F_CMD_VERSION) { // Get U2F version string
|
||||
memcpy(&buf[0], ver_str, 6);
|
||||
memcpy(&buf[6], state_no_error, 2);
|
||||
if (!U2F->ready) return 0;
|
||||
if ((in_buf[0] != 0x00) && (in_len < 5)) return 0;
|
||||
FURI_LOG_D(TAG, "ins=0x%02x", in_buf[1]);
|
||||
if (in_buf[1] == U2F_CMD_REGISTER) { // Register request
|
||||
return u2f_register(U2F, in_buf, out_buf);
|
||||
}
|
||||
else if (in_buf[1] == U2F_CMD_AUTHENTICATE) { // Authenticate request
|
||||
return u2f_authenticate(U2F, in_buf, out_buf);
|
||||
}
|
||||
else if (in_buf[1] == U2F_CMD_VERSION) { // Get U2F version string
|
||||
memcpy(&out_buf[0], ver_str, 6);
|
||||
memcpy(&out_buf[6], state_no_error, 2);
|
||||
return 8;
|
||||
} else {
|
||||
memcpy(&buf[0], state_not_supported, 2);
|
||||
}
|
||||
else if (in_buf[1] == U2F_CMD_APPLET_SELECTION) {
|
||||
return u2f_applet_selection(in_buf, out_buf);
|
||||
}
|
||||
else {
|
||||
memcpy(&out_buf[0], state_not_supported, 2);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void u2f_wink(U2fData* U2F) {
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyWink, U2F->context);
|
||||
if (U2F->callback != NULL) U2F->callback(U2fNotifyWink, U2F->context);
|
||||
}
|
||||
|
||||
void u2f_set_state(U2fData* U2F, uint8_t state) {
|
||||
if(state == 0) {
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyDisconnect, U2F->context);
|
||||
} else {
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyConnect, U2F->context);
|
||||
if (state == 0) {
|
||||
if (U2F->callback != NULL) U2F->callback(U2fNotifyDisconnect, U2F->context);
|
||||
}
|
||||
else {
|
||||
if (U2F->callback != NULL) U2F->callback(U2fNotifyConnect, U2F->context);
|
||||
}
|
||||
U2F->user_present = false;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,14 @@ extern "C" {
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t len[];
|
||||
} __attribute__((packed)) U2fApduCommand;
|
||||
|
||||
typedef enum {
|
||||
U2fNotifyRegister,
|
||||
U2fNotifyAuth,
|
||||
@@ -30,7 +38,7 @@ void u2f_set_event_callback(U2fData* instance, U2fEvtCallback callback, void* co
|
||||
|
||||
void u2f_confirm_user_present(U2fData* instance);
|
||||
|
||||
uint16_t u2f_msg_parse(U2fData* instance, uint8_t* buf, uint16_t len);
|
||||
uint16_t u2f_msg_parse(U2fData* instance, const uint8_t* in_buf, uint16_t len, uint8_t* out_buf);
|
||||
|
||||
void u2f_wink(U2fData* instance);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include "views/u2f_view.h"
|
||||
#include "u2f_nfc.h"
|
||||
#include "u2f_hid.h"
|
||||
#include "u2f.h"
|
||||
|
||||
@@ -54,6 +55,7 @@ struct U2fApp {
|
||||
Widget* widget;
|
||||
FuriTimer* timer;
|
||||
U2fHid* u2f_hid;
|
||||
U2fNfc* u2f_nfc;
|
||||
U2fView* u2f_view;
|
||||
U2fData* u2f_instance;
|
||||
GpioCustomEvent event_cur;
|
||||
|
||||
@@ -140,8 +140,11 @@ static bool u2f_hid_parse_request(U2fHid* u2f_hid) {
|
||||
|
||||
} else if(u2f_hid->packet.cmd == U2F_HID_MSG) { // MSG - U2F message
|
||||
if((u2f_hid->lock == true) && (u2f_hid->packet.cid != u2f_hid->lock_cid)) return false;
|
||||
uint16_t resp_len =
|
||||
u2f_msg_parse(u2f_hid->u2f_instance, u2f_hid->packet.payload, u2f_hid->packet.len);
|
||||
uint16_t resp_len = u2f_msg_parse(
|
||||
u2f_hid->u2f_instance,
|
||||
u2f_hid->packet.payload,
|
||||
u2f_hid->packet.len,
|
||||
u2f_hid->packet.payload);
|
||||
if(resp_len > 0) {
|
||||
u2f_hid->packet.len = resp_len;
|
||||
u2f_hid_send_response(u2f_hid);
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
#include "u2f_nfc.h"
|
||||
#include "furi_hal.h"
|
||||
#include "u2f.h"
|
||||
|
||||
#define TAG "U2F_NFC"
|
||||
|
||||
typedef enum {
|
||||
WorkerEvtReserved = (1 << 0),
|
||||
WorkerEvtStop = (1 << 1),
|
||||
} WorkerEvtFlags;
|
||||
|
||||
struct U2fNfc {
|
||||
FuriThread* thread;
|
||||
U2fData* u2f_instance;
|
||||
uint8_t payload[65535];
|
||||
uint16_t payload_len;
|
||||
uint16_t payload_cursor;
|
||||
};
|
||||
|
||||
static uint16_t
|
||||
u2f_callback(U2fNfc* u2f_nfc, const uint8_t* buff_rx, uint16_t buff_rx_len, uint8_t* buff_tx) {
|
||||
U2fApduCommand* cmd = (U2fApduCommand*)buff_rx;
|
||||
if (cmd->ins == 0xC0) {
|
||||
if (u2f_nfc->payload_len == 0) {
|
||||
FURI_LOG_E(TAG, "requested block but not chaining");
|
||||
buff_tx[0] = 0x69;
|
||||
buff_tx[1] = 0x00;
|
||||
return 2;
|
||||
}
|
||||
|
||||
FURI_LOG_T(TAG, "continued chaining %d/%d", u2f_nfc->payload_cursor, u2f_nfc->payload_len);
|
||||
|
||||
uint16_t max_resp_len = cmd->len[0];
|
||||
if (max_resp_len == 0) {
|
||||
max_resp_len = 256;
|
||||
}
|
||||
|
||||
uint16_t remaining_len = (u2f_nfc->payload_len - 2) - u2f_nfc->payload_cursor;
|
||||
if (remaining_len > max_resp_len) {
|
||||
memcpy(buff_tx, &u2f_nfc->payload[u2f_nfc->payload_cursor], max_resp_len);
|
||||
remaining_len -= max_resp_len;
|
||||
buff_tx[max_resp_len] = 0x61;
|
||||
if (remaining_len >= 256) {
|
||||
buff_tx[max_resp_len + 1] = 0x00;
|
||||
}
|
||||
else {
|
||||
buff_tx[max_resp_len + 1] = remaining_len;
|
||||
}
|
||||
u2f_nfc->payload_cursor += max_resp_len;
|
||||
return max_resp_len + 2;
|
||||
}
|
||||
else {
|
||||
memcpy(
|
||||
buff_tx,
|
||||
&u2f_nfc->payload[u2f_nfc->payload_cursor],
|
||||
u2f_nfc->payload_len - u2f_nfc->payload_cursor);
|
||||
u2f_nfc->payload_len = 0;
|
||||
return remaining_len;
|
||||
}
|
||||
}
|
||||
|
||||
// Presence is implied by touching the NFC devices.
|
||||
u2f_confirm_user_present(u2f_nfc->u2f_instance);
|
||||
|
||||
u2f_nfc->payload_len =
|
||||
u2f_msg_parse(u2f_nfc->u2f_instance, buff_rx, buff_rx_len, u2f_nfc->payload);
|
||||
|
||||
// If this is extended format, send entire response at once
|
||||
if (cmd->len[0] == 0) {
|
||||
FURI_LOG_T(TAG, "single extended response");
|
||||
memcpy(&buff_tx, u2f_nfc->payload, u2f_nfc->payload_len);
|
||||
uint16_t len = u2f_nfc->payload_len;
|
||||
u2f_nfc->payload_len = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
// Otherwise, we need to do chaining.
|
||||
uint16_t max_resp_len = 256;
|
||||
|
||||
// If this message happens to be less than the chaining size, send it all at once.
|
||||
if ((u2f_nfc->payload_len - 2) <= max_resp_len) {
|
||||
FURI_LOG_T(TAG, "single short response");
|
||||
memcpy(buff_tx, u2f_nfc->payload, u2f_nfc->payload_len);
|
||||
uint16_t len = u2f_nfc->payload_len;
|
||||
u2f_nfc->payload_len = 0;
|
||||
return len;
|
||||
}
|
||||
else {
|
||||
memcpy(buff_tx, u2f_nfc->payload, max_resp_len);
|
||||
buff_tx[max_resp_len] = 0x61;
|
||||
uint16_t remaining_len = (u2f_nfc->payload_len - 2) - max_resp_len;
|
||||
if (remaining_len >= max_resp_len) {
|
||||
buff_tx[max_resp_len + 1] = 0x00;
|
||||
}
|
||||
else {
|
||||
buff_tx[max_resp_len + 1] = remaining_len;
|
||||
}
|
||||
u2f_nfc->payload_cursor = max_resp_len;
|
||||
FURI_LOG_T(
|
||||
TAG, "started u2f chaining %d/%d", u2f_nfc->payload_cursor, u2f_nfc->payload_len);
|
||||
return max_resp_len + 2;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t u2f_nfc_worker(void* context) {
|
||||
U2fNfc* u2f_nfc = context;
|
||||
FURI_LOG_D(TAG, "Init");
|
||||
while (furi_hal_nfc_is_busy()) {
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
FuriHalNfcDevData params = {
|
||||
// TODO: Randomize this or something?
|
||||
.uid = {0xCF, 0x72, 0xd4, 0x40},
|
||||
.uid_len = 4,
|
||||
.atqa = {0x00, 0x04},
|
||||
.sak = 0x20,
|
||||
.type = FuriHalNfcTypeA,
|
||||
.interface = FuriHalNfcInterfaceIsoDep,
|
||||
};
|
||||
|
||||
furi_hal_nfc_exit_sleep();
|
||||
|
||||
FURI_LOG_D(TAG, "Start");
|
||||
|
||||
while (1) {
|
||||
uint32_t flags = furi_thread_flags_wait(WorkerEvtStop, FuriFlagWaitAny, 10);
|
||||
if (flags != FuriFlagErrorTimeout) {
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
if (flags & WorkerEvtStop) break;
|
||||
}
|
||||
if (!furi_hal_nfc_listen(¶ms, false, 200)) {
|
||||
FURI_LOG_T(TAG, "wtf");
|
||||
continue;
|
||||
}
|
||||
FuriHalNfcTxRxContext tx_rx = {};
|
||||
tx_rx.tx_bits = 0;
|
||||
tx_rx.tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||
if (!furi_hal_nfc_tx_rx(&tx_rx, 300)) continue;
|
||||
if (tx_rx.rx_bits == 0) continue;
|
||||
u2f_nfc->payload_len = 0;
|
||||
u2f_nfc->payload_cursor = 0;
|
||||
while (true) {
|
||||
uint16_t payload_len =
|
||||
u2f_callback(u2f_nfc, tx_rx.rx_data, tx_rx.rx_bits / 8, tx_rx.tx_data);
|
||||
tx_rx.rx_bits = 0;
|
||||
FURI_LOG_T(TAG, "payload_len=%d", payload_len);
|
||||
if (payload_len == 0) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
tx_rx.tx_bits = payload_len * 8;
|
||||
tx_rx.tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||
if (!furi_hal_nfc_tx_rx(&tx_rx, 300)) break;
|
||||
if (tx_rx.rx_bits == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Cleanup");
|
||||
furi_hal_nfc_sleep();
|
||||
FURI_LOG_D(TAG, "End");
|
||||
return 0;
|
||||
}
|
||||
|
||||
U2fNfc* u2f_nfc_start(U2fData* u2f_inst) {
|
||||
U2fNfc* u2f_nfc = malloc(sizeof(U2fNfc));
|
||||
u2f_nfc->u2f_instance = u2f_inst;
|
||||
u2f_nfc->payload_len = 0;
|
||||
u2f_nfc->payload_cursor = 0;
|
||||
|
||||
u2f_nfc->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(u2f_nfc->thread, "U2fNFCWorker");
|
||||
furi_thread_set_stack_size(u2f_nfc->thread, 4096);
|
||||
furi_thread_set_context(u2f_nfc->thread, u2f_nfc);
|
||||
furi_thread_set_callback(u2f_nfc->thread, u2f_nfc_worker);
|
||||
furi_thread_start(u2f_nfc->thread);
|
||||
|
||||
return u2f_nfc;
|
||||
}
|
||||
|
||||
void u2f_nfc_stop(U2fNfc* u2f_nfc) {
|
||||
furi_assert(u2f_nfc);
|
||||
furi_thread_flags_set(furi_thread_get_id(u2f_nfc->thread), WorkerEvtStop);
|
||||
furi_thread_join(u2f_nfc->thread);
|
||||
furi_thread_free(u2f_nfc->thread);
|
||||
free(u2f_nfc);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "u2f.h"
|
||||
|
||||
typedef struct U2fNfc U2fNfc;
|
||||
|
||||
U2fNfc* u2f_nfc_start(U2fData* u2f_inst);
|
||||
|
||||
void u2f_nfc_stop(U2fNfc* u2f_hid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,12.0,,
|
||||
Version,+,13.0,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
@@ -1198,7 +1198,7 @@ Function,+,furi_hal_nfc_field_on,void,
|
||||
Function,-,furi_hal_nfc_init,void,
|
||||
Function,+,furi_hal_nfc_is_busy,_Bool,
|
||||
Function,+,furi_hal_nfc_is_init,_Bool,
|
||||
Function,+,furi_hal_nfc_listen,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t, _Bool, uint32_t"
|
||||
Function,+,furi_hal_nfc_listen,_Bool,"FuriHalNfcDevData*, _Bool, uint32_t"
|
||||
Function,+,furi_hal_nfc_listen_rx,_Bool,"FuriHalNfcTxRxContext*, uint32_t"
|
||||
Function,+,furi_hal_nfc_listen_sleep,void,
|
||||
Function,+,furi_hal_nfc_listen_start,void,FuriHalNfcDevData*
|
||||
|
||||
|
@@ -206,10 +206,7 @@ bool furi_hal_nfc_activate_nfca(uint32_t timeout, uint32_t* cuid) {
|
||||
}
|
||||
|
||||
bool furi_hal_nfc_listen(
|
||||
uint8_t* uid,
|
||||
uint8_t uid_len,
|
||||
uint8_t* atqa,
|
||||
uint8_t sak,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
bool activate_after_sak,
|
||||
uint32_t timeout) {
|
||||
rfalNfcState state = rfalNfcGetState();
|
||||
@@ -232,16 +229,19 @@ bool furi_hal_nfc_listen(
|
||||
.notifyCb = NULL,
|
||||
.activate_after_sak = activate_after_sak,
|
||||
};
|
||||
if(FURI_BIT(sak, 5)) {
|
||||
if (nfc_data->interface == FuriHalNfcInterfaceIsoDep) {
|
||||
params.compMode = RFAL_COMPLIANCE_MODE_ISO;
|
||||
}
|
||||
else if (FURI_BIT(nfc_data->sak, 5)) {
|
||||
params.compMode = RFAL_COMPLIANCE_MODE_EMV;
|
||||
} else {
|
||||
params.compMode = RFAL_COMPLIANCE_MODE_NFC;
|
||||
}
|
||||
params.lmConfigPA.nfcidLen = uid_len;
|
||||
memcpy(params.lmConfigPA.nfcid, uid, uid_len);
|
||||
params.lmConfigPA.SENS_RES[0] = atqa[0];
|
||||
params.lmConfigPA.SENS_RES[1] = atqa[1];
|
||||
params.lmConfigPA.SEL_RES = sak;
|
||||
params.lmConfigPA.nfcidLen = nfc_data->uid_len;
|
||||
memcpy(params.lmConfigPA.nfcid, nfc_data->uid, nfc_data->uid_len);
|
||||
params.lmConfigPA.SENS_RES[0] = nfc_data->atqa[0];
|
||||
params.lmConfigPA.SENS_RES[1] = nfc_data->atqa[1];
|
||||
params.lmConfigPA.SEL_RES = nfc_data->sak;
|
||||
rfalNfcDiscover(¶ms);
|
||||
|
||||
// Disable EMD suppression.
|
||||
|
||||
@@ -154,6 +154,7 @@ bool furi_hal_nfc_activate_nfca(uint32_t timeout, uint32_t* cuid);
|
||||
/** NFC listen
|
||||
*
|
||||
* @param uid pointer to uid buffer
|
||||
* @param nfc_data pointer to FuriHalNfcDevData
|
||||
* @param uid_len uid length
|
||||
* @param atqa pointer to atqa
|
||||
* @param sak sak
|
||||
@@ -163,10 +164,7 @@ bool furi_hal_nfc_activate_nfca(uint32_t timeout, uint32_t* cuid);
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_nfc_listen(
|
||||
uint8_t* uid,
|
||||
uint8_t uid_len,
|
||||
uint8_t* atqa,
|
||||
uint8_t sak,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
bool activate_after_sak,
|
||||
uint32_t timeout);
|
||||
|
||||
|
||||
@@ -776,7 +776,7 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
|
||||
// Need to save ATS to support ISO-14443A-4 emulation
|
||||
|
||||
while(nfc_worker->state == NfcWorkerStateUidEmulate) {
|
||||
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) {
|
||||
if (furi_hal_nfc_listen(data, false, 100)) {
|
||||
if(furi_hal_nfc_tx_rx(&tx_rx, 100)) {
|
||||
reader_data->size = tx_rx.rx_bits / 8;
|
||||
if(reader_data->size > 0) {
|
||||
@@ -834,7 +834,7 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
|
||||
}
|
||||
|
||||
while(nfc_worker->state == NfcWorkerStateEmulateApdu) { //-V1044
|
||||
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
|
||||
if (furi_hal_nfc_listen(¶ms, false, 300)) {
|
||||
FURI_LOG_D(TAG, "POS terminal detected");
|
||||
if(emv_card_emulation(&tx_rx)) {
|
||||
FURI_LOG_D(TAG, "EMV card emulated");
|
||||
|
||||
@@ -60,7 +60,10 @@ class ImageTools:
|
||||
with Image.open(file) as im:
|
||||
with io.BytesIO() as output:
|
||||
bw = im.convert("1")
|
||||
bw = ImageOps.invert(bw)
|
||||
try:
|
||||
bw = ImageOps.invert(bw)
|
||||
except OSError:
|
||||
bw = bw.point(lambda x: 255 - x)
|
||||
bw.save(output, format="XBM")
|
||||
return output.getvalue()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user