Skip to content
This repository has been archived by the owner on Nov 19, 2024. It is now read-only.

Commit

Permalink
u2f over nfc
Browse files Browse the repository at this point in the history
  • Loading branch information
jbohack committed Jan 12, 2023
1 parent d0d0e8d commit ea0b548
Show file tree
Hide file tree
Showing 13 changed files with 326 additions and 65 deletions.
2 changes: 1 addition & 1 deletion applications/main/nfc/nfc_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(&params, false, 100)) {
printf("Reader detected\r\n");
furi_hal_nfc_sleep();
}
Expand Down
2 changes: 2 additions & 0 deletions applications/main/u2f/scenes/u2f_scene_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
}
}
125 changes: 83 additions & 42 deletions applications/main/u2f/u2f.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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];
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -262,16 +270,16 @@ 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;
}

if(U2F->callback != NULL) U2F->callback(U2fNotifyAuth, U2F->context);
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;
}
}
Expand Down Expand Up @@ -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;
}

Expand All @@ -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) {
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);
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;
}

} else if(buf[1] == U2F_CMD_AUTHENTICATE) { // Authenticate request
return u2f_authenticate(U2F, buf);
memcpy(&out_buf[0], ver_str, 6);
memcpy(&out_buf[6], state_no_error, 2);
return 8;
}

} else if(buf[1] == U2F_CMD_VERSION) { // Get U2F version string
memcpy(&buf[0], ver_str, 6);
memcpy(&buf[6], state_no_error, 2);
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 ((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;
}
10 changes: 9 additions & 1 deletion applications/main/u2f/u2f.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions applications/main/u2f/u2f_app_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions applications/main/u2f/u2f_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit ea0b548

Please sign in to comment.