diff --git a/CMakeLists.txt b/CMakeLists.txt index eba3ab0..7a5ae58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,13 +50,26 @@ option(ENABLE_OATH_APP "Enable/disable OATH application" ON) if(ENABLE_OATH_APP) add_definitions(-DENABLE_OATH_APP=1) message(STATUS "OATH Application: \t\t enabled") - set(USB_ITF_CCID 1) else() add_definitions(-DENABLE_OATH_APP=0) message(STATUS "OATH Application: \t\t disabled") - set(USB_ITF_CCID 0) endif(ENABLE_OATH_APP) +option(ENABLE_OTP_APP "Enable/disable OTP application" ON) +if(ENABLE_OTP_APP) + add_definitions(-DENABLE_OTP_APP=1) + message(STATUS "OTP Application: \t\t enabled") +else() + add_definitions(-DENABLE_OTP_APP=0) + message(STATUS "OTP Application: \t\t disabled") +endif(ENABLE_OTP_APP) + +if(ENABLE_OTP_APP OR ENABLE_OATH_APP) + set(USB_ITF_CCID 1) +else() + set(USB_ITF_CCID 0) +endif() + target_sources(pico_fido PUBLIC ${CMAKE_CURRENT_LIST_DIR}/src/fido/fido.c ${CMAKE_CURRENT_LIST_DIR}/src/fido/files.c @@ -82,6 +95,11 @@ target_sources(pico_fido PUBLIC ${CMAKE_CURRENT_LIST_DIR}/src/fido/oath.c ) endif() +if (${ENABLE_OTP_APP}) +target_sources(pico_fido PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/src/fido/otp.c + ) +endif() set(USB_ITF_HID 1) include(pico-hsm-sdk/pico_hsm_sdk_import.cmake) diff --git a/src/fido/files.h b/src/fido/files.h index 3a459f2..5ef2dc7 100644 --- a/src/fido/files.h +++ b/src/fido/files.h @@ -34,6 +34,8 @@ #define EF_LARGEBLOB 0x1101 // Large Blob Array #define EF_OATH_CRED 0xBA00 // OATH Creds at 0xBA00 - 0xBAFE #define EF_OATH_CODE 0xBAFF +#define EF_OTP_SLOT1 0xBB00 +#define EF_OTP_SLOT2 0xBB01 extern file_t *ef_keydev; extern file_t *ef_certdev; diff --git a/src/fido/otp.c b/src/fido/otp.c new file mode 100644 index 0000000..1cf6c21 --- /dev/null +++ b/src/fido/otp.c @@ -0,0 +1,151 @@ +/* + * 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 . + */ + +#include "fido.h" +#include "hsm.h" +#include "apdu.h" +#include "files.h" +#include "random.h" +#include "version.h" +#include "asn1.h" + +#define FIXED_SIZE 16 +#define KEY_SIZE 16 +#define UID_SIZE 6 +#define KEY_SIZE_OATH 20 +#define ACC_CODE_SIZE 6 + +#define CONFIG1_VALID 0x01 +#define CONFIG2_VALID 0x02 +#define CONFIG1_TOUCH 0x04 +#define CONFIG2_TOUCH 0x08 +#define CONFIG_LED_INV 0x10 +#define CONFIG_STATUS_MASK 0x1f + +static uint8_t config_seq[2] = {1}; + +typedef struct otp_config { + uint8_t fixed_data[FIXED_SIZE]; + uint8_t uid[UID_SIZE]; + uint8_t aes_key[KEY_SIZE]; + uint8_t acc_code[ACC_CODE_SIZE]; + uint8_t fixed_size; + uint8_t ext_flags; + uint8_t tkt_flags; + uint8_t cfg_flags; + uint8_t rfu[2]; + uint16_t crc; +} __attribute__((packed)) otp_config_t; + +static const size_t otp_config_size = sizeof(otp_config_t); + +int otp_process_apdu(); +int otp_unload(); + +const uint8_t otp_aid[] = { + 7, + 0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01 +}; + +app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { + if (!memcmp(aid, otp_aid+1, MIN(aid_len,otp_aid[0]))) { + a->aid = otp_aid; + a->process_apdu = otp_process_apdu; + a->unload = otp_unload; + return a; + } + return NULL; +} + +void __attribute__ ((constructor)) otp_ctor() { + register_app(otp_select); +} + +int otp_unload() { + return CCID_OK; +} + +uint16_t otp_status(uint8_t slot) { + 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[slot]; + res_APDU[res_APDU_size++] = 0; + res_APDU[res_APDU_size++] = (CONFIG2_TOUCH | CONFIG1_TOUCH) | (config_seq[0] > 0 ? CONFIG1_VALID : 0x00) | (config_seq[1] > 0 ? CONFIG2_VALID : 0x00); + return SW_OK(); +} + +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) + return SW_WRONG_DATA(); + uint8_t slot = p1 == 0x01 ? 0 : 1; + file_t *ef = file_new(p1 == 0x01 ? 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(); + } + 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); + low_flash_available(); + config_seq[slot]++; + return otp_status(slot); + } + } + // Delete slot + delete_file(ef); + config_seq[slot] = 0; + return otp_status(slot); + } + 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 }, + { 0x00, 0x0} +}; + +int otp_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(); +}