37 Commits
v4.0 ... main

Author SHA1 Message Date
Pol Henarejos
0b7beeec8c Introduce GET BULK DATA to execute GET DATA in multiple DO with a single APDU.
It saves considerable bandwidth since only one APDU/RAPDU are transmitted.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-19 18:30:50 +01:00
Pol Henarejos
1f037da326 Do no parse flash data as TLV.
Solves #50.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-19 15:57:59 +01:00
Pol Henarejos
ffbdef14b6 Set rollback globally to avoid incompatibilities.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-19 15:50:32 +01:00
Pol Henarejos
ad59aa8c1a Fixed ACL for EF_CHR_CERT.
Fixes #51.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-18 00:52:49 +01:00
Pol Henarejos
fcca95715e Fixed a bug allowing to write without PW3.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-18 00:52:19 +01:00
Pol Henarejos
704df76499 DO is cleared when no data is provided.
Solves #50.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-18 00:38:52 +01:00
Pol Henarejos
e6cc190c4f Do not interpret private DO as TLV.
Solves #50.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-18 00:29:59 +01:00
Pol Henarejos
615737807a Add support for private DO.
Closes #50.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-17 16:46:03 +01:00
Pol Henarejos
e563bb3379 Fixed pw2 verify persistence.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-17 16:45:49 +01:00
Pol Henarejos
374cff588c Fix secure boot enable.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 16:31:12 +01:00
Pol Henarejos
ca8d81fd20 Fix key rotation. Now also rotates cert & metadata.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 01:32:47 +01:00
Pol Henarejos
5d71e69c1d Do not allow slot move from retired to active.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 00:52:25 +01:00
Pol Henarejos
75691b6a42 Fix crash when attestating.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 00:46:58 +01:00
Pol Henarejos
811f33e282 Fix extension set in attestation.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 00:46:26 +01:00
Pol Henarejos
90b62f067d Add support for HIGH/LOW ESP32 LED
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-04 23:47:07 +01:00
Pol Henarejos
2e0f9d6b36 Upgrade to v4.4
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-01 20:42:56 +01:00
Pol Henarejos
6ef122528f Fix phy marker write.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-01 20:42:43 +01:00
Pol Henarejos
9c1dc102ce Upgrade to Pico Keys SDK 8.5
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-29 17:58:40 +01:00
Pol Henarejos
a94603b9e2 Use new VID:PID allocated to Pico OpenPGP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-22 12:34:23 +01:00
Pol Henarejos
6af4cef91b Use new layout
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-22 12:14:27 +01:00
Pol Henarejos
551334a447 Clear dek on error.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-22 12:14:12 +01:00
Pol Henarejos
2ce4f22622 Update README with up-to-date info.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-06 21:20:24 +01:00
Pol Henarejos
822038aba2 Upgrade to v4.2
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-05 19:53:41 +01:00
Pol Henarejos
70b5e35bde Upgrade Pico Keys SDK to v8.2
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-05 19:52:56 +01:00
Pol Henarejos
4638a1c926 Disable button press by default since LED may not be properly configured until it is commissioned.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-05 19:41:02 +01:00
Pol Henarejos
b6366ef1c0 Fix build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-29 20:37:17 +01:00
Pol Henarejos
d4d8ad86d5 Blink led three times to acknowledge proper commissioning.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-29 20:16:44 +01:00
Pol Henarejos
c51d3e7d5e Fix pimoroni led
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-27 22:04:37 +01:00
Pol Henarejos
757d163ce9 Move pointer
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-27 16:03:19 +01:00
Pol Henarejos
2513608ba9 Releaser is available up to 6.7.0
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-26 19:53:32 +01:00
Pol Henarejos
3710146074 Build only necessary boards
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-11 20:03:50 +01:00
Pol Henarejos
5462458622 Update pointer
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-11 19:37:06 +01:00
Pol Henarejos
353471c599 Revert "Move EDDSA to another branch."
This reverts commit b1421e176b.
2025-12-11 19:36:39 +01:00
Pol Henarejos
b2538cf2b6 Revert "Move Secure Boot to another branch."
This reverts commit e136bb26e3.
2025-12-11 19:36:33 +01:00
Pol Henarejos
e136bb26e3 Move Secure Boot to another branch.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-09 21:40:02 +01:00
Pol Henarejos
b1421e176b Move EDDSA to another branch.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-09 16:07:34 +01:00
Pol Henarejos
2a14c771cb Move is_gpg flag for fido2.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-07 20:35:08 +01:00
17 changed files with 354 additions and 121 deletions

View File

@@ -34,7 +34,7 @@ jobs:
- name: Delete private key - name: Delete private key
run: rm private.pem run: rm private.pem
- name: Update nightly release - name: Update nightly release
uses: pyTooling/Actions/releaser@main uses: pyTooling/Actions/releaser@v6.7.0
with: with:
tag: nightly-${{ matrix.refs }} tag: nightly-${{ matrix.refs }}
rm: true rm: true

View File

@@ -17,8 +17,11 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
set(USB_VID 0x2E8A)
set(USB_PID 0x10FF)
if(ESP_PLATFORM) if(ESP_PLATFORM)
set(EXTRA_COMPONENT_DIRS src pico-keys-sdk/src) set(EXTRA_COMPONENT_DIRS pico-keys-sdk/config/esp32/components src/openpgp)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
else() else()
if(NOT ENABLE_EMULATION) if(NOT ENABLE_EMULATION)
@@ -73,7 +76,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/src/openpgp/defs.c ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/defs.c
) )
SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/openpgp/version.h" 2) SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/openpgp/version.h")
if(ESP_PLATFORM) if(ESP_PLATFORM)
project(pico_openpgp) project(pico_openpgp)
@@ -117,7 +120,7 @@ if(NOT ESP_PLATFORM)
-Wl,--gc-sections -Wl,--gc-sections
) )
endif(APPLE) endif(APPLE)
target_link_libraries(pico_openpgp PRIVATE pthread m) target_link_libraries(pico_openpgp PRIVATE pico_keys_sdk mbedtls pthread m)
else() else()
pico_add_extra_outputs(${CMAKE_PROJECT_NAME}) pico_add_extra_outputs(${CMAKE_PROJECT_NAME})
endif() endif()

View File

@@ -3,7 +3,7 @@ This project aims at transforming your Raspberry Pico or ESP32 microcontroller i
OpenPGP cards are used to manage PGP keys and do cryptographic operations, such as keypair generation, signing and asymmetric deciphering. Pico OpenPGP follows the [**OpenPGP 3.4.1** specifications](https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.4.pdf "**OpenPGP 3.4.1** specifications"), available at [GnuPG](http://gnupg.org "GnuPG"). OpenPGP cards are used to manage PGP keys and do cryptographic operations, such as keypair generation, signing and asymmetric deciphering. Pico OpenPGP follows the [**OpenPGP 3.4.1** specifications](https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.4.pdf "**OpenPGP 3.4.1** specifications"), available at [GnuPG](http://gnupg.org "GnuPG").
If you are looking for a OpenPGP + Fido, see: https://github.com/polhenarejos/pico-fido2 If you are looking for a OpenPGP + Fido, see: https://github.com/polhenarejos/pico-fido2. Available through [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App").
## Features ## Features
Pico OpenPGP has implemented the following features: Pico OpenPGP has implemented the following features:
@@ -34,7 +34,7 @@ Pico OpenPGP has implemented the following features:
- Secure Boot and Secure Lock in RP2350 and ESP32-S3 MCUs. - Secure Boot and Secure Lock in RP2350 and ESP32-S3 MCUs.
- One Time Programming to store the master key that encrypts all resident keys and seeds. - One Time Programming to store the master key that encrypts all resident keys and seeds.
- Rescue interface to allow recovery of the device if it becomes unresponsive or undetectable. - Rescue interface to allow recovery of the device if it becomes unresponsive or undetectable.
- LED customization with Pico Commissioner. - LED customization with PicoKey App.
All these features are compliant with the specification. Therefore, if you detect some behaviour that is not expected or it does not follow the rules of specs, please open an issue. All these features are compliant with the specification. Therefore, if you detect some behaviour that is not expected or it does not follow the rules of specs, please open an issue.
@@ -62,26 +62,16 @@ If the Pico is stolen the contents of private and secret keys cannot be read wit
### RP2350 and ESP32-S3 ### RP2350 and ESP32-S3
RP2350 and ESP32-S3 microcontrollers are equipped with advanced security features, including Secure Boot and Secure Lock, ensuring that firmware integrity and authenticity are tightly controlled. Both devices support the storage of the Device Encryption Key (DEK) in an OTP (One-Time Programmable) memory region, making it permanently inaccessible for external access or tampering. This secure, non-volatile region guarantees that critical security keys are embedded into the hardware, preventing unauthorized access and supporting robust defenses against code injection or firmware modification. Together, Secure Boot and Secure Lock enforce firmware authentication, while the DEK in OTP memory solidifies the foundation for secure operations. RP2350 and ESP32-S3 microcontrollers are equipped with advanced security features, including Secure Boot and Secure Lock, ensuring that firmware integrity and authenticity are tightly controlled. Both devices support the storage of the Device Encryption Key (DEK) in an OTP (One-Time Programmable) memory region, making it permanently inaccessible for external access or tampering. This secure, non-volatile region guarantees that critical security keys are embedded into the hardware, preventing unauthorized access and supporting robust defenses against code injection or firmware modification. Together, Secure Boot and Secure Lock enforce firmware authentication, while the DEK in OTP memory solidifies the foundation for secure operations.
### Secure Boot
Secure Boot is a security feature that ensures that only trusted firmware, verified through digital signatures, can be loaded onto the device during the boot process. Once enabled, Secure Boot checks every piece of firmware against a cryptographic signature before execution, rejecting any unauthorized or modified code. This prevents malicious firmware from compromising the devices operation and integrity. With Secure Boot activated, only firmware versions signed by a trusted authority, such as the device manufacturer, will be accepted, ensuring the device remains protected from unauthorized software modifications. **This is irreversible. Once enabled, it CANNOT be disabled.**
**IMPORTANT:** For users wishing to develop and compile custom firmware, a private-public key pair is essential. Activating Secure Boot requires users to generate and manage their own unique private-public key pair. The public key from this pair must be embedded into the device to validate all firmware. Firmware will not boot without a proper digital signature from this key pair. This means that users must sign all future firmware versions with their private key and embed the public key in the device to ensure compatibility.
### Secure Lock
Secure Lock builds on Secure Boot by imposing an even stricter security model. Once activated, Secure Lock prevents any further installation of new boot keys, effectively locking the device to only run firmware that is authorized by the device's primary vendor—in this case, Pico Keys. In addition to preventing additional keys, Secure Lock disables debugging interfaces and puts additional safeguards in place to resist tampering and intrusion attempts. This ensures that the device operates exclusively with the original vendors firmware and resists unauthorized access, making it highly secure against external threats. **This is irreversible. Once enabled, it CANNOT be disabled.**
**IMPORTANT:** Activating Secure Lock not only enables Secure Boot but also invalidates all keys except the official Pico Key. This means that only firmware signed by Pico Key will be recognized, and custom code will no longer be allowed. Once enabled, the Pico Key device will run solely on the official firmware available on the website, with no option for generating or compiling new code for the device.
## Download ## Download
**If you own an ESP32-S3 board, go to [ESP32 Flasher](https://www.picokeys.com/esp32-flasher/) for flashing your Pico OpenPGP.** **If you own an ESP32-S3 board, go to [ESP32 Flasher](https://www.picokeys.com/esp32-flasher/) for flashing your Pico OpenPGP.**
If you own a Raspberry Pico (RP2040 or RP2350), go to [Download page](https://www.picokeys.com/getting-started/), select your vendor and model and download the proper firmware; or go to [Release page](https://www.github.com/polhenarejos/pico-openpgp/releases/) and download the UF2 file for your board. If you own a Raspberry Pico (RP2040 or RP2350), go to [Download page](https://www.picokeys.com/getting-started/), select your vendor and model and download the proper firmware; or go to [Release page](https://www.github.com/polhenarejos/pico-openpgp/releases/) and download the UF2 file for your board.
Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you plan to use it with OpenSC or similar tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner"). UF2 files are shiped with a VID/PID granted by RaspberryPi (2E8A:10FF). If you plan to use it with OpenSC or similar tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App").
You can use whatever VID/PID (i.e., 234b:0000 from FISJ), but remember that you are not authorized to distribute the binary with a VID/PID that you do not own. You can use whatever VID/PID for internal purposes, but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
Note that the pure-browser option [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner") is the most recommended. Note that the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App") is the most recommended.
## Build for Raspberry Pico ## Build for Raspberry Pico
Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive. Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive.
@@ -169,7 +159,7 @@ The way to communicate is exactly the same as with other cards, such as OpenPGP
### Important ### Important
OpenSC relies on PCSC driver, which reads a list (`Info.plist`) that contains a pair of VID/PID of supported readers. In order to be detectable, you have several options: OpenSC relies on PCSC driver, which reads a list (`Info.plist`) that contains a pair of VID/PID of supported readers. In order to be detectable, you have several options:
- Use the pure-browser online [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner") that commissions the Pico Key on-the-fly without external tools. - Use the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App") that commissions the PicoKey on-the-fly without external tools.
- Build and configure the project with the proper VID/PID with `USB_VID` and `USB_PID` parameters in `CMake` (see [Build section](#build "Build section")). Note that you cannot distribute the patched/compiled binary if you do not own the VID/PID or have an explicit authorization. - Build and configure the project with the proper VID/PID with `USB_VID` and `USB_PID` parameters in `CMake` (see [Build section](#build "Build section")). Note that you cannot distribute the patched/compiled binary if you do not own the VID/PID or have an explicit authorization.
## License and Commercial Use ## License and Commercial Use

View File

@@ -1 +0,0 @@
Version=3.6

View File

@@ -1,48 +1,25 @@
#!/bin/bash #!/bin/bash
VERSION_MAJOR="4" VERSION_MAJOR="4"
VERSION_MINOR="0" VERSION_MINOR="4"
NO_EDDSA=0
SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}" SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}"
#if ! [[ -z "${GITHUB_SHA}" ]]; then #if ! [[ -z "${GITHUB_SHA}" ]]; then
# SUFFIX="${SUFFIX}.${GITHUB_SHA}" # SUFFIX="${SUFFIX}.${GITHUB_SHA}"
#fi #fi
if [[ $1 == "--no-eddsa" ]]; then
NO_EDDSA=1
echo "Skipping EDDSA build"
fi
mkdir -p build_release mkdir -p build_release
mkdir -p release mkdir -p release
mkdir -p release_eddsa
rm -rf -- release/* rm -rf -- release/*
if [[ $NO_EDDSA -eq 0 ]]; then
rm -rf -- release_eddsa/*
fi
cd build_release cd build_release
PICO_SDK_PATH="${PICO_SDK_PATH:-../../pico-sdk}" PICO_SDK_PATH="${PICO_SDK_PATH:-../../pico-sdk}"
SECURE_BOOT_PKEY="${SECURE_BOOT_PKEY:-../../ec_private_key.pem}" SECURE_BOOT_PKEY="${SECURE_BOOT_PKEY:-../../ec_private_key.pem}"
board_dir=${PICO_SDK_PATH}/src/boards/include/boards boards=("pico" "pico2")
for board in "$board_dir"/*
for board_name in "${boards[@]}"
do do
board_name="$(basename -- "$board" .h)"
rm -rf -- ./* rm -rf -- ./*
PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name -DSECURE_BOOT_PKEY=${SECURE_BOOT_PKEY} PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name -DSECURE_BOOT_PKEY=${SECURE_BOOT_PKEY}
make -j`nproc` make -j`nproc`
mv pico_openpgp.uf2 ../release/pico_openpgp_$board_name-$SUFFIX.uf2 mv pico_openpgp.uf2 ../release/pico_openpgp_$board_name-$SUFFIX.uf2
done done
# Build with EDDSA
if [[ $NO_EDDSA -eq 0 ]]; then
for board in "$board_dir"/*
do
board_name="$(basename -- "$board" .h)"
rm -rf -- ./*
PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name -DSECURE_BOOT_PKEY=${SECURE_BOOT_PKEY} -DENABLE_EDDSA=1
make -j`nproc`
mv pico_openpgp.uf2 ../release_eddsa/pico_openpgp_$board_name-$SUFFIX-eddsa1.uf2
done
fi

View File

@@ -1,6 +1,6 @@
idf_component_register( idf_component_register(
SRCS ${SOURCES} SRCS ${SOURCES}
INCLUDE_DIRS . ../../pico-keys-sdk/src ../../pico-keys-sdk/src/fs ../../pico-keys-sdk/src/rng ../../pico-keys-sdk/src/usb ../../pico-keys-sdk/tinycbor/src INCLUDE_DIRS .
REQUIRES mbedtls efuse REQUIRES mbedtls efuse pico-keys-sdk
) )
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON) idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON)

View File

@@ -18,6 +18,8 @@
#include "openpgp.h" #include "openpgp.h"
#include "asn1.h" #include "asn1.h"
extern bool is_gpg;
int cmd_get_data() { int cmd_get_data() {
if (apdu.nc > 0) { if (apdu.nc > 0) {
return SW_WRONG_LENGTH(); return SW_WRONG_LENGTH();
@@ -27,10 +29,20 @@ int cmd_get_data() {
if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) {
return SW_REFERENCE_NOT_FOUND(); return SW_REFERENCE_NOT_FOUND();
} }
if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) { if (fid == EF_PRIV_DO_3) {
if (!has_pw2 && !has_pw3) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
}
else if (fid == EF_PRIV_DO_4) {
if (!has_pw3) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
}
else if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) {
return SW_SECURITY_STATUS_NOT_SATISFIED(); return SW_SECURITY_STATUS_NOT_SATISFIED();
} }
if (currentEF && (currentEF->fid & 0x1FF0) == (fid & 0x1FF0)) { //previously selected if (currentEF && currentEF->fid == fid) { // previously selected same EF
ef = currentEF; ef = currentEF;
} }
else { else {
@@ -42,23 +54,25 @@ int cmd_get_data() {
} }
uint16_t fids[] = { 1, fid }; uint16_t fids[] = { 1, fid };
uint16_t data_len = parse_do(fids, 1); uint16_t data_len = parse_do(fids, 1);
uint8_t *p = NULL; if (!(ef->type & FILE_DATA_FLASH)) {
uint16_t tg = 0; uint8_t *p = NULL;
uint16_t tg_len = 0; uint16_t tg = 0;
asn1_ctx_t ctxi; uint16_t tg_len = 0;
asn1_ctx_init(res_APDU, data_len, &ctxi); asn1_ctx_t ctxi;
if (walk_tlv(&ctxi, &p, &tg, &tg_len, NULL)) { asn1_ctx_init(res_APDU, data_len, &ctxi);
uint8_t dec = 2; if (walk_tlv(&ctxi, &p, &tg, &tg_len, NULL)) {
if ((tg & 0x1f) == 0x1f) { uint8_t dec = 2;
dec++; if ((tg & 0x1f) == 0x1f) {
} dec++;
if ((res_APDU[dec - 1] & 0xF0) == 0x80) { }
dec += (res_APDU[dec - 1] & 0x0F); if ((res_APDU[dec - 1] & 0xF0) == 0x80) {
} dec += (res_APDU[dec - 1] & 0x0F);
if (tg_len + dec == data_len) { }
memmove(res_APDU, res_APDU + dec, data_len - dec); if (tg_len + dec == data_len) {
data_len -= dec; memmove(res_APDU, res_APDU + dec, data_len - dec);
res_APDU_size -= dec; data_len -= dec;
res_APDU_size -= dec;
}
} }
} }
if (is_gpg == false) { if (is_gpg == false) {
@@ -126,3 +140,10 @@ int cmd_get_next_data() {
select_file(ef); select_file(ef);
return cmd_get_data(); return cmd_get_data();
} }
int cmd_get_bulk_data() {
if (apdu.nc < 3) {
return SW_WRONG_LENGTH();
}
return bulk_cmd(cmd_get_data);
}

View File

@@ -32,43 +32,54 @@ int cmd_put_data() {
if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) {
return SW_SECURITY_STATUS_NOT_SATISFIED(); return SW_SECURITY_STATUS_NOT_SATISFIED();
} }
if ((fid == EF_PRIV_DO_1 || fid == EF_PRIV_DO_3) && (!has_pw2 && !has_pw3)) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (!(fid == EF_PRIV_DO_1 || fid == EF_PRIV_DO_3) && !has_pw3) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (fid == EF_PW_STATUS) { if (fid == EF_PW_STATUS) {
fid = EF_PW_PRIV; fid = EF_PW_PRIV;
apdu.nc = 4; //we silently ommit the reset parameters apdu.nc = 4; //we silently ommit the reset parameters
} }
if (currentEF && (currentEF->fid & 0x1FF0) == (fid & 0x1FF0)) { //previously selected if (currentEF && currentEF->fid == fid) { // previously selected same EF
ef = currentEF; ef = currentEF;
} }
if (apdu.nc > 0 && (ef->type & FILE_DATA_FLASH)) { if (ef->type & FILE_DATA_FLASH) {
int r = 0; int r = 0;
if (fid == EF_RC) { if (apdu.nc > 0) {
has_rc = false; if (fid == EF_RC) {
if ((r = load_dek()) != PICOKEY_OK) { has_rc = false;
return SW_EXEC_ERROR(); if ((r = load_dek()) != PICOKEY_OK) {
} return SW_EXEC_ERROR();
uint8_t dhash[33]; }
dhash[0] = apdu.nc; uint8_t dhash[33];
double_hash_pin(apdu.data, apdu.nc, dhash + 1); dhash[0] = apdu.nc;
r = file_put_data(ef, dhash, sizeof(dhash)); double_hash_pin(apdu.data, apdu.nc, dhash + 1);
r = file_put_data(ef, dhash, sizeof(dhash));
file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF); file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF);
if (!tf) { if (!tf) {
return SW_REFERENCE_NOT_FOUND(); return SW_REFERENCE_NOT_FOUND();
}
uint8_t def[IV_SIZE + 32 + 32 + 32 + 32];
memcpy(def, file_get_data(tf), file_get_size(tf));
hash_multi(apdu.data, apdu.nc, session_rc);
memcpy(def + IV_SIZE + 32, dek + IV_SIZE, 32);
aes_encrypt_cfb_256(session_rc, def, def + IV_SIZE + 32, 32);
r = file_put_data(tf, def, sizeof(def));
} }
uint8_t def[IV_SIZE + 32 + 32 + 32 + 32]; else {
memcpy(def, file_get_data(tf), file_get_size(tf)); r = file_put_data(ef, apdu.data, apdu.nc);
hash_multi(apdu.data, apdu.nc, session_rc); }
memcpy(def + IV_SIZE + 32, dek + IV_SIZE, 32); if (r != PICOKEY_OK) {
aes_encrypt_cfb_256(session_rc, def, def + IV_SIZE + 32, 32); return SW_MEMORY_FAILURE();
r = file_put_data(tf, def, sizeof(def)); }
low_flash_available();
} }
else { else {
r = file_put_data(ef, apdu.data, apdu.nc); delete_file(ef);
} }
if (r != PICOKEY_OK) {
return SW_MEMORY_FAILURE();
}
low_flash_available();
} }
return SW_OK(); return SW_OK();
} }

View File

@@ -17,8 +17,6 @@
#include "files.h" #include "files.h"
bool is_gpg = true;
extern const uint8_t openpgp_aid[]; extern const uint8_t openpgp_aid[];
extern const uint8_t openpgp_aid_full[]; extern const uint8_t openpgp_aid_full[];
@@ -57,12 +55,12 @@ uint8_t historical_bytes[] = {
uint8_t extended_capabilities[] = { uint8_t extended_capabilities[] = {
10, 0, 10, 0,
0x77, /* 0x7f, /*
* No Secure Messaging supported * No Secure Messaging supported
* GET CHALLENGE supported * GET CHALLENGE supported
* Key import supported * Key import supported
* PW status byte can be put * PW status byte can be put
* No private_use_DO * private_use_DO
* Algorithm attrs are changable * Algorithm attrs are changable
* ENC/DEC with AES * ENC/DEC with AES
* KDF-DO available * KDF-DO available
@@ -70,7 +68,7 @@ uint8_t extended_capabilities[] = {
0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */ 0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */
0x00, 128, /* Max size of GET CHALLENGE */ 0x00, 128, /* Max size of GET CHALLENGE */
0x08, 0x00, /* max. length of cardholder certificate (2KiB) */ 0x08, 0x00, /* max. length of cardholder certificate (2KiB) */
0x00, 0xff, 0x08, 0x00, /* max. length of private DO (2KiB) */
0x00, 0x1 0x00, 0x1
}; };
@@ -116,7 +114,7 @@ file_t file_entries[] = {
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 10 */ { .fid = EF_CH_CERT, .parent = 0, .name = NULL, /* 10 */ { .fid = EF_CH_CERT, .parent = 0, .name = NULL,
.type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *) parse_ch_cert, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *) parse_ch_cert,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 11 */ { .fid = EF_EXLEN_INFO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, /* 11 */ { .fid = EF_EXLEN_INFO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF,
.data = exlen_info, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, .data = exlen_info, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 12 */ { .fid = EF_GFM, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, /* 12 */ { .fid = EF_GFM, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF,
@@ -478,10 +476,28 @@ file_t file_entries[] = {
/* 131 */ { .fid = EF_PW_RETRIES, .parent = 0, .name = NULL, /* 131 */ { .fid = EF_PW_RETRIES, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 131 */ { .fid = EF_PRIV_DO_1, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 132 */ { .fid = EF_PRIV_DO_2, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 133 */ { .fid = EF_PRIV_DO_3, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 134 */ { .fid = EF_PRIV_DO_4, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 135 */ { .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 },
/* 136 */ { .fid = EF_PW_STATUS, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 132 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, /* 137 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF,
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 133 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL, /* 138 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL,
.ef_structure = 0, .acl = ACL_NONE } //end .ef_structure = 0, .acl = ACL_NONE } //end
}; };

View File

@@ -163,6 +163,9 @@
#define EF_DEV_CONF 0x1122 #define EF_DEV_CONF 0x1122
extern bool is_gpg; #define EF_PRIV_DO_1 0x0101
#define EF_PRIV_DO_2 0x0102
#define EF_PRIV_DO_3 0x0103
#define EF_PRIV_DO_4 0x0104
#endif #endif

View File

@@ -22,6 +22,8 @@
#include "asn1.h" #include "asn1.h"
#include "management.h" #include "management.h"
bool is_gpg = true;
int man_process_apdu(); int man_process_apdu();
int man_unload(); int man_unload();
@@ -39,6 +41,7 @@ int man_select(app_t *a, uint8_t force) {
res_APDU_size = strlen((char *) res_APDU); res_APDU_size = strlen((char *) res_APDU);
apdu.ne = res_APDU_size; apdu.ne = res_APDU_size;
init_piv(); init_piv();
is_gpg = false;
return PICOKEY_OK; return PICOKEY_OK;
} }
@@ -145,7 +148,6 @@ int man_process_apdu() {
} }
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) { for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) { if (cmd->ins == INS(apdu)) {
is_gpg = false;
int r = cmd->cmd_handler(); int r = cmd->cmd_handler();
return r; return r;
} }

View File

@@ -219,6 +219,10 @@ void scan_files_openpgp() {
low_flash_available(); low_flash_available();
} }
void release_dek() {
memset(dek, 0, sizeof(dek));
}
extern bool has_pwpiv; extern bool has_pwpiv;
extern uint8_t session_pwpiv[32]; extern uint8_t session_pwpiv[32];
int load_dek() { int load_dek() {
@@ -245,6 +249,7 @@ int load_dek() {
r = aes_decrypt_cfb_256(session_pwpiv, dek, dek + IV_SIZE, 32); r = aes_decrypt_cfb_256(session_pwpiv, dek, dek + IV_SIZE, 32);
} }
if (r != 0) { if (r != 0) {
release_dek();
return PICOKEY_EXEC_ERROR; return PICOKEY_EXEC_ERROR;
} }
if (otp_key_1) { if (otp_key_1) {
@@ -255,10 +260,6 @@ int load_dek() {
return PICOKEY_OK; return PICOKEY_OK;
} }
void release_dek() {
memset(dek, 0, sizeof(dek));
}
int dek_encrypt(uint8_t *data, size_t len) { int dek_encrypt(uint8_t *data, size_t len) {
int r; int r;
if ((r = load_dek()) != PICOKEY_OK) { if ((r = load_dek()) != PICOKEY_OK) {
@@ -281,7 +282,7 @@ int dek_decrypt(uint8_t *data, size_t len) {
void init_openpgp() { void init_openpgp() {
isUserAuthenticated = false; isUserAuthenticated = false;
has_pw1 = has_pw3 = false; has_pw1 = has_pw2 = has_pw3 = false;
algo_dec = EF_ALGO_PRIV2; algo_dec = EF_ALGO_PRIV2;
algo_aut = EF_ALGO_PRIV3; algo_aut = EF_ALGO_PRIV3;
pk_dec = EF_PK_DEC; pk_dec = EF_PK_DEC;
@@ -292,7 +293,7 @@ void init_openpgp() {
int openpgp_unload() { int openpgp_unload() {
isUserAuthenticated = false; isUserAuthenticated = false;
has_pw1 = has_pw3 = false; has_pw1 = has_pw2 = has_pw3 = false;
algo_dec = EF_ALGO_PRIV2; algo_dec = EF_ALGO_PRIV2;
algo_aut = EF_ALGO_PRIV3; algo_aut = EF_ALGO_PRIV3;
pk_dec = EF_PK_DEC; pk_dec = EF_PK_DEC;
@@ -792,6 +793,7 @@ extern int cmd_terminate_df();
extern int cmd_pso(); extern int cmd_pso();
extern int cmd_keypair_gen(); extern int cmd_keypair_gen();
extern int cmd_reset_retry(); extern int cmd_reset_retry();
extern int cmd_get_bulk_data();
#define INS_VERIFY 0x20 #define INS_VERIFY 0x20
#define INS_MSE 0x22 #define INS_MSE 0x22
@@ -806,6 +808,7 @@ extern int cmd_reset_retry();
#define INS_SELECT_DATA 0xA5 #define INS_SELECT_DATA 0xA5
#define INS_GET_DATA 0xCA #define INS_GET_DATA 0xCA
#define INS_GET_NEXT_DATA 0xCC #define INS_GET_NEXT_DATA 0xCC
#define INS_GET_BULK_DATA 0xCE
#define INS_PUT_DATA 0xDA #define INS_PUT_DATA 0xDA
#define INS_IMPORT_DATA 0xDB #define INS_IMPORT_DATA 0xDB
#define INS_TERMINATE_DF 0xE6 #define INS_TERMINATE_DF 0xE6
@@ -829,7 +832,8 @@ static const cmd_t cmds[] = {
{ INS_VERSION, cmd_version_openpgp }, { INS_VERSION, cmd_version_openpgp },
{ INS_SELECT_DATA, cmd_select_data }, { INS_SELECT_DATA, cmd_select_data },
{ INS_GET_NEXT_DATA, cmd_get_next_data }, { INS_GET_NEXT_DATA, cmd_get_next_data },
{ 0x00, 0x0 } { INS_GET_BULK_DATA, cmd_get_bulk_data },
{ 0x00, NULL }
}; };
int openpgp_process_apdu() { int openpgp_process_apdu() {

View File

@@ -124,7 +124,7 @@ static int x509_create_cert(void *pk_ctx, uint8_t algo, uint8_t slot, bool attes
mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x08", 10, 0, &meta[1], 2); 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; 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)); mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x09", 10, 0, &v, sizeof(v));
} }
else { else {
uint8_t wslot = slot; uint8_t wslot = slot;
@@ -153,9 +153,7 @@ static int x509_create_cert(void *pk_ctx, uint8_t algo, uint8_t slot, bool attes
} }
mbedtls_x509write_crt_set_subject_key_identifier(&ctx); mbedtls_x509write_crt_set_subject_key_identifier(&ctx);
mbedtls_x509write_crt_set_authority_key_identifier(&ctx); mbedtls_x509write_crt_set_authority_key_identifier(&ctx);
mbedtls_x509write_crt_set_key_usage(&ctx, mbedtls_x509write_crt_set_key_usage(&ctx, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN);
MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
MBEDTLS_X509_KU_KEY_CERT_SIGN);
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL); int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL);
/* skey cannot be freed, as it is freed later */ /* skey cannot be freed, as it is freed later */
if (attestation) { if (attestation) {
@@ -1020,6 +1018,9 @@ static int cmd_move_key() {
if ((!IS_KEY(to) && to != 0xFF) || !IS_KEY(from)) { if ((!IS_KEY(to) && to != 0xFF) || !IS_KEY(from)) {
return SW_INCORRECT_P1P2(); return SW_INCORRECT_P1P2();
} }
if (IS_RETIRED(from) && IS_ACTIVE(to)) {
return SW_INCORRECT_P1P2();
}
if (from == 0x93) { if (from == 0x93) {
from = EF_PIV_KEY_RETIRED18; from = EF_PIV_KEY_RETIRED18;
} }
@@ -1030,9 +1031,85 @@ static int cmd_move_key() {
if (!(efs = search_by_fid(from, NULL, SPECIFY_EF)) || (!(efd = search_by_fid(to, NULL, SPECIFY_EF)) && to != 0xFF)) { if (!(efs = search_by_fid(from, NULL, SPECIFY_EF)) || (!(efd = search_by_fid(to, NULL, SPECIFY_EF)) && to != 0xFF)) {
return SW_FILE_NOT_FOUND(); return SW_FILE_NOT_FOUND();
} }
uint16_t cert_from_fid = 0;
uint16_t cert_to_fid = 0;
if (from == EF_PIV_KEY_AUTHENTICATION) {
cert_from_fid = EF_PIV_AUTHENTICATION;
}
else if (from == EF_PIV_KEY_SIGNATURE) {
cert_from_fid = EF_PIV_SIGNATURE;
}
else if (from == EF_PIV_KEY_KEYMGM) {
cert_from_fid = EF_PIV_KEY_MANAGEMENT;
}
else if (from == EF_PIV_KEY_CARDAUTH) {
cert_from_fid = EF_PIV_CARD_AUTH;
}
else if (from == EF_PIV_KEY_RETIRED18) {
cert_from_fid = EF_PIV_RETIRED18;
}
else {
cert_from_fid = from + 0xC08B;
}
if (to != 0xFF) {
if (to == EF_PIV_KEY_AUTHENTICATION) {
cert_to_fid = EF_PIV_AUTHENTICATION;
}
else if (to == EF_PIV_KEY_SIGNATURE) {
cert_to_fid = EF_PIV_SIGNATURE;
}
else if (to == EF_PIV_KEY_KEYMGM) {
cert_to_fid = EF_PIV_KEY_MANAGEMENT;
}
else if (to == EF_PIV_KEY_CARDAUTH) {
cert_to_fid = EF_PIV_CARD_AUTH;
}
else if (to == EF_PIV_KEY_RETIRED18) {
cert_to_fid = EF_PIV_RETIRED18;
}
else {
cert_to_fid = to + 0xC08B;
}
}
if (to != 0xFF) { if (to != 0xFF) {
file_put_data(efd, file_get_data(efs), file_get_size(efs)); file_put_data(efd, file_get_data(efs), file_get_size(efs));
} }
file_t *ef_cert_from = search_by_fid(cert_from_fid, NULL, SPECIFY_EF);
if (to != 0xFF) {
file_t *ef_cert_to = search_by_fid(cert_to_fid, NULL, SPECIFY_EF);
if (!ef_cert_to) {
return SW_FILE_NOT_FOUND();
}
if (file_has_data(ef_cert_from)) {
file_put_data(ef_cert_to, file_get_data(ef_cert_from), file_get_size(ef_cert_from));
}
else {
flash_clear_file(ef_cert_to);
}
}
if (ef_cert_from) {
flash_clear_file(ef_cert_from);
}
uint8_t *meta_src = NULL;
int meta_len = meta_find(from, &meta_src);
if (to != 0xFF) {
if (meta_len > 0 && meta_src != NULL) {
uint8_t *meta_copy = (uint8_t *)calloc(1, (size_t)meta_len);
if (!meta_copy) {
return SW_MEMORY_FAILURE();
}
memcpy(meta_copy, meta_src, (size_t)meta_len);
meta_add(to, meta_copy, (uint16_t)meta_len);
free(meta_copy);
}
else {
meta_delete(to);
}
}
meta_delete(from);
flash_clear_file(efs); flash_clear_file(efs);
low_flash_available(); low_flash_available();
return SW_OK(); return SW_OK();
@@ -1154,6 +1231,7 @@ static int cmd_attestation() {
return SW_INCORRECT_PARAMS(); return SW_INCORRECT_PARAMS();
} }
int r = 0; int r = 0;
uint8_t abuf[2048];
if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048) { if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048) {
mbedtls_rsa_context ctx; mbedtls_rsa_context ctx;
mbedtls_rsa_init(&ctx); mbedtls_rsa_init(&ctx);
@@ -1162,7 +1240,7 @@ static int cmd_attestation() {
mbedtls_rsa_free(&ctx); mbedtls_rsa_free(&ctx);
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
} }
r = x509_create_cert(&ctx, meta[0], key_ref, true, res_APDU, 2048); r = x509_create_cert(&ctx, meta[0], key_ref, true, abuf, sizeof(abuf));
mbedtls_rsa_free(&ctx); mbedtls_rsa_free(&ctx);
} }
else if (meta[0] == PIV_ALGO_ECCP256 || meta[0] == PIV_ALGO_ECCP384) { else if (meta[0] == PIV_ALGO_ECCP256 || meta[0] == PIV_ALGO_ECCP384) {
@@ -1173,7 +1251,7 @@ static int cmd_attestation() {
mbedtls_ecdsa_free(&ctx); mbedtls_ecdsa_free(&ctx);
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
} }
r = x509_create_cert(&ctx, meta[0], key_ref, true, res_APDU, 2048); r = x509_create_cert(&ctx, meta[0], key_ref, true, abuf, sizeof(abuf));
mbedtls_ecdsa_free(&ctx); mbedtls_ecdsa_free(&ctx);
} }
else { else {
@@ -1182,7 +1260,7 @@ static int cmd_attestation() {
if (r <= 0) { if (r <= 0) {
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
} }
memmove(res_APDU, res_APDU + 2048 - r, r); memcpy(res_APDU, abuf + sizeof(abuf) - r, r);
res_APDU_size = r; res_APDU_size = r;
return SW_OK(); return SW_OK();
} }

View File

@@ -29,7 +29,7 @@
#define PIV_VERSION_MINOR (PIV_VERSION & 0xff) #define PIV_VERSION_MINOR (PIV_VERSION & 0xff)
#define PIPGP_VERSION 0x0400 #define PIPGP_VERSION 0x0404
#define PIPGP_VERSION_MAJOR ((PIPGP_VERSION >> 8) & 0xff) #define PIPGP_VERSION_MAJOR ((PIPGP_VERSION >> 8) & 0xff)
#define PIPGP_VERSION_MINOR (PIPGP_VERSION & 0xff) #define PIPGP_VERSION_MINOR (PIPGP_VERSION & 0xff)

View File

@@ -201,7 +201,7 @@ def test_extended_capabilities(card):
pytest.skip("Yubikey returns 6B00 when no key") pytest.skip("Yubikey returns 6B00 when no key")
else: else:
a = get_data_object(card, 0xc0) a = get_data_object(card, 0xc0)
assert a == None or match(b'[\x70\x74\x75\x77]\x00\x00.[\x00\x08]\x00\x00\xff[\x00\x01][\x00\x01]', a) assert a == None or match(b'[\x70\x7F\x75\x77]\x00\x00.[\x00\x08]\x00\x08\x00[\x00\x01][\x00\x01]', a)
def test_key_attributes_1(card): def test_key_attributes_1(card):
if card.is_yubikey: if card.is_yubikey:

View File

@@ -28,6 +28,26 @@ from card_const import *
from constants_for_test import * from constants_for_test import *
import pytest import pytest
PRIVATE_DO_0101 = (0x01, 0x01)
PRIVATE_DO_0102 = (0x01, 0x02)
PRIVATE_DO_0103 = (0x01, 0x03)
PRIVATE_DO_0104 = (0x01, 0x04)
def _assert_sw(e, sw_list):
sw = e.args[0]
assert sw in sw_list
def _expect_security_error(callable_):
try:
callable_()
except ValueError as e:
_assert_sw(e, ["6982", "6985"])
return
assert False
class Test_Card_Personalize_Card_2(object): class Test_Card_Personalize_Card_2(object):
def test_verify_pw3_0(self, card): def test_verify_pw3_0(self, card):
v = card.verify(3, PW3_TEST0) v = card.verify(3, PW3_TEST0)
@@ -202,3 +222,112 @@ class Test_Card_Personalize_Card_2(object):
def test_verify_pw3_2(self, card): def test_verify_pw3_2(self, card):
v = card.verify(3, PW3_TEST0) v = card.verify(3, PW3_TEST0)
assert v assert v
def test_private_do_0101_write_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
r = card.cmd_put_data(PRIVATE_DO_0101[0], PRIVATE_DO_0101[1], b"priv0101_pw3_ok")
assert r
def test_private_do_0101_write_fail_with_pw1_81(self, card):
card.cmd_select_openpgp()
v = card.verify(1, PW1_TEST4)
assert v
_expect_security_error(
lambda: card.cmd_put_data(PRIVATE_DO_0101[0], PRIVATE_DO_0101[1], b"priv0101_pw1_81")
)
def test_private_do_0101_write_ok_with_pw1_82(self, card):
card.cmd_select_openpgp()
v = card.verify(2, PW1_TEST4)
assert v
r = card.cmd_put_data(PRIVATE_DO_0101[0], PRIVATE_DO_0101[1], b"priv0101_ok")
assert r
def test_private_do_0101_read_always(self, card):
card.cmd_select_openpgp()
data = get_data_object(card, 0x0101)
assert data == b"priv0101_ok" or data == b"priv0101_pw3_ok"
def test_private_do_0102_write_fail_with_pw1(self, card):
card.cmd_select_openpgp()
v = card.verify(2, PW1_TEST4)
assert v
_expect_security_error(
lambda: card.cmd_put_data(PRIVATE_DO_0102[0], PRIVATE_DO_0102[1], b"priv0102_pw1")
)
def test_private_do_0102_write_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
r = card.cmd_put_data(PRIVATE_DO_0102[0], PRIVATE_DO_0102[1], b"priv0102_ok")
assert r
def test_private_do_0102_read_always(self, card):
card.cmd_select_openpgp()
data = get_data_object(card, 0x0102)
assert data == b"priv0102_ok"
def test_private_do_0103_read_fail_without_auth(self, card):
card.cmd_select_openpgp()
_expect_security_error(lambda: get_data_object(card, 0x0103))
def test_private_do_0103_read_fail_with_pw1_81(self, card):
card.cmd_select_openpgp()
v = card.verify(1, PW1_TEST4)
assert v
_expect_security_error(lambda: get_data_object(card, 0x0103))
def test_private_do_0103_write_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
r = card.cmd_put_data(PRIVATE_DO_0103[0], PRIVATE_DO_0103[1], b"priv0103_pw3_ok")
assert r
def test_private_do_0103_read_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
data = get_data_object(card, 0x0103)
assert data == b"priv0103_pw3_ok"
def test_private_do_0103_write_ok_with_pw1_82(self, card):
card.cmd_select_openpgp()
v = card.verify(2, PW1_TEST4)
assert v
r = card.cmd_put_data(PRIVATE_DO_0103[0], PRIVATE_DO_0103[1], b"priv0103_ok")
assert r
def test_private_do_0103_read_ok_with_pw1_82(self, card):
card.cmd_select_openpgp()
v = card.verify(2, PW1_TEST4)
assert v
data = get_data_object(card, 0x0103)
assert data == b"priv0103_ok"
def test_private_do_0104_read_fail_without_auth(self, card):
card.cmd_select_openpgp()
_expect_security_error(lambda: get_data_object(card, 0x0104))
def test_private_do_0104_read_fail_with_pw1(self, card):
card.cmd_select_openpgp()
v = card.verify(2, PW1_TEST4)
assert v
_expect_security_error(lambda: get_data_object(card, 0x0104))
def test_private_do_0104_write_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
r = card.cmd_put_data(PRIVATE_DO_0104[0], PRIVATE_DO_0104[1], b"priv0104_ok")
assert r
def test_private_do_0104_read_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
data = get_data_object(card, 0x0104)
assert data == b"priv0104_ok"