From 95a4f7201be14523e1740d50bfa5685fa3c8a3d0 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 8 Jan 2025 11:46:49 +0100 Subject: [PATCH 1/6] Move cmd functions to separate files. Signed-off-by: Pol Henarejos --- CMakeLists.txt | 117 ++- pico-keys-sdk | 2 +- src/openpgp/cmd_activate_file.c | 22 + src/openpgp/cmd_challenge.c | 29 + src/openpgp/cmd_change_pin.c | 62 ++ src/openpgp/cmd_get_data.c | 91 ++ src/openpgp/cmd_import_data.c | 208 +++++ src/openpgp/cmd_internal_aut.c | 77 ++ src/openpgp/cmd_keypair_gen.c | 138 +++ src/openpgp/cmd_mse.c | 49 ++ src/openpgp/cmd_pso.c | 213 +++++ src/openpgp/cmd_put_data.c | 74 ++ src/openpgp/cmd_reset_retry.c | 80 ++ src/openpgp/cmd_select.c | 86 ++ src/openpgp/cmd_select_data.c | 54 ++ src/openpgp/cmd_terminate_df.c | 37 + src/openpgp/cmd_verify.c | 67 ++ src/openpgp/cmd_version.c | 26 + src/openpgp/do.c | 386 ++++++++ src/openpgp/do.h | 28 + src/openpgp/openpgp.c | 1460 ++----------------------------- src/openpgp/openpgp.h | 22 + src/openpgp/piv.c | 6 +- src/openpgp/version.h | 10 +- 24 files changed, 1881 insertions(+), 1463 deletions(-) create mode 100644 src/openpgp/cmd_activate_file.c create mode 100644 src/openpgp/cmd_challenge.c create mode 100644 src/openpgp/cmd_change_pin.c create mode 100644 src/openpgp/cmd_get_data.c create mode 100644 src/openpgp/cmd_import_data.c create mode 100644 src/openpgp/cmd_internal_aut.c create mode 100644 src/openpgp/cmd_keypair_gen.c create mode 100644 src/openpgp/cmd_mse.c create mode 100644 src/openpgp/cmd_pso.c create mode 100644 src/openpgp/cmd_put_data.c create mode 100644 src/openpgp/cmd_reset_retry.c create mode 100644 src/openpgp/cmd_select.c create mode 100644 src/openpgp/cmd_select_data.c create mode 100644 src/openpgp/cmd_terminate_df.c create mode 100644 src/openpgp/cmd_verify.c create mode 100644 src/openpgp/cmd_version.c create mode 100644 src/openpgp/do.c create mode 100644 src/openpgp/do.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e7ff723..c776511 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,26 +18,30 @@ cmake_minimum_required(VERSION 3.13) if(ESP_PLATFORM) - set(EXTRA_COMPONENT_DIRS src pico-keys-sdk/src) - include($ENV{IDF_PATH}/tools/cmake/project.cmake) + set(EXTRA_COMPONENT_DIRS src pico-keys-sdk/src) + include($ENV{IDF_PATH}/tools/cmake/project.cmake) else() + if(NOT ENABLE_EMULATION) + include(pico_sdk_import.cmake) + endif() - if(ENABLE_EMULATION) - else() - include(pico_sdk_import.cmake) - endif() + project(pico_openpgp C CXX ASM) - project(pico_openpgp C CXX ASM) + set(CMAKE_C_STANDARD 11) + set(CMAKE_CXX_STANDARD 17) - set(CMAKE_C_STANDARD 11) - set(CMAKE_CXX_STANDARD 17) + if(NOT ENABLE_EMULATION) + pico_sdk_init() + endif() - if(ENABLE_EMULATION) - else() - pico_sdk_init() - endif() + if(NOT DEFINED __FOR_CI) + set(__FOR_CI 0) + endif() + if(__FOR_CI) + add_definitions(-D__FOR_CI) + endif() - add_executable(pico_openpgp) + add_executable(pico_openpgp) endif() set(SOURCES ${SOURCES} @@ -45,56 +49,75 @@ set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/files.c ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/piv.c ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/management.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_select.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_get_data.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_verify.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_put_data.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_select_data.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_import_data.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_version.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_change_pin.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_mse.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_internal_aut.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_challenge.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_activate_file.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_terminate_df.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_pso.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_keypair_gen.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/cmd_reset_retry.c + ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/do.c ) set(USB_ITF_CCID 1) set(USB_ITF_WCID 1) include(pico-keys-sdk/pico_keys_sdk_import.cmake) + +SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/openpgp/version.h") + if(ESP_PLATFORM) - project(pico_openpgp) + project(pico_openpgp) endif() set(INCLUDES ${INCLUDES} ${CMAKE_CURRENT_LIST_DIR}/src/openpgp ) if(NOT ESP_PLATFORM) - target_sources(pico_openpgp PUBLIC ${SOURCES}) - target_include_directories(pico_openpgp PUBLIC ${INCLUDES}) + target_sources(pico_openpgp PUBLIC ${SOURCES}) + target_include_directories(pico_openpgp PUBLIC ${INCLUDES}) + target_compile_options(pico_openpgp PUBLIC + -Wall + ) + if(NOT MSVC) target_compile_options(pico_openpgp PUBLIC - -Wall + -Werror ) + endif() + + if(ENABLE_EMULATION) if(NOT MSVC) - target_compile_options(pico_openpgp PUBLIC - -Werror - ) + target_compile_options(pico_openpgp PUBLIC + -fdata-sections + -ffunction-sections + ) endif() + if(APPLE) + target_link_options(pico_openpgp PUBLIC + -Wl,-dead_strip + ) + elseif(MSVC) + target_compile_options(pico_openpgp PUBLIC + -WX + ) - if(ENABLE_EMULATION) - if(NOT MSVC) - target_compile_options(pico_openpgp PUBLIC - -fdata-sections - -ffunction-sections - ) - endif() - if(APPLE) - target_link_options(pico_openpgp PUBLIC - -Wl,-dead_strip - ) - elseif(MSVC) - target_compile_options(pico_openpgp PUBLIC - -WX - ) - - target_link_libraries(pico_openpgp PUBLIC wsock32 ws2_32 Bcrypt) - else() - target_link_options(pico_openpgp PUBLIC - -Wl,--gc-sections - ) - endif(APPLE) - target_link_libraries(pico_openpgp PRIVATE pthread m) + target_link_libraries(pico_openpgp PUBLIC wsock32 ws2_32 Bcrypt) else() - pico_add_extra_outputs(${CMAKE_PROJECT_NAME}) - endif() + target_link_options(pico_openpgp PUBLIC + -Wl,--gc-sections + ) + endif(APPLE) + target_link_libraries(pico_openpgp PRIVATE pthread m) + else() + pico_add_extra_outputs(${CMAKE_PROJECT_NAME}) + endif() endif() - diff --git a/pico-keys-sdk b/pico-keys-sdk index 9e2b6ac..68a8168 160000 --- a/pico-keys-sdk +++ b/pico-keys-sdk @@ -1 +1 @@ -Subproject commit 9e2b6ac4b6ad7f978b5c28600a007136fc6cb2ce +Subproject commit 68a816895efb56a917520935f2f341960dc8db2c diff --git a/src/openpgp/cmd_activate_file.c b/src/openpgp/cmd_activate_file.c new file mode 100644 index 0000000..77a2c23 --- /dev/null +++ b/src/openpgp/cmd_activate_file.c @@ -0,0 +1,22 @@ +/* + * 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 "openpgp.h" + +int cmd_activate_file() { + return SW_OK(); +} \ No newline at end of file diff --git a/src/openpgp/cmd_challenge.c b/src/openpgp/cmd_challenge.c new file mode 100644 index 0000000..8e9db61 --- /dev/null +++ b/src/openpgp/cmd_challenge.c @@ -0,0 +1,29 @@ +/* + * 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 "openpgp.h" +#include "random.h" + +int cmd_challenge() { + uint8_t *rb = (uint8_t *) random_bytes_get(apdu.ne); + if (!rb) { + return SW_WRONG_LENGTH(); + } + memcpy(res_APDU, rb, apdu.ne); + res_APDU_size = apdu.ne; + return SW_OK(); +} diff --git a/src/openpgp/cmd_change_pin.c b/src/openpgp/cmd_change_pin.c new file mode 100644 index 0000000..5ef9820 --- /dev/null +++ b/src/openpgp/cmd_change_pin.c @@ -0,0 +1,62 @@ +/* + * 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 "openpgp.h" + +int cmd_change_pin() { + if (P1(apdu) != 0x0) { + return SW_WRONG_P1P2(); + } + uint16_t fid = 0x1000 | P2(apdu); + file_t *pw; + if (!(pw = search_by_fid(fid, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + uint8_t pin_len = file_get_data(pw)[0]; + uint16_t r = 0; + if ((r = load_dek()) != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + r = check_pin(pw, apdu.data, pin_len); + if (r != 0x9000) { + return r; + } + uint8_t dhash[33]; + dhash[0] = apdu.nc - pin_len; + double_hash_pin(apdu.data + pin_len, apdu.nc - pin_len, dhash + 1); + file_put_data(pw, dhash, sizeof(dhash)); + + file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF); + if (!tf) { + return SW_REFERENCE_NOT_FOUND(); + } + uint8_t def[IV_SIZE + 32 + 32 + 32 + 32] = {0}; + memcpy(def, file_get_data(tf), file_get_size(tf)); + if (P2(apdu) == 0x81) { + hash_multi(apdu.data + pin_len, apdu.nc - pin_len, session_pw1); + memcpy(def + IV_SIZE, dek + IV_SIZE, 32); + aes_encrypt_cfb_256(session_pw1, def, def + IV_SIZE, 32); + } + else if (P2(apdu) == 0x83) { + hash_multi(apdu.data + pin_len, apdu.nc - pin_len, session_pw3); + memcpy(def + IV_SIZE + 32 + 32, dek + IV_SIZE, 32); + aes_encrypt_cfb_256(session_pw3, def, def + IV_SIZE + 32 + 32, 32); + } + file_put_data(tf, def, sizeof(def)); + low_flash_available(); + return SW_OK(); +} diff --git a/src/openpgp/cmd_get_data.c b/src/openpgp/cmd_get_data.c new file mode 100644 index 0000000..20e034f --- /dev/null +++ b/src/openpgp/cmd_get_data.c @@ -0,0 +1,91 @@ +/* + * 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 "openpgp.h" +#include "asn1.h" + +int cmd_get_data() { + if (apdu.nc > 0) { + return SW_WRONG_LENGTH(); + } + uint16_t fid = (P1(apdu) << 8) | P2(apdu); + file_t *ef; + if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + if (currentEF && (currentEF->fid & 0x1FF0) == (fid & 0x1FF0)) { //previously selected + ef = currentEF; + } + else { + select_file(ef); + } + if (ef->data) { + uint16_t fids[] = { 1, fid }; + uint16_t data_len = parse_do(fids, 1); + uint8_t *p = NULL; + uint16_t tg = 0; + uint16_t tg_len = 0; + asn1_ctx_t ctxi; + asn1_ctx_init(res_APDU, data_len, &ctxi); + if (walk_tlv(&ctxi, &p, &tg, &tg_len, NULL)) { + uint8_t dec = 2; + if ((tg & 0x1f) == 0x1f) { + dec++; + } + if ((res_APDU[dec - 1] & 0xF0) == 0x80) { + dec += (res_APDU[dec - 1] & 0x0F); + } + if (tg_len + dec == data_len) { + memmove(res_APDU, res_APDU + dec, data_len - dec); + data_len -= dec; + res_APDU_size -= dec; + } + } + //if (apdu.ne > data_len) + // apdu.ne = data_len; + } + return SW_OK(); +} + +int cmd_get_next_data() { + file_t *ef = NULL; + if (apdu.nc > 0) { + return SW_WRONG_LENGTH(); + } + if (!currentEF) { + return SW_RECORD_NOT_FOUND(); + } + uint16_t fid = (P1(apdu) << 8) | P2(apdu); + if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + if ((currentEF->fid & 0x1FF0) != (fid & 0x1FF0)) { + return SW_WRONG_P1P2(); + } + fid = currentEF->fid + 1; //curentEF contains private DO. so, we select the next one + if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + select_file(ef); + return cmd_get_data(); +} diff --git a/src/openpgp/cmd_import_data.c b/src/openpgp/cmd_import_data.c new file mode 100644 index 0000000..74df4ec --- /dev/null +++ b/src/openpgp/cmd_import_data.c @@ -0,0 +1,208 @@ +/* + * 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 . + */ + +#ifdef ESP_PLATFORM +#include "esp_compat.h" +#define MBEDTLS_ALLOW_PRIVATE_ACCESS +#else +#include "common.h" +#endif +#include "openpgp.h" +#include "random.h" +#include "do.h" + +uint16_t tag_len(uint8_t **data) { + size_t len = *(*data)++; + if (len == 0x82) { + len = *(*data)++ << 8; + len |= *(*data)++; + } + else if (len == 0x81) { + len = *(*data)++; + } + return len; +} + +int cmd_import_data() { + file_t *ef = NULL; + uint16_t fid = 0x0; + if (P1(apdu) != 0x3F || P2(apdu) != 0xFF) { + return SW_WRONG_P1P2(); + } + if (apdu.nc < 5) { + return SW_WRONG_LENGTH(); + } + uint8_t *start = apdu.data; + if (*start++ != 0x4D) { + return SW_WRONG_DATA(); + } + uint16_t tgl = tag_len(&start); + if (*start != 0xB6 && *start != 0xB8 && *start != 0xA4) { + return SW_WRONG_DATA(); + } + if (*start == 0xB6) { + fid = EF_PK_SIG; + } + else if (*start == 0xB8) { + fid = EF_PK_DEC; + } + else if (*start == 0xA4) { + fid = EF_PK_AUT; + } + else { + return SW_WRONG_DATA(); + } + start++; + if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + start += (*start + 1); + if (*start++ != 0x7F || *start++ != 0x48) { + return SW_WRONG_DATA(); + } + tgl = tag_len(&start); + uint8_t *end = start + tgl, *p[9] = { 0 }; + uint16_t len[9] = { 0 }; + while (start < end) { + uint8_t tag = *start++; + if ((tag >= 0x91 && tag <= 0x97) || tag == 0x99) { + len[tag - 0x91] = tag_len(&start); + } + else { + return SW_WRONG_DATA(); + } + } + if (*start++ != 0x5F || *start++ != 0x48) { + return SW_WRONG_DATA(); + } + tgl = tag_len(&start); + end = start + tgl; + for (int t = 0; start < end && t < 9; t++) { + if (len[t] > 0) { + p[t] = start; + start += len[t]; + } + } + + file_t *algo_ef = search_by_fid(fid - 0x0010, NULL, SPECIFY_EF); + if (!algo_ef) { + return SW_REFERENCE_NOT_FOUND(); + } + const uint8_t *algo = algorithm_attr_rsa2k + 1; + uint16_t algo_len = algorithm_attr_rsa2k[0]; + if (algo_ef && algo_ef->data) { + algo = file_get_data(algo_ef); + algo_len = file_get_size(algo_ef); + } + int r = 0; + if (algo[0] == ALGO_RSA) { + mbedtls_rsa_context rsa; + if (p[0] == NULL || len[0] == 0 || p[1] == NULL || len[1] == 0 || p[2] == NULL || + len[2] == 0) { + return SW_WRONG_DATA(); + } + mbedtls_rsa_init(&rsa); + r = mbedtls_mpi_read_binary(&rsa.E, p[0], len[0]); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + r = mbedtls_mpi_read_binary(&rsa.P, p[1], len[1]); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + r = mbedtls_mpi_read_binary(&rsa.Q, p[2], len[2]); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + r = mbedtls_rsa_import(&rsa, NULL, &rsa.P, &rsa.Q, NULL, &rsa.E); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + r = mbedtls_rsa_complete(&rsa); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + r = mbedtls_rsa_check_privkey(&rsa); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + r = store_keys(&rsa, ALGO_RSA, fid, true); + make_rsa_response(&rsa); + mbedtls_rsa_free(&rsa); + if (r != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + } + else if (algo[0] == ALGO_ECDSA || algo[0] == ALGO_ECDH) { + mbedtls_ecdsa_context ecdsa; + if (p[1] == NULL || len[1] == 0) { + return SW_WRONG_DATA(); + } + mbedtls_ecp_group_id gid = get_ec_group_id_from_attr(algo + 1, algo_len - 1); + if (gid == MBEDTLS_ECP_DP_NONE) { + return SW_FUNC_NOT_SUPPORTED(); + } + mbedtls_ecdsa_init(&ecdsa); + if (gid == MBEDTLS_ECP_DP_CURVE25519) { + mbedtls_ecp_group_load(&ecdsa.grp, gid); + r = mbedtls_mpi_read_binary(&ecdsa.d, p[1], len[1]); + } + else { + r = mbedtls_ecp_read_key(gid, &ecdsa, p[1], len[1]); + } + if (r != 0) { + mbedtls_ecdsa_free(&ecdsa); + return SW_EXEC_ERROR(); + } + r = mbedtls_ecp_mul(&ecdsa.grp, &ecdsa.Q, &ecdsa.d, &ecdsa.grp.G, random_gen, NULL); + if (r != 0) { + mbedtls_ecdsa_free(&ecdsa); + return SW_EXEC_ERROR(); + } + r = store_keys(&ecdsa, ALGO_ECDSA, fid, true); + make_ecdsa_response(&ecdsa); + mbedtls_ecdsa_free(&ecdsa); + if (r != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + } + else { + return SW_FUNC_NOT_SUPPORTED(); + } + if (fid == EF_PK_SIG) { + reset_sig_count(); + } + file_t *pbef = search_by_fid(fid + 3, NULL, SPECIFY_EF); + if (!pbef) { + return SW_REFERENCE_NOT_FOUND(); + } + r = file_put_data(pbef, res_APDU, res_APDU_size); + if (r != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + res_APDU_size = 0; //make_*_response sets a response. we need to overwrite + return SW_OK(); +} diff --git a/src/openpgp/cmd_internal_aut.c b/src/openpgp/cmd_internal_aut.c new file mode 100644 index 0000000..92d4c07 --- /dev/null +++ b/src/openpgp/cmd_internal_aut.c @@ -0,0 +1,77 @@ +/* + * 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 "openpgp.h" +#include "do.h" + +int cmd_internal_aut() { + if (P1(apdu) != 0x00 || P2(apdu) != 0x00) { + return SW_WRONG_P1P2(); + } + if (!has_pw3 && !has_pw2) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + file_t *algo_ef = search_by_fid(algo_aut, NULL, SPECIFY_EF); + if (!algo_ef) { + return SW_REFERENCE_NOT_FOUND(); + } + const uint8_t *algo = algorithm_attr_rsa2k + 1; + if (algo_ef && algo_ef->data) { + algo = file_get_data(algo_ef); + } + file_t *ef = search_by_fid(pk_aut, NULL, SPECIFY_EF); + if (!ef) { + return SW_REFERENCE_NOT_FOUND(); + } + if (wait_button_pressed(EF_UIF_AUT) == true) { + return SW_SECURE_MESSAGE_EXEC_ERROR(); + } + int r = PICOKEY_OK; + if (algo[0] == ALGO_RSA) { + mbedtls_rsa_context ctx; + mbedtls_rsa_init(&ctx); + r = load_private_key_rsa(&ctx, ef, true); + if (r != PICOKEY_OK) { + mbedtls_rsa_free(&ctx); + return SW_EXEC_ERROR(); + } + size_t olen = 0; + r = rsa_sign(&ctx, apdu.data, apdu.nc, res_APDU, &olen); + mbedtls_rsa_free(&ctx); + if (r != 0) { + return SW_EXEC_ERROR(); + } + res_APDU_size = olen; + } + else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA) { + mbedtls_ecdsa_context ctx; + mbedtls_ecdsa_init(&ctx); + r = load_private_key_ecdsa(&ctx, ef, true); + if (r != PICOKEY_OK) { + mbedtls_ecdsa_free(&ctx); + return SW_EXEC_ERROR(); + } + size_t olen = 0; + r = ecdsa_sign(&ctx, apdu.data, apdu.nc, res_APDU, &olen); + mbedtls_ecdsa_free(&ctx); + if (r != 0) { + return SW_EXEC_ERROR(); + } + res_APDU_size = olen; + } + return SW_OK(); +} diff --git a/src/openpgp/cmd_keypair_gen.c b/src/openpgp/cmd_keypair_gen.c new file mode 100644 index 0000000..9329350 --- /dev/null +++ b/src/openpgp/cmd_keypair_gen.c @@ -0,0 +1,138 @@ +/* + * 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 "openpgp.h" +#include "do.h" +#include "random.h" + +int cmd_keypair_gen() { + if (P2(apdu) != 0x0) { + return SW_INCORRECT_P1P2(); + } + if (apdu.nc != 2 && apdu.nc != 5) { + return SW_WRONG_LENGTH(); + } + if (!has_pw3 && P1(apdu) == 0x80) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + + uint16_t fid = 0x0; + int r = PICOKEY_OK; + if (apdu.data[0] == 0xB6) { + fid = EF_PK_SIG; + } + else if (apdu.data[0] == 0xB8) { + fid = EF_PK_DEC; + } + else if (apdu.data[0] == 0xA4) { + fid = EF_PK_AUT; + } + else { + return SW_WRONG_DATA(); + } + + file_t *algo_ef = search_by_fid(fid - 0x0010, NULL, SPECIFY_EF); + if (!algo_ef) { + return SW_REFERENCE_NOT_FOUND(); + } + const uint8_t *algo = algorithm_attr_rsa2k + 1; + uint16_t algo_len = algorithm_attr_rsa2k[0]; + if (algo_ef && algo_ef->data) { + algo = file_get_data(algo_ef); + algo_len = file_get_size(algo_ef); + } + if (P1(apdu) == 0x80) { //generate + if (algo[0] == ALGO_RSA) { + int exponent = 65537, nlen = (algo[1] << 8) | algo[2]; + printf("KEYPAIR RSA %d\r\n", nlen); + //if (nlen != 2048 && nlen != 4096) + // return SW_FUNC_NOT_SUPPORTED(); + mbedtls_rsa_context rsa; + mbedtls_rsa_init(&rsa); + uint8_t index = 0; + r = mbedtls_rsa_gen_key(&rsa, random_gen, &index, nlen, exponent); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + r = store_keys(&rsa, ALGO_RSA, fid, true); + make_rsa_response(&rsa); + mbedtls_rsa_free(&rsa); + if (r != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + } + else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA) { + printf("KEYPAIR ECDSA\r\n"); + mbedtls_ecp_group_id gid = get_ec_group_id_from_attr(algo + 1, algo_len - 1); + if (gid == MBEDTLS_ECP_DP_NONE) { + return SW_FUNC_NOT_SUPPORTED(); + } + mbedtls_ecdsa_context ecdsa; + mbedtls_ecdsa_init(&ecdsa); + uint8_t index = 0; + r = mbedtls_ecdsa_genkey(&ecdsa, gid, random_gen, &index); + if (r != 0) { + mbedtls_ecdsa_free(&ecdsa); + return SW_EXEC_ERROR(); + } + r = store_keys(&ecdsa, algo[0], fid, true); + make_ecdsa_response(&ecdsa); + mbedtls_ecdsa_free(&ecdsa); + if (r != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + } + else { + return SW_FUNC_NOT_SUPPORTED(); + } + file_t *pbef = search_by_fid(fid + 3, NULL, SPECIFY_EF); + if (!pbef) { + return SW_REFERENCE_NOT_FOUND(); + } + r = file_put_data(pbef, res_APDU, res_APDU_size); + if (r != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + if (fid == EF_PK_SIG) { + reset_sig_count(); + } + else if (fid == EF_PK_DEC) { + // OpenPGP does not allow generating AES keys. So, we generate a new one when gen for DEC is called. + // It is a 256 AES key by default. + uint8_t aes_key[32]; //maximum AES key size + uint8_t key_size = 32; + memcpy(aes_key, random_bytes_get(key_size), key_size); + r = store_keys(aes_key, ALGO_AES_256, EF_AES_KEY, true); + /* if storing the key fails, we silently continue */ + //if (r != PICOKEY_OK) + // return SW_EXEC_ERROR(); + } + low_flash_available(); + return SW_OK(); + } + else if (P1(apdu) == 0x81) { //read + file_t *ef = search_by_fid(fid + 3, NULL, SPECIFY_EF); + if (!ef || !ef->data) { + return SW_REFERENCE_NOT_FOUND(); + } + res_APDU_size = file_get_size(ef); + memcpy(res_APDU, file_get_data(ef), res_APDU_size); + return SW_OK(); + } + return SW_INCORRECT_P1P2(); +} diff --git a/src/openpgp/cmd_mse.c b/src/openpgp/cmd_mse.c new file mode 100644 index 0000000..4be170d --- /dev/null +++ b/src/openpgp/cmd_mse.c @@ -0,0 +1,49 @@ +/* + * 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 "openpgp.h" + +int cmd_mse() { + if (P1(apdu) != 0x41 || (P2(apdu) != 0xA4 && P2(apdu) != 0xB8)) { + return SW_WRONG_P1P2(); + } + if (apdu.data[0] != 0x83 || apdu.data[1] != 0x1 || + (apdu.data[2] != 0x2 && apdu.data[2] != 0x3)) { + return SW_WRONG_DATA(); + } + if (P2(apdu) == 0xA4) { + if (apdu.data[2] == 0x2) { + algo_dec = EF_ALGO_PRIV2; + pk_dec = EF_PK_DEC; + } + else if (apdu.data[2] == 0x3) { + algo_dec = EF_ALGO_PRIV3; + pk_dec = EF_PK_AUT; + } + } + else if (P2(apdu) == 0xB8) { + if (apdu.data[2] == 0x2) { + algo_aut = EF_ALGO_PRIV2; + pk_aut = EF_PK_DEC; + } + else if (apdu.data[2] == 0x3) { + algo_aut = EF_ALGO_PRIV3; + pk_aut = EF_PK_AUT; + } + } + return SW_OK(); +} diff --git a/src/openpgp/cmd_pso.c b/src/openpgp/cmd_pso.c new file mode 100644 index 0000000..fd5a891 --- /dev/null +++ b/src/openpgp/cmd_pso.c @@ -0,0 +1,213 @@ +/* + * 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 . + */ + +#ifdef ESP_PLATFORM +#include "esp_compat.h" +#define MBEDTLS_ALLOW_PRIVATE_ACCESS +#else +#include "common.h" +#endif +#include "openpgp.h" +#include "do.h" +#include "random.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/asn1.h" + +int cmd_pso() { + uint16_t algo_fid = 0x0, pk_fid = 0x0; + bool is_aes = false; + if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) { + if (!has_pw3 && !has_pw1) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + algo_fid = EF_ALGO_PRIV1; + pk_fid = EF_PK_SIG; + } + else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { + if (!has_pw3 && !has_pw2) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + algo_fid = algo_dec; + pk_fid = pk_dec; + } + else { + return SW_INCORRECT_P1P2(); + } + file_t *algo_ef = search_by_fid(algo_fid, NULL, SPECIFY_EF); + if (!algo_ef) { + return SW_REFERENCE_NOT_FOUND(); + } + const uint8_t *algo = algorithm_attr_rsa2k + 1; + if (algo_ef && algo_ef->data) { + algo = file_get_data(algo_ef); + } + if (apdu.data[0] == 0x2) { //AES PSO? + if (((apdu.nc - 1) % 16 == 0 && P1(apdu) == 0x80 && P2(apdu) == 0x86) || + (apdu.nc % 16 == 0 && P1(apdu) == 0x86 && P2(apdu) == 0x80)) { + pk_fid = EF_AES_KEY; + is_aes = true; + } + } + file_t *ef = search_by_fid(pk_fid, NULL, SPECIFY_EF); + if (!ef) { + return SW_REFERENCE_NOT_FOUND(); + } + if (wait_button_pressed(pk_fid == EF_PK_SIG ? EF_UIF_SIG : EF_UIF_DEC) == true) { + return SW_SECURE_MESSAGE_EXEC_ERROR(); + } + int r = PICOKEY_OK; + int key_size = file_get_size(ef); + if (is_aes) { + uint8_t aes_key[32]; + r = load_aes_key(aes_key, ef); + if (r != PICOKEY_OK) { + memset(aes_key, 0, sizeof(aes_key)); + return SW_EXEC_ERROR(); + } + if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { //decipher + r = aes_decrypt(aes_key, NULL, key_size, PICO_KEYS_AES_MODE_CBC, apdu.data + 1, apdu.nc - 1); + memset(aes_key, 0, sizeof(aes_key)); + if (r != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + memcpy(res_APDU, apdu.data + 1, apdu.nc - 1); + res_APDU_size = apdu.nc - 1; + } + else if (P1(apdu) == 0x86 && P2(apdu) == 0x80) { //encipher + r = aes_encrypt(aes_key, NULL, key_size, PICO_KEYS_AES_MODE_CBC, apdu.data, apdu.nc); + memset(aes_key, 0, sizeof(aes_key)); + if (r != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + res_APDU[0] = 0x2; + memcpy(res_APDU + 1, apdu.data, apdu.nc); + res_APDU_size = apdu.nc + 1; + } + return SW_OK(); + } + if (algo[0] == ALGO_RSA) { + mbedtls_rsa_context ctx; + mbedtls_rsa_init(&ctx); + r = load_private_key_rsa(&ctx, ef, true); + if (r != PICOKEY_OK) { + mbedtls_rsa_free(&ctx); + return SW_EXEC_ERROR(); + } + if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) { + size_t olen = 0; + r = rsa_sign(&ctx, apdu.data, apdu.nc, res_APDU, &olen); + mbedtls_rsa_free(&ctx); + if (r != 0) { + return SW_EXEC_ERROR(); + } + res_APDU_size = olen; + //apdu.ne = key_size; + inc_sig_count(); + } + else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { + if (apdu.nc < key_size) { //needs padding + memset(apdu.data + apdu.nc, 0, key_size - apdu.nc); + } + size_t olen = 0; + r = mbedtls_rsa_pkcs1_decrypt(&ctx, + random_gen, + NULL, + &olen, + apdu.data + 1, + res_APDU, + key_size); + mbedtls_rsa_free(&ctx); + if (r != 0) { + return SW_EXEC_ERROR(); + } + res_APDU_size = olen; + } + } + else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA) { + if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) { + mbedtls_ecdsa_context ctx; + mbedtls_ecdsa_init(&ctx); + r = load_private_key_ecdsa(&ctx, ef, true); + if (r != PICOKEY_OK) { + mbedtls_ecdsa_free(&ctx); + return SW_EXEC_ERROR(); + } + size_t olen = 0; + r = ecdsa_sign(&ctx, apdu.data, apdu.nc, res_APDU, &olen); + mbedtls_ecdsa_free(&ctx); + if (r != 0) { + return SW_EXEC_ERROR(); + } + res_APDU_size = olen; + inc_sig_count(); + } + else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { + mbedtls_ecdh_context ctx; + uint8_t kdata[67]; + uint8_t *data = apdu.data, *end = data + apdu.nc; + size_t len = 0; + if (mbedtls_asn1_get_tag(&data, end, &len, 0xA6) != 0) { + return SW_WRONG_DATA(); + } + if (*data++ != 0x7f) { + return SW_WRONG_DATA(); + } + if (mbedtls_asn1_get_tag(&data, end, &len, + 0x49) != 0 || + mbedtls_asn1_get_tag(&data, end, &len, 0x86) != 0) { + return SW_WRONG_DATA(); + } + //if (len != 2*key_size-1) + // return SW_WRONG_LENGTH(); + memcpy(kdata, file_get_data(ef), key_size); + if (dek_decrypt(kdata, key_size) != 0) { + return SW_EXEC_ERROR(); + } + mbedtls_ecdh_init(&ctx); + mbedtls_ecp_group_id gid = kdata[0]; + r = mbedtls_ecdh_setup(&ctx, gid); + if (r != 0) { + mbedtls_ecdh_free(&ctx); + return SW_DATA_INVALID(); + } + r = mbedtls_ecp_read_key(gid, (mbedtls_ecdsa_context *)&ctx.ctx.mbed_ecdh, kdata + 1, key_size - 1); + if (r != 0) { + mbedtls_ecdh_free(&ctx); + return SW_DATA_INVALID(); + } + r = mbedtls_ecdh_read_public(&ctx, data - 1, len + 1); + if (r != 0) { + mbedtls_ecdh_free(&ctx); + return SW_DATA_INVALID(); + } + size_t olen = 0; + r = mbedtls_ecdh_calc_secret(&ctx, + &olen, + res_APDU, + MBEDTLS_ECP_MAX_BYTES, + random_gen, + NULL); + if (r != 0) { + mbedtls_ecdh_free(&ctx); + return SW_EXEC_ERROR(); + } + res_APDU_size = olen; + mbedtls_ecdh_free(&ctx); + } + } + return SW_OK(); +} diff --git a/src/openpgp/cmd_put_data.c b/src/openpgp/cmd_put_data.c new file mode 100644 index 0000000..1e39ca6 --- /dev/null +++ b/src/openpgp/cmd_put_data.c @@ -0,0 +1,74 @@ +/* + * 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 "openpgp.h" + +int cmd_put_data() { + uint16_t fid = (P1(apdu) << 8) | P2(apdu); + file_t *ef; + if (fid == EF_RESET_CODE) { + fid = EF_RC; + } + else if (fid == EF_ALGO_SIG || fid == EF_ALGO_DEC || fid == EF_ALGO_AUT) { + fid |= 0x1000; + } + if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + if (fid == EF_PW_STATUS) { + fid = EF_PW_PRIV; + apdu.nc = 4; //we silently ommit the reset parameters + } + if (currentEF && (currentEF->fid & 0x1FF0) == (fid & 0x1FF0)) { //previously selected + ef = currentEF; + } + if (apdu.nc > 0 && (ef->type & FILE_DATA_FLASH)) { + int r = 0; + if (fid == EF_RC) { + has_rc = false; + if ((r = load_dek()) != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + uint8_t dhash[33]; + dhash[0] = apdu.nc; + double_hash_pin(apdu.data, apdu.nc, dhash + 1); + r = file_put_data(ef, dhash, sizeof(dhash)); + + file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF); + if (!tf) { + return SW_REFERENCE_NOT_FOUND(); + } + uint8_t def[IV_SIZE + 32 + 32 + 32 + 32]; + memcpy(def, file_get_data(tf), file_get_size(tf)); + hash_multi(apdu.data, apdu.nc, session_rc); + memcpy(def + IV_SIZE + 32, dek + IV_SIZE, 32); + aes_encrypt_cfb_256(session_rc, def, def + IV_SIZE + 32, 32); + r = file_put_data(tf, def, sizeof(def)); + } + else { + r = file_put_data(ef, apdu.data, apdu.nc); + } + if (r != PICOKEY_OK) { + return SW_MEMORY_FAILURE(); + } + low_flash_available(); + } + return SW_OK(); +} diff --git a/src/openpgp/cmd_reset_retry.c b/src/openpgp/cmd_reset_retry.c new file mode 100644 index 0000000..0df7f45 --- /dev/null +++ b/src/openpgp/cmd_reset_retry.c @@ -0,0 +1,80 @@ +/* + * 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 "openpgp.h" + +int cmd_reset_retry() { + if (P2(apdu) != 0x81) { + return SW_REFERENCE_NOT_FOUND(); + } + if (P1(apdu) == 0x0 || P1(apdu) == 0x2) { + int newpin_len = 0; + file_t *pw = NULL; + has_pw1 = false; + if (!(pw = search_by_fid(EF_PW1, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + if (P1(apdu) == 0x0) { + file_t *rc; + if (!(rc = search_by_fid(EF_RC, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + uint8_t pin_len = file_get_data(rc)[0]; + if (apdu.nc <= pin_len) { + return SW_WRONG_LENGTH(); + } + uint16_t r = check_pin(rc, apdu.data, pin_len); + if (r != 0x9000) { + return r; + } + newpin_len = apdu.nc - pin_len; + has_rc = true; + hash_multi(apdu.data, pin_len, session_rc); + } + else if (P1(apdu) == 0x2) { + if (!has_pw3) { + return SW_CONDITIONS_NOT_SATISFIED(); + } + newpin_len = apdu.nc; + } + int r = 0; + if ((r = load_dek()) != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF); + if (!tf) { + return SW_REFERENCE_NOT_FOUND(); + } + uint8_t def[IV_SIZE + 32 + 32 + 32 + 32]; + memcpy(def, file_get_data(tf), file_get_size(tf)); + hash_multi(apdu.data + (apdu.nc - newpin_len), newpin_len, session_pw1); + memcpy(def + IV_SIZE, dek + IV_SIZE, 32); + aes_encrypt_cfb_256(session_pw1, def, def + IV_SIZE, 32); + r = file_put_data(tf, def, sizeof(def)); + + uint8_t dhash[33]; + dhash[0] = newpin_len; + double_hash_pin(apdu.data + (apdu.nc - newpin_len), newpin_len, dhash + 1); + file_put_data(pw, dhash, sizeof(dhash)); + if (pin_reset_retries(pw, true) != PICOKEY_OK) { + return SW_MEMORY_FAILURE(); + } + low_flash_available(); + return SW_OK(); + } + return SW_INCORRECT_P1P2(); +} diff --git a/src/openpgp/cmd_select.c b/src/openpgp/cmd_select.c new file mode 100644 index 0000000..95f94cf --- /dev/null +++ b/src/openpgp/cmd_select.c @@ -0,0 +1,86 @@ +/* + * 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 "openpgp.h" + +int cmd_select() { + uint8_t p1 = P1(apdu); + uint8_t p2 = P2(apdu); + file_t *pe = NULL; + uint16_t fid = 0x0; + + if (apdu.nc >= 2) { + fid = get_uint16_t_be(apdu.data); + } + + if (!pe) { + if (p1 == 0x0) { //Select MF, DF or EF - File identifier or absent + if (apdu.nc == 0) { + pe = (file_t *) MF; + //ac_fini(); + } + else if (apdu.nc == 2) { + if (!(pe = search_by_fid(fid, NULL, SPECIFY_ANY))) { + return SW_REFERENCE_NOT_FOUND(); + } + } + } + else if (p1 == 0x01) { //Select child DF - DF identifier + if (!(pe = search_by_fid(fid, currentDF, SPECIFY_DF))) { + return SW_REFERENCE_NOT_FOUND(); + } + } + else if (p1 == 0x02) { //Select EF under the current DF - EF identifier + if (!(pe = search_by_fid(fid, currentDF, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + } + else if (p1 == 0x03) { //Select parent DF of the current DF - Absent + if (apdu.nc != 0) { + return SW_REFERENCE_NOT_FOUND(); + } + } + else if (p1 == 0x04) { //Select by DF name - e.g., [truncated] application identifier + if (!(pe = search_by_name(apdu.data, apdu.nc))) { + return SW_REFERENCE_NOT_FOUND(); + } + if (card_terminated) { + return set_res_sw(0x62, 0x85); + } + } + else if (p1 == 0x08) { //Select from the MF - Path without the MF identifier + if (!(pe = search_by_path(apdu.data, apdu.nc, MF))) { + return SW_REFERENCE_NOT_FOUND(); + } + } + else if (p1 == 0x09) { //Select from the current DF - Path without the current DF identifier + if (!(pe = search_by_path(apdu.data, apdu.nc, currentDF))) { + return SW_REFERENCE_NOT_FOUND(); + } + } + } + if ((p2 & 0xfc) == 0x00 || (p2 & 0xfc) == 0x04) { + if ((p2 & 0xfc) == 0x04) { + process_fci(pe, 0); + } + } + else { + return SW_INCORRECT_P1P2(); + } + select_file(pe); + return SW_OK(); +} diff --git a/src/openpgp/cmd_select_data.c b/src/openpgp/cmd_select_data.c new file mode 100644 index 0000000..c99d3b2 --- /dev/null +++ b/src/openpgp/cmd_select_data.c @@ -0,0 +1,54 @@ +/* + * 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 "openpgp.h" + +int cmd_select_data() { + file_t *ef = NULL; + uint16_t fid = 0x0; + if (P2(apdu) != 0x4) { + return SW_WRONG_P1P2(); + } + if (apdu.data[0] != 0x60) { + return SW_WRONG_DATA(); + } + if (apdu.nc != apdu.data[1] + 2 || apdu.nc < 5) { + return SW_WRONG_LENGTH(); + } + if (apdu.data[2] != 0x5C) { + return SW_WRONG_DATA(); + } + if (apdu.data[3] == 2) { + fid = (apdu.data[4] << 8) | apdu.data[5]; + } + else { + fid = apdu.data[4]; + } + if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + fid &= ~0x6000; //Now get private DO + fid += P1(apdu); + if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + select_file(ef); + return SW_OK(); +} diff --git a/src/openpgp/cmd_terminate_df.c b/src/openpgp/cmd_terminate_df.c new file mode 100644 index 0000000..b69071d --- /dev/null +++ b/src/openpgp/cmd_terminate_df.c @@ -0,0 +1,37 @@ +/* + * 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 "openpgp.h" + +int cmd_terminate_df() { + if (P1(apdu) != 0x0 || P2(apdu) != 0x0) { + return SW_INCORRECT_P1P2(); + } + file_t *retries; + if (!(retries = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + if (!has_pw3 && *(file_get_data(retries) + 6) > 0) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + if (apdu.nc != 0) { + return SW_WRONG_LENGTH(); + } + initialize_flash(true); + scan_files(); + return SW_OK(); +} diff --git a/src/openpgp/cmd_verify.c b/src/openpgp/cmd_verify.c new file mode 100644 index 0000000..da12e51 --- /dev/null +++ b/src/openpgp/cmd_verify.c @@ -0,0 +1,67 @@ +/* + * 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 "openpgp.h" + +int cmd_verify() { + uint8_t p1 = P1(apdu); + uint8_t p2 = P2(apdu); + + if (p1 == 0xFF) { + if (apdu.nc != 0) { + return SW_WRONG_DATA(); + } + if (p2 == 0x81) { + has_pw1 = false; + } + else if (p2 == 0x82) { + has_pw2 = false; + } + else if (p2 == 0x83) { + has_pw3 = false; + } + return SW_OK(); + } + else if (p1 != 0x0 || (p2 & 0x60) != 0x0) { + return SW_WRONG_P1P2(); + } + uint16_t fid = 0x1000 | p2; + if (fid == EF_RC && apdu.nc > 0) { + fid = EF_PW1; + } + file_t *pw, *pw_status; + 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 & 0xf)); + if (retries == 0) { + return SW_PIN_BLOCKED(); + } + if ((p2 == 0x81 && has_pw1) || (p2 == 0x82 && has_pw2) || (p2 == 0x83 && has_pw3)) { + return SW_OK(); + } + return set_res_sw(0x63, 0xc0 | retries); +} diff --git a/src/openpgp/cmd_version.c b/src/openpgp/cmd_version.c new file mode 100644 index 0000000..858914b --- /dev/null +++ b/src/openpgp/cmd_version.c @@ -0,0 +1,26 @@ +/* + * 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 "openpgp.h" +#include "version.h" + +int cmd_version() { + res_APDU[res_APDU_size++] = PIPGP_VERSION_MAJOR; + res_APDU[res_APDU_size++] = PIPGP_VERSION_MINOR; + res_APDU[res_APDU_size++] = 0x0; + return SW_OK(); +} \ No newline at end of file diff --git a/src/openpgp/do.c b/src/openpgp/do.c new file mode 100644 index 0000000..28b0a72 --- /dev/null +++ b/src/openpgp/do.c @@ -0,0 +1,386 @@ +/* + * 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 "openpgp.h" +#include "asn1.h" + +int parse_do(uint16_t *fids, int mode) { + int len = 0; + file_t *ef; + for (int i = 0; i < fids[0]; i++) { + if ((ef = search_by_fid(fids[i + 1], NULL, SPECIFY_EF))) { + uint16_t data_len; + if ((ef->type & FILE_DATA_FUNC) == FILE_DATA_FUNC) { + data_len = ((int (*)(const file_t *, int))(ef->data))((const file_t *) ef, mode); + } + else { + if (ef->data) { + data_len = file_get_size(ef); + } + else { + data_len = 0; + } + if (mode == 1) { + if (fids[0] > 1 && res_APDU_size > 0) { + if (fids[i + 1] < 0x0100) { + res_APDU[res_APDU_size++] = fids[i + 1] & 0xff; + } + else { + res_APDU[res_APDU_size++] = fids[i + 1] >> 8; + res_APDU[res_APDU_size++] = fids[i + 1] & 0xff; + } + res_APDU_size += format_tlv_len(data_len, res_APDU + res_APDU_size); + } + if (ef->data) { + memcpy(res_APDU + res_APDU_size, file_get_data(ef), data_len); + } + res_APDU_size += data_len; + } + } + len += data_len; + } + } + return len; +} + +int parse_trium(uint16_t fid, uint8_t num, size_t size) { + for (uint8_t i = 0; i < num; i++) { + file_t *ef; + if ((ef = search_by_fid(fid + i, NULL, SPECIFY_EF)) && ef->data) { + uint16_t data_len = file_get_size(ef); + memcpy(res_APDU + res_APDU_size, file_get_data(ef), data_len); + res_APDU_size += data_len; + } + else { + memset(res_APDU + res_APDU_size, 0, size); + res_APDU_size += size; + } + } + return num * size; +} + +int parse_ch_data(const file_t *f, int mode) { + uint16_t fids[] = { + 3, + EF_CH_NAME, EF_LANG_PREF, EF_SEX, + }; + res_APDU[res_APDU_size++] = EF_CH_DATA & 0xff; + res_APDU[res_APDU_size++] = 0x82; + uint8_t *lp = res_APDU + res_APDU_size; + res_APDU_size += 2; + parse_do(fids, mode); + uint16_t lpdif = res_APDU + res_APDU_size - lp - 2; + *lp++ = lpdif >> 8; + *lp++ = lpdif & 0xff; + return lpdif + 4; +} + +int parse_sec_tpl(const file_t *f, int mode) { + res_APDU[res_APDU_size++] = EF_SEC_TPL & 0xff; + res_APDU[res_APDU_size++] = 5; + file_t *ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY); + if (ef && ef->data) { + res_APDU[res_APDU_size++] = EF_SIG_COUNT & 0xff; + res_APDU[res_APDU_size++] = 3; + memcpy(res_APDU + res_APDU_size, file_get_data(ef), 3); + res_APDU_size += 3; + } + return 5 + 2; +} + +int parse_ch_cert(const file_t *f, int mode) { + return 0; +} + +int parse_fp(const file_t *f, int mode) { + res_APDU[res_APDU_size++] = EF_FP & 0xff; + res_APDU[res_APDU_size++] = 60; + return parse_trium(EF_FP_SIG, 3, 20) + 2; +} + +int parse_cafp(const file_t *f, int mode) { + res_APDU[res_APDU_size++] = EF_CA_FP & 0xff; + res_APDU[res_APDU_size++] = 60; + return parse_trium(EF_FP_CA1, 3, 20) + 2; +} + +int parse_ts(const file_t *f, int mode) { + res_APDU[res_APDU_size++] = EF_TS_ALL & 0xff; + res_APDU[res_APDU_size++] = 12; + return parse_trium(EF_TS_SIG, 3, 4) + 2; +} + +int parse_keyinfo(const file_t *f, int mode) { + int init_len = res_APDU_size; + if (res_APDU_size > 0) { + res_APDU[res_APDU_size++] = EF_KEY_INFO & 0xff; + res_APDU[res_APDU_size++] = 6; + } + file_t *ef = search_by_fid(EF_PK_SIG, NULL, SPECIFY_ANY); + res_APDU[res_APDU_size++] = 0x00; + if (ef && ef->data) { + res_APDU[res_APDU_size++] = 0x01; + } + else { + res_APDU[res_APDU_size++] = 0x00; + } + + ef = search_by_fid(EF_PK_DEC, NULL, SPECIFY_ANY); + res_APDU[res_APDU_size++] = 0x01; + if (ef && ef->data) { + res_APDU[res_APDU_size++] = 0x01; + } + else { + res_APDU[res_APDU_size++] = 0x00; + } + + ef = search_by_fid(EF_PK_AUT, NULL, SPECIFY_ANY); + res_APDU[res_APDU_size++] = 0x02; + if (ef && ef->data) { + res_APDU[res_APDU_size++] = 0x01; + } + else { + res_APDU[res_APDU_size++] = 0x00; + } + return res_APDU_size - init_len; +} + +int parse_pw_status(const file_t *f, int mode) { + file_t *ef; + int init_len = res_APDU_size; + if (res_APDU_size > 0) { + res_APDU[res_APDU_size++] = EF_PW_STATUS & 0xff; + res_APDU[res_APDU_size++] = 7; + } + ef = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_ANY); + if (ef && ef->data) { + memcpy(res_APDU + res_APDU_size, file_get_data(ef), 7); + res_APDU_size += 7; + } + return res_APDU_size - init_len; +} + +#define ALGO_RSA_1K 0 +#define ALGO_RSA_2k 1 +#define ALGO_RSA_3K 2 +#define ALGO_RSA_4K 3 +#define ALGO_X448 4 +#define ALGO_P256K1 5 +#define ALGO_P256R1 6 +#define ALGO_P384R1 7 +#define ALGO_P521R1 8 +#define ALGO_BP256R1 9 +#define ALGO_BP384R1 10 +#define ALGO_BP512R1 11 +#define ALGO_CV22519 12 + +const uint8_t algorithm_attr_x448[] = { + 4, + ALGO_ECDH, + /* OID of X448 */ + 0x2b, 0x65, 0x6f +}; + +const uint8_t algorithm_attr_rsa1k[] = { + 6, + ALGO_RSA, + 0x04, 0x00, /* Length modulus (in bit): 1024 */ + 0x00, 0x20, /* Length exponent (in bit): 32 */ + 0x00 /* 0: Acceptable format is: P and Q */ +}; + +const uint8_t algorithm_attr_rsa2k[] = { + 6, + ALGO_RSA, + 0x08, 0x00, /* Length modulus (in bit): 2048 */ + 0x00, 0x20, /* Length exponent (in bit): 32 */ + 0x00 /* 0: Acceptable format is: P and Q */ +}; + +const uint8_t algorithm_attr_rsa3k[] = { + 6, + ALGO_RSA, + 0x0C, 0x00, /* Length modulus (in bit): 3072 */ + 0x00, 0x20, /* Length exponent (in bit): 32 */ + 0x00 /* 0: Acceptable format is: P and Q */ +}; + +const uint8_t algorithm_attr_rsa4k[] = { + 6, + ALGO_RSA, + 0x10, 0x00, /* Length modulus (in bit): 4096 */ + 0x00, 0x20, /* Length exponent (in bit): 32 */ + 0x00 /* 0: Acceptable format is: P and Q */ +}; + +const uint8_t algorithm_attr_p256k1[] = { + 6, + ALGO_ECDSA, + 0x2b, 0x81, 0x04, 0x00, 0x0a +}; + +const uint8_t algorithm_attr_p256r1[] = { + 9, + ALGO_ECDSA, + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 +}; + +const uint8_t algorithm_attr_p384r1[] = { + 6, + ALGO_ECDSA, + 0x2B, 0x81, 0x04, 0x00, 0x22 +}; + +const uint8_t algorithm_attr_p521r1[] = { + 6, + ALGO_ECDSA, + 0x2B, 0x81, 0x04, 0x00, 0x23 +}; + +const uint8_t algorithm_attr_bp256r1[] = { + 10, + ALGO_ECDSA, + 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07 +}; + +const uint8_t algorithm_attr_bp384r1[] = { + 10, + ALGO_ECDSA, + 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B +}; + +const uint8_t algorithm_attr_bp512r1[] = { + 10, + ALGO_ECDSA, + 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D +}; + +const uint8_t algorithm_attr_cv25519[] = { + 11, + ALGO_ECDH, + 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 +}; + +int parse_algo(const uint8_t *algo, uint16_t tag) { + res_APDU[res_APDU_size++] = tag & 0xff; + memcpy(res_APDU + res_APDU_size, algo, algo[0] + 1); + res_APDU_size += algo[0] + 1; + return algo[0] + 2; +} + +int parse_algoinfo(const file_t *f, int mode) { + int datalen = 0; + if (f->fid == EF_ALGO_INFO) { + res_APDU[res_APDU_size++] = EF_ALGO_INFO & 0xff; + res_APDU[res_APDU_size++] = 0x82; + uint8_t *lp = res_APDU + res_APDU_size; + res_APDU_size += 2; + datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_SIG); + datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_SIG); + datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_SIG); + datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_SIG); + datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_SIG); + datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_SIG); + datalen += parse_algo(algorithm_attr_p384r1, EF_ALGO_SIG); + datalen += parse_algo(algorithm_attr_p521r1, EF_ALGO_SIG); + datalen += parse_algo(algorithm_attr_bp256r1, EF_ALGO_SIG); + datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_SIG); + datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_SIG); + + datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_p384r1, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_p521r1, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_bp256r1, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_cv25519, EF_ALGO_DEC); + datalen += parse_algo(algorithm_attr_x448, EF_ALGO_DEC); + + datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_AUT); + datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_AUT); + datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_AUT); + datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_AUT); + datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_AUT); + datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_AUT); + datalen += parse_algo(algorithm_attr_p384r1, EF_ALGO_AUT); + datalen += parse_algo(algorithm_attr_p521r1, EF_ALGO_AUT); + datalen += parse_algo(algorithm_attr_bp256r1, EF_ALGO_AUT); + datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_AUT); + datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_AUT); + uint16_t lpdif = res_APDU + res_APDU_size - lp - 2; + *lp++ = lpdif >> 8; + *lp++ = lpdif & 0xff; + datalen = lpdif + 4; + } + else if (f->fid == EF_ALGO_SIG || f->fid == EF_ALGO_DEC || f->fid == EF_ALGO_AUT) { + uint16_t fid = 0x1000 | f->fid; + file_t *ef; + if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF)) || !ef->data) { + datalen += parse_algo(algorithm_attr_rsa2k, f->fid); + } + else { + uint16_t len = file_get_size(ef); + if (res_APDU_size > 0) { + res_APDU[res_APDU_size++] = f->fid & 0xff; + res_APDU[res_APDU_size++] = len & 0xff; + datalen += 2; + } + memcpy(res_APDU + res_APDU_size, file_get_data(ef), len); + res_APDU_size += len; + datalen += len; + } + } + return datalen; +} + +int parse_app_data(const file_t *f, int mode) { + uint16_t fids[] = { + 6, + EF_FULL_AID, EF_HIST_BYTES, EF_EXLEN_INFO, EF_GFM, EF_DISCRETE_DO, EF_KEY_INFO + }; + res_APDU[res_APDU_size++] = EF_APP_DATA & 0xff; + res_APDU[res_APDU_size++] = 0x82; + uint8_t *lp = res_APDU + res_APDU_size; + res_APDU_size += 2; + parse_do(fids, mode); + uint16_t lpdif = res_APDU + res_APDU_size - lp - 2; + *lp++ = lpdif >> 8; + *lp++ = lpdif & 0xff; + return lpdif + 4; +} + +int parse_discrete_do(const file_t *f, int mode) { + uint16_t fids[] = { + 11, + EF_EXT_CAP, EF_ALGO_SIG, EF_ALGO_DEC, EF_ALGO_AUT, EF_PW_STATUS, EF_FP, EF_CA_FP, EF_TS_ALL, + EF_UIF_SIG, EF_UIF_DEC, EF_UIF_AUT + }; + res_APDU[res_APDU_size++] = EF_DISCRETE_DO & 0xff; + res_APDU[res_APDU_size++] = 0x82; + uint8_t *lp = res_APDU + res_APDU_size; + res_APDU_size += 2; + parse_do(fids, mode); + uint16_t lpdif = res_APDU + res_APDU_size - lp - 2; + *lp++ = lpdif >> 8; + *lp++ = lpdif & 0xff; + return lpdif + 4; +} diff --git a/src/openpgp/do.h b/src/openpgp/do.h new file mode 100644 index 0000000..1a34242 --- /dev/null +++ b/src/openpgp/do.h @@ -0,0 +1,28 @@ +/* + * 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 . + */ + +extern const uint8_t algorithm_attr_p256k1[]; +extern const uint8_t algorithm_attr_p256r1[]; +extern const uint8_t algorithm_attr_p384r1[]; +extern const uint8_t algorithm_attr_p521r1[]; +extern const uint8_t algorithm_attr_bp256r1[]; +extern const uint8_t algorithm_attr_bp384r1[]; +extern const uint8_t algorithm_attr_bp512r1[]; +extern const uint8_t algorithm_attr_cv25519[]; +extern const uint8_t algorithm_attr_x448[]; +extern const uint8_t algorithm_attr_rsa2k[]; +extern const uint8_t algorithm_attr_rsa4096[]; diff --git a/src/openpgp/openpgp.c b/src/openpgp/openpgp.c index 536d922..6a55d00 100644 --- a/src/openpgp/openpgp.c +++ b/src/openpgp/openpgp.c @@ -23,16 +23,13 @@ #endif #include "openpgp.h" #include "version.h" -#include "files.h" #include "random.h" #include "eac.h" -#include "crypto_utils.h" -#include "mbedtls/ecdh.h" #include "mbedtls/asn1.h" -#include "asn1.h" #include "usb.h" #include "ccid/ccid.h" #include "otp.h" +#include "do.h" uint8_t PICO_PRODUCT = 3; @@ -43,9 +40,8 @@ bool has_rc = false; uint8_t session_pw1[32]; uint8_t session_rc[32]; uint8_t session_pw3[32]; -static uint8_t dek[IV_SIZE + 32]; -static uint16_t algo_dec = EF_ALGO_PRIV2, algo_aut = EF_ALGO_PRIV3, pk_dec = EF_PK_DEC, - pk_aut = EF_PK_AUT; +uint8_t dek[IV_SIZE + 32]; +uint16_t algo_dec = EF_ALGO_PRIV2, algo_aut = EF_ALGO_PRIV3, pk_dec = EF_PK_DEC, pk_aut = EF_PK_AUT; uint8_t openpgp_aid[] = { 6, @@ -68,7 +64,7 @@ int openpgp_process_apdu(); extern uint32_t board_button_read(void); -static bool wait_button_pressed(uint16_t fid) { +bool wait_button_pressed(uint16_t fid) { uint32_t val = EV_PRESS_BUTTON; #ifndef ENABLE_EMULATION file_t *ef = search_by_fid(fid, NULL, SPECIFY_ANY); @@ -100,74 +96,6 @@ void select_file(file_t *pe) { } } -static int cmd_select() { - uint8_t p1 = P1(apdu); - uint8_t p2 = P2(apdu); - file_t *pe = NULL; - uint16_t fid = 0x0; - - if (apdu.nc >= 2) { - fid = get_uint16_t_be(apdu.data); - } - - if (!pe) { - if (p1 == 0x0) { //Select MF, DF or EF - File identifier or absent - if (apdu.nc == 0) { - pe = (file_t *) MF; - //ac_fini(); - } - else if (apdu.nc == 2) { - if (!(pe = search_by_fid(fid, NULL, SPECIFY_ANY))) { - return SW_REFERENCE_NOT_FOUND(); - } - } - } - else if (p1 == 0x01) { //Select child DF - DF identifier - if (!(pe = search_by_fid(fid, currentDF, SPECIFY_DF))) { - return SW_REFERENCE_NOT_FOUND(); - } - } - else if (p1 == 0x02) { //Select EF under the current DF - EF identifier - if (!(pe = search_by_fid(fid, currentDF, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - } - else if (p1 == 0x03) { //Select parent DF of the current DF - Absent - if (apdu.nc != 0) { - return SW_REFERENCE_NOT_FOUND(); - } - } - else if (p1 == 0x04) { //Select by DF name - e.g., [truncated] application identifier - if (!(pe = search_by_name(apdu.data, apdu.nc))) { - return SW_REFERENCE_NOT_FOUND(); - } - if (card_terminated) { - return set_res_sw(0x62, 0x85); - } - } - else if (p1 == 0x08) { //Select from the MF - Path without the MF identifier - if (!(pe = search_by_path(apdu.data, apdu.nc, MF))) { - return SW_REFERENCE_NOT_FOUND(); - } - } - else if (p1 == 0x09) { //Select from the current DF - Path without the current DF identifier - if (!(pe = search_by_path(apdu.data, apdu.nc, currentDF))) { - return SW_REFERENCE_NOT_FOUND(); - } - } - } - if ((p2 & 0xfc) == 0x00 || (p2 & 0xfc) == 0x04) { - if ((p2 & 0xfc) == 0x04) { - process_fci(pe, 0); - } - } - else { - return SW_INCORRECT_P1P2(); - } - select_file(pe); - return SW_OK(); -} - void scan_files() { scan_flash(); file_t *ef; @@ -406,457 +334,6 @@ INITIALIZER( openpgp_ctor ) { register_app(openpgp_select_aid, openpgp_aid); } -int parse_do(uint16_t *fids, int mode) { - int len = 0; - file_t *ef; - for (int i = 0; i < fids[0]; i++) { - if ((ef = search_by_fid(fids[i + 1], NULL, SPECIFY_EF))) { - uint16_t data_len; - if ((ef->type & FILE_DATA_FUNC) == FILE_DATA_FUNC) { - data_len = ((int (*)(const file_t *, int))(ef->data))((const file_t *) ef, mode); - } - else { - if (ef->data) { - data_len = file_get_size(ef); - } - else { - data_len = 0; - } - if (mode == 1) { - if (fids[0] > 1 && res_APDU_size > 0) { - if (fids[i + 1] < 0x0100) { - res_APDU[res_APDU_size++] = fids[i + 1] & 0xff; - } - else { - res_APDU[res_APDU_size++] = fids[i + 1] >> 8; - res_APDU[res_APDU_size++] = fids[i + 1] & 0xff; - } - res_APDU_size += format_tlv_len(data_len, res_APDU + res_APDU_size); - } - if (ef->data) { - memcpy(res_APDU + res_APDU_size, file_get_data(ef), data_len); - } - res_APDU_size += data_len; - } - } - len += data_len; - } - } - return len; -} - -int parse_trium(uint16_t fid, uint8_t num, size_t size) { - for (uint8_t i = 0; i < num; i++) { - file_t *ef; - if ((ef = search_by_fid(fid + i, NULL, SPECIFY_EF)) && ef->data) { - uint16_t data_len = file_get_size(ef); - memcpy(res_APDU + res_APDU_size, file_get_data(ef), data_len); - res_APDU_size += data_len; - } - else { - memset(res_APDU + res_APDU_size, 0, size); - res_APDU_size += size; - } - } - return num * size; -} - -int parse_ch_data(const file_t *f, int mode) { - uint16_t fids[] = { - 3, - EF_CH_NAME, EF_LANG_PREF, EF_SEX, - }; - res_APDU[res_APDU_size++] = EF_CH_DATA & 0xff; - res_APDU[res_APDU_size++] = 0x82; - uint8_t *lp = res_APDU + res_APDU_size; - res_APDU_size += 2; - parse_do(fids, mode); - uint16_t lpdif = res_APDU + res_APDU_size - lp - 2; - *lp++ = lpdif >> 8; - *lp++ = lpdif & 0xff; - return lpdif + 4; -} - -int inc_sig_count() { - file_t *pw_status; - if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF)) || !pw_status->data) { - return SW_REFERENCE_NOT_FOUND(); - } - if (file_get_data(pw_status)[0] == 0) { - has_pw1 = false; - } - file_t *ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY); - if (!ef || !ef->data) { - return PICOKEY_ERR_FILE_NOT_FOUND; - } - uint8_t *p = file_get_data(ef); - uint32_t counter = (p[0] << 16) | (p[1] << 8) | p[2]; - counter++; - uint8_t q[3] = { (counter >> 16) & 0xff, (counter >> 8) & 0xff, counter & 0xff }; - int r = file_put_data(ef, q, sizeof(q)); - if (r != PICOKEY_OK) { - return PICOKEY_EXEC_ERROR; - } - low_flash_available(); - return PICOKEY_OK; -} - -int reset_sig_count() { - file_t *ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY); - if (!ef || !ef->data) { - return PICOKEY_ERR_FILE_NOT_FOUND; - } - uint8_t q[3] = { 0 }; - int r = file_put_data(ef, q, sizeof(q)); - if (r != PICOKEY_OK) { - return PICOKEY_EXEC_ERROR; - } - low_flash_available(); - return PICOKEY_OK; -} - -int parse_sec_tpl(const file_t *f, int mode) { - res_APDU[res_APDU_size++] = EF_SEC_TPL & 0xff; - res_APDU[res_APDU_size++] = 5; - file_t *ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY); - if (ef && ef->data) { - res_APDU[res_APDU_size++] = EF_SIG_COUNT & 0xff; - res_APDU[res_APDU_size++] = 3; - memcpy(res_APDU + res_APDU_size, file_get_data(ef), 3); - res_APDU_size += 3; - } - return 5 + 2; -} - -int parse_ch_cert(const file_t *f, int mode) { - return 0; -} - -int parse_fp(const file_t *f, int mode) { - res_APDU[res_APDU_size++] = EF_FP & 0xff; - res_APDU[res_APDU_size++] = 60; - return parse_trium(EF_FP_SIG, 3, 20) + 2; -} - -int parse_cafp(const file_t *f, int mode) { - res_APDU[res_APDU_size++] = EF_CA_FP & 0xff; - res_APDU[res_APDU_size++] = 60; - return parse_trium(EF_FP_CA1, 3, 20) + 2; -} - -int parse_ts(const file_t *f, int mode) { - res_APDU[res_APDU_size++] = EF_TS_ALL & 0xff; - res_APDU[res_APDU_size++] = 12; - return parse_trium(EF_TS_SIG, 3, 4) + 2; -} - -int parse_keyinfo(const file_t *f, int mode) { - int init_len = res_APDU_size; - if (res_APDU_size > 0) { - res_APDU[res_APDU_size++] = EF_KEY_INFO & 0xff; - res_APDU[res_APDU_size++] = 6; - } - file_t *ef = search_by_fid(EF_PK_SIG, NULL, SPECIFY_ANY); - res_APDU[res_APDU_size++] = 0x00; - if (ef && ef->data) { - res_APDU[res_APDU_size++] = 0x01; - } - else { - res_APDU[res_APDU_size++] = 0x00; - } - - ef = search_by_fid(EF_PK_DEC, NULL, SPECIFY_ANY); - res_APDU[res_APDU_size++] = 0x01; - if (ef && ef->data) { - res_APDU[res_APDU_size++] = 0x01; - } - else { - res_APDU[res_APDU_size++] = 0x00; - } - - ef = search_by_fid(EF_PK_AUT, NULL, SPECIFY_ANY); - res_APDU[res_APDU_size++] = 0x02; - if (ef && ef->data) { - res_APDU[res_APDU_size++] = 0x01; - } - else { - res_APDU[res_APDU_size++] = 0x00; - } - return res_APDU_size - init_len; -} - -int parse_pw_status(const file_t *f, int mode) { - file_t *ef; - int init_len = res_APDU_size; - if (res_APDU_size > 0) { - res_APDU[res_APDU_size++] = EF_PW_STATUS & 0xff; - res_APDU[res_APDU_size++] = 7; - } - ef = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_ANY); - if (ef && ef->data) { - memcpy(res_APDU + res_APDU_size, file_get_data(ef), 7); - res_APDU_size += 7; - } - return res_APDU_size - init_len; -} - -#define ALGO_RSA_1K 0 -#define ALGO_RSA_2k 1 -#define ALGO_RSA_3K 2 -#define ALGO_RSA_4K 3 -#define ALGO_X448 4 -#define ALGO_P256K1 5 -#define ALGO_P256R1 6 -#define ALGO_P384R1 7 -#define ALGO_P521R1 8 -#define ALGO_BP256R1 9 -#define ALGO_BP384R1 10 -#define ALGO_BP512R1 11 -#define ALGO_CV22519 12 - -static const uint8_t algorithm_attr_x448[] = { - 4, - ALGO_ECDH, - /* OID of X448 */ - 0x2b, 0x65, 0x6f -}; - -static const uint8_t algorithm_attr_rsa1k[] = { - 6, - ALGO_RSA, - 0x04, 0x00, /* Length modulus (in bit): 1024 */ - 0x00, 0x20, /* Length exponent (in bit): 32 */ - 0x00 /* 0: Acceptable format is: P and Q */ -}; - -static const uint8_t algorithm_attr_rsa2k[] = { - 6, - ALGO_RSA, - 0x08, 0x00, /* Length modulus (in bit): 2048 */ - 0x00, 0x20, /* Length exponent (in bit): 32 */ - 0x00 /* 0: Acceptable format is: P and Q */ -}; - -static const uint8_t algorithm_attr_rsa3k[] = { - 6, - ALGO_RSA, - 0x0C, 0x00, /* Length modulus (in bit): 3072 */ - 0x00, 0x20, /* Length exponent (in bit): 32 */ - 0x00 /* 0: Acceptable format is: P and Q */ -}; - -static const uint8_t algorithm_attr_rsa4k[] = { - 6, - ALGO_RSA, - 0x10, 0x00, /* Length modulus (in bit): 4096 */ - 0x00, 0x20, /* Length exponent (in bit): 32 */ - 0x00 /* 0: Acceptable format is: P and Q */ -}; - -static const uint8_t algorithm_attr_p256k1[] = { - 6, - ALGO_ECDSA, - 0x2b, 0x81, 0x04, 0x00, 0x0a -}; - -static const uint8_t algorithm_attr_p256r1[] = { - 9, - ALGO_ECDSA, - 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 -}; - -static const uint8_t algorithm_attr_p384r1[] = { - 6, - ALGO_ECDSA, - 0x2B, 0x81, 0x04, 0x00, 0x22 -}; - -static const uint8_t algorithm_attr_p521r1[] = { - 6, - ALGO_ECDSA, - 0x2B, 0x81, 0x04, 0x00, 0x23 -}; - -static const uint8_t algorithm_attr_bp256r1[] = { - 10, - ALGO_ECDSA, - 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07 -}; - -static const uint8_t algorithm_attr_bp384r1[] = { - 10, - ALGO_ECDSA, - 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B -}; - -static const uint8_t algorithm_attr_bp512r1[] = { - 10, - ALGO_ECDSA, - 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D -}; - -static const uint8_t algorithm_attr_cv25519[] = { - 11, - ALGO_ECDH, - 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 -}; - -int parse_algo(const uint8_t *algo, uint16_t tag) { - res_APDU[res_APDU_size++] = tag & 0xff; - memcpy(res_APDU + res_APDU_size, algo, algo[0] + 1); - res_APDU_size += algo[0] + 1; - return algo[0] + 2; -} - -int parse_algoinfo(const file_t *f, int mode) { - int datalen = 0; - if (f->fid == EF_ALGO_INFO) { - res_APDU[res_APDU_size++] = EF_ALGO_INFO & 0xff; - res_APDU[res_APDU_size++] = 0x82; - uint8_t *lp = res_APDU + res_APDU_size; - res_APDU_size += 2; - datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_SIG); - datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_SIG); - datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_SIG); - datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_SIG); - datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_SIG); - datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_SIG); - datalen += parse_algo(algorithm_attr_p384r1, EF_ALGO_SIG); - datalen += parse_algo(algorithm_attr_p521r1, EF_ALGO_SIG); - datalen += parse_algo(algorithm_attr_bp256r1, EF_ALGO_SIG); - datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_SIG); - datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_SIG); - - datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_p384r1, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_p521r1, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_bp256r1, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_cv25519, EF_ALGO_DEC); - datalen += parse_algo(algorithm_attr_x448, EF_ALGO_DEC); - - datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_AUT); - datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_AUT); - datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_AUT); - datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_AUT); - datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_AUT); - datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_AUT); - datalen += parse_algo(algorithm_attr_p384r1, EF_ALGO_AUT); - datalen += parse_algo(algorithm_attr_p521r1, EF_ALGO_AUT); - datalen += parse_algo(algorithm_attr_bp256r1, EF_ALGO_AUT); - datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_AUT); - datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_AUT); - uint16_t lpdif = res_APDU + res_APDU_size - lp - 2; - *lp++ = lpdif >> 8; - *lp++ = lpdif & 0xff; - datalen = lpdif + 4; - } - else if (f->fid == EF_ALGO_SIG || f->fid == EF_ALGO_DEC || f->fid == EF_ALGO_AUT) { - uint16_t fid = 0x1000 | f->fid; - file_t *ef; - if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF)) || !ef->data) { - datalen += parse_algo(algorithm_attr_rsa2k, f->fid); - } - else { - uint16_t len = file_get_size(ef); - if (res_APDU_size > 0) { - res_APDU[res_APDU_size++] = f->fid & 0xff; - res_APDU[res_APDU_size++] = len & 0xff; - datalen += 2; - } - memcpy(res_APDU + res_APDU_size, file_get_data(ef), len); - res_APDU_size += len; - datalen += len; - } - } - return datalen; -} - -int parse_app_data(const file_t *f, int mode) { - uint16_t fids[] = { - 6, - EF_FULL_AID, EF_HIST_BYTES, EF_EXLEN_INFO, EF_GFM, EF_DISCRETE_DO, EF_KEY_INFO - }; - res_APDU[res_APDU_size++] = EF_APP_DATA & 0xff; - res_APDU[res_APDU_size++] = 0x82; - uint8_t *lp = res_APDU + res_APDU_size; - res_APDU_size += 2; - parse_do(fids, mode); - uint16_t lpdif = res_APDU + res_APDU_size - lp - 2; - *lp++ = lpdif >> 8; - *lp++ = lpdif & 0xff; - return lpdif + 4; -} - -int parse_discrete_do(const file_t *f, int mode) { - uint16_t fids[] = { - 11, - EF_EXT_CAP, EF_ALGO_SIG, EF_ALGO_DEC, EF_ALGO_AUT, EF_PW_STATUS, EF_FP, EF_CA_FP, EF_TS_ALL, - EF_UIF_SIG, EF_UIF_DEC, EF_UIF_AUT - }; - res_APDU[res_APDU_size++] = EF_DISCRETE_DO & 0xff; - res_APDU[res_APDU_size++] = 0x82; - uint8_t *lp = res_APDU + res_APDU_size; - res_APDU_size += 2; - parse_do(fids, mode); - uint16_t lpdif = res_APDU + res_APDU_size - lp - 2; - *lp++ = lpdif >> 8; - *lp++ = lpdif & 0xff; - return lpdif + 4; -} - -static int cmd_get_data() { - if (apdu.nc > 0) { - return SW_WRONG_LENGTH(); - } - uint16_t fid = (P1(apdu) << 8) | P2(apdu); - file_t *ef; - if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) { - return SW_SECURITY_STATUS_NOT_SATISFIED(); - } - if (currentEF && (currentEF->fid & 0x1FF0) == (fid & 0x1FF0)) { //previously selected - ef = currentEF; - } - else { - select_file(ef); - } - if (ef->data) { - uint16_t fids[] = { 1, fid }; - uint16_t data_len = parse_do(fids, 1); - uint8_t *p = NULL; - uint16_t tg = 0; - uint16_t tg_len = 0; - asn1_ctx_t ctxi; - asn1_ctx_init(res_APDU, data_len, &ctxi); - if (walk_tlv(&ctxi, &p, &tg, &tg_len, NULL)) { - uint8_t dec = 2; - if ((tg & 0x1f) == 0x1f) { - dec++; - } - if ((res_APDU[dec - 1] & 0xF0) == 0x80) { - dec += (res_APDU[dec - 1] & 0x0F); - } - if (tg_len + dec == data_len) { - memmove(res_APDU, res_APDU + dec, data_len - dec); - data_len -= dec; - res_APDU_size -= dec; - } - } - //if (apdu.ne > data_len) - // apdu.ne = data_len; - } - return SW_OK(); -} - int pin_reset_retries(const file_t *pin, bool force) { if (!pin) { return PICOKEY_ERR_NULL_PARAM; @@ -954,215 +431,42 @@ int check_pin(const file_t *pin, const uint8_t *data, size_t len) { return SW_OK(); } -static int cmd_verify() { - uint8_t p1 = P1(apdu); - uint8_t p2 = P2(apdu); - - if (p1 == 0xFF) { - if (apdu.nc != 0) { - return SW_WRONG_DATA(); - } - if (p2 == 0x81) { - has_pw1 = false; - } - else if (p2 == 0x82) { - has_pw2 = false; - } - else if (p2 == 0x83) { - has_pw3 = false; - } - return SW_OK(); - } - else if (p1 != 0x0 || (p2 & 0x60) != 0x0) { - return SW_WRONG_P1P2(); - } - uint16_t fid = 0x1000 | p2; - if (fid == EF_RC && apdu.nc > 0) { - fid = EF_PW1; - } - file_t *pw, *pw_status; - if (!(pw = search_by_fid(fid, NULL, SPECIFY_EF))) { +int inc_sig_count() { + file_t *pw_status; + if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF)) || !pw_status->data) { 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 & 0xf)); - if (retries == 0) { - return SW_PIN_BLOCKED(); - } - if ((p2 == 0x81 && has_pw1) || (p2 == 0x82 && has_pw2) || (p2 == 0x83 && has_pw3)) { - return SW_OK(); - } - return set_res_sw(0x63, 0xc0 | retries); -} - -static int cmd_put_data() { - uint16_t fid = (P1(apdu) << 8) | P2(apdu); - file_t *ef; - if (fid == EF_RESET_CODE) { - fid = EF_RC; - } - else if (fid == EF_ALGO_SIG || fid == EF_ALGO_DEC || fid == EF_ALGO_AUT) { - fid |= 0x1000; - } - if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { - return SW_SECURITY_STATUS_NOT_SATISFIED(); - } - if (fid == EF_PW_STATUS) { - fid = EF_PW_PRIV; - apdu.nc = 4; //we silently ommit the reset parameters - } - if (currentEF && (currentEF->fid & 0x1FF0) == (fid & 0x1FF0)) { //previously selected - ef = currentEF; - } - if (apdu.nc > 0 && (ef->type & FILE_DATA_FLASH)) { - int r = 0; - if (fid == EF_RC) { - has_rc = false; - if ((r = load_dek()) != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - uint8_t dhash[33]; - dhash[0] = apdu.nc; - double_hash_pin(apdu.data, apdu.nc, dhash + 1); - r = file_put_data(ef, dhash, sizeof(dhash)); - - file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF); - if (!tf) { - return SW_REFERENCE_NOT_FOUND(); - } - uint8_t def[IV_SIZE + 32 + 32 + 32 + 32]; - memcpy(def, file_get_data(tf), file_get_size(tf)); - hash_multi(apdu.data, apdu.nc, session_rc); - memcpy(def + IV_SIZE + 32, dek + IV_SIZE, 32); - aes_encrypt_cfb_256(session_rc, def, def + IV_SIZE + 32, 32); - r = file_put_data(tf, def, sizeof(def)); - } - else { - r = file_put_data(ef, apdu.data, apdu.nc); - } - if (r != PICOKEY_OK) { - return SW_MEMORY_FAILURE(); - } - low_flash_available(); - } - return SW_OK(); -} - -static int cmd_change_pin() { - if (P1(apdu) != 0x0) { - return SW_WRONG_P1P2(); - } - uint16_t fid = 0x1000 | P2(apdu); - file_t *pw; - if (!(pw = search_by_fid(fid, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - uint8_t pin_len = file_get_data(pw)[0]; - uint16_t r = 0; - if ((r = load_dek()) != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - r = check_pin(pw, apdu.data, pin_len); - if (r != 0x9000) { - return r; - } - uint8_t dhash[33]; - dhash[0] = apdu.nc - pin_len; - double_hash_pin(apdu.data + pin_len, apdu.nc - pin_len, dhash + 1); - file_put_data(pw, dhash, sizeof(dhash)); - - file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF); - if (!tf) { - return SW_REFERENCE_NOT_FOUND(); - } - uint8_t def[IV_SIZE + 32 + 32 + 32 + 32] = {0}; - memcpy(def, file_get_data(tf), file_get_size(tf)); - if (P2(apdu) == 0x81) { - hash_multi(apdu.data + pin_len, apdu.nc - pin_len, session_pw1); - memcpy(def + IV_SIZE, dek + IV_SIZE, 32); - aes_encrypt_cfb_256(session_pw1, def, def + IV_SIZE, 32); - } - else if (P2(apdu) == 0x83) { - hash_multi(apdu.data + pin_len, apdu.nc - pin_len, session_pw3); - memcpy(def + IV_SIZE + 32 + 32, dek + IV_SIZE, 32); - aes_encrypt_cfb_256(session_pw3, def, def + IV_SIZE + 32 + 32, 32); - } - file_put_data(tf, def, sizeof(def)); - low_flash_available(); - return SW_OK(); -} - -static int cmd_reset_retry() { - if (P2(apdu) != 0x81) { - return SW_REFERENCE_NOT_FOUND(); - } - if (P1(apdu) == 0x0 || P1(apdu) == 0x2) { - int newpin_len = 0; - file_t *pw = NULL; + if (file_get_data(pw_status)[0] == 0) { has_pw1 = false; - if (!(pw = search_by_fid(EF_PW1, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - if (P1(apdu) == 0x0) { - file_t *rc; - if (!(rc = search_by_fid(EF_RC, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - uint8_t pin_len = file_get_data(rc)[0]; - if (apdu.nc <= pin_len) { - return SW_WRONG_LENGTH(); - } - uint16_t r = check_pin(rc, apdu.data, pin_len); - if (r != 0x9000) { - return r; - } - newpin_len = apdu.nc - pin_len; - has_rc = true; - hash_multi(apdu.data, pin_len, session_rc); - } - else if (P1(apdu) == 0x2) { - if (!has_pw3) { - return SW_CONDITIONS_NOT_SATISFIED(); - } - newpin_len = apdu.nc; - } - int r = 0; - if ((r = load_dek()) != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF); - if (!tf) { - return SW_REFERENCE_NOT_FOUND(); - } - uint8_t def[IV_SIZE + 32 + 32 + 32 + 32]; - memcpy(def, file_get_data(tf), file_get_size(tf)); - hash_multi(apdu.data + (apdu.nc - newpin_len), newpin_len, session_pw1); - memcpy(def + IV_SIZE, dek + IV_SIZE, 32); - aes_encrypt_cfb_256(session_pw1, def, def + IV_SIZE, 32); - r = file_put_data(tf, def, sizeof(def)); - - uint8_t dhash[33]; - dhash[0] = newpin_len; - double_hash_pin(apdu.data + (apdu.nc - newpin_len), newpin_len, dhash + 1); - file_put_data(pw, dhash, sizeof(dhash)); - if (pin_reset_retries(pw, true) != PICOKEY_OK) { - return SW_MEMORY_FAILURE(); - } - low_flash_available(); - return SW_OK(); } - return SW_INCORRECT_P1P2(); + file_t *ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY); + if (!ef || !ef->data) { + return PICOKEY_ERR_FILE_NOT_FOUND; + } + uint8_t *p = file_get_data(ef); + uint32_t counter = (p[0] << 16) | (p[1] << 8) | p[2]; + counter++; + uint8_t q[3] = { (counter >> 16) & 0xff, (counter >> 8) & 0xff, counter & 0xff }; + int r = file_put_data(ef, q, sizeof(q)); + if (r != PICOKEY_OK) { + return PICOKEY_EXEC_ERROR; + } + low_flash_available(); + return PICOKEY_OK; +} + +int reset_sig_count() { + file_t *ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY); + if (!ef || !ef->data) { + return PICOKEY_ERR_FILE_NOT_FOUND; + } + uint8_t q[3] = { 0 }; + int r = file_put_data(ef, q, sizeof(q)); + if (r != PICOKEY_OK) { + return PICOKEY_EXEC_ERROR; + } + low_flash_available(); + return PICOKEY_OK; } int store_keys(void *key_ctx, int type, uint16_t key_id, bool use_kek) { @@ -1355,124 +659,6 @@ void make_ecdsa_response(mbedtls_ecdsa_context *ecdsa) { res_APDU_size += plen; } -static int cmd_keypair_gen() { - if (P2(apdu) != 0x0) { - return SW_INCORRECT_P1P2(); - } - if (apdu.nc != 2 && apdu.nc != 5) { - return SW_WRONG_LENGTH(); - } - if (!has_pw3 && P1(apdu) == 0x80) { - return SW_SECURITY_STATUS_NOT_SATISFIED(); - } - - uint16_t fid = 0x0; - int r = PICOKEY_OK; - if (apdu.data[0] == 0xB6) { - fid = EF_PK_SIG; - } - else if (apdu.data[0] == 0xB8) { - fid = EF_PK_DEC; - } - else if (apdu.data[0] == 0xA4) { - fid = EF_PK_AUT; - } - else { - return SW_WRONG_DATA(); - } - - file_t *algo_ef = search_by_fid(fid - 0x0010, NULL, SPECIFY_EF); - if (!algo_ef) { - return SW_REFERENCE_NOT_FOUND(); - } - const uint8_t *algo = algorithm_attr_rsa2k + 1; - uint16_t algo_len = algorithm_attr_rsa2k[0]; - if (algo_ef && algo_ef->data) { - algo = file_get_data(algo_ef); - algo_len = file_get_size(algo_ef); - } - if (P1(apdu) == 0x80) { //generate - if (algo[0] == ALGO_RSA) { - int exponent = 65537, nlen = (algo[1] << 8) | algo[2]; - printf("KEYPAIR RSA %d\r\n", nlen); - //if (nlen != 2048 && nlen != 4096) - // return SW_FUNC_NOT_SUPPORTED(); - mbedtls_rsa_context rsa; - mbedtls_rsa_init(&rsa); - uint8_t index = 0; - r = mbedtls_rsa_gen_key(&rsa, random_gen, &index, nlen, exponent); - if (r != 0) { - mbedtls_rsa_free(&rsa); - return SW_EXEC_ERROR(); - } - r = store_keys(&rsa, ALGO_RSA, fid, true); - make_rsa_response(&rsa); - mbedtls_rsa_free(&rsa); - if (r != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - } - else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA) { - printf("KEYPAIR ECDSA\r\n"); - mbedtls_ecp_group_id gid = get_ec_group_id_from_attr(algo + 1, algo_len - 1); - if (gid == MBEDTLS_ECP_DP_NONE) { - return SW_FUNC_NOT_SUPPORTED(); - } - mbedtls_ecdsa_context ecdsa; - mbedtls_ecdsa_init(&ecdsa); - uint8_t index = 0; - r = mbedtls_ecdsa_genkey(&ecdsa, gid, random_gen, &index); - if (r != 0) { - mbedtls_ecdsa_free(&ecdsa); - return SW_EXEC_ERROR(); - } - r = store_keys(&ecdsa, algo[0], fid, true); - make_ecdsa_response(&ecdsa); - mbedtls_ecdsa_free(&ecdsa); - if (r != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - } - else { - return SW_FUNC_NOT_SUPPORTED(); - } - file_t *pbef = search_by_fid(fid + 3, NULL, SPECIFY_EF); - if (!pbef) { - return SW_REFERENCE_NOT_FOUND(); - } - r = file_put_data(pbef, res_APDU, res_APDU_size); - if (r != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - if (fid == EF_PK_SIG) { - reset_sig_count(); - } - else if (fid == EF_PK_DEC) { - // OpenPGP does not allow generating AES keys. So, we generate a new one when gen for DEC is called. - // It is a 256 AES key by default. - uint8_t aes_key[32]; //maximum AES key size - uint8_t key_size = 32; - memcpy(aes_key, random_bytes_get(key_size), key_size); - r = store_keys(aes_key, ALGO_AES_256, EF_AES_KEY, true); - /* if storing the key fails, we silently continue */ - //if (r != PICOKEY_OK) - // return SW_EXEC_ERROR(); - } - low_flash_available(); - return SW_OK(); - } - else if (P1(apdu) == 0x81) { //read - file_t *ef = search_by_fid(fid + 3, NULL, SPECIFY_EF); - if (!ef || !ef->data) { - return SW_REFERENCE_NOT_FOUND(); - } - res_APDU_size = file_get_size(ef); - memcpy(res_APDU, file_get_data(ef), res_APDU_size); - return SW_OK(); - } - return SW_INCORRECT_P1P2(); -} - int rsa_sign(mbedtls_rsa_context *ctx, const uint8_t *data, size_t data_len, @@ -1562,563 +748,23 @@ int ecdsa_sign(mbedtls_ecdsa_context *ctx, return r; } -static int cmd_pso() { - uint16_t algo_fid = 0x0, pk_fid = 0x0; - bool is_aes = false; - if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) { - if (!has_pw3 && !has_pw1) { - return SW_SECURITY_STATUS_NOT_SATISFIED(); - } - algo_fid = EF_ALGO_PRIV1; - pk_fid = EF_PK_SIG; - } - else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { - if (!has_pw3 && !has_pw2) { - return SW_SECURITY_STATUS_NOT_SATISFIED(); - } - algo_fid = algo_dec; - pk_fid = pk_dec; - } - else { - return SW_INCORRECT_P1P2(); - } - file_t *algo_ef = search_by_fid(algo_fid, NULL, SPECIFY_EF); - if (!algo_ef) { - return SW_REFERENCE_NOT_FOUND(); - } - const uint8_t *algo = algorithm_attr_rsa2k + 1; - if (algo_ef && algo_ef->data) { - algo = file_get_data(algo_ef); - } - if (apdu.data[0] == 0x2) { //AES PSO? - if (((apdu.nc - 1) % 16 == 0 && P1(apdu) == 0x80 && P2(apdu) == 0x86) || - (apdu.nc % 16 == 0 && P1(apdu) == 0x86 && P2(apdu) == 0x80)) { - pk_fid = EF_AES_KEY; - is_aes = true; - } - } - file_t *ef = search_by_fid(pk_fid, NULL, SPECIFY_EF); - if (!ef) { - return SW_REFERENCE_NOT_FOUND(); - } - if (wait_button_pressed(pk_fid == EF_PK_SIG ? EF_UIF_SIG : EF_UIF_DEC) == true) { - return SW_SECURE_MESSAGE_EXEC_ERROR(); - } - int r = PICOKEY_OK; - int key_size = file_get_size(ef); - if (is_aes) { - uint8_t aes_key[32]; - r = load_aes_key(aes_key, ef); - if (r != PICOKEY_OK) { - memset(aes_key, 0, sizeof(aes_key)); - return SW_EXEC_ERROR(); - } - if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { //decipher - r = aes_decrypt(aes_key, NULL, key_size, PICO_KEYS_AES_MODE_CBC, apdu.data + 1, apdu.nc - 1); - memset(aes_key, 0, sizeof(aes_key)); - if (r != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - memcpy(res_APDU, apdu.data + 1, apdu.nc - 1); - res_APDU_size = apdu.nc - 1; - } - else if (P1(apdu) == 0x86 && P2(apdu) == 0x80) { //encipher - r = aes_encrypt(aes_key, NULL, key_size, PICO_KEYS_AES_MODE_CBC, apdu.data, apdu.nc); - memset(aes_key, 0, sizeof(aes_key)); - if (r != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - res_APDU[0] = 0x2; - memcpy(res_APDU + 1, apdu.data, apdu.nc); - res_APDU_size = apdu.nc + 1; - } - return SW_OK(); - } - if (algo[0] == ALGO_RSA) { - mbedtls_rsa_context ctx; - mbedtls_rsa_init(&ctx); - r = load_private_key_rsa(&ctx, ef, true); - if (r != PICOKEY_OK) { - mbedtls_rsa_free(&ctx); - return SW_EXEC_ERROR(); - } - if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) { - size_t olen = 0; - r = rsa_sign(&ctx, apdu.data, apdu.nc, res_APDU, &olen); - mbedtls_rsa_free(&ctx); - if (r != 0) { - return SW_EXEC_ERROR(); - } - res_APDU_size = olen; - //apdu.ne = key_size; - inc_sig_count(); - } - else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { - if (apdu.nc < key_size) { //needs padding - memset(apdu.data + apdu.nc, 0, key_size - apdu.nc); - } - size_t olen = 0; - r = mbedtls_rsa_pkcs1_decrypt(&ctx, - random_gen, - NULL, - &olen, - apdu.data + 1, - res_APDU, - key_size); - mbedtls_rsa_free(&ctx); - if (r != 0) { - return SW_EXEC_ERROR(); - } - res_APDU_size = olen; - } - } - else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA) { - if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) { - mbedtls_ecdsa_context ctx; - mbedtls_ecdsa_init(&ctx); - r = load_private_key_ecdsa(&ctx, ef, true); - if (r != PICOKEY_OK) { - mbedtls_ecdsa_free(&ctx); - return SW_EXEC_ERROR(); - } - size_t olen = 0; - r = ecdsa_sign(&ctx, apdu.data, apdu.nc, res_APDU, &olen); - mbedtls_ecdsa_free(&ctx); - if (r != 0) { - return SW_EXEC_ERROR(); - } - res_APDU_size = olen; - inc_sig_count(); - } - else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { - mbedtls_ecdh_context ctx; - uint8_t kdata[67]; - uint8_t *data = apdu.data, *end = data + apdu.nc; - size_t len = 0; - if (mbedtls_asn1_get_tag(&data, end, &len, 0xA6) != 0) { - return SW_WRONG_DATA(); - } - if (*data++ != 0x7f) { - return SW_WRONG_DATA(); - } - if (mbedtls_asn1_get_tag(&data, end, &len, - 0x49) != 0 || - mbedtls_asn1_get_tag(&data, end, &len, 0x86) != 0) { - return SW_WRONG_DATA(); - } - //if (len != 2*key_size-1) - // return SW_WRONG_LENGTH(); - memcpy(kdata, file_get_data(ef), key_size); - if (dek_decrypt(kdata, key_size) != 0) { - return SW_EXEC_ERROR(); - } - mbedtls_ecdh_init(&ctx); - mbedtls_ecp_group_id gid = kdata[0]; - r = mbedtls_ecdh_setup(&ctx, gid); - if (r != 0) { - mbedtls_ecdh_free(&ctx); - return SW_DATA_INVALID(); - } - r = mbedtls_ecp_read_key(gid, (mbedtls_ecdsa_context *)&ctx.ctx.mbed_ecdh, kdata + 1, key_size - 1); - if (r != 0) { - mbedtls_ecdh_free(&ctx); - return SW_DATA_INVALID(); - } - r = mbedtls_ecdh_read_public(&ctx, data - 1, len + 1); - if (r != 0) { - mbedtls_ecdh_free(&ctx); - return SW_DATA_INVALID(); - } - size_t olen = 0; - r = mbedtls_ecdh_calc_secret(&ctx, - &olen, - res_APDU, - MBEDTLS_ECP_MAX_BYTES, - random_gen, - NULL); - if (r != 0) { - mbedtls_ecdh_free(&ctx); - return SW_EXEC_ERROR(); - } - res_APDU_size = olen; - mbedtls_ecdh_free(&ctx); - } - } - return SW_OK(); -} - -static int cmd_terminate_df() { - if (P1(apdu) != 0x0 || P2(apdu) != 0x0) { - return SW_INCORRECT_P1P2(); - } - file_t *retries; - if (!(retries = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - if (!has_pw3 && *(file_get_data(retries) + 6) > 0) { - return SW_SECURITY_STATUS_NOT_SATISFIED(); - } - if (apdu.nc != 0) { - return SW_WRONG_LENGTH(); - } - initialize_flash(true); - scan_files(); - return SW_OK(); -} - -static int cmd_activate_file() { - return SW_OK(); -} - -static int cmd_challenge() { - uint8_t *rb = (uint8_t *) random_bytes_get(apdu.ne); - if (!rb) { - return SW_WRONG_LENGTH(); - } - memcpy(res_APDU, rb, apdu.ne); - res_APDU_size = apdu.ne; - return SW_OK(); -} - -static int cmd_internal_aut() { - if (P1(apdu) != 0x00 || P2(apdu) != 0x00) { - return SW_WRONG_P1P2(); - } - if (!has_pw3 && !has_pw2) { - return SW_SECURITY_STATUS_NOT_SATISFIED(); - } - file_t *algo_ef = search_by_fid(algo_aut, NULL, SPECIFY_EF); - if (!algo_ef) { - return SW_REFERENCE_NOT_FOUND(); - } - const uint8_t *algo = algorithm_attr_rsa2k + 1; - if (algo_ef && algo_ef->data) { - algo = file_get_data(algo_ef); - } - file_t *ef = search_by_fid(pk_aut, NULL, SPECIFY_EF); - if (!ef) { - return SW_REFERENCE_NOT_FOUND(); - } - if (wait_button_pressed(EF_UIF_AUT) == true) { - return SW_SECURE_MESSAGE_EXEC_ERROR(); - } - int r = PICOKEY_OK; - if (algo[0] == ALGO_RSA) { - mbedtls_rsa_context ctx; - mbedtls_rsa_init(&ctx); - r = load_private_key_rsa(&ctx, ef, true); - if (r != PICOKEY_OK) { - mbedtls_rsa_free(&ctx); - return SW_EXEC_ERROR(); - } - size_t olen = 0; - r = rsa_sign(&ctx, apdu.data, apdu.nc, res_APDU, &olen); - mbedtls_rsa_free(&ctx); - if (r != 0) { - return SW_EXEC_ERROR(); - } - res_APDU_size = olen; - } - else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA) { - mbedtls_ecdsa_context ctx; - mbedtls_ecdsa_init(&ctx); - r = load_private_key_ecdsa(&ctx, ef, true); - if (r != PICOKEY_OK) { - mbedtls_ecdsa_free(&ctx); - return SW_EXEC_ERROR(); - } - size_t olen = 0; - r = ecdsa_sign(&ctx, apdu.data, apdu.nc, res_APDU, &olen); - mbedtls_ecdsa_free(&ctx); - if (r != 0) { - return SW_EXEC_ERROR(); - } - res_APDU_size = olen; - } - return SW_OK(); -} - -static int cmd_mse() { - if (P1(apdu) != 0x41 || (P2(apdu) != 0xA4 && P2(apdu) != 0xB8)) { - return SW_WRONG_P1P2(); - } - if (apdu.data[0] != 0x83 || apdu.data[1] != 0x1 || - (apdu.data[2] != 0x2 && apdu.data[2] != 0x3)) { - return SW_WRONG_DATA(); - } - if (P2(apdu) == 0xA4) { - if (apdu.data[2] == 0x2) { - algo_dec = EF_ALGO_PRIV2; - pk_dec = EF_PK_DEC; - } - else if (apdu.data[2] == 0x3) { - algo_dec = EF_ALGO_PRIV3; - pk_dec = EF_PK_AUT; - } - } - else if (P2(apdu) == 0xB8) { - if (apdu.data[2] == 0x2) { - algo_aut = EF_ALGO_PRIV2; - pk_aut = EF_PK_DEC; - } - else if (apdu.data[2] == 0x3) { - algo_aut = EF_ALGO_PRIV3; - pk_aut = EF_PK_AUT; - } - } - return SW_OK(); -} - -uint16_t tag_len(uint8_t **data) { - size_t len = *(*data)++; - if (len == 0x82) { - len = *(*data)++ << 8; - len |= *(*data)++; - } - else if (len == 0x81) { - len = *(*data)++; - } - return len; -} - -static int cmd_import_data() { - file_t *ef = NULL; - uint16_t fid = 0x0; - if (P1(apdu) != 0x3F || P2(apdu) != 0xFF) { - return SW_WRONG_P1P2(); - } - if (apdu.nc < 5) { - return SW_WRONG_LENGTH(); - } - uint8_t *start = apdu.data; - if (*start++ != 0x4D) { - return SW_WRONG_DATA(); - } - uint16_t tgl = tag_len(&start); - if (*start != 0xB6 && *start != 0xB8 && *start != 0xA4) { - return SW_WRONG_DATA(); - } - if (*start == 0xB6) { - fid = EF_PK_SIG; - } - else if (*start == 0xB8) { - fid = EF_PK_DEC; - } - else if (*start == 0xA4) { - fid = EF_PK_AUT; - } - else { - return SW_WRONG_DATA(); - } - start++; - if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { - return SW_SECURITY_STATUS_NOT_SATISFIED(); - } - start += (*start + 1); - if (*start++ != 0x7F || *start++ != 0x48) { - return SW_WRONG_DATA(); - } - tgl = tag_len(&start); - uint8_t *end = start + tgl, *p[9] = { 0 }; - uint16_t len[9] = { 0 }; - while (start < end) { - uint8_t tag = *start++; - if ((tag >= 0x91 && tag <= 0x97) || tag == 0x99) { - len[tag - 0x91] = tag_len(&start); - } - else { - return SW_WRONG_DATA(); - } - } - if (*start++ != 0x5F || *start++ != 0x48) { - return SW_WRONG_DATA(); - } - tgl = tag_len(&start); - end = start + tgl; - for (int t = 0; start < end && t < 9; t++) { - if (len[t] > 0) { - p[t] = start; - start += len[t]; - } - } - - file_t *algo_ef = search_by_fid(fid - 0x0010, NULL, SPECIFY_EF); - if (!algo_ef) { - return SW_REFERENCE_NOT_FOUND(); - } - const uint8_t *algo = algorithm_attr_rsa2k + 1; - uint16_t algo_len = algorithm_attr_rsa2k[0]; - if (algo_ef && algo_ef->data) { - algo = file_get_data(algo_ef); - algo_len = file_get_size(algo_ef); - } - int r = 0; - if (algo[0] == ALGO_RSA) { - mbedtls_rsa_context rsa; - if (p[0] == NULL || len[0] == 0 || p[1] == NULL || len[1] == 0 || p[2] == NULL || - len[2] == 0) { - return SW_WRONG_DATA(); - } - mbedtls_rsa_init(&rsa); - r = mbedtls_mpi_read_binary(&rsa.E, p[0], len[0]); - if (r != 0) { - mbedtls_rsa_free(&rsa); - return SW_EXEC_ERROR(); - } - r = mbedtls_mpi_read_binary(&rsa.P, p[1], len[1]); - if (r != 0) { - mbedtls_rsa_free(&rsa); - return SW_EXEC_ERROR(); - } - r = mbedtls_mpi_read_binary(&rsa.Q, p[2], len[2]); - if (r != 0) { - mbedtls_rsa_free(&rsa); - return SW_EXEC_ERROR(); - } - r = mbedtls_rsa_import(&rsa, NULL, &rsa.P, &rsa.Q, NULL, &rsa.E); - if (r != 0) { - mbedtls_rsa_free(&rsa); - return SW_EXEC_ERROR(); - } - r = mbedtls_rsa_complete(&rsa); - if (r != 0) { - mbedtls_rsa_free(&rsa); - return SW_EXEC_ERROR(); - } - r = mbedtls_rsa_check_privkey(&rsa); - if (r != 0) { - mbedtls_rsa_free(&rsa); - return SW_EXEC_ERROR(); - } - r = store_keys(&rsa, ALGO_RSA, fid, true); - make_rsa_response(&rsa); - mbedtls_rsa_free(&rsa); - if (r != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - } - else if (algo[0] == ALGO_ECDSA || algo[0] == ALGO_ECDH) { - mbedtls_ecdsa_context ecdsa; - if (p[1] == NULL || len[1] == 0) { - return SW_WRONG_DATA(); - } - mbedtls_ecp_group_id gid = get_ec_group_id_from_attr(algo + 1, algo_len - 1); - if (gid == MBEDTLS_ECP_DP_NONE) { - return SW_FUNC_NOT_SUPPORTED(); - } - mbedtls_ecdsa_init(&ecdsa); - if (gid == MBEDTLS_ECP_DP_CURVE25519) { - mbedtls_ecp_group_load(&ecdsa.grp, gid); - r = mbedtls_mpi_read_binary(&ecdsa.d, p[1], len[1]); - } - else { - r = mbedtls_ecp_read_key(gid, &ecdsa, p[1], len[1]); - } - if (r != 0) { - mbedtls_ecdsa_free(&ecdsa); - return SW_EXEC_ERROR(); - } - r = mbedtls_ecp_mul(&ecdsa.grp, &ecdsa.Q, &ecdsa.d, &ecdsa.grp.G, random_gen, NULL); - if (r != 0) { - mbedtls_ecdsa_free(&ecdsa); - return SW_EXEC_ERROR(); - } - r = store_keys(&ecdsa, ALGO_ECDSA, fid, true); - make_ecdsa_response(&ecdsa); - mbedtls_ecdsa_free(&ecdsa); - if (r != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - } - else { - return SW_FUNC_NOT_SUPPORTED(); - } - if (fid == EF_PK_SIG) { - reset_sig_count(); - } - file_t *pbef = search_by_fid(fid + 3, NULL, SPECIFY_EF); - if (!pbef) { - return SW_REFERENCE_NOT_FOUND(); - } - r = file_put_data(pbef, res_APDU, res_APDU_size); - if (r != PICOKEY_OK) { - return SW_EXEC_ERROR(); - } - res_APDU_size = 0; //make_*_response sets a response. we need to overwrite - return SW_OK(); -} - -static int cmd_version() { - res_APDU[res_APDU_size++] = PIPGP_VERSION_MAJOR; - res_APDU[res_APDU_size++] = PIPGP_VERSION_MINOR; - res_APDU[res_APDU_size++] = 0x0; - return SW_OK(); -} - -static int cmd_select_data() { - file_t *ef = NULL; - uint16_t fid = 0x0; - if (P2(apdu) != 0x4) { - return SW_WRONG_P1P2(); - } - if (apdu.data[0] != 0x60) { - return SW_WRONG_DATA(); - } - if (apdu.nc != apdu.data[1] + 2 || apdu.nc < 5) { - return SW_WRONG_LENGTH(); - } - if (apdu.data[2] != 0x5C) { - return SW_WRONG_DATA(); - } - if (apdu.data[3] == 2) { - fid = (apdu.data[4] << 8) | apdu.data[5]; - } - else { - fid = apdu.data[4]; - } - if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { - return SW_SECURITY_STATUS_NOT_SATISFIED(); - } - fid &= ~0x6000; //Now get private DO - fid += P1(apdu); - if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - select_file(ef); - return SW_OK(); -} - -static int cmd_get_next_data() { - file_t *ef = NULL; - if (apdu.nc > 0) { - return SW_WRONG_LENGTH(); - } - if (!currentEF) { - return SW_RECORD_NOT_FOUND(); - } - uint16_t fid = (P1(apdu) << 8) | P2(apdu); - if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { - return SW_SECURITY_STATUS_NOT_SATISFIED(); - } - if ((currentEF->fid & 0x1FF0) != (fid & 0x1FF0)) { - return SW_WRONG_P1P2(); - } - fid = currentEF->fid + 1; //curentEF contains private DO. so, we select the next one - if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { - return SW_REFERENCE_NOT_FOUND(); - } - select_file(ef); - return cmd_get_data(); -} +extern int cmd_select(); +extern int cmd_get_data(); +extern int cmd_get_next_data(); +extern int cmd_put_data(); +extern int cmd_verify(); +extern int cmd_select_data(); +extern int cmd_version(); +extern int cmd_import_data(); +extern int cmd_change_pin(); +extern int cmd_mse(); +extern int cmd_internal_aut(); +extern int cmd_challenge(); +extern int cmd_activate_file(); +extern int cmd_terminate_df(); +extern int cmd_pso(); +extern int cmd_keypair_gen(); +extern int cmd_reset_retry(); #define INS_VERIFY 0x20 #define INS_MSE 0x22 diff --git a/src/openpgp/openpgp.h b/src/openpgp/openpgp.h index 513c366..2072262 100644 --- a/src/openpgp/openpgp.h +++ b/src/openpgp/openpgp.h @@ -27,9 +27,17 @@ #include "apdu.h" #include "mbedtls/rsa.h" #include "mbedtls/ecdsa.h" +#include "crypto_utils.h" +#include "files.h" extern bool has_pw1; +extern bool has_pw2; extern bool has_pw3; +extern bool has_rc; +extern uint8_t session_pw1[32]; +extern uint8_t session_rc[32]; +extern uint8_t session_pw3[32]; +extern uint8_t dek[IV_SIZE + 32]; extern int store_keys(void *key_ctx, int type, uint16_t key_id, bool use_kek); extern void make_rsa_response(mbedtls_rsa_context *rsa); @@ -56,4 +64,18 @@ extern int pin_reset_retries(const file_t *pin, bool force); #define ALGO_AES_192 0x72 #define ALGO_AES_256 0x74 +extern void select_file(file_t *pe); +extern int parse_do(uint16_t *fids, int mode); +extern int load_dek(); +extern int check_pin(const file_t *pin, const uint8_t *data, size_t len); +extern mbedtls_ecp_group_id get_ec_group_id_from_attr(const uint8_t *algo, size_t algo_len); +extern int reset_sig_count(); +extern uint16_t algo_dec, algo_aut, pk_dec, pk_aut; +extern bool wait_button_pressed(uint16_t fid); +extern void scan_files(); +extern int load_aes_key(uint8_t *aes_key, file_t *fkey); +extern int inc_sig_count(); +extern int dek_encrypt(uint8_t *data, size_t len); +extern int dek_decrypt(uint8_t *data, size_t len); + #endif diff --git a/src/openpgp/piv.c b/src/openpgp/piv.c index dc72d99..e2568bf 100644 --- a/src/openpgp/piv.c +++ b/src/openpgp/piv.c @@ -165,7 +165,7 @@ static int x509_create_cert(void *pk_ctx, uint8_t algo, uint8_t slot, bool attes return ret; } -static void scan_files() { +static void scan_files_piv() { scan_flash(); file_t *ef = search_by_fid(EF_PIV_KEY_CARDMGM, NULL, SPECIFY_EF); if ((ef = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_ANY))) { @@ -264,7 +264,7 @@ static void scan_files() { } void init_piv() { - scan_files(); + scan_files_piv(); has_pwpiv = false; // cmd_select(); } @@ -945,7 +945,7 @@ static int cmd_asym_keygen() { return SW_OK(); } -int cmd_put_data() { +static int cmd_put_data() { if (P1(apdu) != 0x3F || P2(apdu) != 0xFF) { return SW_INCORRECT_P1P2(); } diff --git a/src/openpgp/version.h b/src/openpgp/version.h index 2d4ae1b..554c686 100644 --- a/src/openpgp/version.h +++ b/src/openpgp/version.h @@ -23,15 +23,15 @@ #define OPGP_VERSION_MAJOR ((OPGP_VERSION >> 8) & 0xff) #define OPGP_VERSION_MINOR (OPGP_VERSION & 0xff) +#define PIV_VERSION 0x0507 + +#define PIV_VERSION_MAJOR ((PIV_VERSION >> 8) & 0xff) +#define PIV_VERSION_MINOR (PIV_VERSION & 0xff) + #define PIPGP_VERSION 0x0300 #define PIPGP_VERSION_MAJOR ((PIPGP_VERSION >> 8) & 0xff) #define PIPGP_VERSION_MINOR (PIPGP_VERSION & 0xff) -#define PIV_VERSION 0x0507 - -#define PIV_VERSION_MAJOR ((PIV_VERSION >> 8) & 0xff) -#define PIV_VERSION_MINOR (PIV_VERSION & 0xff) - #endif From 79912339b02ab53a011ca7cf07b47201e0c569cd Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 8 Jan 2025 15:18:40 +0100 Subject: [PATCH 2/6] Add OTP for emulation and test. Signed-off-by: Pol Henarejos --- pico-keys-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pico-keys-sdk b/pico-keys-sdk index 68a8168..3d91287 160000 --- a/pico-keys-sdk +++ b/pico-keys-sdk @@ -1 +1 @@ -Subproject commit 68a816895efb56a917520935f2f341960dc8db2c +Subproject commit 3d912878f1627719a006291eef5d60142a2f474f From abb4d2326ce976a6c42a4354e61c8ae16eeec5ff Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 8 Jan 2025 15:18:59 +0100 Subject: [PATCH 3/6] Fix change PIN for RP2350. Fixes #27. Signed-off-by: Pol Henarejos --- src/openpgp/cmd_change_pin.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/openpgp/cmd_change_pin.c b/src/openpgp/cmd_change_pin.c index 5ef9820..b39c098 100644 --- a/src/openpgp/cmd_change_pin.c +++ b/src/openpgp/cmd_change_pin.c @@ -16,6 +16,7 @@ */ #include "openpgp.h" +#include "otp.h" int cmd_change_pin() { if (P1(apdu) != 0x0) { @@ -31,6 +32,12 @@ int cmd_change_pin() { if ((r = load_dek()) != PICOKEY_OK) { return SW_EXEC_ERROR(); } + + if (otp_key_1) { + for (int i = 0; i < 32; i++) { + dek[IV_SIZE + i] ^= otp_key_1[i]; + } + } r = check_pin(pw, apdu.data, pin_len); if (r != 0x9000) { return r; From 46d35bd50fdc8274eec87422051e421ab21bff34 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 15 Jan 2025 10:53:27 +0100 Subject: [PATCH 4/6] Add rollback version 1. Signed-off-by: Pol Henarejos --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c776511..e0c7f62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ set(USB_ITF_CCID 1) set(USB_ITF_WCID 1) include(pico-keys-sdk/pico_keys_sdk_import.cmake) -SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/openpgp/version.h") +SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/openpgp/version.h" 1) if(ESP_PLATFORM) project(pico_openpgp) From 5629500a22d744bf96daadd23d515d09c77706d5 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 15 Jan 2025 10:53:39 +0100 Subject: [PATCH 5/6] Fix header project. Signed-off-by: Pol Henarejos --- src/openpgp/management.c | 2 +- src/openpgp/management.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openpgp/management.c b/src/openpgp/management.c index 1c1d1ba..166fd9c 100644 --- a/src/openpgp/management.c +++ b/src/openpgp/management.c @@ -1,5 +1,5 @@ /* - * This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido). + * 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 diff --git a/src/openpgp/management.h b/src/openpgp/management.h index a8a6331..369adc2 100644 --- a/src/openpgp/management.h +++ b/src/openpgp/management.h @@ -1,5 +1,5 @@ /* - * This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido). + * 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 From 1015d2f6974f0577c4af89ebdea099f895fda04b Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 15 Jan 2025 10:54:42 +0100 Subject: [PATCH 6/6] Upgrade to v3.2. Signed-off-by: Pol Henarejos --- VERSION | 2 +- build_pico_openpgp.sh | 2 +- src/openpgp/version.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 60e44a7..19fd49a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version=2.2 +Version=3.2 diff --git a/build_pico_openpgp.sh b/build_pico_openpgp.sh index 5f4de6f..4b41085 100755 --- a/build_pico_openpgp.sh +++ b/build_pico_openpgp.sh @@ -1,7 +1,7 @@ #!/bin/bash VERSION_MAJOR="3" -VERSION_MINOR="0" +VERSION_MINOR="2" SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}" #if ! [[ -z "${GITHUB_SHA}" ]]; then # SUFFIX="${SUFFIX}.${GITHUB_SHA}" diff --git a/src/openpgp/version.h b/src/openpgp/version.h index 554c686..c7c33b2 100644 --- a/src/openpgp/version.h +++ b/src/openpgp/version.h @@ -29,7 +29,7 @@ #define PIV_VERSION_MINOR (PIV_VERSION & 0xff) -#define PIPGP_VERSION 0x0300 +#define PIPGP_VERSION 0x0302 #define PIPGP_VERSION_MAJOR ((PIPGP_VERSION >> 8) & 0xff) #define PIPGP_VERSION_MINOR (PIPGP_VERSION & 0xff)