26 Commits
v3.0 ... v5.4

Author SHA1 Message Date
Pol Henarejos
07729f807b Upgrade to version 5.4
This passes from previous version 3.0 to 5.4 due to compatibility issues with Yubico software, which expects +5.4

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-05-15 19:02:32 +02:00
Pol Henarejos
e0c793dd0a Fix empty challenge.
Now a new fresh challenge is generated on every select command.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-05-12 16:19:22 +02:00
Pol Henarejos
9d6003d1e5 Add more features to README
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-05-11 20:10:44 +02:00
Pol Henarejos
147a93d7fb Update README.md
Added Pico Patcher link.
2023-05-11 20:08:39 +02:00
Pol Henarejos
f12c55805c Put again commands to FIDO app for interoperability.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-02 01:19:23 +02:00
Pol Henarejos
7e10e25f96 Added management application.
Used for Yubico clients.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-02 01:05:19 +02:00
Pol Henarejos
9052c66a7f Fix returning otp status over ccid.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-02 00:29:22 +02:00
Pol Henarejos
443ca69547 Added get config capabilities command.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-02 00:17:39 +02:00
Pol Henarejos
415c1b2e9c Enable U2F applet selection.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-02 00:00:09 +02:00
Pol Henarejos
d87c1530c7 Return otp_status if selected applet OTP id.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 23:48:47 +02:00
Pol Henarejos
f90baaf095 Do not respond a challenge-response command if no challenge-response app is configured.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 23:37:51 +02:00
Pol Henarejos
1d7bdb0861 Added support for swap.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 23:35:19 +02:00
Pol Henarejos
fa811e2a0f If slot is configured with a challenge-response app, do nothing when pressed.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 23:25:57 +02:00
Pol Henarejos
ff498ebfdf Added support for update config.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 19:11:00 +02:00
Pol Henarejos
cceb735cc0 Fix order of fields of Yubico OTP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 01:15:59 +02:00
Pol Henarejos
5a9de32e02 Added support for challenge-response for Yubico OTP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 01:13:52 +02:00
Pol Henarejos
c9eacc4a3d Added support for challenge-response HMAC SHA1.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 01:05:09 +02:00
Pol Henarejos
c23d92ea28 Added support for OTP YubiOTP.
It generates a 44 byte string, modhex encoded, following the specification of Yubikey for OTP YubiOTP. When button is pressed, it sends the 44-byte OTP to the host machine, as if it was typed.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-31 20:03:02 +02:00
Pol Henarejos
da04fbb824 Add crc check.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 23:44:37 +02:00
Pol Henarejos
0bfa760903 Undo previous commit.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 01:14:06 +02:00
Pol Henarejos
bd9d4286d5 Added fix for emulation conditional build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 01:04:22 +02:00
Pol Henarejos
3d1c68fa40 Added support for APPEND_CR.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 00:52:08 +02:00
Pol Henarejos
26ac66e813 Added support for OTP HOTP and OTP Static.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 00:37:51 +02:00
Pol Henarejos
05afcd706e Fix OATH calculation result when called multiple times.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 00:37:31 +02:00
Pol Henarejos
8c90dd55bd Added support for button pressed callback.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-28 23:33:14 +02:00
Pol Henarejos
c6c1d0c6eb Added features to README
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-27 00:19:11 +02:00
8 changed files with 500 additions and 21 deletions

View File

@@ -95,6 +95,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_config.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_vendor.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_large_blobs.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/management.c
)
if (${ENABLE_OATH_APP})
set(SOURCES ${SOURCES}

View File

@@ -27,6 +27,13 @@ Pico FIDO has implemented the following features:
- credBlobs extension
- largeBlobKey extension
- largeBlobs support (2048 bytes máx.)
- OATH (based on YKOATH protocol specification)
- TOTP / HOTP
- Yubikey OTP
- Challenge-response generation
- Emulated keyboard interface
- Button press generates an OTP that is written directly is it was typed
- Yubico YKMAN compatible
All these features are compliant with the specification. Therefore, if you detect some behaviour that is not expected or it does not follow the rules of specs, please open an issue.
@@ -38,6 +45,15 @@ If the Pico is stolen the contents of private and secret keys can be read.
## Download
Please, go to the [Release page](https://github.com/polhenarejos/pico-fido/releases "Release page") and download the UF2 file for your board.
Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you are planning to use it with OpenSC or similar, you should modify Info.plist of CCID driver to add these VID/PID or use the [Pico Patcher tool](https://www.picokeys.com/pico-patcher/).
Alternatively you can use the legacy VID/PID patcher as follows:
`./patch_vidpid.sh VID:PID input_hsm_file.uf2 output_hsm_file.uf2`
You can use whatever VID/PID (i.e., 234b:0000 from FISJ), but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
Note that the pure-browser option [Pico Patcher tool](https://www.picokeys.com/pico-patcher/) is the most recommended.
## Build
Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive.

View File

@@ -23,6 +23,32 @@
#include "files.h"
#include "hid/ctap_hid.h"
const uint8_t u2f_aid[] = {
7,
0xA0, 0x00, 0x00, 0x05, 0x27, 0x10, 0x02
};
int u2f_unload();
int u2f_process_apdu();
app_t *u2f_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
if (!memcmp(aid, u2f_aid + 1, MIN(aid_len, u2f_aid[0]))) {
a->aid = u2f_aid;
a->process_apdu = u2f_process_apdu;
a->unload = u2f_unload;
return a;
}
return NULL;
}
void __attribute__((constructor)) u2f_ctor() {
register_app(u2f_select);
}
int u2f_unload() {
return CCID_OK;
}
const uint8_t *bogus_firefox =
(const uint8_t *)
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
@@ -110,3 +136,27 @@ int cmd_register() {
ef_certdev_size + olen;
return SW_OK();
}
extern int cmd_register();
extern int cmd_authenticate();
extern int cmd_version();
static const cmd_t cmds[] = {
{ CTAP_REGISTER, cmd_register },
{ CTAP_AUTHENTICATE, cmd_authenticate },
{ CTAP_VERSION, cmd_version },
{ 0x00, 0x0 }
};
int u2f_process_apdu() {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) {
int r = cmd->cmd_handler();
return r;
}
}
return SW_INS_NOT_SUPPORTED();
}

116
src/fido/management.c Normal file
View File

@@ -0,0 +1,116 @@
/*
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
* Copyright (c) 2022 Pol Henarejos.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "fido.h"
#include "hsm.h"
#include "apdu.h"
#include "version.h"
int man_process_apdu();
int man_unload();
const uint8_t man_aid[] = {
8,
0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17
};
app_t *man_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
if (!memcmp(aid, man_aid + 1, MIN(aid_len, man_aid[0]))) {
a->aid = man_aid;
a->process_apdu = man_process_apdu;
a->unload = man_unload;
sprintf((char *)res_APDU, "%d.%d.0", PICO_FIDO_VERSION_MAJOR, PICO_FIDO_VERSION_MINOR);
res_APDU_size = strlen((char *)res_APDU);
apdu.ne = res_APDU_size;
return a;
}
return NULL;
}
void __attribute__((constructor)) man_ctor() {
register_app(man_select);
}
int man_unload() {
return CCID_OK;
}
int man_get_config() {
res_APDU_size = 0;
res_APDU[res_APDU_size++] = 0; // Overall length. Filled later
res_APDU[res_APDU_size++] = 0x01;
res_APDU[res_APDU_size++] = 2;
res_APDU[res_APDU_size++] = 0x02;
res_APDU[res_APDU_size++] = 0x01 | 0x02 | 0x20;
res_APDU[res_APDU_size++] = 0x02;
res_APDU[res_APDU_size++] = 4;
#ifndef ENABLE_EMULATION
pico_get_unique_board_id_string((char *) res_APDU + res_APDU_size, 4);
#endif
res_APDU_size += 4;
res_APDU[res_APDU_size++] = 0x03;
res_APDU[res_APDU_size++] = 2;
res_APDU[res_APDU_size++] = 0x02;
res_APDU[res_APDU_size++] = 0x01 | 0x02 | 0x20;
res_APDU[res_APDU_size++] = 0x04;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = 0x01;
res_APDU[res_APDU_size++] = 0x05;
res_APDU[res_APDU_size++] = 3;
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
res_APDU[res_APDU_size++] = 0;
res_APDU[res_APDU_size++] = 0x08;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = 0x80;
res_APDU[res_APDU_size++] = 0x0A;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = 0x00;
res_APDU[res_APDU_size++] = 0x0D;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = 0x00;
res_APDU[res_APDU_size++] = 0x0E;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = 0x00;
res_APDU[0] = res_APDU_size - 1;
return 0;
}
int cmd_read_config() {
man_get_config();
return SW_OK();
}
#define INS_READ_CONFIG 0x1D
static const cmd_t cmds[] = {
{ INS_READ_CONFIG, cmd_read_config },
{ 0x00, 0x0 }
};
int man_process_apdu() {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) {
int r = cmd->cmd_handler();
return r;
}
}
return SW_INS_NOT_SUPPORTED();
}

View File

@@ -82,6 +82,7 @@ app_t *oath_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
memset(res_APDU + res_APDU_size, 0, 8); res_APDU_size += 8;
#endif
if (file_has_data(search_dynamic_file(EF_OATH_CODE)) == true) {
random_gen(NULL, challenge, sizeof(challenge));
res_APDU[res_APDU_size++] = TAG_CHALLENGE;
res_APDU[res_APDU_size++] = sizeof(challenge);
memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge));

View File

@@ -22,6 +22,11 @@
#include "random.h"
#include "version.h"
#include "asn1.h"
#include "hid/ctap_hid.h"
#ifndef ENABLE_EMULATION
#include "bsp/board.h"
#endif
#include "mbedtls/aes.h"
#define FIXED_SIZE 16
#define KEY_SIZE 16
@@ -36,6 +41,52 @@
#define CONFIG_LED_INV 0x10
#define CONFIG_STATUS_MASK 0x1f
/* EXT Flags */
#define SERIAL_BTN_VISIBLE 0x01 // Serial number visible at startup (button press)
#define SERIAL_USB_VISIBLE 0x02 // Serial number visible in USB iSerial field
#define SERIAL_API_VISIBLE 0x04 // Serial number visible via API call
#define USE_NUMERIC_KEYPAD 0x08 // Use numeric keypad for digits
#define FAST_TRIG 0x10 // Use fast trig if only cfg1 set
#define ALLOW_UPDATE 0x20 // Allow update of existing configuration (selected flags + access code)
#define DORMANT 0x40 // Dormant config (woken up, flag removed, requires update flag)
#define LED_INV 0x80 // LED idle state is off rather than on
#define EXTFLAG_UPDATE_MASK (SERIAL_BTN_VISIBLE | SERIAL_USB_VISIBLE | SERIAL_API_VISIBLE | USE_NUMERIC_KEYPAD | FAST_TRIG | ALLOW_UPDATE | DORMANT | LED_INV)
/* TKT Flags */
#define TAB_FIRST 0x01 // Send TAB before first part
#define APPEND_TAB1 0x02 // Send TAB after first part
#define APPEND_TAB2 0x04 // Send TAB after second part
#define APPEND_DELAY1 0x08 // Add 0.5s delay after first part
#define APPEND_DELAY2 0x10 // Add 0.5s delay after second part
#define APPEND_CR 0x20 // Append CR as final character
#define OATH_HOTP 0x40 // OATH HOTP mode
#define CHAL_RESP 0x40 // Challenge-response enabled (both must be set)
#define PROTECT_CFG2 0x80 // Block update of config 2 unless config 2 is configured and has this bit set
#define TKTFLAG_UPDATE_MASK (TAB_FIRST | APPEND_TAB1 | APPEND_TAB2 | APPEND_DELAY1 | APPEND_DELAY2 | APPEND_CR)
/* CFG Flags */
#define SEND_REF 0x01 // Send reference string (0..F) before data
#define PACING_10MS 0x04 // Add 10ms intra-key pacing
#define PACING_20MS 0x08 // Add 20ms intra-key pacing
#define STATIC_TICKET 0x20 // Static ticket generation
// Static
#define SHORT_TICKET 0x02 // Send truncated ticket (half length)
#define STRONG_PW1 0x10 // Strong password policy flag #1 (mixed case)
#define STRONG_PW2 0x40 // Strong password policy flag #2 (subtitute 0..7 to digits)
#define MAN_UPDATE 0x80 // Allow manual (local) update of static OTP
// Challenge (no keyboard)
#define HMAC_LT64 0x04 // Set when HMAC message is less than 64 bytes
#define CHAL_BTN_TRIG 0x08 // Challenge-response operation requires button press
#define CHAL_YUBICO 0x20 // Challenge-response enabled - Yubico OTP mode
#define CHAL_HMAC 0x22 // Challenge-response enabled - HMAC-SHA1
// OATH
#define OATH_HOTP8 0x02 // Generate 8 digits HOTP rather than 6 digits
#define OATH_FIXED_MODHEX1 0x10 // First byte in fixed part sent as modhex
#define OATH_FIXED_MODHEX2 0x40 // First two bytes in fixed part sent as modhex
#define OATH_FIXED_MODHEX 0x50 // Fixed part sent as modhex
#define OATH_FIXED_MASK 0x50 // Mask to get out fixed flags
#define CFGFLAG_UPDATE_MASK (PACING_10MS | PACING_20MS)
static uint8_t config_seq = { 1 };
typedef struct otp_config {
@@ -75,14 +126,178 @@ app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
config_seq = 0;
}
otp_status();
memmove(res_APDU, res_APDU + 1, 6);
res_APDU_size = 6;
apdu.ne = res_APDU_size;
return a;
}
return NULL;
}
uint8_t modhex_tab[] = {'c', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'r', 't', 'u', 'v'};
int encode_modhex(const uint8_t *in, size_t len, uint8_t *out) {
for (int l = 0; l < len; l++) {
*out++ = modhex_tab[in[l] >> 4];
*out++ = modhex_tab[in[l] & 0xf];
}
return 0;
}
static bool scanned = false;
extern void scan_all();
void init_otp() {
if (scanned == false) {
scan_all();
for (int i = 0; i < 2; i++) {
file_t *ef = search_dynamic_file(EF_OTP_SLOT1 + i);
uint8_t *data = file_get_data(ef);
otp_config_t *otp_config = (otp_config_t *)data;
if (file_has_data(ef) && !(otp_config->tkt_flags & OATH_HOTP) && !(otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET)) {
uint16_t counter = (data[otp_config_size] << 8) | data[otp_config_size + 1];
if (++counter <= 0x7fff) {
uint8_t new_data[otp_config_size + 8];
memcpy(new_data, data, sizeof(new_data));
new_data[otp_config_size] = counter >> 8;
new_data[otp_config_size + 1] = counter & 0xff;
flash_write_data_to_file(ef, new_data, sizeof(new_data));
}
}
}
scanned = true;
low_flash_available();
}
}
extern int calculate_oath(uint8_t truncate,
const uint8_t *key,
size_t key_len,
const uint8_t *chal,
size_t chal_len);
#ifndef ENABLE_EMULATION
static uint8_t session_counter[2] = {0};
#endif
int otp_button_pressed(uint8_t slot) {
init_otp();
#ifndef ENABLE_EMULATION
file_t *ef = search_dynamic_file(slot == 1 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
const uint8_t *data = file_get_data(ef);
otp_config_t *otp_config = (otp_config_t *)data;
if (file_has_data(ef) == false) {
return 1;
}
if (otp_config->cfg_flags & CHAL_YUBICO && otp_config->tkt_flags & CHAL_RESP) {
return 2;
}
if (otp_config->tkt_flags & OATH_HOTP) {
uint8_t tmp_key[KEY_SIZE + 2];
tmp_key[0] = 0x01;
memcpy(tmp_key + 2, otp_config->aes_key, KEY_SIZE);
uint64_t imf = 0;
const uint8_t *p = data + otp_config_size;
imf |= (uint64_t)*p++ << 56;
imf |= (uint64_t)*p++ << 48;
imf |= (uint64_t)*p++ << 40;
imf |= (uint64_t)*p++ << 32;
imf |= *p++ << 24;
imf |= *p++ << 16;
imf |= *p++ << 8;
imf |= *p++;
if (imf == 0) {
imf = ((otp_config->uid[4] << 8) | otp_config->uid[5]) << 4;
}
uint8_t chal[8] = {imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8, imf & 0xff};
res_APDU_size = 0;
int ret = calculate_oath(1, tmp_key, sizeof(tmp_key), chal, sizeof(chal));
if (ret == CCID_OK) {
uint32_t base = otp_config->cfg_flags & OATH_HOTP8 ? 1e8 : 1e6;
uint32_t number = (res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5];
number %= base;
char number_str[9];
if (otp_config->cfg_flags & OATH_HOTP8) {
sprintf(number_str, "%08lu", (long unsigned int)number);
add_keyboard_buffer((const uint8_t *)number_str, 8, true);
}
else {
sprintf(number_str, "%06lu", (long unsigned int)number);
add_keyboard_buffer((const uint8_t *)number_str, 6, true);
}
imf++;
uint8_t new_chal[8] = {imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8, imf & 0xff};
uint8_t new_otp_config[otp_config_size + sizeof(new_chal)];
memcpy(new_otp_config, otp_config, otp_config_size);
memcpy(new_otp_config + otp_config_size, new_chal, sizeof(new_chal));
flash_write_data_to_file(ef, new_otp_config, sizeof(new_otp_config));
low_flash_available();
}
if (otp_config->tkt_flags & APPEND_CR) {
append_keyboard_buffer((const uint8_t *)"\r", 1);
}
}
else if (otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET) {
if (otp_config->cfg_flags & SHORT_TICKET) {
otp_config->fixed_size /= 2;
}
add_keyboard_buffer(otp_config->fixed_data, otp_config->fixed_size, false);
if (otp_config->tkt_flags & APPEND_CR) {
append_keyboard_buffer((const uint8_t *)"\x28", 1);
}
}
else {
uint8_t otpk[22], *po = otpk;
bool update_counter = false;
uint16_t counter = (data[otp_config_size] << 8) | data[otp_config_size + 1], crc = 0;
uint32_t ts = board_millis() / 1000;
if (counter == 0) {
update_counter = true;
counter = 1;
}
memcpy(po, otp_config->fixed_data, 6);
po += 6;
memcpy(po, otp_config->uid, UID_SIZE);
po += UID_SIZE;
*po++ = counter & 0xff;
*po++ = counter >> 8;
ts >>= 3;
*po++ = ts & 0xff;
*po++ = ts >> 8;
*po++ = ts >> 16;
*po++ = session_counter[slot - 1];
random_gen(NULL, po, 2);
po += 2;
crc = calculate_crc(otpk + 6, 14);
*po++ = ~crc & 0xff;
*po++ = ~crc >> 8;
mbedtls_aes_context ctx;
mbedtls_aes_init(&ctx);
mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128);
mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, otpk + 6, otpk + 6);
mbedtls_aes_free(&ctx);
uint8_t otp_out[44];
encode_modhex(otpk, sizeof(otpk), otp_out);
add_keyboard_buffer((const uint8_t *)otp_out, sizeof(otp_out), true);
if (otp_config->tkt_flags & APPEND_CR) {
append_keyboard_buffer((const uint8_t *)"\r", 1);
}
if (++session_counter[slot - 1] == 0) {
if (++counter <= 0x7fff) {
update_counter = true;
}
}
if (update_counter == true) {
uint8_t new_data[otp_config_size + 8];
memcpy(new_data, data, sizeof(new_data));
new_data[otp_config_size] = counter >> 8;
new_data[otp_config_size + 1] = counter & 0xff;
flash_write_data_to_file(ef, new_data, sizeof(new_data));
low_flash_available();
}
}
#endif
return 0;
}
void __attribute__((constructor)) otp_ctor() {
register_app(otp_select);
button_pressed_cb = otp_button_pressed;
}
int otp_unload() {
@@ -90,29 +305,39 @@ int otp_unload() {
}
uint16_t otp_status() {
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
res_APDU[res_APDU_size++] = 0;
res_APDU[res_APDU_size++] = config_seq;
res_APDU[res_APDU_size++] = 0;
res_APDU[res_APDU_size++] = (CONFIG2_TOUCH | CONFIG1_TOUCH) |
if (scanned == false) {
scan_all();
scanned = true;
}
res_APDU_size = 0;
res_APDU[1] = PICO_FIDO_VERSION_MAJOR;
res_APDU[2] = PICO_FIDO_VERSION_MINOR;
res_APDU[3] = 0;
res_APDU[4] = config_seq;
res_APDU[5] = (CONFIG2_TOUCH | CONFIG1_TOUCH) |
(file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ? CONFIG1_VALID :
0x00) |
(file_has_data(search_dynamic_file(EF_OTP_SLOT2)) ? CONFIG2_VALID :
0x00);
res_APDU[6] = 0;
return SW_OK();
}
bool check_crc(const otp_config_t *data) {
uint16_t crc = calculate_crc((const uint8_t *)data, otp_config_size);
return crc == 0xF0B8;
}
extern int man_get_config();
int cmd_otp() {
uint8_t p1 = P1(apdu), p2 = P2(apdu);
if (p2 != 0x00) {
return SW_INCORRECT_P1P2();
}
if (p1 == 0x01 || p1 == 0x03) { // Configure slot
if (apdu.nc != otp_config_size + ACC_CODE_SIZE) {
return SW_WRONG_LENGTH();
}
if (apdu.data[48] != 0 || apdu.data[49] != 0) {
otp_config_t *odata = (otp_config_t *)apdu.data;
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
return SW_WRONG_DATA();
}
file_t *ef = file_new(p1 == 0x01 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
@@ -124,7 +349,8 @@ int cmd_otp() {
}
for (int c = 0; c < otp_config_size; c++) {
if (apdu.data[c] != 0) {
flash_write_data_to_file(ef, apdu.data, otp_config_size);
memset(apdu.data + otp_config_size, 0, 8); // Add 8 bytes extra
flash_write_data_to_file(ef, apdu.data, otp_config_size + 8);
low_flash_available();
config_seq++;
return otp_status();
@@ -138,24 +364,93 @@ int cmd_otp() {
}
return otp_status();
}
else if (p1 == 0x04 || p1 == 0x05) {
otp_config_t *odata = (otp_config_t *)apdu.data;
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
return SW_WRONG_DATA();
}
file_t *ef = search_dynamic_file(p1 == 0x04 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
if (file_has_data(ef)) {
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
memcpy(apdu.data, file_get_data(ef), FIXED_SIZE + UID_SIZE + KEY_SIZE);
odata->fixed_size = otpc->fixed_size;
odata->ext_flags = (otpc->ext_flags & ~EXTFLAG_UPDATE_MASK) | (odata->ext_flags & EXTFLAG_UPDATE_MASK);
odata->tkt_flags = (otpc->tkt_flags & ~TKTFLAG_UPDATE_MASK) | (odata->tkt_flags & TKTFLAG_UPDATE_MASK);
odata->cfg_flags = (otpc->cfg_flags & ~CFGFLAG_UPDATE_MASK) | (odata->cfg_flags & CFGFLAG_UPDATE_MASK);
flash_write_data_to_file(ef, apdu.data, otp_config_size);
low_flash_available();
}
}
else if (p1 == 0x06) {
uint8_t tmp[otp_config_size + 8];
bool ef1_data = false;
file_t *ef1 = file_new(EF_OTP_SLOT1);
file_t *ef2 = file_new(EF_OTP_SLOT2);
if (file_has_data(ef1)) {
memcpy(tmp, file_get_data(ef1), file_get_size(ef1));
ef1_data = true;
}
if (file_has_data(ef2)) {
flash_write_data_to_file(ef1, file_get_data(ef2), file_get_size(ef2));
}
else {
delete_file(ef1);
}
if (ef1_data) {
flash_write_data_to_file(ef2, tmp, sizeof(tmp));
}
else {
delete_file(ef2);
}
low_flash_available();
}
else if (p1 == 0x10) {
#ifndef ENABLE_EMULATION
pico_get_unique_board_id_string((char *) res_APDU, 4);
#endif
res_APDU_size = 4;
}
else if (p1 == 0x13) {
man_get_config();
}
else if (p1 == 0x30 || p1 == 0x38 || p1 == 0x20 || p1 == 0x28) {
file_t *ef = search_dynamic_file(p1 == 0x30 || p1 == 0x20 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
if (file_has_data(ef)) {
otp_config_t *otp_config = (otp_config_t *)file_get_data(ef);
if (!(otp_config->cfg_flags & CHAL_YUBICO && otp_config->tkt_flags & CHAL_RESP)) {
return SW_WRONG_DATA();
}
int ret = 0;
if (p1 == 0x30 || p1 == 0x38) {
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), otp_config->aes_key, KEY_SIZE, apdu.data, 8, res_APDU);
if (ret == 0) {
res_APDU_size = 20;
}
}
else if (p1 == 0x20 || p1 == 0x28) {
uint8_t challenge[16];
memcpy(challenge, apdu.data, 6);
#ifndef ENABLE_EMULATION
pico_get_unique_board_id_string((char *) challenge + 6, 10);
#endif
mbedtls_aes_context ctx;
mbedtls_aes_init(&ctx);
mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128);
ret = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, challenge, res_APDU);
mbedtls_aes_free(&ctx);
if (ret == 0) {
res_APDU_size = 16;
}
}
}
}
return SW_OK();
}
#define INS_OTP 0x01
#define INS_DELETE 0x02
#define INS_SET_CODE 0x03
#define INS_RESET 0x04
#define INS_LIST 0xa1
#define INS_CALCULATE 0xa2
#define INS_VALIDATE 0xa3
#define INS_CALC_ALL 0xa4
#define INS_SEND_REMAINING 0xa5
static const cmd_t cmds[] = {
{ INS_OTP, cmd_otp },

View File

@@ -18,7 +18,7 @@
#ifndef __VERSION_H_
#define __VERSION_H_
#define PICO_FIDO_VERSION 0x0300
#define PICO_FIDO_VERSION 0x0504
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)