diff --git a/CMakeLists.txt b/CMakeLists.txt index 554bfb5..7b9e20c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ add_executable(pico_openpgp) set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/openpgp.c ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/files.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/piv.c ) set(INCLUDES ${INCLUDES} diff --git a/src/openpgp/piv.c b/src/openpgp/piv.c new file mode 100644 index 0000000..2b48376 --- /dev/null +++ b/src/openpgp/piv.c @@ -0,0 +1,182 @@ +/* + * This file is part of the Pico OpenPGP distribution (https://github.com/polhenarejos/pico-openpgp). + * 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 "common.h" +#include "files.h" +#include "apdu.h" +#include "pico_keys.h" +#include "eac.h" +#include "version.h" +#include "pico/unique_id.h" + +extern bool has_pw1; + +uint8_t piv_aid[] = { + 5, + 0xA0, 0x00, 0x00, 0x03, 0x8, +}; +uint8_t yk_aid[] = { + 8, + 0xA0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x1, 0x1 +}; +uint8_t mgmt_aid[] = { + 8, + 0xA0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 +}; + +int piv_process_apdu(); + +static void scan_files() { + scan_flash(); +} + +void init_piv() { + scan_files(); + //cmd_select(); +} + +int piv_unload() { + return CCID_OK; +} + +void select_piv_aid() { + res_APDU[res_APDU_size++] = 0x61; + res_APDU[res_APDU_size++] = 0; //filled later + res_APDU[res_APDU_size++] = 0x4F; + res_APDU[res_APDU_size++] = 2; + res_APDU[res_APDU_size++] = 0x01; + res_APDU[res_APDU_size++] = 0x00; + res_APDU[res_APDU_size++] = 0x79; + res_APDU[res_APDU_size++] = 9; + memcpy(res_APDU + res_APDU_size, "\xA0\x00\x00\x03\x08\x00\x00\x10\x00", 9); + res_APDU_size += 9; + const char *app_label = "Pico Keys PIV"; + res_APDU[res_APDU_size++] = 0x50; + res_APDU[res_APDU_size++] = strlen(app_label); + memcpy(res_APDU + res_APDU_size, app_label, strlen(app_label)); + + res_APDU[res_APDU_size++] = 0xAC; + res_APDU[res_APDU_size++] = 12; + res_APDU[res_APDU_size++] = 0x80; + res_APDU[res_APDU_size++] = 7; + memcpy(res_APDU + res_APDU_size, "\x07\x08\x0A\x0C\x11\x14\x2E", 7); + res_APDU_size += 7; + res_APDU[res_APDU_size++] = 0x6; + res_APDU[res_APDU_size++] = 1; + res_APDU[res_APDU_size++] = 0x00; +} + +int piv_select_aid(app_t *a) { + a->process_apdu = piv_process_apdu; + a->unload = piv_unload; + init_piv(); + select_piv_aid(); + return CCID_OK; +} + +void __attribute__((constructor)) piv_ctor() { + register_app(piv_select_aid, piv_aid); + register_app(piv_select_aid, yk_aid); + register_app(piv_select_aid, mgmt_aid); +} + +static int cmd_version() { + res_APDU[res_APDU_size++] = PIV_VERSION_MAJOR; + res_APDU[res_APDU_size++] = PIV_VERSION_MINOR; + res_APDU[res_APDU_size++] = 0x0; + return SW_OK(); +} + +static int cmd_select() { + if (P2(apdu) != 0x1) { + return SW_WRONG_P1P2(); + } + if (memcmp(apdu.data, piv_aid, 5) == 0) { + select_piv_aid(); + } + return SW_OK(); +} + +static int cmd_get_serial() { + pico_unique_board_id_t unique_id; + pico_get_unique_board_id(&unique_id); + memcpy(res_APDU, unique_id.id, 4); + res_APDU_size = 4; + return SW_OK(); +} + +extern int check_pin(const file_t *pin, const uint8_t *data, size_t len); +static int cmd_verify() { + uint8_t key_ref = P2(apdu); + if (P1(apdu) != 0x00 && P1(apdu) != 0xFF) { + return SW_INCORRECT_P1P2(); + } + if (key_ref != 0x80) { + return SW_REFERENCE_NOT_FOUND(); + } + file_t *pw, *pw_status; + uint16_t fid = 0x0; + if (key_ref == 0x80) { + fid = EF_PW1; + } + if (!(pw = search_by_fid(fid, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + if (file_get_data(pw)[0] == 0) { //not initialized + return SW_REFERENCE_NOT_FOUND(); + } + if (apdu.nc > 0) { + return check_pin(pw, apdu.data, apdu.nc); + } + uint8_t retries = *(file_get_data(pw_status) + 3 + (fid & 0x3)); + if (retries == 0) { + return SW_PIN_BLOCKED(); + } + if ((key_ref == 0x80 && has_pw1)) { + return SW_OK(); + } + return set_res_sw(0x63, 0xc0 | retries); +} + +#define INS_VERIFY 0x20 +#define INS_VERSION 0xFD +#define INS_SELECT 0xA4 +#define INS_YK_SERIAL 0xF8 +#define INS_VERIFY 0x20 + +static const cmd_t cmds[] = { + { INS_VERSION, cmd_version }, + { INS_SELECT, cmd_select }, + { INS_YK_SERIAL, cmd_get_serial }, + { INS_VERIFY, cmd_verify }, + { 0x00, 0x0 } +}; + +int piv_process_apdu() { + sm_unwrap(); + for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) { + if (cmd->ins == INS(apdu)) { + int r = cmd->cmd_handler(); + sm_wrap(); + return r; + } + } + return SW_INS_NOT_SUPPORTED(); +} diff --git a/src/openpgp/version.h b/src/openpgp/version.h index 1b4b827..d0d70f8 100644 --- a/src/openpgp/version.h +++ b/src/openpgp/version.h @@ -24,9 +24,14 @@ #define OPGP_VERSION_MINOR (OPGP_VERSION & 0xff) -#define PIPGP_VERSION 0x010C +#define PIPGP_VERSION 0x0200 #define PIPGP_VERSION_MAJOR ((PIPGP_VERSION >> 8) & 0xff) #define PIPGP_VERSION_MINOR (PIPGP_VERSION & 0xff) +#define PIV_VERSION 0x0500 + +#define PIV_VERSION_MAJOR ((PIV_VERSION >> 8) & 0xff) +#define PIV_VERSION_MINOR (PIV_VERSION & 0xff) + #endif