/* * 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(); }