diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 66e8795..213100b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -14,10 +14,10 @@ name: "Emulation and test"
on:
workflow_dispatch:
push:
- branches: [ "main", "eddsa" ]
+ branches: [ "main", "piv", "eddsa" ]
pull_request:
# The branches below must be a subset of the branches above
- branches: [ "main", "eddsa" ]
+ branches: [ "main", "piv", "eddsa" ]
schedule:
- cron: '23 5 * * 4'
diff --git a/.gitmodules b/.gitmodules
index f25a157..852c02c 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "pico-keys-sdk"]
path = pico-keys-sdk
- url = ../pico-keys-sdk
+ url = https://github.com/polhenarejos/pico-keys-sdk
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e8cef21..0464422 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,11 @@
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)
+else()
+
if(ENABLE_EMULATION)
else()
include(pico_sdk_import.cmake)
@@ -33,10 +38,13 @@ pico_sdk_init()
endif()
add_executable(pico_openpgp)
+endif()
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/src/openpgp/openpgp.c
${CMAKE_CURRENT_LIST_DIR}/src/openpgp/files.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/piv.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/management.c
)
set(INCLUDES ${INCLUDES}
@@ -45,7 +53,11 @@ set(INCLUDES ${INCLUDES}
set(USB_ITF_CCID 1)
include(pico-keys-sdk/pico_keys_sdk_import.cmake)
+if(ESP_PLATFORM)
+ project(pico_fido)
+endif()
+if(NOT ESP_PLATFORM)
target_sources(pico_openpgp PUBLIC ${SOURCES})
target_include_directories(pico_openpgp PUBLIC ${INCLUDES})
@@ -69,11 +81,13 @@ target_link_options(pico_openpgp PUBLIC
-Wl,--gc-sections
)
endif (APPLE)
+target_link_libraries(pico_openpgp PRIVATE pthread m)
else()
pico_add_extra_outputs(pico_openpgp)
#target_compile_definitions(pico_openpgp PRIVATE MBEDTLS_ECDSA_DETERMINISTIC=1)
-target_link_libraries(pico_openpgp PRIVATE pico_hsm_sdk pico_stdlib tinyusb_device tinyusb_board pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc)
+target_link_libraries(pico_openpgp PRIVATE pico_keys_sdk pico_stdlib tinyusb_device tinyusb_board pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id pico_aon_timer)
+endif()
endif()
diff --git a/VERSION b/VERSION
index 31da747..60e44a7 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version=1.10
+Version=2.2
diff --git a/build_pico_openpgp.sh b/build_pico_openpgp.sh
index 138a8ce..c6ea85f 100755
--- a/build_pico_openpgp.sh
+++ b/build_pico_openpgp.sh
@@ -1,7 +1,7 @@
#!/bin/bash
-VERSION_MAJOR="1"
-VERSION_MINOR="10"
+VERSION_MAJOR="2"
+VERSION_MINOR="2"
rm -rf release/*
cd build_release
@@ -17,6 +17,7 @@ for board in adafruit_feather_rp2040 \
eetree_gamekit_rp2040 \
garatronic_pybstick26_rp2040 \
melopero_shake_rp2040 \
+ nullbits_bit_c_pro \
pico \
pico_w \
pimoroni_badger2040 \
@@ -31,6 +32,7 @@ for board in adafruit_feather_rp2040 \
pimoroni_servo2040 \
pimoroni_tiny2040 \
pimoroni_tiny2040_2mb \
+ pololu_3pi_2040_robot \
seeed_xiao_rp2040 \
solderparty_rp2040_stamp \
solderparty_rp2040_stamp_carrier \
@@ -40,6 +42,8 @@ for board in adafruit_feather_rp2040 \
sparkfun_thingplus \
vgaboard \
waveshare_rp2040_lcd_0.96 \
+ waveshare_rp2040_lcd_1.28 \
+ waveshare_rp2040_one \
waveshare_rp2040_plus_4mb \
waveshare_rp2040_plus_16mb \
waveshare_rp2040_zero \
diff --git a/patch_vidpid.sh b/patch_vidpid.sh
index 8787200..3966d4d 100755
--- a/patch_vidpid.sh
+++ b/patch_vidpid.sh
@@ -17,7 +17,7 @@
# along with this program. If not, see .
#
-VERSION_MAJOR="4" #Version of Pico CCID Core
+VERSION_MAJOR="5" #Version of Pico Keys SDK
VERSION_MINOR="0"
echo "----------------------------"
diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake
index 28efe9e..a0721d0 100644
--- a/pico_sdk_import.cmake
+++ b/pico_sdk_import.cmake
@@ -18,9 +18,20 @@ if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_P
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
+ set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
+ message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
+endif ()
+
+if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
+ set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
+ message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
+endif()
+
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
@@ -29,11 +40,22 @@ if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
- FetchContent_Declare(
- pico_sdk
- GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
- GIT_TAG master
- )
+ # GIT_SUBMODULES_RECURSE was added in 3.17
+ if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
+ GIT_SUBMODULES_RECURSE FALSE
+ )
+ else ()
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
+ )
+ endif ()
+
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
diff --git a/src/openpgp/files.c b/src/openpgp/files.c
index 6fd335b..1d185b6 100644
--- a/src/openpgp/files.c
+++ b/src/openpgp/files.c
@@ -39,6 +39,7 @@ extern int parse_algoinfo(const file_t *f, int mode);
extern int parse_app_data(const file_t *f, int mode);
extern int parse_discrete_do(const file_t *f, int mode);
extern int parse_pw_status(const file_t *f, int mode);
+extern int piv_parse_discovery(const file_t *f);
uint8_t historical_bytes[] = {
10, 0,
@@ -249,10 +250,236 @@ file_t file_entries[] = {
/* 56 */ { .fid = EF_CH_3, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_NONE },
+ // ** PIV ** //
+ /* 57 */ { .fid = EF_PIV_ADMIN_DATA, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 58 */ { .fid = EF_PIV_ATTESTATION, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 59 */ { .fid = EF_PIV_MSCMAP, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 60 */ { .fid = EF_PIV_MSROOTS1, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 61 */ { .fid = EF_PIV_MSROOTS2, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 62 */ { .fid = EF_PIV_MSROOTS3, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 63 */ { .fid = EF_PIV_MSROOTS4, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 64 */ { .fid = EF_PIV_MSROOTS5, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 65 */ { .fid = EF_PIV_KEY_AUTHENTICATION, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 66 */ { .fid = EF_PIV_KEY_CARDMGM, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 67 */ { .fid = EF_PIV_KEY_SIGNATURE, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 68 */ { .fid = EF_PIV_KEY_KEYMGM, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 69 */ { .fid = EF_PIV_KEY_CARDAUTH, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 70 */ { .fid = EF_PIV_KEY_RETIRED1, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 71 */ { .fid = EF_PIV_KEY_RETIRED2, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 72 */ { .fid = EF_PIV_KEY_RETIRED3, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 73 */ { .fid = EF_PIV_KEY_RETIRED4, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 74 */ { .fid = EF_PIV_KEY_RETIRED5, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 75 */ { .fid = EF_PIV_KEY_RETIRED6, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 76 */ { .fid = EF_PIV_KEY_RETIRED7, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 77 */ { .fid = EF_PIV_KEY_RETIRED8, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 78 */ { .fid = EF_PIV_KEY_RETIRED9, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 79 */ { .fid = EF_PIV_KEY_RETIRED10, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 80 */ { .fid = EF_PIV_KEY_RETIRED11, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 81 */ { .fid = EF_PIV_KEY_RETIRED12, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 82 */ { .fid = EF_PIV_KEY_RETIRED12, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 83 */ { .fid = EF_PIV_KEY_RETIRED13, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 84 */ { .fid = EF_PIV_KEY_RETIRED14, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 85 */ { .fid = EF_PIV_KEY_RETIRED15, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 86 */ { .fid = EF_PIV_KEY_RETIRED16, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 87 */ { .fid = EF_PIV_KEY_RETIRED17, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 88 */ { .fid = EF_PIV_KEY_RETIRED18, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 89 */ { .fid = EF_PIV_KEY_RETIRED19, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 90 */ { .fid = EF_PIV_KEY_RETIRED20, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 91 */ { .fid = EF_PIV_KEY_ATTESTATION, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 92 */ { .fid = EF_PIV_CAPABILITY, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 93 */ { .fid = EF_PIV_CHUID, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 94 */ { .fid = EF_PIV_AUTHENTICATION, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 95 */ { .fid = EF_PIV_FINGERPRINTS, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 96 */ { .fid = EF_PIV_SECURITY, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 97 */ { .fid = EF_PIV_FACIAL, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 98 */ { .fid = EF_PIV_PRINTED, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 99 */ { .fid = EF_PIV_SIGNATURE, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 100 */ { .fid = EF_PIV_KEY_MANAGEMENT, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 101 */ { .fid = EF_PIV_CARD_AUTH, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 102 */ { .fid = EF_PIV_DISCOVERY, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *) piv_parse_discovery,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 103 */ { .fid = EF_PIV_KEY_HISTORY, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 104 */ { .fid = EF_PIV_IRIS, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 105 */ { .fid = EF_PIV_BITGT, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 106 */ { .fid = EF_PIV_SM_SIGNER, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 107 */ { .fid = EF_PIV_PC_REF_DATA, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 108 */ { .fid = EF_PIV_RETIRED1, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 109 */ { .fid = EF_PIV_RETIRED2, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 110 */ { .fid = EF_PIV_RETIRED3, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 111 */ { .fid = EF_PIV_RETIRED4, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 112 */ { .fid = EF_PIV_RETIRED5, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 113 */ { .fid = EF_PIV_RETIRED6, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 114 */ { .fid = EF_PIV_RETIRED7, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 115 */ { .fid = EF_PIV_RETIRED8, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 116 */ { .fid = EF_PIV_RETIRED9, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 117 */ { .fid = EF_PIV_RETIRED10, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 118 */ { .fid = EF_PIV_RETIRED11, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 119 */ { .fid = EF_PIV_RETIRED12, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 120 */ { .fid = EF_PIV_RETIRED13, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 121 */ { .fid = EF_PIV_RETIRED14, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 122 */ { .fid = EF_PIV_RETIRED15, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 123 */ { .fid = EF_PIV_RETIRED16, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 124 */ { .fid = EF_PIV_RETIRED17, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 125 */ { .fid = EF_PIV_RETIRED18, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 126 */ { .fid = EF_PIV_RETIRED19, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 127 */ { .fid = EF_PIV_RETIRED20, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
+ /* 128 */ { .fid = EF_PIV_PIN, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 129 */ { .fid = EF_PIV_PUK, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
+ /* 130 */ { .fid = EF_META, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_NONE },
+ /* 131 */ { .fid = EF_PW_RETRIES, .parent = 0, .name = NULL,
+ .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
+ .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
- /* 57 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF,
+ /* 132 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF,
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
- /* 58 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL,
+ /* 133 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL,
.ef_structure = 0, .acl = ACL_NONE } //end
};
diff --git a/src/openpgp/files.h b/src/openpgp/files.h
index 1918927..e6fd717 100644
--- a/src/openpgp/files.h
+++ b/src/openpgp/files.h
@@ -28,6 +28,7 @@
#define EF_ALGO_PRIV2 0x10c2
#define EF_ALGO_PRIV3 0x10c3
#define EF_PW_PRIV 0x10c4
+#define EF_PW_RETRIES 0x10c5
#define EF_PK_SIG 0x10d1
#define EF_PK_DEC 0x10d2
#define EF_PK_AUT 0x10d3
@@ -81,4 +82,85 @@
#define EF_EXLEN_INFO 0x7f66 //C
#define EF_GFM 0x7f74 //C
+// PIV
+
+#define EF_PIV_PIN 0x1184
+#define EF_PIV_PUK 0x1185
+
+#define EF_PIV_ADMIN_DATA 0xff00
+#define EF_PIV_ATTESTATION 0xff01
+#define EF_PIV_MSCMAP 0xff10
+#define EF_PIV_MSROOTS1 0xff11
+#define EF_PIV_MSROOTS2 0xff12
+#define EF_PIV_MSROOTS3 0xff13
+#define EF_PIV_MSROOTS4 0xff14
+#define EF_PIV_MSROOTS5 0xff15
+
+#define EF_PIV_KEY_AUTHENTICATION 0x009a
+#define EF_PIV_KEY_CARDMGM 0x009b
+#define EF_PIV_KEY_SIGNATURE 0x009c
+#define EF_PIV_KEY_KEYMGM 0x009d
+#define EF_PIV_KEY_CARDAUTH 0x009e
+#define EF_PIV_KEY_RETIRED1 0x0082
+#define EF_PIV_KEY_RETIRED2 0x0083
+#define EF_PIV_KEY_RETIRED3 0x0084
+#define EF_PIV_KEY_RETIRED4 0x0085
+#define EF_PIV_KEY_RETIRED5 0x0086
+#define EF_PIV_KEY_RETIRED6 0x0087
+#define EF_PIV_KEY_RETIRED7 0x0088
+#define EF_PIV_KEY_RETIRED8 0x0089
+#define EF_PIV_KEY_RETIRED9 0x008a
+#define EF_PIV_KEY_RETIRED10 0x008b
+#define EF_PIV_KEY_RETIRED11 0x008c
+#define EF_PIV_KEY_RETIRED12 0x008d
+#define EF_PIV_KEY_RETIRED13 0x008e
+#define EF_PIV_KEY_RETIRED14 0x008f
+#define EF_PIV_KEY_RETIRED15 0x0090
+#define EF_PIV_KEY_RETIRED16 0x0091
+#define EF_PIV_KEY_RETIRED17 0x0092
+#define EF_PIV_KEY_RETIRED18 0x0096 // It's 0x93 but assigned to EF_SIG_COUNT
+#define EF_PIV_KEY_RETIRED19 0x0094
+#define EF_PIV_KEY_RETIRED20 0x0095
+#define EF_PIV_KEY_ATTESTATION 0x00fb // It's 0xf9 but assigned to EF_KDF
+
+#define EF_PIV_CAPABILITY 0xc107
+#define EF_PIV_CHUID 0xc102
+#define EF_PIV_AUTHENTICATION 0xc105 /* cert for 9a key */
+#define EF_PIV_FINGERPRINTS 0xc103
+#define EF_PIV_SECURITY 0xc106
+#define EF_PIV_FACIAL 0xc108
+#define EF_PIV_PRINTED 0xc109
+#define EF_PIV_SIGNATURE 0xc10a /* cert for 9c key */
+#define EF_PIV_KEY_MANAGEMENT 0xc10b /* cert for 9d key */
+#define EF_PIV_CARD_AUTH 0xc101 /* cert for 9e key */
+#define EF_PIV_DISCOVERY 0x007e
+#define EF_PIV_KEY_HISTORY 0xc10c
+#define EF_PIV_IRIS 0xc121
+#define EF_PIV_BITGT 0x7f61
+#define EF_PIV_SM_SIGNER 0xc122
+#define EF_PIV_PC_REF_DATA 0xc123
+
+#define EF_PIV_RETIRED1 0xc10d
+#define EF_PIV_RETIRED2 0xc10e
+#define EF_PIV_RETIRED3 0xc10f
+#define EF_PIV_RETIRED4 0xc110
+#define EF_PIV_RETIRED5 0xc111
+#define EF_PIV_RETIRED6 0xc112
+#define EF_PIV_RETIRED7 0xc113
+#define EF_PIV_RETIRED8 0xc114
+#define EF_PIV_RETIRED9 0xc115
+#define EF_PIV_RETIRED10 0xc116
+#define EF_PIV_RETIRED11 0xc117
+#define EF_PIV_RETIRED12 0xc118
+#define EF_PIV_RETIRED13 0xc119
+#define EF_PIV_RETIRED14 0xc11a
+#define EF_PIV_RETIRED15 0xc11b
+#define EF_PIV_RETIRED16 0xc11c
+#define EF_PIV_RETIRED17 0xc11d
+#define EF_PIV_RETIRED18 0xc11e
+#define EF_PIV_RETIRED19 0xc11f
+#define EF_PIV_RETIRED20 0xc120
+
+#define EF_DEV_CONF 0x1122
+
#endif
diff --git a/src/openpgp/management.c b/src/openpgp/management.c
new file mode 100644
index 0000000..c6568b9
--- /dev/null
+++ b/src/openpgp/management.c
@@ -0,0 +1,153 @@
+/*
+ * This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "pico_keys.h"
+#include "apdu.h"
+#include "version.h"
+#include "files.h"
+#include "asn1.h"
+#include "management.h"
+
+int man_process_apdu();
+int man_unload();
+
+const uint8_t man_aid[] = {
+ 8,
+ 0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17
+};
+
+extern void init_piv();
+int man_select(app_t *a, uint8_t force) {
+ (void) force;
+ a->process_apdu = man_process_apdu;
+ a->unload = man_unload;
+ sprintf((char *) res_APDU, "%d.%d.0", PIV_VERSION_MAJOR, PIV_VERSION_MINOR);
+ res_APDU_size = strlen((char *) res_APDU);
+ apdu.ne = res_APDU_size;
+ init_piv();
+ return CCID_OK;
+}
+
+INITIALIZER( man_ctor ) {
+ register_app(man_select, man_aid);
+}
+
+int man_unload() {
+ return CCID_OK;
+}
+
+bool cap_supported(uint16_t cap) {
+ file_t *ef = search_dynamic_file(EF_DEV_CONF);
+ if (file_has_data(ef)) {
+ uint16_t tag = 0x0;
+ uint8_t *tag_data = NULL, *p = NULL;
+ uint16_t tag_len = 0;
+ asn1_ctx_t ctxi;
+ asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
+ while (walk_tlv(&ctxi, &p, &tag, &tag_len, &tag_data)) {
+ if (tag == TAG_USB_ENABLED) {
+ uint16_t ecaps = tag_data[0];
+ if (tag_len == 2) {
+ ecaps = (tag_data[0] << 8) | tag_data[1];
+ }
+ return ecaps & cap;
+ }
+ }
+ }
+ return true;
+}
+
+int man_get_config() {
+ file_t *ef = search_dynamic_file(EF_DEV_CONF);
+ res_APDU_size = 0;
+ res_APDU[res_APDU_size++] = 0; // Overall length. Filled later
+ res_APDU[res_APDU_size++] = TAG_USB_SUPPORTED;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = CAP_PIV | CAP_OPENPGP;
+ res_APDU[res_APDU_size++] = TAG_SERIAL;
+ res_APDU[res_APDU_size++] = 4;
+ memcpy(res_APDU + res_APDU_size, pico_serial.id, 4);
+ res_APDU_size += 4;
+ res_APDU[res_APDU_size++] = TAG_FORM_FACTOR;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = 0x01;
+ res_APDU[res_APDU_size++] = TAG_VERSION;
+ res_APDU[res_APDU_size++] = 3;
+ res_APDU[res_APDU_size++] = PIV_VERSION_MAJOR;
+ res_APDU[res_APDU_size++] = PIV_VERSION_MINOR;
+ res_APDU[res_APDU_size++] = 0;
+ res_APDU[res_APDU_size++] = TAG_NFC_SUPPORTED;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = 0x00;
+ if (!file_has_data(ef)) {
+ res_APDU[res_APDU_size++] = TAG_USB_ENABLED;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = CAP_PIV | CAP_OPENPGP;
+ res_APDU[res_APDU_size++] = TAG_DEVICE_FLAGS;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = FLAG_EJECT;
+ res_APDU[res_APDU_size++] = TAG_CONFIG_LOCK;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = 0x00;
+ res_APDU[res_APDU_size++] = TAG_NFC_ENABLED;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = 0x00;
+ }
+ else {
+ memcpy(res_APDU + res_APDU_size, file_get_data(ef), file_get_size(ef));
+ res_APDU_size += file_get_size(ef);
+ }
+ res_APDU[0] = res_APDU_size - 1;
+ return 0;
+}
+
+int cmd_read_config() {
+ man_get_config();
+ return SW_OK();
+}
+
+int cmd_write_config() {
+ if (apdu.data[0] != apdu.nc - 1) {
+ return SW_WRONG_DATA();
+ }
+ file_t *ef = file_new(EF_DEV_CONF);
+ file_put_data(ef, apdu.data + 1, apdu.nc - 1);
+ low_flash_available();
+ return SW_OK();
+}
+
+#define INS_READ_CONFIG 0x1D
+#define INS_WRITE_CONFIG 0x1C
+
+static const cmd_t cmds[] = {
+ { INS_READ_CONFIG, cmd_read_config },
+ { INS_WRITE_CONFIG, cmd_write_config },
+ { 0x00, 0x0 }
+};
+
+int man_process_apdu() {
+ if (CLA(apdu) != 0x00) {
+ return SW_CLA_NOT_SUPPORTED();
+ }
+ for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
+ if (cmd->ins == INS(apdu)) {
+ int r = cmd->cmd_handler();
+ return r;
+ }
+ }
+ return SW_INS_NOT_SUPPORTED();
+}
diff --git a/src/openpgp/management.h b/src/openpgp/management.h
new file mode 100644
index 0000000..a8a6331
--- /dev/null
+++ b/src/openpgp/management.h
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _MANAGEMENT_H_
+#define _MANAGEMENT_H_
+
+#include
+#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
+#include "pico/stdlib.h"
+#endif
+
+#define TAG_USB_SUPPORTED 0x01
+#define TAG_SERIAL 0x02
+#define TAG_USB_ENABLED 0x03
+#define TAG_FORM_FACTOR 0x04
+#define TAG_VERSION 0x05
+#define TAG_AUTO_EJECT_TIMEOUT 0x06
+#define TAG_CHALRESP_TIMEOUT 0x07
+#define TAG_DEVICE_FLAGS 0x08
+#define TAG_APP_VERSIONS 0x09
+#define TAG_CONFIG_LOCK 0x0A
+#define TAG_UNLOCK 0x0B
+#define TAG_REBOOT 0x0C
+#define TAG_NFC_SUPPORTED 0x0D
+#define TAG_NFC_ENABLED 0x0E
+
+#define CAP_OTP 0x01
+#define CAP_U2F 0x02
+#define CAP_FIDO2 0x200
+#define CAP_OATH 0x20
+#define CAP_PIV 0x10
+#define CAP_OPENPGP 0x08
+#define CAP_HSMAUTH 0x100
+
+#define FLAG_REMOTE_WAKEUP 0x40
+#define FLAG_EJECT 0x80
+
+extern bool cap_supported(uint16_t cap);
+extern int man_get_config();
+
+#endif //_MANAGEMENT_H
diff --git a/src/openpgp/openpgp.c b/src/openpgp/openpgp.c
index 8463e4d..68ac8fb 100644
--- a/src/openpgp/openpgp.c
+++ b/src/openpgp/openpgp.c
@@ -15,15 +15,18 @@
* 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 "version.h"
#include "files.h"
#include "random.h"
#include "eac.h"
#include "crypto_utils.h"
-#include "mbedtls/rsa.h"
-#include "mbedtls/ecdsa.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/asn1.h"
#include "asn1.h"
@@ -61,7 +64,6 @@ char atr_openpgp[] = {
int openpgp_process_apdu();
-
extern uint32_t board_button_read(void);
static bool wait_button_pressed(uint16_t fid) {
@@ -169,58 +171,9 @@ void scan_files() {
file_t *ef;
if ((ef = search_by_fid(EF_FULL_AID, NULL, SPECIFY_ANY))) {
ef->data = openpgp_aid_full;
-#ifndef ENABLE_EMULATION
- pico_get_unique_board_id_string((char *) ef->data + 12, 4);
-#else
- memset((char *) ef->data + 12, 0, 4);
-#endif
- }
- if ((ef = search_by_fid(EF_PW1, NULL, SPECIFY_ANY))) {
- if (!ef->data) {
- printf("PW1 is empty. Initializing with default password\r\n");
- const uint8_t def[6] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 };
- uint8_t dhash[33];
- dhash[0] = sizeof(def);
- double_hash_pin(def, sizeof(def), dhash + 1);
- flash_write_data_to_file(ef, dhash, sizeof(dhash));
- }
- }
- if ((ef = search_by_fid(EF_RC, NULL, SPECIFY_ANY))) {
- if (!ef->data) {
- printf("RC is empty. Initializing with default password\r\n");
-
- const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
- uint8_t dhash[33];
- dhash[0] = sizeof(def);
- double_hash_pin(def, sizeof(def), dhash + 1);
- flash_write_data_to_file(ef, dhash, sizeof(dhash));
- }
- }
- if ((ef = search_by_fid(EF_PW3, NULL, SPECIFY_ANY))) {
- if (!ef->data) {
- printf("PW3 is empty. Initializing with default password\r\n");
-
- const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
- uint8_t dhash[33];
- dhash[0] = sizeof(def);
- double_hash_pin(def, sizeof(def), dhash + 1);
- flash_write_data_to_file(ef, dhash, sizeof(dhash));
- }
- }
- if ((ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY))) {
- if (!ef->data) {
- printf("SigCount is empty. Initializing to zero\r\n");
- const uint8_t def[3] = { 0 };
- flash_write_data_to_file(ef, def, sizeof(def));
- }
- }
- if ((ef = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_ANY))) {
- if (!ef->data) {
- printf("PW status is empty. Initializing to default\r\n");
- const uint8_t def[] = { 0x1, 127, 127, 127, 3, 3, 3 };
- flash_write_data_to_file(ef, def, sizeof(def));
- }
+ memcpy(ef->data + 12, pico_serial.id, 4);
}
+ bool reset_dek = false;
if ((ef = search_by_fid(EF_DEK, NULL, SPECIFY_ANY))) {
if (!ef->data) {
printf("DEK is empty\r\n");
@@ -240,50 +193,105 @@ void scan_files() {
aes_encrypt_cfb_256(session_pw3, def, def + IV_SIZE + 32, 32);
aes_encrypt_cfb_256(session_pw3, def, def + IV_SIZE + 32 + 32, 32);
memset(session_pw3, 0, sizeof(session_pw3));
- flash_write_data_to_file(ef, def, sizeof(def));
+ file_put_data(ef, def, sizeof(def));
+ reset_dek = true;
+ }
+ }
+ if ((ef = search_by_fid(EF_PW1, NULL, SPECIFY_ANY))) {
+ if (!ef->data || reset_dek) {
+ printf("PW1 is empty. Initializing with default password\r\n");
+ const uint8_t def[6] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 };
+ uint8_t dhash[33];
+ dhash[0] = sizeof(def);
+ double_hash_pin(def, sizeof(def), dhash + 1);
+ file_put_data(ef, dhash, sizeof(dhash));
+ }
+ }
+ if ((ef = search_by_fid(EF_RC, NULL, SPECIFY_ANY))) {
+ if (!ef->data || reset_dek) {
+ printf("RC is empty. Initializing with default password\r\n");
+
+ const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
+ uint8_t dhash[33];
+ dhash[0] = sizeof(def);
+ double_hash_pin(def, sizeof(def), dhash + 1);
+ file_put_data(ef, dhash, sizeof(dhash));
+ }
+ }
+ if ((ef = search_by_fid(EF_PW3, NULL, SPECIFY_ANY))) {
+ if (!ef->data || reset_dek) {
+ printf("PW3 is empty. Initializing with default password\r\n");
+
+ const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
+ uint8_t dhash[33];
+ dhash[0] = sizeof(def);
+ double_hash_pin(def, sizeof(def), dhash + 1);
+ file_put_data(ef, dhash, sizeof(dhash));
+ }
+ }
+ if ((ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY))) {
+ if (!ef->data) {
+ printf("SigCount is empty. Initializing to zero\r\n");
+ const uint8_t def[3] = { 0 };
+ file_put_data(ef, def, sizeof(def));
+ }
+ }
+ if ((ef = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_ANY))) {
+ if (!ef->data) {
+ printf("PW status is empty. Initializing to default\r\n");
+ const uint8_t def[] = { 0x1, 127, 127, 127, 3, 3, 3 };
+ file_put_data(ef, def, sizeof(def));
}
}
if ((ef = search_by_fid(EF_UIF_SIG, NULL, SPECIFY_ANY))) {
if (!ef->data) {
printf("UIF SIG is empty. Initializing to default\r\n");
const uint8_t def[] = { 0x0, 0x20 };
- flash_write_data_to_file(ef, def, sizeof(def));
+ file_put_data(ef, def, sizeof(def));
}
}
if ((ef = search_by_fid(EF_UIF_DEC, NULL, SPECIFY_ANY))) {
if (!ef->data) {
printf("UIF DEC is empty. Initializing to default\r\n");
const uint8_t def[] = { 0x0, 0x20 };
- flash_write_data_to_file(ef, def, sizeof(def));
+ file_put_data(ef, def, sizeof(def));
}
}
if ((ef = search_by_fid(EF_UIF_AUT, NULL, SPECIFY_ANY))) {
if (!ef->data) {
printf("UIF AUT is empty. Initializing to default\r\n");
const uint8_t def[] = { 0x0, 0x20 };
- flash_write_data_to_file(ef, def, sizeof(def));
+ file_put_data(ef, def, sizeof(def));
}
}
if ((ef = search_by_fid(EF_KDF, NULL, SPECIFY_ANY))) {
if (!ef->data) {
printf("KDF is empty. Initializing to default\r\n");
const uint8_t def[] = { 0x81, 0x1, 0x0 };
- flash_write_data_to_file(ef, def, sizeof(def));
+ file_put_data(ef, def, sizeof(def));
}
}
-
if ((ef = search_by_fid(EF_SEX, NULL, SPECIFY_ANY))) {
if (!ef->data) {
printf("Sex is empty. Initializing to default\r\n");
const uint8_t def[] = { 0x30 };
- flash_write_data_to_file(ef, def, sizeof(def));
+ file_put_data(ef, def, sizeof(def));
+ }
+ }
+ if ((ef = search_by_fid(EF_PW_RETRIES, NULL, SPECIFY_ANY))) {
+ if (!ef->data) {
+ printf("PW retries is empty. Initializing to default\r\n");
+ const uint8_t def[] = { 0x1, 3, 3, 3 };
+ file_put_data(ef, def, sizeof(def));
}
}
low_flash_available();
}
+extern bool has_pwpiv;
+extern uint8_t session_pwpiv[32];
int load_dek() {
- if (!has_pw1 && !has_pw2 && !has_pw3) {
+ if (!has_pw1 && !has_pw2 && !has_pw3 && !has_pwpiv) {
return CCID_NO_LOGIN;
}
file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF);
@@ -300,6 +308,11 @@ int load_dek() {
memcpy(dek + IV_SIZE, file_get_data(tf) + IV_SIZE + 32 + 32, 32);
r = aes_decrypt_cfb_256(session_pw3, dek, dek + IV_SIZE, 32);
}
+ else if (has_pwpiv) {
+ memcpy(dek, file_get_data(tf), IV_SIZE);
+ memcpy(dek + IV_SIZE, file_get_data(tf) + IV_SIZE + 32 + 32 + 32, 32);
+ r = aes_decrypt_cfb_256(session_pwpiv, dek, dek + IV_SIZE, 32);
+ }
if (r != 0) {
return CCID_EXEC_ERROR;
}
@@ -353,7 +366,7 @@ int openpgp_unload() {
extern char __StackLimit;
int heapLeft() {
-#ifndef ENABLE_EMULATION
+#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
char *p = malloc(256); // try to avoid undue fragmentation
int left = &__StackLimit - p;
free(p);
@@ -363,30 +376,27 @@ int heapLeft() {
return left;
}
-app_t *openpgp_select_aid(app_t *a, const uint8_t *aid, uint8_t aid_len) {
- if (!memcmp(aid, openpgp_aid + 1, MIN(aid_len, openpgp_aid[0]))) {
- a->aid = openpgp_aid;
- a->process_apdu = openpgp_process_apdu;
- a->unload = openpgp_unload;
- init_openpgp();
- process_fci(file_openpgp, 1);
- memcpy(res_APDU + res_APDU_size, "\x64\x06\x53\x04", 4);
- res_APDU_size += 4;
- int heap_left = heapLeft();
- res_APDU[res_APDU_size++] = ((heap_left >> 24) & 0xff);
- res_APDU[res_APDU_size++] = ((heap_left >> 16) & 0xff);
- res_APDU[res_APDU_size++] = ((heap_left >> 8) & 0xff);
- res_APDU[res_APDU_size++] = ((heap_left >> 0) & 0xff);
- res_APDU[1] += 8;
- apdu.ne = res_APDU_size;
- return a;
- }
- return NULL;
+int openpgp_select_aid(app_t *a, uint8_t force) {
+ (void) force;
+ a->process_apdu = openpgp_process_apdu;
+ a->unload = openpgp_unload;
+ init_openpgp();
+ process_fci(file_openpgp, 1);
+ memcpy(res_APDU + res_APDU_size, "\x64\x06\x53\x04", 4);
+ res_APDU_size += 4;
+ int heap_left = heapLeft();
+ res_APDU[res_APDU_size++] = ((heap_left >> 24) & 0xff);
+ res_APDU[res_APDU_size++] = ((heap_left >> 16) & 0xff);
+ res_APDU[res_APDU_size++] = ((heap_left >> 8) & 0xff);
+ res_APDU[res_APDU_size++] = ((heap_left >> 0) & 0xff);
+ res_APDU[1] += 8;
+ apdu.ne = res_APDU_size;
+ return CCID_OK;
}
-void __attribute__((constructor)) openpgp_ctor() {
+INITIALIZER( openpgp_ctor ) {
ccid_atr = (uint8_t *) atr_openpgp;
- register_app(openpgp_select_aid);
+ register_app(openpgp_select_aid, openpgp_aid);
}
int parse_do(uint16_t *fids, int mode) {
@@ -465,7 +475,7 @@ int inc_sig_count() {
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] == 1) {
+ if (file_get_data(pw_status)[0] == 0) {
has_pw1 = false;
}
file_t *ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY);
@@ -476,7 +486,7 @@ int inc_sig_count() {
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 = flash_write_data_to_file(ef, q, sizeof(q));
+ int r = file_put_data(ef, q, sizeof(q));
if (r != CCID_OK) {
return CCID_EXEC_ERROR;
}
@@ -490,7 +500,7 @@ int reset_sig_count() {
return CCID_ERR_FILE_NOT_FOUND;
}
uint8_t q[3] = { 0 };
- int r = flash_write_data_to_file(ef, q, sizeof(q));
+ int r = file_put_data(ef, q, sizeof(q));
if (r != CCID_OK) {
return CCID_EXEC_ERROR;
}
@@ -583,15 +593,6 @@ int parse_pw_status(const file_t *f, int mode) {
return res_APDU_size - init_len;
}
-#define ALGO_RSA 0x01
-#define ALGO_ECDH 0x12
-#define ALGO_ECDSA 0x13
-#define ALGO_EDDSA 0x16
-#define ALGO_AES 0x70
-#define ALGO_AES_128 0x71
-#define ALGO_AES_192 0x72
-#define ALGO_AES_256 0x74
-
#define ALGO_RSA_1K 0
#define ALGO_RSA_2k 1
#define ALGO_RSA_3K 2
@@ -834,8 +835,10 @@ static int cmd_get_data() {
uint16_t data_len = parse_do(fids, 1);
uint8_t *p = NULL;
uint16_t tg = 0;
- size_t tg_len = 0;
- if (walk_tlv(res_APDU, data_len, &p, &tg, &tg_len, NULL)) {
+ 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++;
@@ -860,17 +863,22 @@ int pin_reset_retries(const file_t *pin, bool force) {
return CCID_ERR_NULL_PARAM;
}
file_t *pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF);
- if (!pw_status) {
+ file_t *pw_retries = search_by_fid(EF_PW_RETRIES, NULL, SPECIFY_EF);
+ if (!pw_status || !pw_retries) {
return CCID_ERR_FILE_NOT_FOUND;
}
- uint8_t p[7];
- memcpy(p, file_get_data(pw_status), 7);
- uint8_t retries = p[3 + (pin->fid & 0x3)];
+ if (3 + (pin->fid & 0xf) >= file_get_size(pw_status) || (pin->fid & 0xf) >= file_get_size(pw_retries)) {
+ return CCID_ERR_MEMORY_FATAL;
+ }
+ uint8_t p[64];
+ memcpy(p, file_get_data(pw_status), file_get_size(pw_status));
+ uint8_t retries = p[3 + (pin->fid & 0xf)];
if (retries == 0 && force == false) { //blocked
return CCID_ERR_BLOCKED;
}
- p[3 + (pin->fid & 0x3)] = 3;
- int r = flash_write_data_to_file(pw_status, p, file_get_size(pw_status));
+ uint8_t max_retries = file_get_data(pw_retries)[(pin->fid & 0xf)];
+ p[3 + (pin->fid & 0xf)] = max_retries;
+ int r = file_put_data(pw_status, p, file_get_size(pw_status));
low_flash_available();
return r;
}
@@ -883,19 +891,19 @@ int pin_wrong_retry(const file_t *pin) {
if (!pw_status) {
return CCID_ERR_FILE_NOT_FOUND;
}
- uint8_t p[7];
- memcpy(p, file_get_data(pw_status), 7);
- if (p[3 + (pin->fid & 0x3)] > 0) {
- p[3 + (pin->fid & 0x3)] -= 1;
- int r = flash_write_data_to_file(pw_status, p, file_get_size(pw_status));
+ uint8_t p[64];
+ memcpy(p, file_get_data(pw_status), file_get_size(pw_status));
+ if (p[3 + (pin->fid & 0xf)] > 0) {
+ p[3 + (pin->fid & 0xf)] -= 1;
+ int r = file_put_data(pw_status, p, file_get_size(pw_status));
if (r != CCID_OK) {
return r;
}
low_flash_available();
- if (p[3 + (pin->fid & 0x3)] == 0) {
+ if (p[3 + (pin->fid & 0xf)] == 0) {
return CCID_ERR_BLOCKED;
}
- return p[3 + (pin->fid & 0x3)];
+ return p[3 + (pin->fid & 0xf)];
}
return CCID_ERR_BLOCKED;
}
@@ -986,7 +994,7 @@ static int cmd_verify() {
if (apdu.nc > 0) {
return check_pin(pw, apdu.data, apdu.nc);
}
- uint8_t retries = *(file_get_data(pw_status) + 3 + (fid & 0x3));
+ uint8_t retries = *(file_get_data(pw_status) + 3 + (fid & 0xf));
if (retries == 0) {
return SW_PIN_BLOCKED();
}
@@ -1028,21 +1036,21 @@ static int cmd_put_data() {
uint8_t dhash[33];
dhash[0] = apdu.nc;
double_hash_pin(apdu.data, apdu.nc, dhash + 1);
- r = flash_write_data_to_file(ef, dhash, sizeof(dhash));
+ 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];
+ 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 = flash_write_data_to_file(tf, def, sizeof(def));
+ r = file_put_data(tf, def, sizeof(def));
}
else {
- r = flash_write_data_to_file(ef, apdu.data, apdu.nc);
+ r = file_put_data(ef, apdu.data, apdu.nc);
}
if (r != CCID_OK) {
return SW_MEMORY_FAILURE();
@@ -1073,13 +1081,13 @@ static int cmd_change_pin() {
uint8_t dhash[33];
dhash[0] = apdu.nc - pin_len;
double_hash_pin(apdu.data + pin_len, apdu.nc - pin_len, dhash + 1);
- flash_write_data_to_file(pw, dhash, sizeof(dhash));
+ 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];
+ 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);
@@ -1091,7 +1099,7 @@ static int cmd_change_pin() {
memcpy(def + IV_SIZE + 32 + 32, dek + IV_SIZE, 32);
aes_encrypt_cfb_256(session_pw3, def, def + IV_SIZE + 32 + 32, 32);
}
- flash_write_data_to_file(tf, def, sizeof(def));
+ file_put_data(tf, def, sizeof(def));
low_flash_available();
return SW_OK();
}
@@ -1138,17 +1146,17 @@ static int cmd_reset_retry() {
if (!tf) {
return SW_REFERENCE_NOT_FOUND();
}
- uint8_t def[IV_SIZE + 32 + 32 + 32];
+ 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 = flash_write_data_to_file(tf, def, sizeof(def));
+ 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);
- flash_write_data_to_file(pw, dhash, sizeof(dhash));
+ file_put_data(pw, dhash, sizeof(dhash));
if (pin_reset_retries(pw, true) != CCID_OK) {
return SW_MEMORY_FAILURE();
}
@@ -1158,7 +1166,7 @@ static int cmd_reset_retry() {
return SW_INCORRECT_P1P2();
}
-int store_keys(void *key_ctx, int type, uint16_t key_id) {
+int store_keys(void *key_ctx, int type, uint16_t key_id, bool use_kek) {
int r, key_size = 0;
uint8_t kdata[4096 / 8]; //worst
@@ -1179,10 +1187,10 @@ int store_keys(void *key_ctx, int type, uint16_t key_id) {
}
else if (type == ALGO_ECDSA || type == ALGO_ECDH || type == ALGO_EDDSA) {
mbedtls_ecp_keypair *ecdsa = (mbedtls_ecp_keypair *) key_ctx;
- key_size = mbedtls_mpi_size(&ecdsa->d);
+ size_t olen = 0;
kdata[0] = ecdsa->grp.id & 0xff;
- mbedtls_ecp_write_key(ecdsa, kdata + 1, key_size);
- key_size++;
+ mbedtls_ecp_write_key_ext(ecdsa, &olen, kdata + 1, sizeof(kdata) - 1);
+ key_size = olen + 1;
}
else if (type & ALGO_AES) {
if (type == ALGO_AES_128) {
@@ -1196,14 +1204,16 @@ int store_keys(void *key_ctx, int type, uint16_t key_id) {
}
memcpy(kdata, key_ctx, key_size);
}
- r = dek_encrypt(kdata, key_size);
- if (r != CCID_OK) {
- return r;
+ if (use_kek) {
+ r = dek_encrypt(kdata, key_size);
+ if (r != CCID_OK) {
+ return r;
+ }
}
//r = aes_encrypt_cfb_256(file_read(pw3->data+2), session_pw3, kdata, key_size);
//if (r != CCID_OK)
// return r;
- r = flash_write_data_to_file(ef, kdata, key_size);
+ r = file_put_data(ef, kdata, key_size);
if (r != CCID_OK) {
return r;
}
@@ -1211,11 +1221,11 @@ int store_keys(void *key_ctx, int type, uint16_t key_id) {
return CCID_OK;
}
-int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) {
+int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey, bool use_dek) {
int key_size = file_get_size(fkey);
uint8_t kdata[4096 / 8];
memcpy(kdata, file_get_data(fkey), key_size);
- if (dek_decrypt(kdata, key_size) != 0) {
+ if (use_dek && dek_decrypt(kdata, key_size) != 0) {
return CCID_EXEC_ERROR;
}
if (mbedtls_mpi_read_binary(&ctx->P, kdata, key_size / 2) != 0) {
@@ -1245,11 +1255,11 @@ int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) {
return CCID_OK;
}
-int load_private_key_ecdsa(mbedtls_ecp_keypair *ctx, file_t *fkey) {
+int load_private_key_ecdsa(mbedtls_ecp_keypair *ctx, file_t *fkey, bool use_dek) {
int key_size = file_get_size(fkey);
uint8_t kdata[67]; //Worst case, 521 bit + 1byte
memcpy(kdata, file_get_data(fkey), key_size);
- if (dek_decrypt(kdata, key_size) != 0) {
+ if (use_dek && dek_decrypt(kdata, key_size) != 0) {
return CCID_EXEC_ERROR;
}
mbedtls_ecp_group_id gid = kdata[0];
@@ -1258,6 +1268,17 @@ int load_private_key_ecdsa(mbedtls_ecp_keypair *ctx, file_t *fkey) {
mbedtls_ecp_keypair_free(ctx);
return CCID_EXEC_ERROR;
}
+ mbedtls_platform_zeroize(kdata, sizeof(kdata));
+ if (ctx->grp.id == MBEDTLS_ECP_DP_ED25519) {
+ r = mbedtls_ecp_point_edwards(&ctx->grp, &ctx->Q, &ctx->d, random_gen, NULL);
+ }
+ else {
+ r = mbedtls_ecp_mul(&ctx->grp, &ctx->Q, &ctx->d, &ctx->grp.G, random_gen, NULL);
+ }
+ if (r != 0) {
+ mbedtls_ecdsa_free(ctx);
+ return CCID_EXEC_ERROR;
+ }
return CCID_OK;
}
@@ -1393,7 +1414,7 @@ static int cmd_keypair_gen() {
mbedtls_rsa_free(&rsa);
return SW_EXEC_ERROR();
}
- r = store_keys(&rsa, ALGO_RSA, fid);
+ r = store_keys(&rsa, ALGO_RSA, fid, true);
make_rsa_response(&rsa);
mbedtls_rsa_free(&rsa);
if (r != CCID_OK) {
@@ -1414,7 +1435,7 @@ static int cmd_keypair_gen() {
mbedtls_ecp_keypair_free(&ecdsa);
return SW_EXEC_ERROR();
}
- r = store_keys(&ecdsa, algo[0], fid);
+ r = store_keys(&ecdsa, algo[0], fid, true);
make_ecdsa_response(&ecdsa);
mbedtls_ecp_keypair_free(&ecdsa);
if (r != CCID_OK) {
@@ -1428,7 +1449,7 @@ static int cmd_keypair_gen() {
if (!pbef) {
return SW_REFERENCE_NOT_FOUND();
}
- r = flash_write_data_to_file(pbef, res_APDU, res_APDU_size);
+ r = file_put_data(pbef, res_APDU, res_APDU_size);
if (r != CCID_OK) {
return SW_EXEC_ERROR();
}
@@ -1441,7 +1462,7 @@ static int cmd_keypair_gen() {
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);
+ r = store_keys(aes_key, ALGO_AES_256, EF_AES_KEY, true);
/* if storing the key fails, we silently continue */
//if (r != CCID_OK)
// return SW_EXEC_ERROR();
@@ -1469,7 +1490,7 @@ int rsa_sign(mbedtls_rsa_context *ctx,
uint8_t *d = (uint8_t *) data, *end = d + data_len, *hsh = NULL;
size_t seq_len = 0, hash_len = 0;
int key_size = ctx->len, r = 0;
- mbedtls_md_type_t md = MBEDTLS_MD_SHA256;
+ mbedtls_md_type_t md = MBEDTLS_MD_NONE;
if (mbedtls_asn1_get_tag(&d, end, &seq_len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) == 0) {
mbedtls_asn1_buf mdb;
@@ -1609,7 +1630,7 @@ static int cmd_pso() {
return SW_EXEC_ERROR();
}
if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { //decipher
- r = aes_decrypt(aes_key, NULL, key_size, HSM_AES_MODE_CBC, apdu.data + 1, apdu.nc - 1);
+ 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 != CCID_OK) {
return SW_EXEC_ERROR();
@@ -1618,7 +1639,7 @@ static int cmd_pso() {
res_APDU_size = apdu.nc - 1;
}
else if (P1(apdu) == 0x86 && P2(apdu) == 0x80) { //encipher
- r = aes_encrypt(aes_key, NULL, key_size, HSM_AES_MODE_CBC, apdu.data, apdu.nc);
+ 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 != CCID_OK) {
return SW_EXEC_ERROR();
@@ -1632,7 +1653,7 @@ static int cmd_pso() {
if (algo[0] == ALGO_RSA) {
mbedtls_rsa_context ctx;
mbedtls_rsa_init(&ctx);
- r = load_private_key_rsa(&ctx, ef);
+ r = load_private_key_rsa(&ctx, ef, true);
if (r != CCID_OK) {
mbedtls_rsa_free(&ctx);
return SW_EXEC_ERROR();
@@ -1671,7 +1692,7 @@ static int cmd_pso() {
if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) {
mbedtls_ecp_keypair ctx;
mbedtls_ecp_keypair_init(&ctx);
- r = load_private_key_ecdsa(&ctx, ef);
+ r = load_private_key_ecdsa(&ctx, ef, true);
if (r != CCID_OK) {
mbedtls_ecp_keypair_free(&ctx);
return SW_EXEC_ERROR();
@@ -1801,7 +1822,7 @@ static int cmd_internal_aut() {
if (algo[0] == ALGO_RSA) {
mbedtls_rsa_context ctx;
mbedtls_rsa_init(&ctx);
- r = load_private_key_rsa(&ctx, ef);
+ r = load_private_key_rsa(&ctx, ef, true);
if (r != CCID_OK) {
mbedtls_rsa_free(&ctx);
return SW_EXEC_ERROR();
@@ -1817,7 +1838,7 @@ static int cmd_internal_aut() {
else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA || algo[0] == ALGO_EDDSA) {
mbedtls_ecp_keypair ctx;
mbedtls_ecp_keypair_init(&ctx);
- r = load_private_key_ecdsa(&ctx, ef);
+ r = load_private_key_ecdsa(&ctx, ef, true);
if (r != CCID_OK) {
mbedtls_ecp_keypair_free(&ctx);
return SW_EXEC_ERROR();
@@ -1864,7 +1885,7 @@ static int cmd_mse() {
return SW_OK();
}
-size_t tag_len(uint8_t **data) {
+uint16_t tag_len(uint8_t **data) {
size_t len = *(*data)++;
if (len == 0x82) {
len = *(*data)++ << 8;
@@ -1889,7 +1910,7 @@ static int cmd_import_data() {
if (*start++ != 0x4D) {
return SW_WRONG_DATA();
}
- size_t tgl = tag_len(&start);
+ uint16_t tgl = tag_len(&start);
if (*start != 0xB6 && *start != 0xB8 && *start != 0xA4) {
return SW_WRONG_DATA();
}
@@ -1917,7 +1938,8 @@ static int cmd_import_data() {
return SW_WRONG_DATA();
}
tgl = tag_len(&start);
- uint8_t *end = start + tgl, len[9] = { 0 }, *p[9] = { 0 };
+ 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) {
@@ -1987,7 +2009,7 @@ static int cmd_import_data() {
mbedtls_rsa_free(&rsa);
return SW_EXEC_ERROR();
}
- r = store_keys(&rsa, ALGO_RSA, fid);
+ r = store_keys(&rsa, ALGO_RSA, fid, true);
make_rsa_response(&rsa);
mbedtls_rsa_free(&rsa);
if (r != CCID_OK) {
@@ -2025,13 +2047,16 @@ static int cmd_import_data() {
mbedtls_ecp_keypair_free(&ecdsa);
return SW_EXEC_ERROR();
}
- r = store_keys(&ecdsa, ALGO_ECDSA, fid);
+ r = store_keys(&ecdsa, ALGO_ECDSA, fid, true);
make_ecdsa_response(&ecdsa);
mbedtls_ecp_keypair_free(&ecdsa);
if (r != CCID_OK) {
return SW_EXEC_ERROR();
}
}
+ else {
+ return SW_FUNC_NOT_SUPPORTED();
+ }
if (fid == EF_PK_SIG) {
reset_sig_count();
}
@@ -2039,7 +2064,7 @@ static int cmd_import_data() {
if (!pbef) {
return SW_REFERENCE_NOT_FOUND();
}
- r = flash_write_data_to_file(pbef, res_APDU, res_APDU_size);
+ r = file_put_data(pbef, res_APDU, res_APDU_size);
if (r != CCID_OK) {
return SW_EXEC_ERROR();
}
diff --git a/src/openpgp/openpgp.h b/src/openpgp/openpgp.h
index 5714094..513c366 100644
--- a/src/openpgp/openpgp.h
+++ b/src/openpgp/openpgp.h
@@ -19,14 +19,41 @@
#define __OPENPGP_H_
#include "stdlib.h"
-#ifndef ENABLE_EMULATION
+#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
#include
#endif
-#include "hsm.h"
+#include "pico_keys.h"
#include "apdu.h"
+#include "mbedtls/rsa.h"
+#include "mbedtls/ecdsa.h"
extern bool has_pw1;
extern bool has_pw3;
+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);
+extern void make_ecdsa_response(mbedtls_ecdsa_context *ecdsa);
+extern int ecdsa_sign(mbedtls_ecdsa_context *ctx,
+ const uint8_t *data,
+ size_t data_len,
+ uint8_t *out,
+ size_t *out_len);
+extern int rsa_sign(mbedtls_rsa_context *ctx,
+ const uint8_t *data,
+ size_t data_len,
+ uint8_t *out,
+ size_t *out_len);
+extern int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey, bool use_dek);
+extern int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey, bool use_dek);
+extern int pin_reset_retries(const file_t *pin, bool force);
+
+#define ALGO_RSA 0x01
+#define ALGO_ECDH 0x12
+#define ALGO_ECDSA 0x13
+#define ALGO_AES 0x70
+#define ALGO_AES_128 0x71
+#define ALGO_AES_192 0x72
+#define ALGO_AES_256 0x74
+
#endif
diff --git a/src/openpgp/piv.c b/src/openpgp/piv.c
new file mode 100644
index 0000000..6eae778
--- /dev/null
+++ b/src/openpgp/piv.c
@@ -0,0 +1,1330 @@
+/*
+ * 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 "files.h"
+#include "apdu.h"
+#include "pico_keys.h"
+#include "random.h"
+#include "eac.h"
+#include "crypto_utils.h"
+#include "version.h"
+#include "asn1.h"
+#include "mbedtls/aes.h"
+#include "mbedtls/des.h"
+#include "mbedtls/x509_crt.h"
+#include "openpgp.h"
+
+#define PIV_ALGO_3DES 0x03
+#define PIV_ALGO_AES128 0x08
+#define PIV_ALGO_AES192 0x0a
+#define PIV_ALGO_AES256 0x0c
+#define PIV_ALGO_RSA1024 0x06
+#define PIV_ALGO_RSA2048 0x07
+#define PIV_ALGO_RSA3072 0x05
+#define PIV_ALGO_RSA4096 0x16
+#define PIV_ALGO_ECCP256 0x11
+#define PIV_ALGO_ECCP384 0x14
+#define PIV_ALGO_X25519 0xE1
+
+#define PINPOLICY_DEFAULT 0
+#define PINPOLICY_NEVER 1
+#define PINPOLICY_ONCE 2
+#define PINPOLICY_ALWAYS 3
+
+#define TOUCHPOLICY_DEFAULT 0
+#define TOUCHPOLICY_NEVER 1
+#define TOUCHPOLICY_ALWAYS 2
+#define TOUCHPOLICY_CACHED 3
+#define TOUCHPOLICY_AUTO 0xFF
+
+#define ORIGIN_GENERATED 0x01
+#define ORIGIN_IMPORTED 0x02
+
+#define IS_RETIRED(x) ((x) >= EF_PIV_KEY_RETIRED1 && (x) <= EF_PIV_KEY_RETIRED20)
+#define IS_ACTIVE(x) ((x) >= EF_PIV_KEY_AUTHENTICATION && (x) <= EF_PIV_KEY_CARDAUTH)
+#define IS_KEY(x) ((IS_ACTIVE((x))) || (IS_RETIRED((x))))
+
+uint8_t piv_aid[] = {
+ 5,
+ 0xA0, 0x00, 0x00, 0x03, 0x8,
+};
+uint8_t yk_aid[] = {
+ 8,
+ 0xA0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x1, 0x1
+};
+
+bool has_pwpiv = false;
+uint8_t session_pwpiv[32];
+
+int piv_process_apdu();
+
+static int get_serial() {
+ uint32_t serial = (pico_serial.id[0] & 0x7F) << 24 | pico_serial.id[1] << 16 | pico_serial.id[2] << 8 | pico_serial.id[3];
+ return serial;
+}
+
+static int x509_create_cert(void *pk_ctx, uint8_t algo, uint8_t slot, bool attestation, uint8_t *buffer, size_t buffer_size) {
+ mbedtls_x509write_cert ctx;
+ mbedtls_x509write_crt_init(&ctx);
+ mbedtls_x509write_crt_set_version(&ctx, MBEDTLS_X509_CRT_VERSION_3);
+ mbedtls_x509write_crt_set_validity(&ctx, "20240325000000", "20741231235959");
+ uint8_t serial[20];
+ random_gen(NULL, serial, sizeof(serial));
+ mbedtls_x509write_crt_set_serial_raw(&ctx, serial, sizeof(serial));
+ mbedtls_pk_context skey, ikey;
+ mbedtls_ecdsa_context actx; // attestation key
+ mbedtls_pk_init(&skey);
+ mbedtls_pk_init(&ikey);
+ if (algo == PIV_ALGO_RSA1024 || algo == PIV_ALGO_RSA2048) {
+ mbedtls_pk_setup(&skey, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA));
+ }
+ else if (algo == PIV_ALGO_ECCP256 || algo == PIV_ALGO_ECCP384) {
+ mbedtls_pk_setup(&skey, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
+ }
+ skey.pk_ctx = pk_ctx;
+ mbedtls_x509write_crt_set_subject_key(&ctx, &skey);
+ char buf_sname[256];
+ if (attestation) {
+ sprintf(buf_sname, "C=ES,O=Pico Keys,CN=Pico OpenPGP PIV Attestation %X", slot);
+ mbedtls_x509write_crt_set_subject_name(&ctx, buf_sname);
+ mbedtls_x509write_crt_set_issuer_name(&ctx, "C=ES,O=Pico Keys,CN=Pico OpenPGP PIV Slot F9");
+ file_t *ef_key = search_by_fid(EF_PIV_KEY_ATTESTATION, NULL, SPECIFY_EF);
+ mbedtls_ecdsa_init(&actx);
+ load_private_key_ecdsa(&actx, ef_key, false);
+ mbedtls_pk_setup(&ikey, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
+ ikey.pk_ctx = &actx;
+ mbedtls_x509write_crt_set_issuer_key(&ctx, &ikey);
+ uint8_t ver[] = {PIV_VERSION_MAJOR, PIV_VERSION_MINOR, 0};
+ mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x03", 10, 0, ver, sizeof(ver));
+ uint32_t serial = get_serial();
+ mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x07", 10, 0, (const uint8_t *)&serial, sizeof(serial));
+ int meta_len = 0;
+ uint8_t *meta;
+ if ((meta_len = meta_find(slot, &meta)) >= 0) {
+ mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x08", 10, 0, &meta[1], 2);
+ }
+ uint8_t v = 1;
+ mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x09", 10, 0, &v, sizeof(serial));
+ }
+ else {
+ uint8_t wslot = slot;
+ if (slot == EF_PIV_KEY_ATTESTATION) {
+ wslot = 0xF9;
+ }
+ else if (slot == EF_PIV_KEY_RETIRED18) {
+ wslot = 0x93;
+ }
+ sprintf(buf_sname, "C=ES,O=Pico Keys,CN=Pico OpenPGP PIV Slot %X", wslot);
+ mbedtls_x509write_crt_set_issuer_name(&ctx, buf_sname);
+ mbedtls_x509write_crt_set_subject_name(&ctx, buf_sname);
+ mbedtls_x509write_crt_set_issuer_key(&ctx, &skey);
+ }
+ if (algo == PIV_ALGO_ECCP384) {
+ mbedtls_x509write_crt_set_md_alg(&ctx, MBEDTLS_MD_SHA384);
+ }
+ else {
+ mbedtls_x509write_crt_set_md_alg(&ctx, MBEDTLS_MD_SHA256);
+ }
+ if (slot == EF_PIV_KEY_ATTESTATION) {
+ mbedtls_x509write_crt_set_basic_constraints(&ctx, 1, 1);
+ }
+ else {
+ mbedtls_x509write_crt_set_basic_constraints(&ctx, 0, 0);
+ }
+ mbedtls_x509write_crt_set_subject_key_identifier(&ctx);
+ mbedtls_x509write_crt_set_authority_key_identifier(&ctx);
+ mbedtls_x509write_crt_set_key_usage(&ctx,
+ MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
+ MBEDTLS_X509_KU_KEY_CERT_SIGN);
+ int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL);
+ /* skey cannot be freed, as it is freed later */
+ if (attestation) {
+ mbedtls_ecdsa_free(&actx);
+ }
+ mbedtls_x509write_crt_free(&ctx);
+ return ret;
+}
+
+static void scan_files() {
+ 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))) {
+ if (file_get_size(ef) == 0) {
+ printf("PW status is empty. Initializing to default\r\n");
+ const uint8_t def[] = { 0x1, 127, 127, 127, 3, 3, 3, 3, 3 };
+ file_put_data(ef, def, sizeof(def));
+ }
+ else if (file_get_size(ef) == 7) {
+ printf("PW status is older. Initializing to default\r\n");
+ uint8_t def[9] = { 0 };
+ memcpy(def, file_get_data(ef), 7);
+ def[7] = def[8] = 3; // PIV retries
+ file_put_data(ef, def, sizeof(def));
+ }
+ }
+ if ((ef = search_by_fid(EF_PW_RETRIES, NULL, SPECIFY_ANY))) {
+ if (file_get_size(ef) == 0) {
+ printf("PW retries is empty. Initializing to default\r\n");
+ const uint8_t def[] = { 0x1, 3, 3, 3, 3, 3 };
+ file_put_data(ef, def, sizeof(def));
+ }
+ else if (file_get_size(ef) == 4) {
+ printf("PW retries is older. Initializing to default\r\n");
+ uint8_t def[6] = { 0 };
+ memcpy(def, file_get_data(ef), 4);
+ def[4] = def[5] = 3; // PIV retries
+ file_put_data(ef, def, sizeof(def));
+ }
+ }
+ bool reset_dek = false;
+ if ((ef = search_by_fid(EF_DEK, NULL, SPECIFY_ANY))) {
+ if (file_get_size(ef) == 0 || file_get_size(ef) == IV_SIZE+32*3) {
+ printf("DEK is empty or older\r\n");
+ const uint8_t defpin[6] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 };
+ const uint8_t *dek = random_bytes_get(IV_SIZE + 32);
+ uint8_t def[IV_SIZE + 32 + 32 + 32 + 32];
+ if (file_get_size(ef) > 0) {
+ memcpy(def, file_get_data(ef), file_get_size(ef));
+ }
+ else {
+ memcpy(def, dek, IV_SIZE);
+ }
+ memcpy(def + IV_SIZE + 32*3, dek + IV_SIZE, 32);
+ hash_multi(defpin, sizeof(defpin), session_pwpiv);
+ aes_encrypt_cfb_256(session_pwpiv, def, def + IV_SIZE + 32*3, 32);
+ file_put_data(ef, def, sizeof(def));
+
+ has_pwpiv = true;
+ uint8_t *key = (uint8_t *)"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08";
+ file_t *ef = search_by_fid(EF_PIV_KEY_CARDMGM, NULL, SPECIFY_ANY);
+ file_put_data(ef, key, 24);
+ uint8_t meta[] = { PIV_ALGO_AES192, PINPOLICY_ALWAYS, TOUCHPOLICY_ALWAYS, ORIGIN_GENERATED };
+ meta_add(EF_PIV_KEY_CARDMGM, meta, sizeof(meta));
+ has_pwpiv = false;
+ memset(session_pwpiv, 0, sizeof(session_pwpiv));
+
+ reset_dek = true;
+ }
+ }
+ if ((ef = search_by_fid(EF_PIV_PIN, NULL, SPECIFY_ANY))) {
+ if (!ef->data || reset_dek) {
+ printf("PIV PIN is empty. Initializing with default password\r\n");
+ const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF, 0xFF };
+ uint8_t dhash[33];
+ dhash[0] = sizeof(def);
+ double_hash_pin(def, sizeof(def), dhash + 1);
+ file_put_data(ef, dhash, sizeof(dhash));
+ }
+ }
+ if ((ef = search_by_fid(EF_PIV_PUK, NULL, SPECIFY_ANY))) {
+ if (!ef->data) {
+ printf("PIV PUK is empty. Initializing with default password\r\n");
+ const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
+ uint8_t dhash[33];
+ dhash[0] = sizeof(def);
+ double_hash_pin(def, sizeof(def), dhash + 1);
+ file_put_data(ef, dhash, sizeof(dhash));
+ }
+ }
+ if ((ef = search_by_fid(EF_PIV_KEY_ATTESTATION, NULL, SPECIFY_ANY))) {
+ if (!ef->data) {
+ printf("ATTESTATION key is empty. Initializing with random one\r\n");
+ mbedtls_ecdsa_context ecdsa;
+ mbedtls_ecdsa_init(&ecdsa);
+ int r = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP384R1, random_gen, NULL);
+ r = store_keys(&ecdsa, ALGO_ECDSA, EF_PIV_KEY_ATTESTATION, false);
+ uint8_t cert[2048];
+ r = x509_create_cert(&ecdsa, PIV_ALGO_ECCP384, EF_PIV_KEY_ATTESTATION, false, cert, sizeof(cert));
+ ef = search_by_fid(EF_PIV_ATTESTATION, NULL, SPECIFY_ANY);
+ file_put_data(ef, cert + sizeof(cert) - r, r);
+ mbedtls_ecdsa_free(&ecdsa);
+ }
+ }
+ low_flash_available();
+}
+
+void init_piv() {
+ scan_files();
+ has_pwpiv = false;
+ // cmd_select();
+}
+
+int piv_unload() {
+ return CCID_OK;
+}
+
+void select_piv_aid() {
+ res_APDU[res_APDU_size++] = 0x61;
+ res_APDU[res_APDU_size++] = 0; //filled later
+ res_APDU[res_APDU_size++] = 0x4F;
+ res_APDU[res_APDU_size++] = 2;
+ res_APDU[res_APDU_size++] = 0x01;
+ res_APDU[res_APDU_size++] = 0x00;
+ res_APDU[res_APDU_size++] = 0x79;
+ res_APDU[res_APDU_size++] = 9;
+ memcpy(res_APDU + res_APDU_size, "\xA0\x00\x00\x03\x08\x00\x00\x10\x00", 9);
+ res_APDU_size += 9;
+ const char *app_label = "Pico Keys PIV";
+ res_APDU[res_APDU_size++] = 0x50;
+ res_APDU[res_APDU_size++] = strlen(app_label);
+ memcpy(res_APDU + res_APDU_size, app_label, strlen(app_label));
+
+ res_APDU[res_APDU_size++] = 0xAC;
+ res_APDU[res_APDU_size++] = 12;
+ res_APDU[res_APDU_size++] = 0x80;
+ res_APDU[res_APDU_size++] = 7;
+ memcpy(res_APDU + res_APDU_size, "\x07\x08\x0A\x0C\x11\x14\x2E", 7);
+ res_APDU_size += 7;
+ res_APDU[res_APDU_size++] = 0x6;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = 0x00;
+}
+
+int piv_select_aid(app_t *a, uint8_t force) {
+ (void) force;
+ a->process_apdu = piv_process_apdu;
+ a->unload = piv_unload;
+ init_piv();
+ select_piv_aid();
+ return CCID_OK;
+}
+
+INITIALIZER( piv_ctor ) {
+ register_app(piv_select_aid, piv_aid);
+ register_app(piv_select_aid, yk_aid);
+}
+
+static int cmd_version() {
+ res_APDU[res_APDU_size++] = PIV_VERSION_MAJOR;
+ res_APDU[res_APDU_size++] = PIV_VERSION_MINOR;
+ res_APDU[res_APDU_size++] = 0x0;
+ return SW_OK();
+}
+
+static int cmd_select() {
+ if (P2(apdu) != 0x1) {
+ return SW_WRONG_P1P2();
+ }
+ if (memcmp(apdu.data, piv_aid, 5) == 0) {
+ select_piv_aid();
+ }
+ return SW_OK();
+}
+
+int piv_parse_discovery(const file_t *ef) {
+ memcpy(res_APDU, "\x7E\x12\x4F\x0B\xA0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00\x5F\x2F\x02\x40\x10", 20);
+ res_APDU_size = 20;
+ return res_APDU_size;
+}
+
+static int cmd_get_serial() {
+ uint32_t serial = get_serial();
+ res_APDU[res_APDU_size++] = serial >> 24;
+ res_APDU[res_APDU_size++] = serial >> 16;
+ res_APDU[res_APDU_size++] = serial >> 8;
+ res_APDU[res_APDU_size++] = serial & 0xFF;
+ return SW_OK();
+}
+
+extern int check_pin(const file_t *pin, const uint8_t *data, size_t len);
+static int cmd_verify() {
+ uint8_t key_ref = P2(apdu);
+ if (P1(apdu) != 0x00 && P1(apdu) != 0xFF) {
+ return SW_INCORRECT_P1P2();
+ }
+ if (key_ref != 0x80) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ file_t *pw, *pw_status;
+ uint16_t fid = EF_PIV_PIN;
+ 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) {
+ uint16_t ret = check_pin(pw, apdu.data, apdu.nc);
+ if (ret == 0x9000) {
+ has_pwpiv = true;
+ hash_multi(apdu.data, apdu.nc, session_pwpiv);
+ }
+ return ret; //SW already set
+ }
+ uint8_t retries = *(file_get_data(pw_status) + 3 + (fid & 0xf));
+ if (retries == 0) {
+ return SW_PIN_BLOCKED();
+ }
+ if ((key_ref == 0x80 && has_pwpiv)) {
+ return SW_OK();
+ }
+ return set_res_sw(0x63, 0xc0 | retries);
+}
+
+static int cmd_get_data() {
+ if (P1(apdu) != 0x3F || P2(apdu) != 0xFF) {
+ return SW_INCORRECT_P1P2();
+ }
+ if (apdu.data[0] != 0x5C || (apdu.data[1] & 0x80) || apdu.data[1] >= 4 || apdu.data[1] == 0) {
+ return SW_WRONG_DATA();
+ }
+ uint32_t fid = apdu.data[2];
+ for (uint8_t lt = 1; lt < apdu.data[1]; lt++) {
+ fid <<= 8;
+ fid |= apdu.data[2 + lt];
+ }
+ if ((fid & 0xFFFF00) != 0x5FC100 && (fid & 0xFFFF) != EF_PIV_BITGT && (fid & 0xFFFF) != EF_PIV_DISCOVERY && (fid & 0xFFFF) != EF_PIV_ATTESTATION) {
+ return SW_FILE_NOT_FOUND();
+ }
+ file_t *ef = NULL;
+ if ((ef = search_by_fid((uint16_t)(fid & 0xFFFF), NULL, SPECIFY_EF))) {
+ uint16_t data_len = 0;
+ res_APDU_size = 2; // Minimum: TAG+LEN
+ if ((ef->type & FILE_DATA_FUNC) == FILE_DATA_FUNC) {
+ data_len = ((int (*)(const file_t *))(ef->data))((const file_t *) ef);
+ }
+ else {
+ if (ef->data) {
+ data_len = file_get_size(ef);
+ memcpy(res_APDU + res_APDU_size, file_get_data(ef), data_len);
+ }
+ }
+ if (data_len == 0) {
+ return SW_FILE_NOT_FOUND();
+ }
+ if (data_len > 255) {
+ memmove(res_APDU + res_APDU_size + 2, res_APDU + res_APDU_size, data_len);
+ }
+ else if (data_len > 127) {
+ memmove(res_APDU + res_APDU_size + 1, res_APDU + res_APDU_size, data_len);
+ }
+ res_APDU[0] = 0x53;
+ res_APDU_size = 1 + format_tlv_len(data_len, res_APDU + 1) + data_len;
+ }
+ else {
+ return SW_FILE_NOT_FOUND();
+ }
+ return SW_OK();
+}
+
+static int cmd_get_metadata() {
+ if (P1(apdu) != 0x00) {
+ return SW_INCORRECT_P1P2();
+ }
+ uint8_t *meta = NULL;
+ uint16_t key_ref = P2(apdu);
+ if (key_ref == 0x80) {
+ key_ref = EF_PIV_PIN;
+ }
+ else if (key_ref == 0x81) {
+ key_ref = EF_PIV_PUK;
+ }
+ file_t *ef_key = search_by_fid(key_ref, NULL, SPECIFY_EF);
+ if (!file_has_data(ef_key)) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ if (key_ref != EF_PIV_PIN && key_ref != EF_PIV_PUK) {
+ int meta_len = 0;
+ if ((meta_len = meta_find(key_ref, &meta)) <= 0) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ res_APDU[res_APDU_size++] = 0x1;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = meta[0];
+ res_APDU[res_APDU_size++] = 0x2;
+ res_APDU[res_APDU_size++] = 2;
+ res_APDU[res_APDU_size++] = meta[1];
+ res_APDU[res_APDU_size++] = meta[2];
+ res_APDU[res_APDU_size++] = 0x3;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = meta[3];
+ if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048 || meta[0] == PIV_ALGO_RSA3072 || meta[0] == PIV_ALGO_RSA4096 || meta[0] == PIV_ALGO_ECCP256 || meta[0] == PIV_ALGO_ECCP384) {
+ res_APDU[res_APDU_size++] = 0x4;
+ res_APDU[res_APDU_size++] = 0; // Filled later
+ uint8_t *pk = &res_APDU[res_APDU_size];
+ if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048 || meta[0] == PIV_ALGO_RSA3072 || meta[0] == PIV_ALGO_RSA4096) {
+ mbedtls_rsa_context ctx;
+ mbedtls_rsa_init(&ctx);
+ int r = load_private_key_rsa(&ctx, ef_key, false);
+ if (r != CCID_OK) {
+ mbedtls_rsa_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ res_APDU[res_APDU_size++] = 0x81;
+ res_APDU[res_APDU_size++] = 0x82;
+ put_uint16_t(mbedtls_mpi_size(&ctx.N), res_APDU + res_APDU_size); res_APDU_size += 2;
+ mbedtls_mpi_write_binary(&ctx.N, res_APDU + res_APDU_size, mbedtls_mpi_size(&ctx.N));
+ res_APDU_size += mbedtls_mpi_size(&ctx.N);
+ res_APDU[res_APDU_size++] = 0x82;
+ res_APDU[res_APDU_size++] = mbedtls_mpi_size(&ctx.E) & 0xff;
+ mbedtls_mpi_write_binary(&ctx.E, res_APDU + res_APDU_size, mbedtls_mpi_size(&ctx.E));
+ res_APDU_size += mbedtls_mpi_size(&ctx.E);
+ mbedtls_rsa_free(&ctx);
+ }
+ else {
+ mbedtls_ecdsa_context ctx;
+ mbedtls_ecdsa_init(&ctx);
+ int r = load_private_key_ecdsa(&ctx, ef_key, false);
+ if (r != CCID_OK) {
+ mbedtls_ecdsa_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ uint8_t pt[MBEDTLS_ECP_MAX_PT_LEN];
+ size_t plen = 0;
+ mbedtls_ecp_point_write_binary(&ctx.grp, &ctx.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &plen, pt, sizeof(pt));
+ mbedtls_ecdsa_free(&ctx);
+ res_APDU[res_APDU_size++] = 0x86;
+ if (plen >= 128) {
+ res_APDU[res_APDU_size++] = 0x81;
+ }
+ res_APDU[res_APDU_size++] = plen;
+ memcpy(res_APDU + res_APDU_size, pt, plen);
+ res_APDU_size += plen;
+ }
+ uint16_t pk_len = res_APDU_size - (pk - res_APDU);
+ if (pk_len > 255) {
+ memmove(pk + 2, pk, pk_len);
+ pk[-1] = 0x82;
+ pk[0] = pk_len >> 8;
+ pk[1] = pk_len & 0xff;
+ res_APDU_size += 2;
+ }
+ else if (pk_len > 127) {
+ memmove(pk + 1, pk, pk_len);
+ pk[-1] = 0x81;
+ pk[0] = pk_len;
+ res_APDU_size += 1;
+ }
+ else {
+ pk[-1] = pk_len;
+ }
+ }
+ }
+ if (key_ref == EF_PIV_PIN || key_ref == EF_PIV_PUK || key_ref == EF_PIV_KEY_CARDMGM) {
+ uint8_t dhash[32];
+ int32_t eq = false;
+ if (key_ref == EF_PIV_PIN) {
+ double_hash_pin((const uint8_t *)"\x31\x32\x33\x34\x35\x36\xFF\xFF", 8, dhash);
+ eq = memcmp(dhash, file_get_data(ef_key) + 1, file_get_size(ef_key) - 1);
+ }
+ else if (key_ref == EF_PIV_PUK) {
+ double_hash_pin((const uint8_t *)"\x31\x32\x33\x34\x35\x36\x37\x38", 8, dhash);
+ eq = memcmp(dhash, file_get_data(ef_key) + 1, file_get_size(ef_key) - 1);
+ }
+ else if (key_ref == EF_PIV_KEY_CARDMGM) {
+ eq = memcmp("\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08", file_get_data(ef_key), file_get_size(ef_key));
+ }
+ res_APDU[res_APDU_size++] = 0x5;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size++] = eq;
+ if (key_ref == EF_PIV_PIN || key_ref == EF_PIV_PUK) {
+ file_t *pw_status;
+ if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF))) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ uint8_t retries = *(file_get_data(pw_status) + 3 + (key_ref & 0xf));
+ if (!(pw_status = search_by_fid(EF_PW_RETRIES, NULL, SPECIFY_EF))) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ uint8_t total = *(file_get_data(pw_status) + (key_ref & 0xf));
+ res_APDU[res_APDU_size++] = 0x6;
+ res_APDU[res_APDU_size++] = 2;
+ res_APDU[res_APDU_size++] = total;
+ res_APDU[res_APDU_size++] = retries;
+ }
+ }
+ return SW_OK();
+}
+uint8_t challenge[16];
+bool has_challenge = false;
+bool has_mgm = false;
+static int cmd_authenticate() {
+ uint8_t algo = P1(apdu), key_ref = P2(apdu);
+ if (apdu.nc == 0) {
+ return SW_WRONG_LENGTH();
+ }
+ if (apdu.data[0] != 0x7C) {
+ return SW_WRONG_DATA();
+ }
+ if (key_ref == EF_PIV_KEY_CARDMGM) {
+ if (algo != PIV_ALGO_AES128 && algo != PIV_ALGO_AES192 && algo != PIV_ALGO_AES256 && algo != PIV_ALGO_3DES) {
+ return SW_INCORRECT_P1P2();
+ }
+ file_t *ef_mgm = search_by_fid(key_ref, NULL, SPECIFY_EF);
+ if (!file_has_data(ef_mgm)) {
+ return SW_MEMORY_FAILURE();
+ }
+ uint16_t mgm_len = file_get_size(ef_mgm);
+ if ((algo == PIV_ALGO_AES128 && mgm_len != 16) || (algo == PIV_ALGO_AES192 && mgm_len != 24) || (algo == PIV_ALGO_AES256 && mgm_len != 32) || (algo == PIV_ALGO_3DES && mgm_len != 24)) {
+ return SW_INCORRECT_P1P2();
+ }
+ }
+ uint8_t *meta = NULL;
+ int meta_len = 0;
+ if ((meta_len = meta_find(key_ref, &meta)) <= 0) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ if (meta[1] == PINPOLICY_DEFAULT) {
+ if (key_ref == EF_PIV_KEY_SIGNATURE) {
+ meta[1] = PINPOLICY_ALWAYS;
+ }
+ else {
+ meta[1] = PINPOLICY_ONCE;
+ }
+ }
+ if ((meta[1] == PINPOLICY_ALWAYS || meta[1] == PINPOLICY_ONCE) && (!has_pwpiv && (key_ref == EF_PIV_KEY_AUTHENTICATION || key_ref == EF_PIV_KEY_SIGNATURE || key_ref == EF_PIV_KEY_KEYMGM || key_ref == EF_PIV_KEY_CARDAUTH || IS_RETIRED(key_ref)))) {
+ return SW_SECURITY_STATUS_NOT_SATISFIED();
+ }
+ uint8_t chal_len = (algo == PIV_ALGO_3DES ? sizeof(challenge) / 2 : sizeof(challenge));
+ asn1_ctx_t ctxi, a7c = { 0 };
+ asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
+ if (!asn1_find_tag(&ctxi, 0x7C, &a7c) || asn1_len(&ctxi) == 0) {
+ return SW_WRONG_DATA();
+ }
+ asn1_ctx_t a80 = { 0 }, a81 = { 0 }, a82 = { 0 };
+ asn1_find_tag(&a7c, 0x80, &a80);
+ asn1_find_tag(&a7c, 0x81, &a81);
+ asn1_find_tag(&a7c, 0x82, &a82);
+ if (a80.data) {
+ if (a80.len == 0) {
+ memcpy(challenge, random_bytes_get(sizeof(challenge)), sizeof(challenge));
+ if (algo == PIV_ALGO_AES128 || algo == PIV_ALGO_AES192 || algo == PIV_ALGO_AES256 || algo == PIV_ALGO_3DES) {
+ if (key_ref != EF_PIV_KEY_CARDMGM) {
+ return SW_INCORRECT_P1P2();
+ }
+ file_t *ef_mgm = search_by_fid(key_ref, NULL, SPECIFY_EF);
+ if (!file_has_data(ef_mgm)) {
+ return SW_MEMORY_FAILURE();
+ }
+ uint16_t mgm_len = file_get_size(ef_mgm);
+ res_APDU[res_APDU_size++] = 0x7C;
+ res_APDU[res_APDU_size++] = 18;
+ res_APDU[res_APDU_size++] = 0x80;
+ res_APDU[res_APDU_size++] = 16;
+ int r = 0;
+ if (algo == PIV_ALGO_3DES) {
+ mbedtls_des3_context ctx;
+ mbedtls_des3_init(&ctx);
+ r = mbedtls_des3_set3key_enc(&ctx, file_get_data(ef_mgm));
+ if (r != 0) {
+ mbedtls_des3_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ r = mbedtls_des3_crypt_ecb(&ctx, challenge, res_APDU + res_APDU_size);
+ res_APDU_size += 8;
+ mbedtls_des3_free(&ctx);
+ }
+ else {
+ mbedtls_aes_context ctx;
+ mbedtls_aes_init(&ctx);
+ r = mbedtls_aes_setkey_enc(&ctx, file_get_data(ef_mgm), mgm_len * 8);
+ if (r != 0) {
+ mbedtls_aes_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ r = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, challenge, res_APDU + res_APDU_size);
+ res_APDU_size += 16;
+ mbedtls_aes_free(&ctx);
+ }
+ if (r != 0) {
+ return SW_EXEC_ERROR();
+ }
+ has_challenge = true;
+ }
+ }
+ else {
+ if (!has_challenge) {
+ return SW_INCORRECT_PARAMS();
+ }
+ if (!asn1_len(&a81)) {
+ return SW_INCORRECT_PARAMS();
+ }
+ if (key_ref != EF_PIV_KEY_CARDMGM) {
+ return SW_INCORRECT_P1P2();
+ }
+ if (memcmp(a80.data, challenge, a80.len) == 0) {
+ has_mgm = true;
+ }
+ }
+ }
+ if (a81.data) {
+ if (!a81.len) {
+ memcpy(challenge, random_bytes_get(sizeof(challenge)), sizeof(challenge));
+ res_APDU[res_APDU_size++] = 0x7C;
+ res_APDU[res_APDU_size++] = chal_len + 2;
+ res_APDU[res_APDU_size++] = 0x81;
+ res_APDU[res_APDU_size++] = chal_len;
+ memcpy(res_APDU + res_APDU_size, challenge, chal_len);
+ res_APDU_size += chal_len;
+ has_challenge = true;
+ }
+ else {
+ file_t *ef_key = search_by_fid(key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, NULL, SPECIFY_EF);
+ if (!file_has_data(ef_key)) {
+ return SW_MEMORY_FAILURE();
+ }
+ if (algo == PIV_ALGO_RSA1024 || algo == PIV_ALGO_RSA2048 || algo == PIV_ALGO_RSA3072 || algo == PIV_ALGO_RSA4096) {
+ mbedtls_rsa_context ctx;
+ mbedtls_rsa_init(&ctx);
+ int r = load_private_key_rsa(&ctx, ef_key, false);
+ if (r != CCID_OK) {
+ mbedtls_rsa_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ size_t olen = file_get_size(ef_key);
+ if (algo == PIV_ALGO_RSA1024) {
+ memcpy(res_APDU, "\x7C\x81\x00\x82\x81\x00", 6);
+ res_APDU_size = 6;
+ }
+ else {
+ memcpy(res_APDU, "\x7C\x82\x00\x00\x82\x82\x00\x00", 8);
+ res_APDU_size = 8;
+ }
+ r = mbedtls_rsa_private(&ctx, random_gen, NULL, a81.data, res_APDU + res_APDU_size);
+ mbedtls_rsa_free(&ctx);
+ if (algo == PIV_ALGO_RSA1024) {
+ res_APDU[res_APDU_size - 1] = olen;
+ res_APDU[res_APDU_size - 4] = olen + 3;
+ }
+ else {
+ res_APDU[res_APDU_size - 2] = olen >> 8;
+ res_APDU[res_APDU_size - 1] = olen & 0xFF;
+ res_APDU[res_APDU_size - 6] = (olen + 4) >> 8;
+ res_APDU[res_APDU_size - 5] = (olen + 4) & 0xFF;
+ }
+ res_APDU_size += olen;
+ if (r != 0) {
+ return SW_EXEC_ERROR();
+ }
+ }
+ else if (algo == PIV_ALGO_ECCP256 || algo == PIV_ALGO_ECCP384) {
+ mbedtls_ecdsa_context ctx;
+ mbedtls_ecdsa_init(&ctx);
+ int r = load_private_key_ecdsa(&ctx, ef_key, false);
+ if (r != CCID_OK) {
+ mbedtls_ecdsa_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ size_t olen = 0;
+ memcpy(res_APDU, "\x7C\x00\x82\x00", 4);
+ res_APDU_size = 4;
+ r = mbedtls_ecdsa_write_signature(&ctx, algo == PIV_ALGO_ECCP256 ? MBEDTLS_MD_SHA256 : MBEDTLS_MD_SHA384, a81.data, a81.len, res_APDU + res_APDU_size, MBEDTLS_ECDSA_MAX_LEN, &olen, random_gen, NULL);
+ mbedtls_ecdsa_free(&ctx);
+ res_APDU[res_APDU_size - 1] = olen;
+ res_APDU[res_APDU_size - 3] = olen + 2;
+ res_APDU_size += olen;
+ if (r != 0) {
+ return SW_EXEC_ERROR();
+ }
+ }
+ else if (algo == PIV_ALGO_AES128 || algo == PIV_ALGO_AES192 || algo == PIV_ALGO_AES256 || algo == PIV_ALGO_3DES) {
+ uint16_t key_len = file_get_size(ef_key);
+ memcpy(res_APDU, "\x7C\x12\x82\x10", 4);
+ res_APDU_size = 4;
+ int r = 0;
+ if (algo == PIV_ALGO_3DES) {
+ if (a81.len % 8 != 0) {
+ return SW_DATA_INVALID();
+ }
+ mbedtls_des3_context ctx;
+ mbedtls_des3_init(&ctx);
+ r = mbedtls_des3_set3key_enc(&ctx, file_get_data(ef_key));
+ if (r != 0) {
+ mbedtls_des3_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ r = mbedtls_des3_crypt_ecb(&ctx, a81.data, res_APDU + res_APDU_size);
+ mbedtls_des3_free(&ctx);
+ res_APDU_size += 8;
+ }
+ else {
+ if (a81.len % 16 != 0) {
+ return SW_DATA_INVALID();
+ }
+ mbedtls_aes_context ctx;
+ mbedtls_aes_init(&ctx);
+ r = mbedtls_aes_setkey_enc(&ctx, file_get_data(ef_key), key_len * 8);
+ if (r != 0) {
+ mbedtls_aes_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ r = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, a81.data, res_APDU + res_APDU_size);
+ mbedtls_aes_free(&ctx);
+ res_APDU_size += 16;
+ }
+ if (r != 0) {
+ return SW_EXEC_ERROR();
+ }
+ }
+ }
+ }
+ if (a82.data) {
+ if (!a82.len) {
+ // Should be handled by a81 or a80
+ }
+ else {
+ if (key_ref != EF_PIV_KEY_CARDMGM) {
+ return SW_INCORRECT_P1P2();
+ }
+ if (!has_challenge) {
+ return SW_INCORRECT_PARAMS();
+ }
+ if (chal_len != a82.len) {
+ return SW_DATA_INVALID();
+ }
+ file_t *ef_key = search_by_fid(key_ref, NULL, SPECIFY_EF);
+ if (!file_has_data(ef_key)) {
+ return SW_MEMORY_FAILURE();
+ }
+ uint16_t key_len = file_get_size(ef_key);
+ int r = 0;
+ if (algo == PIV_ALGO_3DES)
+ {
+ mbedtls_des3_context ctx;
+ mbedtls_des3_init(&ctx);
+ r = mbedtls_des3_set3key_dec(&ctx, file_get_data(ef_key));
+ if (r != 0) {
+ mbedtls_des3_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ r = mbedtls_des3_crypt_ecb(&ctx, a82.data, res_APDU);
+ mbedtls_des3_free(&ctx);
+ }
+ else {
+ mbedtls_aes_context ctx;
+ mbedtls_aes_init(&ctx);
+ r = mbedtls_aes_setkey_dec(&ctx, file_get_data(ef_key), key_len * 8);
+ if (r != 0) {
+ mbedtls_aes_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ r = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_DECRYPT, a82.data, res_APDU);
+ mbedtls_aes_free(&ctx);
+ }
+ if (r != 0) {
+ return SW_EXEC_ERROR();
+ }
+ if (memcmp(res_APDU, challenge, chal_len) != 0) {
+ return SW_DATA_INVALID();
+ }
+ }
+ }
+ if (meta[1] == PINPOLICY_ALWAYS) {
+ has_pwpiv = false;
+ }
+ return SW_OK();
+}
+
+static int cmd_asym_keygen() {
+ uint8_t key_ref = P2(apdu);
+ if (apdu.nc == 0) {
+ return SW_WRONG_LENGTH();
+ }
+ if (apdu.data[0] != 0xAC) {
+ return SW_WRONG_DATA();
+ }
+ if (P1(apdu) != 0x0) {
+ return SW_INCORRECT_P1P2();
+ }
+ if (!has_mgm) {
+ return SW_SECURITY_STATUS_NOT_SATISFIED();
+ }
+ if (key_ref != EF_PIV_KEY_AUTHENTICATION && key_ref != EF_PIV_KEY_SIGNATURE && key_ref != EF_PIV_KEY_KEYMGM && key_ref != EF_PIV_KEY_CARDAUTH && !(key_ref >= EF_PIV_KEY_RETIRED1 && key_ref <= EF_PIV_KEY_RETIRED20)) {
+ return SW_INCORRECT_P1P2();
+ }
+ asn1_ctx_t ctxi, aac = {0};
+ asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
+ if (!asn1_find_tag(&ctxi, 0xAC, &aac) || asn1_len(&aac) == 0) {
+ return SW_WRONG_DATA();
+ }
+ asn1_ctx_t a80 = {0}, aaa = {0}, aab = {0};
+ asn1_find_tag(&aac, 0x80, &a80);
+ asn1_find_tag(&aac, 0xAA, &aaa);
+ asn1_find_tag(&aac, 0xAB, &aab);
+ if (asn1_len(&a80) == 0) {
+ return SW_WRONG_DATA();
+ }
+ uint16_t key_cert = 0;
+ if (key_ref == EF_PIV_KEY_AUTHENTICATION) {
+ key_cert = EF_PIV_AUTHENTICATION;
+ }
+ else if (key_ref == EF_PIV_KEY_SIGNATURE) {
+ key_cert = EF_PIV_SIGNATURE;
+ }
+ else if (key_ref == EF_PIV_KEY_KEYMGM) {
+ key_cert = EF_PIV_KEY_MANAGEMENT;
+ }
+ else if (key_ref == EF_PIV_KEY_CARDAUTH) {
+ key_cert = EF_PIV_CARD_AUTH;
+ }
+ else {
+ key_cert = key_ref + 0xC08B;
+ }
+ if (a80.data[0] == PIV_ALGO_RSA1024 || a80.data[0] == PIV_ALGO_RSA2048) {
+ printf("KEYPAIR RSA\r\n");
+ asn1_ctx_t a81 = {0};
+ asn1_find_tag(&aac, 0x81, &a81);
+ mbedtls_rsa_context rsa;
+ mbedtls_rsa_init(&rsa);
+ int exponent = 65537, nlen = (a80.data[0] == PIV_ALGO_RSA1024 ? 1024 : 2048);
+ if (asn1_len(&a81)) {
+ exponent = (int)asn1_get_uint(&a81);
+ }
+ int r = mbedtls_rsa_gen_key(&rsa, random_gen, NULL, nlen, exponent);
+ if (r != 0) {
+ mbedtls_rsa_free(&rsa);
+ return SW_EXEC_ERROR();
+ }
+ make_rsa_response(&rsa);
+ uint8_t cert[2048];
+ r = x509_create_cert(&rsa, a80.data[0], key_ref, false, cert, sizeof(cert));
+ file_t *ef = search_by_fid(key_cert, NULL, SPECIFY_ANY);
+ file_put_data(ef, cert + sizeof(cert) - r, r);
+ r = store_keys(&rsa, ALGO_RSA, key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, false);
+ mbedtls_rsa_free(&rsa);
+ if (r != CCID_OK) {
+ return SW_EXEC_ERROR();
+ }
+ }
+ else if (a80.data[0] == PIV_ALGO_ECCP256 || a80.data[0] == PIV_ALGO_ECCP384) {
+ printf("KEYPAIR ECDSA\r\n");
+ mbedtls_ecp_group_id gid = a80.data[0] == PIV_ALGO_ECCP256 ? MBEDTLS_ECP_DP_SECP256R1 : MBEDTLS_ECP_DP_SECP384R1;
+ mbedtls_ecdsa_context ecdsa;
+ mbedtls_ecdsa_init(&ecdsa);
+ int r = mbedtls_ecdsa_genkey(&ecdsa, gid, random_gen, NULL);
+ if (r != 0) {
+ mbedtls_ecdsa_free(&ecdsa);
+ return SW_EXEC_ERROR();
+ }
+ make_ecdsa_response(&ecdsa);
+ uint8_t cert[2048];
+ r = x509_create_cert(&ecdsa, a80.data[0], key_ref, false, cert, sizeof(cert));
+ file_t *ef = search_by_fid(key_cert, NULL, SPECIFY_ANY);
+ file_put_data(ef, cert + sizeof(cert) - r, r);
+ r = store_keys(&ecdsa, ALGO_ECDSA, key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, false);
+ mbedtls_ecdsa_free(&ecdsa);
+ if (r != CCID_OK) {
+ return SW_EXEC_ERROR();
+ }
+ }
+ else if (a80.data[0] == PIV_ALGO_X25519) {
+ }
+ else {
+ return SW_DATA_INVALID();
+ }
+ uint8_t def_pinpol = PINPOLICY_ONCE;
+ if (key_ref == EF_PIV_KEY_SIGNATURE) {
+ def_pinpol = PINPOLICY_ALWAYS;
+ }
+ uint8_t meta[] = {a80.data[0], asn1_len(&aaa) ? aaa.data[0] : def_pinpol, asn1_len(&aab) ? aab.data[0] : TOUCHPOLICY_ALWAYS, ORIGIN_GENERATED};
+ meta_add(key_ref, meta, sizeof(meta));
+ low_flash_available();
+ return SW_OK();
+}
+
+int cmd_put_data() {
+ if (P1(apdu) != 0x3F || P2(apdu) != 0xFF) {
+ return SW_INCORRECT_P1P2();
+ }
+ asn1_ctx_t ctxi, a5c = {0}, a53 = {0};
+ asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
+ if (apdu.data[0] != 0x7E && apdu.data[0] != 0x7F && (!asn1_find_tag(&ctxi, 0x5C, &a5c) || !asn1_find_tag(&ctxi, 0x53, &a53))) {
+ return SW_WRONG_DATA();
+ }
+ if (a5c.data && a53.data) {
+ if (a5c.len != 3 || a5c.data[0] != 0x5F || a5c.data[1] != 0xC1) {
+ return SW_WRONG_DATA();
+ }
+ uint16_t fid = (a5c.data[1] << 8 | a5c.data[2]);
+ file_t *ef = search_by_fid(fid, NULL, SPECIFY_EF);
+ if (!ef) {
+ return SW_MEMORY_FAILURE();
+ }
+ if (a53.len > 0) {
+ file_put_data(ef, a53.data, a53.len);
+ }
+ else {
+ flash_clear_file(ef);
+ }
+ low_flash_available();
+ }
+ return SW_OK();
+}
+
+static int cmd_set_mgmkey() {
+ if (P1(apdu) != 0xFF) {
+ return SW_INCORRECT_P1P2();
+ }
+ if (apdu.nc < 5) {
+ return SW_WRONG_LENGTH();
+ }
+ uint8_t touch = P2(apdu);
+ if (touch != 0xFF && touch != 0xFE) {
+ if (touch == 0xFF) {
+ touch = TOUCHPOLICY_NEVER;
+ }
+ else if (touch == 0xFE) {
+ touch = TOUCHPOLICY_ALWAYS;
+ }
+ }
+ uint8_t algo = apdu.data[0], key_ref = apdu.data[1], pinlen = apdu.data[2];
+ if ((key_ref != EF_PIV_KEY_CARDMGM) || (!(algo == PIV_ALGO_AES128 && pinlen == 16) && !(algo == PIV_ALGO_AES192 && pinlen == 24) && !(algo == PIV_ALGO_AES256 && pinlen == 32) && !(algo == PIV_ALGO_3DES && pinlen == 24))) {
+ return SW_WRONG_DATA();
+ }
+ file_t *ef = search_by_fid(key_ref, NULL, SPECIFY_ANY);
+ file_put_data(ef, apdu.data + 3, pinlen);
+ uint8_t *meta = NULL, new_meta[4];
+ int meta_len = 0;
+ if ((meta_len = meta_find(key_ref, &meta)) <= 0) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ memcpy(new_meta, meta, 4);
+ new_meta[0] = algo;
+ new_meta[2] = touch;
+ meta_add(key_ref, new_meta, sizeof(new_meta));
+ low_flash_available();
+ return SW_OK();
+}
+
+static int cmd_move_key() {
+ if (apdu.nc != 0) {
+ return SW_WRONG_LENGTH();
+ }
+ uint8_t to = P1(apdu), from = P2(apdu);
+ if ((!IS_KEY(to) && to != 0xFF) || !IS_KEY(from)) {
+ return SW_INCORRECT_P1P2();
+ }
+ if (from == 0x93) {
+ from = EF_PIV_KEY_RETIRED18;
+ }
+ if (to == 0x93) {
+ to = EF_PIV_KEY_RETIRED18;
+ }
+ file_t *efs, *efd;
+ if (!(efs = search_by_fid(from, NULL, SPECIFY_EF)) || (!(efd = search_by_fid(to, NULL, SPECIFY_EF)) && to != 0xFF)) {
+ return SW_FILE_NOT_FOUND();
+ }
+ if (to != 0xFF) {
+ file_put_data(efd, file_get_data(efs), file_get_size(efs));
+ }
+ flash_clear_file(efs);
+ low_flash_available();
+ return SW_OK();
+}
+
+static int cmd_change_pin() {
+ uint8_t pin_ref = P2(apdu);
+ if (P1(apdu) != 0x0 || (pin_ref != 0x80 && pin_ref != 0x81)) {
+ return SW_INCORRECT_P1P2();
+ }
+ file_t *ef = search_by_fid(pin_ref == 0x80 ? EF_PIV_PIN : EF_PIV_PUK, NULL, SPECIFY_ANY);
+ if (!ef) {
+ return SW_MEMORY_FAILURE();
+ }
+ uint8_t *pin_data = file_get_data(ef), pin_len = apdu.nc - pin_data[0];
+ uint16_t ret = check_pin(ef, apdu.data, pin_data[0]);
+ if (ret != 0x9000) {
+ return ret;
+ }
+ uint8_t dhash[33];
+ dhash[0] = pin_len;
+ double_hash_pin(apdu.data + pin_data[0], pin_len, dhash + 1);
+ file_put_data(ef, dhash, sizeof(dhash));
+ low_flash_available();
+ return SW_OK();
+}
+
+static int cmd_reset_retry() {
+ if (P1(apdu) != 0x0 || P2(apdu) != 0x80) {
+ return SW_INCORRECT_P1P2();
+ }
+ file_t *ef = search_by_fid(EF_PIV_PUK, NULL, SPECIFY_ANY);
+ if (!ef) {
+ return SW_MEMORY_FAILURE();
+ }
+ uint8_t *puk_data = file_get_data(ef), pin_len = apdu.nc - puk_data[0];
+ uint16_t ret = check_pin(ef, apdu.data, puk_data[0]);
+ if (ret != 0x9000) {
+ return ret;
+ }
+ uint8_t dhash[33];
+ dhash[0] = pin_len;
+ double_hash_pin(apdu.data + puk_data[0], pin_len, dhash + 1);
+ ef = search_by_fid(EF_PIV_PIN, NULL, SPECIFY_ANY);
+ file_put_data(ef, dhash, sizeof(dhash));
+ pin_reset_retries(ef, true);
+ low_flash_available();
+ return SW_OK();
+}
+
+static int cmd_set_retries() {
+ file_t *ef = search_by_fid(EF_PW_RETRIES, NULL, SPECIFY_ANY);
+ if (!ef) {
+ return SW_MEMORY_FAILURE();
+ }
+ uint8_t *tmp = (uint8_t *)calloc(1, file_get_size(ef));
+ memcpy(tmp, file_get_data(ef), file_get_size(ef));
+ tmp[4] = P1(apdu);
+ tmp[5] = P2(apdu);
+ file_put_data(ef, tmp, file_get_size(ef));
+ free(tmp);
+
+ ef = search_by_fid(EF_PIV_PIN, NULL, SPECIFY_ANY);
+ const uint8_t def_pin[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF, 0xFF };
+ uint8_t dhash[33];
+ dhash[0] = sizeof(def_pin);
+ double_hash_pin(def_pin, sizeof(def_pin), dhash + 1);
+ file_put_data(ef, dhash, sizeof(dhash));
+ pin_reset_retries(ef, true);
+
+ ef = search_by_fid(EF_PIV_PUK, NULL, SPECIFY_ANY);
+ const uint8_t def_puk[8] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38};
+ dhash[0] = sizeof(def_puk);
+ double_hash_pin(def_puk, sizeof(def_puk), dhash + 1);
+ file_put_data(ef, dhash, sizeof(dhash));
+ pin_reset_retries(ef, true);
+
+ low_flash_available();
+ return SW_OK();
+}
+
+static int cmd_reset() {
+ if (P1(apdu) != 0x0 || P2(apdu) != 0x0) {
+ return SW_INCORRECT_P1P2();
+ }
+ file_t *pw_status;
+ if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF)))
+ {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ uint8_t retPIN = *(file_get_data(pw_status) + 3 + (EF_PIV_PIN & 0xf)), retPUK = *(file_get_data(pw_status) + 3 + (EF_PIV_PUK & 0xf));
+ if (retPIN != 0 || retPUK != 0) {
+ return SW_INCORRECT_PARAMS();
+ }
+ initialize_flash(true);
+ low_flash_available();
+ init_piv();
+ return SW_OK();
+}
+
+static int cmd_attestation() {
+ uint8_t key_ref = P1(apdu);
+ if (P2(apdu) != 0x00) {
+ return SW_INCORRECT_P1P2();
+ }
+ if (!IS_KEY(key_ref)) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ file_t *ef_key = NULL;
+ int meta_len = 0;
+ uint8_t *meta = NULL;
+ if (!(ef_key = search_by_fid(key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, NULL, SPECIFY_EF)) || !file_has_data(ef_key)) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ if ((meta_len = meta_find(key_ref, &meta)) <= 0) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ if (meta[3] != ORIGIN_GENERATED) {
+ return SW_INCORRECT_PARAMS();
+ }
+ int r = 0;
+ if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048) {
+ mbedtls_rsa_context ctx;
+ mbedtls_rsa_init(&ctx);
+ r = load_private_key_rsa(&ctx, ef_key, false);
+ if (r != CCID_OK) {
+ mbedtls_rsa_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ r = x509_create_cert(&ctx, meta[0], key_ref, true, res_APDU, 2048);
+ mbedtls_rsa_free(&ctx);
+ }
+ else if (meta[0] == PIV_ALGO_ECCP256 || meta[0] == PIV_ALGO_ECCP384) {
+ mbedtls_ecdsa_context ctx;
+ mbedtls_ecdsa_init(&ctx);
+ r = load_private_key_ecdsa(&ctx, ef_key, false);
+ if (r != CCID_OK) {
+ mbedtls_ecdsa_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ r = x509_create_cert(&ctx, meta[0], key_ref, true, res_APDU, 2048);
+ mbedtls_ecdsa_free(&ctx);
+ }
+ else {
+ return SW_WRONG_DATA();
+ }
+ if (r <= 0) {
+ return SW_EXEC_ERROR();
+ }
+ memmove(res_APDU, res_APDU + 2048 - r, r);
+ res_APDU_size = r;
+ return SW_OK();
+}
+
+static int cmd_import_asym() {
+ uint8_t algo = P1(apdu), key_ref = P2(apdu);
+ if (key_ref != EF_PIV_KEY_AUTHENTICATION && key_ref != EF_PIV_KEY_SIGNATURE && key_ref != EF_PIV_KEY_KEYMGM && key_ref != EF_PIV_KEY_CARDAUTH && !(key_ref >= EF_PIV_KEY_RETIRED1 && key_ref <= EF_PIV_KEY_RETIRED20)) {
+ return SW_INCORRECT_P1P2();
+ }
+ asn1_ctx_t ctxi, aaa = {0}, aab = {0};
+ asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
+ asn1_find_tag(&ctxi, 0xAA, &aaa);
+ asn1_find_tag(&ctxi, 0xAB, &aab);
+ if (algo == PIV_ALGO_RSA1024 || algo == PIV_ALGO_RSA2048 || algo == PIV_ALGO_RSA3072 || algo == PIV_ALGO_RSA4096) {
+ asn1_ctx_t a1 = { 0 }, a2 = { 0 };
+ asn1_find_tag(&ctxi, 0x01, &a1);
+ asn1_find_tag(&ctxi, 0x02, &a2);
+ if (asn1_len(&a1) <= 0 || asn1_len(&a2) <= 0) {
+ return SW_WRONG_DATA();
+ }
+ mbedtls_rsa_context rsa;
+ mbedtls_rsa_init(&rsa);
+ int r = mbedtls_mpi_read_binary(&rsa.P, a1.data, a1.len);
+ if (r != 0) {
+ mbedtls_rsa_free(&rsa);
+ return SW_WRONG_DATA();
+ }
+ r = mbedtls_mpi_read_binary(&rsa.Q, a2.data, a2.len);
+ if (r != 0) {
+ mbedtls_rsa_free(&rsa);
+ return SW_WRONG_DATA();
+ }
+ int exponent = 65537;
+ mbedtls_mpi_lset(&rsa.E, exponent);
+ 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, key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, false);
+ mbedtls_rsa_free(&rsa);
+ if (r != 0) {
+ return SW_EXEC_ERROR();
+ }
+ }
+ else if (algo == PIV_ALGO_ECCP256 || algo == PIV_ALGO_ECCP384) {
+ asn1_ctx_t a6 = {0};
+ asn1_find_tag(&ctxi, 0x06, &a6);
+ if (asn1_len(&a6) <= 0) {
+ return SW_WRONG_DATA();
+ }
+ mbedtls_ecp_group_id gid = algo == PIV_ALGO_ECCP256 ? MBEDTLS_ECP_DP_SECP256R1 : MBEDTLS_ECP_DP_SECP384R1;
+ mbedtls_ecdsa_context ecdsa;
+ mbedtls_ecdsa_init(&ecdsa);
+ int r = mbedtls_ecp_read_key(gid, &ecdsa, a6.data, a6.len);
+ 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 = mbedtls_ecp_check_pub_priv(&ecdsa, &ecdsa, random_gen, NULL);
+ if (r != 0) {
+ mbedtls_ecdsa_free(&ecdsa);
+ return SW_EXEC_ERROR();
+ }
+ r = store_keys(&ecdsa, ALGO_ECDSA, key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, false);
+ mbedtls_ecdsa_free(&ecdsa);
+ if (r != 0) {
+ return SW_EXEC_ERROR();
+ }
+ }
+ else {
+ return SW_WRONG_DATA();
+ }
+ uint8_t def_pinpol = PINPOLICY_ONCE;
+ if (key_ref == EF_PIV_KEY_SIGNATURE) {
+ def_pinpol = PINPOLICY_ALWAYS;
+ }
+ uint8_t meta[] = { algo, asn1_len(&aaa) ? aaa.data[0] : def_pinpol, asn1_len(&aab) ? aab.data[0] : TOUCHPOLICY_ALWAYS, ORIGIN_IMPORTED };
+ meta_add(key_ref, meta, sizeof(meta));
+ return SW_OK();
+}
+
+#define INS_VERIFY 0x20
+#define INS_VERSION 0xFD
+#define INS_SELECT 0xA4
+#define INS_YK_SERIAL 0xF8
+#define INS_VERIFY 0x20
+#define INS_GET_DATA 0xCB
+#define INS_GET_METADATA 0xF7
+#define INS_AUTHENTICATE 0x87
+#define INS_ASYM_KEYGEN 0x47
+#define INS_PUT_DATA 0xDB
+#define INS_SET_MGMKEY 0xFF
+#define INS_MOVE_KEY 0xF6
+#define INS_CHANGE_PIN 0x24
+#define INS_RESET_RETRY 0x2C
+#define INS_SET_RETRIES 0xFA
+#define INS_RESET 0xFB
+#define INS_ATTESTATION 0xF9
+#define INS_IMPORT_ASYM 0xFE
+
+static const cmd_t cmds[] = {
+ { INS_VERSION, cmd_version },
+ { INS_SELECT, cmd_select },
+ { INS_YK_SERIAL, cmd_get_serial },
+ { INS_VERIFY, cmd_verify },
+ { INS_GET_DATA, cmd_get_data },
+ { INS_GET_METADATA, cmd_get_metadata },
+ { INS_AUTHENTICATE, cmd_authenticate },
+ { INS_ASYM_KEYGEN, cmd_asym_keygen },
+ { INS_PUT_DATA, cmd_put_data },
+ { INS_SET_MGMKEY, cmd_set_mgmkey },
+ { INS_MOVE_KEY, cmd_move_key },
+ { INS_CHANGE_PIN, cmd_change_pin },
+ { INS_RESET_RETRY, cmd_reset_retry },
+ { INS_SET_RETRIES, cmd_set_retries },
+ { INS_RESET, cmd_reset },
+ { INS_ATTESTATION, cmd_attestation },
+ { INS_IMPORT_ASYM, cmd_import_asym },
+ { 0x00, 0x0 }
+};
+
+int piv_process_apdu() {
+ sm_unwrap();
+ for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
+ if (cmd->ins == INS(apdu)) {
+ int r = cmd->cmd_handler();
+ sm_wrap();
+ return r;
+ }
+ }
+ return SW_INS_NOT_SUPPORTED();
+}
diff --git a/src/openpgp/version.h b/src/openpgp/version.h
index cb1a172..818e948 100644
--- a/src/openpgp/version.h
+++ b/src/openpgp/version.h
@@ -24,9 +24,14 @@
#define OPGP_VERSION_MINOR (OPGP_VERSION & 0xff)
-#define PIPGP_VERSION 0x010A
+#define PIPGP_VERSION 0x0202
#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
diff --git a/tests/docker/bullseye/Dockerfile b/tests/docker/bullseye/Dockerfile
new file mode 100644
index 0000000..60a1c0d
--- /dev/null
+++ b/tests/docker/bullseye/Dockerfile
@@ -0,0 +1,43 @@
+FROM debian:bullseye
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+RUN apt update && apt upgrade -y
+RUN apt install -y apt-utils
+RUN apt autoremove -y
+RUN rm -rf /var/cache/apt/archives/*
+RUN apt install -y libccid \
+ libpcsclite-dev \
+ git \
+ autoconf \
+ pkg-config \
+ libtool \
+ help2man \
+ automake \
+ gcc \
+ make \
+ build-essential \
+ opensc \
+ python3 \
+ python3-pip \
+ swig \
+ cmake \
+ vsmartcard-vpcd \
+ libgcrypt-dev \
+ libssl-dev \
+ check \
+ gengetopt \
+ && rm -rf /var/lib/apt/lists/*
+RUN pip3 install pytest pycvc cryptography pyscard
+RUN git clone https://github.com/Yubico/yubico-piv-tool
+WORKDIR /yubico-piv-tool
+RUN git checkout tags/yubico-piv-tool-2.5.1
+ADD tests/docker/jammy/yubico-piv-tool.patch /yubico-piv-tool/yubico-piv-tool.patch
+RUN git apply yubico-piv-tool.patch
+RUN mkdir build
+WORKDIR /yubico-piv-tool/build
+RUN cmake .. -DENABLE_HARDWARE_TESTS=1
+RUN make -j`nproc`
+RUN make install
+WORKDIR /
+RUN ldconfig
diff --git a/tests/docker/jammy/Dockerfile b/tests/docker/jammy/Dockerfile
index b6d1092..677968e 100644
--- a/tests/docker/jammy/Dockerfile
+++ b/tests/docker/jammy/Dockerfile
@@ -22,6 +22,20 @@ RUN apt install -y libccid \
cmake \
vsmartcard-vpcd \
libgcrypt-dev \
+ libssl-dev \
+ check \
+ gengetopt \
&& rm -rf /var/lib/apt/lists/*
RUN pip3 install pytest pycvc cryptography pyscard
+RUN git clone https://github.com/Yubico/yubico-piv-tool
+WORKDIR /yubico-piv-tool
+RUN git checkout tags/yubico-piv-tool-2.5.1
+ADD tests/docker/jammy/yubico-piv-tool.patch /yubico-piv-tool/yubico-piv-tool.patch
+RUN git apply yubico-piv-tool.patch
+RUN mkdir build
+WORKDIR /yubico-piv-tool/build
+RUN cmake .. -DENABLE_HARDWARE_TESTS=1
+RUN make -j`nproc`
+RUN make install
WORKDIR /
+RUN ldconfig
diff --git a/tests/docker/jammy/yubico-piv-tool.patch b/tests/docker/jammy/yubico-piv-tool.patch
new file mode 100644
index 0000000..028cdf0
--- /dev/null
+++ b/tests/docker/jammy/yubico-piv-tool.patch
@@ -0,0 +1,68 @@
+diff --git a/lib/tests/api.c b/lib/tests/api.c
+index fb7c1a8..b569ec3 100644
+--- a/lib/tests/api.c
++++ b/lib/tests/api.c
+@@ -515,7 +515,7 @@ START_TEST(test_pin_policy_always) {
+ unsigned char rand[128] = {0};
+
+ size_t sig_len = sizeof(signature);
+- size_t padlen = 256;
++ size_t padlen = 512;
+ unsigned int enc_len;
+ unsigned int data_len;
+
+@@ -1009,8 +1009,8 @@ END_TEST
+ START_TEST(test_pin_cache) {
+ ykpiv_rc res;
+ ykpiv_state *local_state;
+- unsigned char data[256] = {0};
+- unsigned char data_in[256] = {0};
++ unsigned char data[512] = {0};
++ unsigned char data_in[512] = {0};
+ int len = sizeof(data);
+ size_t len2 = sizeof(data);
+
+@@ -1028,17 +1028,17 @@ START_TEST(test_pin_cache) {
+ ck_assert_int_eq(res, YKPIV_OK);
+
+ // Verify decryption does not work without auth
+- res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
++ res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA4096, 0x9a);
+ ck_assert_int_eq(res, YKPIV_AUTHENTICATION_ERROR);
+
+ // Verify decryption does work when authed
+ res = ykpiv_verify_select(g_state, "123456", 6, NULL, true);
+ ck_assert_int_eq(res, YKPIV_OK);
+- res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
++ res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA4096, 0x9a);
+ ck_assert_int_eq(res, YKPIV_OK);
+
+ // Verify PIN policy allows continuing to decrypt without re-verifying
+- res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
++ res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA4096, 0x9a);
+ ck_assert_int_eq(res, YKPIV_OK);
+
+ // Create a new ykpiv state, connect, and close it.
+@@ -1059,7 +1059,7 @@ START_TEST(test_pin_cache) {
+ //
+ // Note that you can verify that this fails by rebuilding with
+ // DISABLE_PIN_CACHE set to 1.
+- res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
++ res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA4096, 0x9a);
+ ck_assert_int_eq(res, YKPIV_OK);
+ }
+ END_TEST
+diff --git a/tools/confirm.sh b/tools/confirm.sh
+index 81c10ac..4ab15c5 100755
+--- a/tools/confirm.sh
++++ b/tools/confirm.sh
+@@ -20,7 +20,8 @@ echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WA
+ echo "******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* *******" >&0
+ echo >&0
+ echo -n "Are you SURE you wish to proceed? If so, type 'CONFIRM': " >&0
+-
++echo "0"
++exit 0
+ read CONFIRM
+ if [[ "x$CONFIRM" != "xCONFIRM" ]]; then
+ echo "1"
diff --git a/tests/docker_env.sh b/tests/docker_env.sh
index c11fcb0..2f05b59 100644
--- a/tests/docker_env.sh
+++ b/tests/docker_env.sh
@@ -46,10 +46,10 @@
# default values, can be overridden by the environment
-: ${MBEDTLS_DOCKER_GUEST:=jammy}
+: ${MBEDTLS_DOCKER_GUEST:=bullseye}
-DOCKER_IMAGE_TAG="pico-hsm-test:${MBEDTLS_DOCKER_GUEST}"
+DOCKER_IMAGE_TAG="pico-openpgp-test:${MBEDTLS_DOCKER_GUEST}"
# Make sure docker is available
if ! which docker > /dev/null; then
@@ -79,7 +79,7 @@ ${DOCKER} image build \
--cache-from=${DOCKER_IMAGE_TAG} \
--network host \
--build-arg MAKEFLAGS_PARALLEL="-j ${NUM_PROC}" \
- tests/docker/${MBEDTLS_DOCKER_GUEST}
+ -f tests/docker/${MBEDTLS_DOCKER_GUEST}/Dockerfile .
run_in_docker()
{
diff --git a/tests/scripts/attestation.sh b/tests/scripts/attestation.sh
new file mode 100755
index 0000000..e797bbe
--- /dev/null
+++ b/tests/scripts/attestation.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+source ./tests/scripts/func.sh
+
+echo -n " Fetch attestation certificate... "
+piv read-cert -sf9 -o sf9.pem
+test $? -eq 0 && echo -e ".\t${OK}" || exit $?
+
+algs=("RSA1024" "RSA2048" "ECCP256" "ECCP384")
+slots=("9a" "9c" "9d" "9e" "82" "83" "84" "85" "86" "87" "88" "89" "8a" "8b" "8c" "8d" "8e" "8f" "90" "91" "92" "93" "94" "95")
+for alg in ${algs[*]}; do
+ for slot in ${slots[*]}; do
+ echo " Test attestation with ${alg} in slot ${slot}"
+ echo -n " Keygen... "
+ gen_and_check $alg $slot && echo -e ".\t${OK}" || exit $?
+
+ echo -n " Fetch attesting certificate... "
+ piv attest -s$slot -o attestation.pem
+ test $? -eq 0 && echo -e ".\t${OK}" || exit $?
+
+ echo -n " OpenSSL verify attestation... "
+ e=$(openssl verify -CAfile sf9.pem attestation.pem 2>&1)
+ test $? -eq 0 && echo -n "." || exit $?
+ grep -q ": OK" <<< $e && echo -e ".\t${OK}" || exit $?
+
+ echo -n " Key deletion... "
+ delete_key $alg $slot && echo -e ".\t${OK}" || exit $?
+
+ done
+done
+
+rm -rf cert.pem
+rm -rf sf9.pem
diff --git a/tests/scripts/cli-test.sh b/tests/scripts/cli-test.sh
new file mode 100755
index 0000000..e03f36a
--- /dev/null
+++ b/tests/scripts/cli-test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+chmod a+x tests/scripts/*.sh
+
+echo "======== CLI Test suite ========"
+./tests/scripts/yubico-piv-tool.sh
diff --git a/tests/scripts/func.sh b/tests/scripts/func.sh
new file mode 100755
index 0000000..ef17273
--- /dev/null
+++ b/tests/scripts/func.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+OK="\033[32mok\033[0m"
+FAIL="\033[31mfail\033[0m"
+
+READER="u"
+
+piv() {
+ yubico-piv-tool -r${READER} -a$@
+}
+
+gen_and_check() {
+ e=$(piv generate -s$2 -A$1 -opublic.pem 2>&1)
+ test $? -eq 0 && echo -n "." || exit $?
+ grep -q "Successfully generated a new private key" <<< $e && echo -n "." || exit $?
+ e=$(piv status 2>&1)
+ e=${e//$'\t'/}
+ e=${e//$'\n'/}
+ test $? -eq 0 && echo -n "." || exit $?
+ grep -q "Slot $2:Algorithm:$1" <<< $e && echo -n "." || exit $?
+}
+delete_key() {
+ piv delete-key -s$2 > /dev/null 2>&1
+ test $? -eq 0 && echo -n "." || exit $?
+ piv delete-cert -s$2 > /dev/null 2>&1
+ test $? -eq 0 && echo -n "." || exit $?
+ e=$(piv status 2>&1)
+ test $? -eq 0 && echo -n "." || exit $?
+ q=$(grep -q "Slot $2: Algorithm: $1" <<< $e)
+ test $? -eq 1 && echo -n "." || exit $?
+ rm -rf public.pem
+}
+gen_and_delete() {
+ gen_and_check $1 $2
+ test $? -eq 0 && echo -n "." || exit $?
+ delete_key $1 $2
+ test $? -eq 0 && echo -n "." || exit $?
+}
diff --git a/tests/scripts/keygen.sh b/tests/scripts/keygen.sh
new file mode 100755
index 0000000..4edfc93
--- /dev/null
+++ b/tests/scripts/keygen.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+source ./tests/scripts/func.sh
+
+algs=("RSA1024" "RSA2048" "ECCP256" "ECCP384")
+slots=("9a" "9c" "9d" "9e" "82" "83" "84" "85" "86" "87" "88" "89" "8a" "8b" "8c" "8d" "8e" "8f" "90" "91" "92" "93" "94" "95")
+for alg in ${algs[*]}; do
+ for slot in ${slots[*]}; do
+ echo -n " Test ${alg} in slot ${slot}... "
+ gen_and_delete ${alg} $slot && echo -e ".\t${OK}" || exit $?
+ done
+done
diff --git a/tests/scripts/signatures.sh b/tests/scripts/signatures.sh
new file mode 100755
index 0000000..3a93c02
--- /dev/null
+++ b/tests/scripts/signatures.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+source ./tests/scripts/func.sh
+
+algs=("RSA1024" "RSA2048" "ECCP256" "ECCP384")
+slots=("9a" "9c" "9d" "9e" "82" "83" "84" "85" "86" "87" "88" "89" "8a" "8b" "8c" "8d" "8e" "8f" "90" "91" "92" "93" "94" "95")
+for alg in ${algs[*]}; do
+ for slot in ${slots[*]}; do
+ echo " Test signature with ${alg} in slot ${slot}"
+ echo -n " Keygen... "
+ gen_and_check $alg $slot && echo -e ".\t${OK}" || exit $?
+
+ echo -n " Test request certificate... "
+ e=$(piv verify -arequest -P123456 -s$slot -S'/CN=bar/OU=test/O=example.com/' -ipublic.pem -ocert.pem 2>&1)
+ test $? -eq 0 && echo -n "." || exit $?
+ grep -q "Successfully verified PIN" <<< $e && echo -n "." || exit $?
+ grep -q "Successfully generated a certificate request" <<< $e && echo -e ".\t${OK}" || exit $?
+
+ echo -n " OpenSSL verify request... "
+ e=$(openssl req -verify -in cert.pem 2>&1)
+ test $? -eq 0 && echo -n "." || exit $?
+ grep -q " OK" <<< $e && echo -e ".\t${OK}" || exit $?
+
+ echo -n " Test self-signed certificate... "
+ e=$(piv verify -aselfsign -P123456 -s$slot -S'/CN=bar/OU=test/O=example.com/' -ipublic.pem -ocert.pem 2>&1)
+ test $? -eq 0 && echo -n "." || exit $?
+ grep -q "Successfully verified PIN" <<< $e && echo -n "." || exit $?
+ grep -q "Successfully generated a new self signed certificate" <<< $e && echo -e ".\t${OK}" || exit $?
+
+ echo -n " Test signature... "
+ e=$(piv verify-pin -atest-signature -s$slot -P123456 -icert.pem 2>&1)
+ test $? -eq 0 && echo -n "." || exit $?
+ grep -q "Successful" <<< $e && echo -e ".\t${OK}" || exit $?
+
+ echo -n " OpenSSL verify cert... "
+ e=$(openssl verify -CAfile cert.pem cert.pem 2>&1)
+ test $? -eq 0 && echo -n "." || exit $?
+ grep -q ": OK" <<< $e && echo -e ".\t${OK}" || exit $?
+
+ echo -n " Key deletion... "
+ delete_key $alg $slot && echo -e ".\t${OK}" || exit $?
+
+ done
+done
+
+rm -rf cert.pem
diff --git a/tests/scripts/version.sh b/tests/scripts/version.sh
new file mode 100755
index 0000000..5715a01
--- /dev/null
+++ b/tests/scripts/version.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+source ./tests/scripts/func.sh
+
+# Get version
+echo -n " Test version... "
+e=$(piv version 2>&1)
+test $? -eq 0 && echo -n "." || exit $?
+grep -q "Application version" <<< $e && echo -n "." || exit $?
+grep -q " found" <<< $e && echo -e ".\t${OK}" || exit $?
diff --git a/tests/scripts/yubico-piv-test.sh b/tests/scripts/yubico-piv-test.sh
new file mode 100755
index 0000000..1f8b197
--- /dev/null
+++ b/tests/scripts/yubico-piv-test.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+source ./tests/scripts/func.sh
+reset
+test $? -eq 0 || exit $?
+
+echo -n " Test PKCS11 tool..."
+gen_and_check rsa:2048
+test $? -eq 0 && echo -n "." || exit $?
+e=$(pkcs11-tool --test -l --pin 648219 2>&1)
+test $? -eq 0 && echo -n "." || exit $?
+grep -q "No errors" <<< $e && echo -n "." || exit $?
+pkcs11-tool -l --pin 648219 --delete-object --type privkey --id 1 > /dev/null 2>&1
+test $? -eq 0 && echo -e ".\t${OK}" || exit $?
+#e=$(pkcs11-tool --test-ec -l --pin 648219 --id 1 --key-type ec:secp256r1 2>&1)
+#test $? -eq 0 && echo -n "." || exit $?
+#grep -q "==> OK" <<< $e && echo -e ".\t${OK}" || exit $?
diff --git a/tests/scripts/yubico-piv-tool.sh b/tests/scripts/yubico-piv-tool.sh
new file mode 100755
index 0000000..a4230a4
--- /dev/null
+++ b/tests/scripts/yubico-piv-tool.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+source ./tests/scripts/func.sh
+echo "==== Test version ===="
+./tests/scripts/version.sh
+test $? -eq 0 || {
+ echo -e "\t${FAIL}"
+ exit 1
+}
+
+echo "==== Test asymmetric keygen ===="
+./tests/scripts/keygen.sh
+test $? -eq 0 || {
+ echo -e "\t${FAIL}"
+ exit 1
+}
+
+echo "==== Test self-signed certificates ===="
+./tests/scripts/signatures.sh
+test $? -eq 0 || {
+ echo -e "\t${FAIL}"
+ exit 1
+}
+
+echo "==== Test attestation ===="
+./tests/scripts/attestation.sh
+test $? -eq 0 || {
+ echo -e "\t${FAIL}"
+ exit 1
+}
diff --git a/tests/start-up-and-test.sh b/tests/start-up-and-test.sh
index f3fb9be..c17c958 100755
--- a/tests/start-up-and-test.sh
+++ b/tests/start-up-and-test.sh
@@ -1,7 +1,30 @@
-#!/bin/bash -eu
+#!/bin/bash
+OK="\t\033[32mok\033[0m"
+FAIL="\t\033[31mfail\033[0m"
+
+fail() {
+ echo -e "${FAIL}"
+ exit 1
+}
+
+echo -n "Start PCSC..."
/usr/sbin/pcscd &
-sleep 2
-rm -rf memory.flash
-./build_in_docker/pico_openpgp > /dev/null &
+test $? -eq 0 && echo -e "${OK}" || {
+ echo -e "${FAIL}"
+ exit 1
+}
+sleep 1
+rm -f memory.flash
+echo -n "Start Pico OpenPGP..."
+./build_in_docker/pico_openpgp > /dev/null 2>&1 &
+test $? -eq 0 && echo -n "." || fail
+sleep 1
+ATR="3b:da:18:ff:81:b1:fe:75:1f:03:00:31:f5:73:c0:01:60:00:90:00:1c"
+e=$(opensc-tool -an 2>&1)
+grep -q "${ATR}" <<< $e && echo -n "." || fail
+test $? -eq 0 && echo -e "${OK}" || fail
+
pytest tests -W ignore::DeprecationWarning
+
+./tests/scripts/cli-test.sh