From 1f4d638119ccb3330830c9a155fb965f568b96a7 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Sun, 14 Dec 2025 18:45:02 +0100 Subject: [PATCH 1/3] Build minimal picokey app. Signed-off-by: Pol Henarejos --- CMakeLists.txt | 118 ++++++++++++++++++++++--------------- pico_keys_sdk_import.cmake | 2 +- src/fs/files.c | 28 +++++++++ src/version.c | 23 ++++++++ 4 files changed, 121 insertions(+), 50 deletions(-) create mode 100644 src/fs/files.c create mode 100644 src/version.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 85def50..0adf96b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,66 +17,86 @@ cmake_minimum_required(VERSION 3.16) -if(ESP_PLATFORM) -set(EXTRA_COMPONENT_DIRS src) -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(USB_ITF_CCID 1) -#set(USB_ITF_HID 1) -include(pico_keys_sdk_import.cmake) -project(pico_keys_sdk) + if(ESP_PLATFORM) + set(EXTRA_COMPONENT_DIRS src) + include($ENV{IDF_PATH}/tools/cmake/project.cmake) else() + if(NOT ENABLE_EMULATION) + set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1) + include(pico_sdk_import.cmake) + endif() - if(ENABLE_EMULATION) - else() - include(pico_sdk_import.cmake) - endif() + project(picokey C CXX ASM) - project(pico_keys 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 DEFINED __FOR_CI) + set(__FOR_CI 0) + endif() + if(__FOR_CI) + add_definitions(-D__FOR_CI) + 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(picokey) +endif() set(USB_ITF_CCID 1) -set(USB_ITF_HID 1) +set(USB_ITF_WCID 1) +include(cmake/version.cmake) include(pico_keys_sdk_import.cmake) - -add_executable(pico_keys_sdk_exe) - -target_compile_options(pico_keys_sdk_exe PUBLIC - -Wall - -Werror +if(NOT ESP_PLATFORM) + set(SOURCES ${PICO_KEYS_SOURCES}) +endif() +set(SOURCES ${SOURCES} + ${CMAKE_CURRENT_LIST_DIR}/src/fs/files.c + ${CMAKE_CURRENT_LIST_DIR}/src/version.c ) -if(ENABLE_EMULATION) -target_compile_options(pico_keys_sdk_exe PUBLIC - -fdata-sections - -ffunction-sections - ) -if(APPLE) -target_link_options(pico_keys_sdk_exe PUBLIC - -Wl,-dead_strip - ) -else() -target_link_options(pico_keys_sdk_exe PUBLIC - -Wl,--gc-sections - ) -endif (APPLE) -else() -pico_add_extra_outputs(pico_keys_sdk_exe) +SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/pico_keys_version.h" 2) -target_link_libraries(pico_keys_sdk_exe PRIVATE pico_keys_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board) +if(ESP_PLATFORM) + project(picokey) endif() +if(NOT ESP_PLATFORM) + target_sources(picokey PUBLIC ${SOURCES}) + target_include_directories(picokey PUBLIC ${INCLUDES}) + + target_compile_options(picokey PUBLIC + -Wall + ) + if(NOT MSVC) + target_compile_options(picokey PUBLIC + -Werror + ) + endif() + + if(ENABLE_EMULATION) + if(NOT MSVC) + target_compile_options(picokey PUBLIC + -fdata-sections + -ffunction-sections + ) + endif() + if(APPLE) + target_link_options(picokey PUBLIC + -Wl,-dead_strip + ) + elseif(MSVC) + target_compile_options(picokey PUBLIC + -WX + ) + + target_link_libraries(picokey PUBLIC wsock32 ws2_32 Bcrypt) + else() + target_link_options(picokey PUBLIC + -Wl,--gc-sections + ) + endif(APPLE) + target_link_libraries(picokey PRIVATE pthread m) + else() + pico_add_extra_outputs(${CMAKE_PROJECT_NAME}) + endif() endif() + diff --git a/pico_keys_sdk_import.cmake b/pico_keys_sdk_import.cmake index fc6ea6b..346febf 100644 --- a/pico_keys_sdk_import.cmake +++ b/pico_keys_sdk_import.cmake @@ -15,7 +15,7 @@ # along with this program. If not, see . # -include(pico-keys-sdk/cmake/version.cmake) +include(pico-keys-sdk/cmake/version.cmake OPTIONAL) option(VIDPID "Set specific VID/PID from a known platform {NitroHSM, NitroFIDO2, NitroStart, NitroPro, Nitro3, Yubikey5, YubikeyNeo, YubiHSM, Gnuk, GnuPG}" "None") diff --git a/src/fs/files.c b/src/fs/files.c new file mode 100644 index 0000000..7608e02 --- /dev/null +++ b/src/fs/files.c @@ -0,0 +1,28 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "file.h" + +file_t file_entries[] = { + /* 0 */ { .fid = 0x3f00, .parent = 0xff, .name = NULL, .type = FILE_TYPE_DF, .data = NULL, + .ef_structure = 0, .acl = { 0 } }, // MF + /* 1 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL, + .ef_structure = 0, .acl = { 0 } } //end +}; + +const file_t *MF = &file_entries[0]; +const file_t *file_last = &file_entries[sizeof(file_entries) / sizeof(file_t) - 1]; diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..369ddb2 --- /dev/null +++ b/src/version.c @@ -0,0 +1,23 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "pico_keys.h" +#include "pico_keys_version.h" + +const uint8_t PICO_PRODUCT = 0; +const uint8_t PICO_VERSION_MAJOR = PICO_KEYS_SDK_VERSION_MAJOR; +const uint8_t PICO_VERSION_MINOR = PICO_KEYS_SDK_VERSION_MINOR; From 015fb617593f3a169eb7916ac235357fa91cbf95 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 15 Dec 2025 01:17:26 +0100 Subject: [PATCH 2/3] Add sign with keydev to rescue. Signed-off-by: Pol Henarejos --- src/rescue.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/rescue.c b/src/rescue.c index 48f9b2e..44174f0 100644 --- a/src/rescue.c +++ b/src/rescue.c @@ -23,6 +23,9 @@ #include "pico/bootrom.h" #include "hardware/watchdog.h" #endif +#include "mbedtls/ecdsa.h" +#include "mbedtls/sha256.h" +#include "random.h" int rescue_process_apdu(); int rescue_unload(); @@ -54,6 +57,8 @@ int rescue_select(app_t *a, uint8_t force) { res_APDU[res_APDU_size++] = PICO_PRODUCT; res_APDU[res_APDU_size++] = PICO_VERSION_MAJOR; res_APDU[res_APDU_size++] = PICO_VERSION_MINOR; + memcpy(res_APDU + res_APDU_size, pico_serial.id, sizeof(pico_serial.id)); + res_APDU_size += sizeof(pico_serial.id); apdu.ne = res_APDU_size; if (force) { scan_flash(); @@ -69,6 +74,43 @@ int rescue_unload() { return PICOKEY_OK; } +int cmd_keydev_sign() { + if (apdu.nc == 0) { + return SW_WRONG_LENGTH(); + } + uint8_t hash[32]; + mbedtls_sha256(apdu.data, 16, hash, false); + if (!otp_key_2) { + return SW_INS_NOT_SUPPORTED(); + } + mbedtls_ecdsa_context ecdsa; + mbedtls_ecdsa_init(&ecdsa); + int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256K1, &ecdsa, otp_key_2, 32); + if (ret != 0) { + mbedtls_ecdsa_free(&ecdsa); + return SW_EXEC_ERROR(); + } + uint16_t key_size = 2 * (int)((mbedtls_ecp_curve_info_from_grp_id(MBEDTLS_ECP_DP_SECP256K1)->bit_size + 7) / 8); + mbedtls_mpi r, s; + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + ret = mbedtls_ecdsa_sign(&ecdsa.MBEDTLS_PRIVATE(grp), &r, &s, &ecdsa.MBEDTLS_PRIVATE(d), hash, sizeof(hash), random_gen, NULL); + if (ret != 0) { + mbedtls_ecdsa_free(&ecdsa); + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + return SW_EXEC_ERROR(); + } + + mbedtls_mpi_write_binary(&r, res_APDU, key_size / 2); res_APDU_size = key_size / 2; + mbedtls_mpi_write_binary(&s, res_APDU + res_APDU_size, key_size / 2); res_APDU_size += key_size / 2; + mbedtls_ecdsa_free(&ecdsa); + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + return SW_OK(); +} + int cmd_write() { if (apdu.nc < 2) { return SW_WRONG_LENGTH(); @@ -163,12 +205,14 @@ int cmd_reboot_bootsel() { } #endif +#define INS_KEYDEV_SIGN 0x10 #define INS_WRITE 0x1C #define INS_SECURE 0x1D #define INS_READ 0x1E #define INS_REBOOT_BOOTSEL 0x1F static const cmd_t cmds[] = { + { INS_KEYDEV_SIGN, cmd_keydev_sign }, { INS_WRITE, cmd_write }, #if defined(PICO_RP2350) || defined(ESP_PLATFORM) { INS_SECURE, cmd_secure }, From 7dc7be090919e7638616e93f21ff4c10c1373dec Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 15 Dec 2025 14:34:04 +0100 Subject: [PATCH 3/3] Add device public key recovery and upload attestation certification. Signed-off-by: Pol Henarejos --- src/rescue.c | 102 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/src/rescue.c b/src/rescue.c index 44174f0..acba24c 100644 --- a/src/rescue.c +++ b/src/rescue.c @@ -75,39 +75,85 @@ int rescue_unload() { } int cmd_keydev_sign() { - if (apdu.nc == 0) { - return SW_WRONG_LENGTH(); - } - uint8_t hash[32]; - mbedtls_sha256(apdu.data, 16, hash, false); - if (!otp_key_2) { - return SW_INS_NOT_SUPPORTED(); - } - mbedtls_ecdsa_context ecdsa; - mbedtls_ecdsa_init(&ecdsa); - int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256K1, &ecdsa, otp_key_2, 32); - if (ret != 0) { - mbedtls_ecdsa_free(&ecdsa); - return SW_EXEC_ERROR(); - } - uint16_t key_size = 2 * (int)((mbedtls_ecp_curve_info_from_grp_id(MBEDTLS_ECP_DP_SECP256K1)->bit_size + 7) / 8); - mbedtls_mpi r, s; - mbedtls_mpi_init(&r); - mbedtls_mpi_init(&s); + uint8_t p1 = P1(apdu); + if (p1 == 0x01) { + if (apdu.nc != 32) { + return SW_WRONG_LENGTH(); + } + if (!otp_key_2) { + return SW_INS_NOT_SUPPORTED(); + } + mbedtls_ecdsa_context ecdsa; + mbedtls_ecdsa_init(&ecdsa); + int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256K1, &ecdsa, otp_key_2, 32); + if (ret != 0) { + mbedtls_ecdsa_free(&ecdsa); + return SW_EXEC_ERROR(); + } + uint16_t key_size = 2 * (int)((mbedtls_ecp_curve_info_from_grp_id(MBEDTLS_ECP_DP_SECP256K1)->bit_size + 7) / 8); + mbedtls_mpi r, s; + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); - ret = mbedtls_ecdsa_sign(&ecdsa.MBEDTLS_PRIVATE(grp), &r, &s, &ecdsa.MBEDTLS_PRIVATE(d), hash, sizeof(hash), random_gen, NULL); - if (ret != 0) { + ret = mbedtls_ecdsa_sign(&ecdsa.MBEDTLS_PRIVATE(grp), &r, &s, &ecdsa.MBEDTLS_PRIVATE(d), apdu.data, apdu.nc, random_gen, NULL); + if (ret != 0) { + mbedtls_ecdsa_free(&ecdsa); + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + return SW_EXEC_ERROR(); + } + + mbedtls_mpi_write_binary(&r, res_APDU, key_size / 2); res_APDU_size = key_size / 2; + mbedtls_mpi_write_binary(&s, res_APDU + res_APDU_size, key_size / 2); res_APDU_size += key_size / 2; mbedtls_ecdsa_free(&ecdsa); mbedtls_mpi_free(&r); mbedtls_mpi_free(&s); - return SW_EXEC_ERROR(); } - - mbedtls_mpi_write_binary(&r, res_APDU, key_size / 2); res_APDU_size = key_size / 2; - mbedtls_mpi_write_binary(&s, res_APDU + res_APDU_size, key_size / 2); res_APDU_size += key_size / 2; - mbedtls_ecdsa_free(&ecdsa); - mbedtls_mpi_free(&r); - mbedtls_mpi_free(&s); + else if (p1 == 0x02) { + // Return public key + if (!otp_key_2) { + return SW_INS_NOT_SUPPORTED(); + } + if (apdu.nc != 0) { + return SW_WRONG_LENGTH(); + } + mbedtls_ecp_keypair ecp; + mbedtls_ecp_keypair_init(&ecp); + int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256K1, &ecp, otp_key_2, 32); + if (ret != 0) { + mbedtls_ecp_keypair_free(&ecp); + return SW_EXEC_ERROR(); + } + ret = mbedtls_ecp_mul(&ecp.MBEDTLS_PRIVATE(grp), &ecp.MBEDTLS_PRIVATE(Q), &ecp.MBEDTLS_PRIVATE(d), &ecp.MBEDTLS_PRIVATE(grp).G, random_gen, NULL); + if (ret != 0) { + mbedtls_ecp_keypair_free(&ecp); + return SW_EXEC_ERROR(); + } + size_t olen = 0; + ret = mbedtls_ecp_point_write_binary(&ecp.MBEDTLS_PRIVATE(grp), &ecp.MBEDTLS_PRIVATE(Q), MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, res_APDU, 4096); + if (ret != 0) { + mbedtls_ecp_keypair_free(&ecp); + return SW_EXEC_ERROR(); + } + res_APDU_size = (uint16_t)olen; + mbedtls_ecp_keypair_free(&ecp); + } + else if (p1 == 0x03) { + // Upload device attestation certificate + if (apdu.nc == 0) { + return SW_WRONG_LENGTH(); + } + file_t *ef_devcert = file_new(0x2F02); // EF_DEVCERT + if (!ef_devcert) { + return SW_FILE_NOT_FOUND(); + } + file_put_data(ef_devcert, apdu.data, (uint16_t)apdu.nc); + res_APDU_size = 0; + low_flash_available(); + } + else { + return SW_INCORRECT_P1P2(); + } return SW_OK(); }