22 Commits
v1.0 ... v1.2

Author SHA1 Message Date
Pol Henarejos
6ddb118bbf Small fix.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-29 15:49:39 +02:00
Pol Henarejos
6c4cb4b12a README uses markdown.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-29 15:47:53 +02:00
Pol Henarejos
456dd24fe5 Script to build Pico OpenPGP releases.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-29 15:47:16 +02:00
Pol Henarejos
e13a4fc121 File for live patching to burn custom VID/PID.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-29 15:47:02 +02:00
Pol Henarejos
49cee088b8 Since we cannot use version of APDU (as it always has to be 3.4 for OpenPGP), we define the version here.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-29 15:46:45 +02:00
Pol Henarejos
74197de147 Adding README
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-29 15:46:10 +02:00
Pol Henarejos
193220e59e Adding DEK to private keys.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-27 20:16:21 +02:00
Pol Henarejos
06745515eb Return SW_OK on VERIFY if user already logged in.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 21:46:13 +02:00
Pol Henarejos
30bb31a9c9 Adding life status for PW1 for PSO:CDS (single or several commands).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 18:10:27 +02:00
Pol Henarejos
f0e7cdc18d Adding capability to edit PW STATUS.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 18:06:23 +02:00
Pol Henarejos
283289fbc6 Moving retries to PW STATUS.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 18:04:25 +02:00
Pol Henarejos
dc988e2a88 Signature counter is reset on keypair generation.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 16:43:18 +02:00
Pol Henarejos
1594da7533 Fix with signature counter.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 16:39:04 +02:00
Pol Henarejos
2c47816686 Fix logging in with PW 82.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 16:38:50 +02:00
Pol Henarejos
b0b30aff3a Adding increment of signature counter.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 16:38:23 +02:00
Pol Henarejos
30e5f12b25 Added access checks.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 16:03:10 +02:00
Pol Henarejos
3c2bfbc119 Moving some ASN1 stuff to mbedtls.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 12:14:58 +02:00
Pol Henarejos
77842f23e7 Moving signature calls outside.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 12:04:19 +02:00
Pol Henarejos
b67a902eb6 Fixing signature. It uses raw signature instead of heading it with asn1.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-26 09:36:36 +02:00
Pol Henarejos
e2c8d2e0aa Fix ECDH calc secret.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-25 19:41:06 +02:00
Pol Henarejos
d87334bfbc Added INTERNAL AUTHENTICATE.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-25 19:05:22 +02:00
Pol Henarejos
6fef2dd1dc Parsing 0x82 as PW2.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-04-25 17:56:28 +02:00
8 changed files with 595 additions and 163 deletions

View File

@@ -82,6 +82,7 @@ target_sources(pico_openpgp PUBLIC
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/des.c ${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/des.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/nist_kw.c ${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/nist_kw.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/hkdf.c ${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/hkdf.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/asn1parse.c
) )
target_include_directories(pico_openpgp PUBLIC target_include_directories(pico_openpgp PUBLIC

121
README.md Normal file
View File

@@ -0,0 +1,121 @@
# Pico OpenPGP
This project aims at transforming your Raspberry Pico into a Smart Card with an OpenPGP applet integrated. The Pico works as a reader with an embedded OpenPGP card, like a USB card.
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").
## Features
Pico OpenPGP has implemented the following features:
- Key generation and encrypted storage.
- RSA key generation from 1024 to 4096 bits.
- ECDSA key generation from 192 to 521 bits.
- ECC curves secp256r1, secp384r1, secp521r1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1, secp256k1.
- SHA1, SHA224, SHA256, SHA384, SHA512 digests.
- RSA-PKCS and raw RSA signature.
- ECDSA raw and hash signature.
- ECDH key derivation.
- PIN authorization.
- PKCS11 compliant interface.
- HRNG (hardware random number generator).
- Device Encryption Key (DEK).
- USB/CCID support with OpenSC, openssl, etc.
- Extended APDU support.
- Lifecycle card (termination and activation).
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.
### About Gnuk
This project was inspired by [Gnuk](https://wiki.debian.org/GNUK "Gnuk"), a same project but focused on STM32 processor family. Despite the initial idea was to port Gnuk to the Raspberry Pico family, the underlaying architecture is widely different (although they run on ARM). For instance, the Pico has two ARM cores, with an appropiate SDK able to leverage them. Also, Pico has an internal flash storage, which is farly larger compared to STM32 ROM storage. Finally, the Pico has a complete USB interface based on TinyUSB, which difficults to port Gnuk. These are only few examples of the difficulties of porting Gnuk to the Raspberry Pico.
As a consequence, Pico OpenPGP is designed from zero. Well, not strictly from zero, as it borrows some of the buffering between USB and CCID interfaces from Gnuk. Cryptographic operations are implemented with MBEDTLS library.
## Security considerations
All secret keys (asymmetric and symmetric) are stored encrypted in the flash memory of the Raspberry Pico. DEK is used as a 256 bit AES key to protect private and secret keys. Keys are never stored in RAM except for signature and decryption operations and only during the process. All keys (including DEK) are loaded and cleared every time to avoid potential security flaws.
At the same time, DEK is encrypted with doubled salted and hashed PIN. Also, the PIN is hashed in memory during the session. Hence, PIN is never stored in plain text neither in flash nor in memory. Note that PIN is conveyed from the host to the Pico in plain text if no secure channel is provided.
If the Pico is stolen the contents of private and secret keys cannot be read without the PIN, even if the flash memory is dumped.
## Download
Please, go to the [Release page](https://github.com/polhenarejos/pico-openpgp/releases "Release page")) 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 are planning to use it with OpenSC or similar, you should modify Info.plist of CCID driver to add these VID/PID or use the VID/PID patcher as follows: `./patch_vidpid.sh VID:PID input_openpgp_file.uf2 output_openpgp_file.uf2`
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.
## Build
Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive.
git clone https://github.com/polhenarejos/pico-openpgp
cd pico-openpgp
mkdir build
cd build
PICO_SDK_PATH=/path/to/pico-sdk cmake .. -DPICO_BOARD=board_type -DUSB_VID=0x1234 -DUSB_PID=0x5678
make
Note that PICO_BOARD, USB_VID and USB_PID are optional. If not provided, pico board and VID/PID FEFF:FCFD will be used.
After make ends, the binary file pico_openpgp.uf2 will be generated. Put your pico board into loading mode, by pushing BOOTSEL button while pluging on, and copy the UF2 to the new fresh usb mass storage Pico device. Once copied, the pico mass storage will be disconnected automatically and the pico board will reset with the new firmware. A blinking led will indicate the device is ready to work.
## Operation time
### Keypair generation
Generating EC keys is almost instant. RSA keypair generation takes some time, specially for 3072 and 4096 bits.
### Keypair generation
Generating EC keys is almost instant. RSA keypair generation takes some time, specially for `3072` and `4096` bits.
| RSA key length (bits) | Average time (seconds) |
| :---: | :---: |
| 1024 | 16 |
| 2048 | 124 |
| 3072 | 600 |
| 4096 | ~1000 |
### Signature and decrypt
| RSA key length (bits) | Average time (seconds) |
| :---: | :---: |
| 1024 | 1 |
| 2048 | 3 |
| 3072 | 7 |
| 4096 | 15 |
## Led blink
Pico OpenPGP uses the led to indicate the current status. Four states are available:
### Press to confirm
The Led is almost on all the time. It goes off for 100 miliseconds every second.
![Press to confirm](https://user-images.githubusercontent.com/55573252/162008917-6a730eac-396c-44cc-890e-802294be30a3.gif)
### Idle mode
In idle mode, the Pico OpenPGP goes to sleep. It waits for a command and it is awaken by the driver. The Led is almost off all the time. It goes on for 500 milliseconds every second.
![Idle mode](https://user-images.githubusercontent.com/55573252/162008980-d5a5caad-072e-400c-98e3-2c606b4b2af9.gif)
### Active mode
In active mode, the Pico OpenPGP is awaken and ready to receive a command. It blinks four times in a second.
![Active](https://user-images.githubusercontent.com/55573252/162008997-1ea8cd7e-5384-4893-9dcb-b473153fc375.gif)
### Processing
While processing, the Pico OpenPGP is busy and cannot receive additional commands until the current is processed. In this state, the Led blinks 20 times in a second.
![Processing](https://user-images.githubusercontent.com/55573252/162009007-df45111e-2473-4a92-97c5-15c3cd19babd.gif)
## Driver
Pico OpenPGP uses the `openpgp` driver provided by [OpenSC](https://github.com/OpenSC/OpenSC/ "OpenSC"). This driver utilizes the standardized PKCS#11 interface to communicate with the user and it can be used with many engines that accept PKCS#11 interface, such as OpenSSL, P11 library or pkcs11-tool.
It also accepts the use of GnuPG programs (`gpg` and `gpg2`) to manipulate the card. For instance, it can be used with the `gpg --edit-card --expert` interface to change the cryptographic keys, generate new keypairs or simply set the cardholder name.
Pico OpenPGP relies on PKCS#15 structure to store and manipulate the internal files (PINs, private keys, certificates, etc.) and directories. Therefore, it accepts the commands from `pkcs15-tool`. For instance, `pkcs15-tool -D` will list all elements stored in the Pico OpenPGP.
The way to communicate is exactly the same as with other cards, such as OpenPGP or similar.
### 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 must patch the UF2 binary (if you just downloaded from the [Release section](https://github.com/polhenarejos/pico-openpgp/releases "Release section")) or 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.
## Credits
Pico OpenPGP uses the following libraries or portion of code:
- mbedTLS for cryptographic operations.
- gnuk for low level CCID procedures support.
- TinyUSB for low level USB procedures.

1
VERSION Normal file
View File

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

21
build_pico_openpgp.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
VERSION_MAJOR="1"
VERSION_MINOR="2"
rm -rf release/*
cd build_release
for board in adafruit_feather_rp2040 adafruit_itsybitsy_rp2040 adafruit_qtpy_rp2040 adafruit_trinkey_qt2040 arduino_nano_rp2040_connect melopero_shake_rp2040 pimoroni_interstate75 pimoroni_keybow2040 pimoroni_pga2040 pimoroni_picolipo_4mb pimoroni_picolipo_16mb pimoroni_picosystem pimoroni_plasma2040 pimoroni_tiny2040 pybstick26_rp2040 sparkfun_micromod sparkfun_promicro sparkfun_thingplus vgaboard waveshare_rp2040_lcd_0.96 waveshare_rp2040_plus_4mb waveshare_rp2040_plus_16mb waveshare_rp2040_zero
do
rm -rf *
PICO_SDK_PATH=~/Devel/pico/pico-sdk cmake .. -DPICO_BOARD=$board
make -kj20
mv pico_openpgp.uf2 ../release/pico_openpgp_$board-$VERSION_MAJOR.$VERSION_MINOR.uf2
done
rm -rf *
PICO_SDK_PATH=~/Devel/pico/pico-sdk cmake ..
make -kj20
mv pico_openpgp.uf2 ../release/pico_openpgp_pico_generic-$VERSION_MAJOR.$VERSION_MINOR.uf2

94
patch_vidpid.sh Executable file
View File

@@ -0,0 +1,94 @@
#!/bin/bash
#
# 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 <http://www.gnu.org/licenses/>.
#
VERSION_MAJOR="1" #Version of Pico CCID Core
VERSION_MINOR="1"
echo "----------------------------"
echo "VID/PID patcher for Pico OpenPGP"
echo "----------------------------"
echo ""
if [ "$#" -le 0 ]; then
echo "Usage: $0 VID:PID [input_uf2_file] [output_uf2_file]"
exit 1
fi
IFS=':' read -r -a ARR <<< "$1"
if [ ${#ARR[@]} -ne 2 ]; then
echo "ERROR: Specify vendor and product ids as VID:PID (e.g., $0 CAFE:1234)"
exit 1
fi
VID=${ARR[0]}
PID=${ARR[1]}
if [ ${#VID} -ne 4 ]; then
echo "ERROR: VID length must be 4 hexadecimal characters"
exit 1
fi
if [ ${#PID} -ne 4 ]; then
echo "ERROR: PID length must be 4 hexadecimal characters"
exit 1
fi
if ! [[ $VID =~ ^[0-9A-Fa-f]{1,}$ ]] ; then
echo "ERROR: VID must contain hexadecimal characters"
exit 1
fi
if ! [[ $PID =~ ^[0-9A-Fa-f]{1,}$ ]] ; then
echo "ERROR: PID must contain hexadecimal characters"
exit 1
fi
UF2_FILE_IF="pico_openpgp.uf2"
UF2_FILE_OF="$UF2_FILE_IF"
if [ "$#" -ge 2 ]; then
UF2_FILE_IF="$2"
UF2_FILE_OF="$UF2_FILE_IF"
fi
if [ "$#" -ge 3 ]; then
UF2_FILE_OF="$3"
fi
echo -n "Patching ${UF2_FILE_IF}... "
if [[ ! -f "$UF2_FILE_IF" ]]; then
echo "ERROR: UF2 file ${UF2_FILE_IF} does not exist"
exit 1
fi
if [ "$UF2_FILE_IF" != "$UF2_FILE_OF" ]; then
cp -R $UF2_FILE_IF $UF2_FILE_OF
fi
LITTLE_VID="\x${VID:2:2}\x${VID:0:2}"
LITTLE_PID="\x${PID:2:2}\x${PID:0:2}"
perl -pi -e "s/\xff\xfe\xfd\xfc\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/$LITTLE_VID$LITTLE_PID\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/" $UF2_FILE_OF
echo "Done!"
echo ""
echo "Patched file was saved in ${UF2_FILE_OF}"

View File

@@ -54,15 +54,15 @@ uint8_t historical_bytes[] = {
uint8_t extended_capabilities[] = { uint8_t extended_capabilities[] = {
10, 0, 10, 0,
0x74, /* 0x76, /*
* 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 * No private_use_DO
* Algorithm attrs are changable * Algorithm attrs are changable
* No DEC with AES * ENC/DEC with AES
* KDF-DO available * No KDF-DO available
*/ */
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 */
@@ -126,21 +126,20 @@ file_t file_entries[] = {
/* 39 */ { .fid = EF_PW1, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 39 */ { .fid = EF_PW1, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 40 */ { .fid = EF_RC, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 40 */ { .fid = EF_RC, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 41 */ { .fid = EF_PW3, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 41 */ { .fid = EF_PW3, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 42 */ { .fid = EF_PW1_RETRIES, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 42 */ { .fid = EF_ALGO_PRIV1, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 43 */ { .fid = EF_RC_RETRIES, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 43 */ { .fid = EF_ALGO_PRIV2, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 44 */ { .fid = EF_PW3_RETRIES, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 44 */ { .fid = EF_ALGO_PRIV3, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 45 */ { .fid = EF_ALGO_PRIV1, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, /* 45 */ { .fid = EF_PK_SIG, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 46 */ { .fid = EF_ALGO_PRIV2, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, /* 46 */ { .fid = EF_PK_DEC, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 47 */ { .fid = EF_ALGO_PRIV3, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, /* 47 */ { .fid = EF_PK_AUT, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 48 */ { .fid = EF_PK_SIG, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 48 */ { .fid = EF_PB_SIG, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 49 */ { .fid = EF_PK_DEC, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 49 */ { .fid = EF_PB_DEC, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 50 */ { .fid = EF_PK_AUT, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 50 */ { .fid = EF_PB_AUT, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 51 */ { .fid = EF_PB_SIG, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 51 */ { .fid = EF_PW_PRIV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 52 */ { .fid = EF_PB_DEC, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, /* 52 */ { .fid = EF_DEK, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_NONE },
/* 53 */ { .fid = EF_PB_AUT, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 54 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, /* 53 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 55 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = ACL_NONE } //end /* 54 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = ACL_NONE } //end
}; };
const file_t *MF = &file_entries[0]; const file_t *MF = &file_entries[0];

View File

@@ -24,18 +24,17 @@
#define EF_PW1 0x1081 #define EF_PW1 0x1081
#define EF_RC 0x1082 #define EF_RC 0x1082
#define EF_PW3 0x1083 #define EF_PW3 0x1083
#define EF_PW1_RETRIES 0x1084
#define EF_RC_RETRIES 0x1085
#define EF_PW3_RETRIES 0x1086
#define EF_ALGO_PRIV1 0x10c1 #define EF_ALGO_PRIV1 0x10c1
#define EF_ALGO_PRIV2 0x10c2 #define EF_ALGO_PRIV2 0x10c2
#define EF_ALGO_PRIV3 0x10c3 #define EF_ALGO_PRIV3 0x10c3
#define EF_PW_PRIV 0x10c4
#define EF_PK_SIG 0x10d1 #define EF_PK_SIG 0x10d1
#define EF_PK_DEC 0x10d2 #define EF_PK_DEC 0x10d2
#define EF_PK_AUT 0x10d3 #define EF_PK_AUT 0x10d3
#define EF_PB_SIG 0x10d4 #define EF_PB_SIG 0x10d4
#define EF_PB_DEC 0x10d5 #define EF_PB_DEC 0x10d5
#define EF_PB_AUT 0x10d6 #define EF_PB_AUT 0x10d6
#define EF_DEK 0x1099
#define EF_EXT_HEADER 0x004d //C #define EF_EXT_HEADER 0x004d //C
#define EF_FULL_AID 0x004f //S #define EF_FULL_AID 0x004f //S

View File

@@ -25,10 +25,14 @@
#include "mbedtls/rsa.h" #include "mbedtls/rsa.h"
#include "mbedtls/ecdsa.h" #include "mbedtls/ecdsa.h"
#include "mbedtls/ecdh.h" #include "mbedtls/ecdh.h"
#include "mbedtls/asn1.h"
bool has_pw1 = false; bool has_pw1 = false;
bool has_pw2 = false;
bool has_pw3 = false; bool has_pw3 = false;
uint8_t session_pw1[32];
uint8_t session_pw3[32]; uint8_t session_pw3[32];
static uint8_t dek[IV_SIZE+32];
uint8_t openpgp_aid[] = { uint8_t openpgp_aid[] = {
6, 6,
@@ -73,17 +77,9 @@ static int cmd_select() {
file_t *pe = NULL; file_t *pe = NULL;
uint16_t fid = 0x0; uint16_t fid = 0x0;
// Only "first or only occurence" supported
//if ((p2 & 0xF3) != 0x00) {
// return SW_INCORRECT_P1P2();
//}
if (apdu.cmd_apdu_data_len >= 2) if (apdu.cmd_apdu_data_len >= 2)
fid = get_uint16_t(apdu.cmd_apdu_data, 0); fid = get_uint16_t(apdu.cmd_apdu_data, 0);
//if ((fid & 0xff00) == (KEY_PREFIX << 8))
// fid = (PRKD_PREFIX << 8) | (fid & 0xff);
if (!pe) { if (!pe) {
if (p1 == 0x0) { //Select MF, DF or EF - File identifier or absent if (p1 == 0x0) { //Select MF, DF or EF - File identifier or absent
if (apdu.cmd_apdu_data_len == 0) { if (apdu.cmd_apdu_data_len == 0) {
@@ -154,12 +150,6 @@ void scan_files() {
dhash[0] = sizeof(def); dhash[0] = sizeof(def);
double_hash_pin(def, sizeof(def), dhash+1); double_hash_pin(def, sizeof(def), dhash+1);
flash_write_data_to_file(ef, dhash, sizeof(dhash)); flash_write_data_to_file(ef, dhash, sizeof(dhash));
ef = search_by_fid(EF_PW1_RETRIES, NULL, SPECIFY_ANY);
if (ef && !ef->data) {
const uint8_t retries = 3;
flash_write_data_to_file(ef, &retries, sizeof(retries));
}
} }
} }
if ((ef = search_by_fid(EF_RC, NULL, SPECIFY_ANY))) { if ((ef = search_by_fid(EF_RC, NULL, SPECIFY_ANY))) {
@@ -171,12 +161,6 @@ void scan_files() {
dhash[0] = sizeof(def); dhash[0] = sizeof(def);
double_hash_pin(def, sizeof(def), dhash+1); double_hash_pin(def, sizeof(def), dhash+1);
flash_write_data_to_file(ef, dhash, sizeof(dhash)); flash_write_data_to_file(ef, dhash, sizeof(dhash));
ef = search_by_fid(EF_RC_RETRIES, NULL, SPECIFY_ANY);
if (ef && !ef->data) {
const uint8_t retries = 3;
flash_write_data_to_file(ef, &retries, sizeof(retries));
}
} }
} }
if ((ef = search_by_fid(EF_PW3, NULL, SPECIFY_ANY))) { if ((ef = search_by_fid(EF_PW3, NULL, SPECIFY_ANY))) {
@@ -188,12 +172,6 @@ void scan_files() {
dhash[0] = sizeof(def); dhash[0] = sizeof(def);
double_hash_pin(def, sizeof(def), dhash+1); double_hash_pin(def, sizeof(def), dhash+1);
flash_write_data_to_file(ef, dhash, sizeof(dhash)); flash_write_data_to_file(ef, dhash, sizeof(dhash));
ef = search_by_fid(EF_PW3_RETRIES, NULL, SPECIFY_ANY);
if (ef && !ef->data) {
const uint8_t retries = 3;
flash_write_data_to_file(ef, &retries, sizeof(retries));
}
} }
} }
if ((ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY))) { if ((ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY))) {
@@ -203,9 +181,79 @@ void scan_files() {
flash_write_data_to_file(ef, def, sizeof(def)); flash_write_data_to_file(ef, def, sizeof(def));
} }
} }
if ((ef = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_ANY))) {
if (!ef->data) {
TU_LOG1("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));
}
}
if ((ef = search_by_fid(EF_DEK, NULL, SPECIFY_ANY))) {
if (!ef->data) {
TU_LOG1("DEK is empty\r\n");
const uint8_t def1[6] = { 0x31,0x32,0x33,0x34,0x35,0x36 };
const uint8_t def3[8] = { 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38 };
uint8_t def[IV_SIZE+32+32];
const uint8_t *dek = random_bytes_get(IV_SIZE+32);
memcpy(def, dek, IV_SIZE+32);
memcpy(def+IV_SIZE+32, dek+IV_SIZE, 32);
hash_multi(def1, sizeof(def1), session_pw1);
aes_encrypt_cfb_256(session_pw1, def, def+IV_SIZE, 32);
memset(session_pw1, 0, sizeof(session_pw1));
hash_multi(def3, sizeof(def3), session_pw3);
aes_encrypt_cfb_256(session_pw3, def, def+IV_SIZE+32, 32);
memset(session_pw1, 0, sizeof(session_pw1));
flash_write_data_to_file(ef, def, sizeof(def));
}
}
low_flash_available(); low_flash_available();
} }
int load_dek() {
if (!has_pw1 && !has_pw2 && !has_pw3)
return CCID_NO_LOGIN;
file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF);
if (!tf)
return CCID_ERR_FILE_NOT_FOUND;
int r = CCID_OK;
if (has_pw1 || has_pw2) {
memcpy(dek, file_read(tf->data+sizeof(uint16_t)), IV_SIZE+32);
r = aes_decrypt_cfb_256(session_pw1, dek, dek+IV_SIZE, 32);
}
else if (has_pw3) {
memcpy(dek, file_read(tf->data+sizeof(uint16_t)), IV_SIZE);
memcpy(dek+IV_SIZE, file_read(tf->data+sizeof(uint16_t)+IV_SIZE+32), 32);
r = aes_decrypt_cfb_256(session_pw3, dek, dek+IV_SIZE, 32);
}
if (r != 0)
return CCID_EXEC_ERROR;
return CCID_OK;
}
void release_dek() {
memset(dek, 0, sizeof(dek));
}
int dek_encrypt(uint8_t *data, size_t len) {
int r;
if ((r = load_dek()) != CCID_OK)
return r;
r = aes_encrypt_cfb_256(dek+IV_SIZE, dek, data, len);
release_dek();
return r;
}
int dek_decrypt(uint8_t *data, size_t len) {
int r;
if ((r = load_dek()) != CCID_OK)
return r;
r = aes_decrypt_cfb_256(dek+IV_SIZE, dek, data, len);
release_dek();
return r;
}
void init_openpgp() { void init_openpgp() {
isUserAuthenticated = false; isUserAuthenticated = false;
has_pw1 = has_pw3 = false; has_pw1 = has_pw3 = false;
@@ -303,6 +351,38 @@ int parse_ch_data(const file_t *f, int mode) {
return lpdif+4; return lpdif+4;
} }
int inc_sig_count() {
file_t *pw_status;
if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF)) || !pw_status->data)
return SW_REFERENCE_NOT_FOUND();
if (file_read_uint8(pw_status->data+2) == 1)
has_pw1 = false;
file_t *ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY);
if (!ef || !ef->data)
return CCID_ERR_FILE_NOT_FOUND;
uint8_t *p = file_read(ef->data+2);
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));
if (r != CCID_OK)
return CCID_EXEC_ERROR;
low_flash_available();
return CCID_OK;
}
int reset_sig_count() {
file_t *ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY);
if (!ef || !ef->data)
return CCID_ERR_FILE_NOT_FOUND;
uint8_t q[3] = { 0 };
int r = flash_write_data_to_file(ef, q, sizeof(q));
if (r != CCID_OK)
return CCID_EXEC_ERROR;
low_flash_available();
return CCID_OK;
}
int parse_sec_tpl(const file_t *f, int mode) { int parse_sec_tpl(const file_t *f, int mode) {
res_APDU[res_APDU_size++] = EF_SEC_TPL & 0xff; res_APDU[res_APDU_size++] = EF_SEC_TPL & 0xff;
res_APDU[res_APDU_size++] = 5; res_APDU[res_APDU_size++] = 5;
@@ -374,21 +454,10 @@ int parse_pw_status(const file_t *f, int mode) {
res_APDU[res_APDU_size++] = EF_PW_STATUS & 0xff; res_APDU[res_APDU_size++] = EF_PW_STATUS & 0xff;
res_APDU[res_APDU_size++] = 7; res_APDU[res_APDU_size++] = 7;
} }
res_APDU[res_APDU_size++] = 0x1; ef = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_ANY);
res_APDU[res_APDU_size++] = 127;
res_APDU[res_APDU_size++] = 127;
res_APDU[res_APDU_size++] = 127;
ef = search_by_fid(EF_PW1_RETRIES, NULL, SPECIFY_ANY);
if (ef && ef->data) { if (ef && ef->data) {
res_APDU[res_APDU_size++] = file_read_uint8(ef->data+2); memcpy(res_APDU+res_APDU_size, file_read(ef->data+2), 7);
} res_APDU_size += 7;
ef = search_by_fid(EF_RC_RETRIES, NULL, SPECIFY_ANY);
if (ef && ef->data) {
res_APDU[res_APDU_size++] = file_read_uint8(ef->data+2);
}
ef = search_by_fid(EF_PW3_RETRIES, NULL, SPECIFY_ANY);
if (ef && ef->data) {
res_APDU[res_APDU_size++] = file_read_uint8(ef->data+2);
} }
return res_APDU_size-init_len; return res_APDU_size-init_len;
} }
@@ -523,7 +592,9 @@ int parse_algoinfo(const file_t *f, int mode) {
res_APDU[res_APDU_size++] = EF_ALGO_INFO & 0xff; res_APDU[res_APDU_size++] = EF_ALGO_INFO & 0xff;
uint8_t *lp = res_APDU+res_APDU_size; uint8_t *lp = res_APDU+res_APDU_size;
res_APDU_size++; res_APDU_size++;
datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_SIG); datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_SIG); datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_SIG); datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_SIG); datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_SIG);
@@ -533,7 +604,9 @@ int parse_algoinfo(const file_t *f, int mode) {
datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_SIG); datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_SIG); datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_DEC); datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_DEC); datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_DEC); datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_DEC); datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_DEC);
@@ -545,7 +618,9 @@ int parse_algoinfo(const file_t *f, int mode) {
datalen += parse_algo(algorithm_attr_cv25519, EF_ALGO_DEC); datalen += parse_algo(algorithm_attr_cv25519, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_x448, EF_ALGO_DEC); datalen += parse_algo(algorithm_attr_x448, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_AUT); datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_AUT); datalen += parse_algo(algorithm_attr_rsa4k, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_AUT); datalen += parse_algo(algorithm_attr_p256k1, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_AUT); datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_AUT);
@@ -631,14 +706,16 @@ static int cmd_get_data() {
int pin_reset_retries(const file_t *pin, bool force) { int pin_reset_retries(const file_t *pin, bool force) {
if (!pin) if (!pin)
return CCID_ERR_NULL_PARAM; return CCID_ERR_NULL_PARAM;
const file_t *act = search_by_fid(pin->fid+3, NULL, SPECIFY_EF); file_t *pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF);
if (!act) if (!pw_status)
return CCID_ERR_FILE_NOT_FOUND; return CCID_ERR_FILE_NOT_FOUND;
uint8_t retries = file_read_uint8(act->data+2); uint8_t p[7];
memcpy(p, file_read(pw_status->data+2), 7);
uint8_t retries = p[3+(pin->fid&0x3)];
if (retries == 0 && force == false) //blocked if (retries == 0 && force == false) //blocked
return CCID_ERR_BLOCKED; return CCID_ERR_BLOCKED;
retries = 3; p[3+(pin->fid&0x3)] = 3;
int r = flash_write_data_to_file((file_t *)act, &retries, sizeof(retries)); int r = flash_write_data_to_file(pw_status, p, file_read_uint16(pw_status->data));
low_flash_available(); low_flash_available();
return r; return r;
} }
@@ -646,19 +723,20 @@ int pin_reset_retries(const file_t *pin, bool force) {
int pin_wrong_retry(const file_t *pin) { int pin_wrong_retry(const file_t *pin) {
if (!pin) if (!pin)
return CCID_ERR_NULL_PARAM; return CCID_ERR_NULL_PARAM;
const file_t *act = search_by_fid(pin->fid+3, NULL, SPECIFY_EF); file_t *pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF);
if (!act) if (!pw_status)
return CCID_ERR_FILE_NOT_FOUND; return CCID_ERR_FILE_NOT_FOUND;
uint8_t retries = file_read_uint8(act->data+2); uint8_t p[7];
if (retries > 0) { memcpy(p, file_read(pw_status->data+2), 7);
retries -= 1; if (p[3+(pin->fid&0x3)] > 0) {
int r = flash_write_data_to_file((file_t *)act, &retries, sizeof(retries)); p[3+(pin->fid&0x3)] -= 1;
int r = flash_write_data_to_file(pw_status, p, file_read_uint16(pw_status->data));
if (r != CCID_OK) if (r != CCID_OK)
return r; return r;
low_flash_available(); low_flash_available();
if (retries == 0) if (p[3+(pin->fid&0x3)] == 0)
return CCID_ERR_BLOCKED; return CCID_ERR_BLOCKED;
return retries; return p[3+(pin->fid&0x3)];
} }
return CCID_ERR_BLOCKED; return CCID_ERR_BLOCKED;
} }
@@ -670,7 +748,7 @@ int check_pin(const file_t *pin, const uint8_t *data, size_t len) {
return SW_REFERENCE_NOT_FOUND(); return SW_REFERENCE_NOT_FOUND();
} }
isUserAuthenticated = false; isUserAuthenticated = false;
has_pw1 = has_pw3 = false; //has_pw1 = has_pw3 = false;
uint8_t dhash[32]; uint8_t dhash[32];
double_hash_pin(data, len, dhash); double_hash_pin(data, len, dhash);
@@ -689,8 +767,13 @@ int check_pin(const file_t *pin, const uint8_t *data, size_t len) {
if (r != CCID_OK) if (r != CCID_OK)
return SW_MEMORY_FAILURE(); return SW_MEMORY_FAILURE();
isUserAuthenticated = true; isUserAuthenticated = true;
if (pin->fid == EF_PW1) if (pin->fid == EF_PW1) {
if (P2(apdu) == 0x81)
has_pw1 = true; has_pw1 = true;
else
has_pw2 = true;
hash_multi(data, len, session_pw1);
}
else if (pin->fid == EF_PW3) { else if (pin->fid == EF_PW3) {
has_pw3 = true; has_pw3 = true;
hash_multi(data, len, session_pw3); hash_multi(data, len, session_pw3);
@@ -702,25 +785,39 @@ static int cmd_verify() {
uint8_t p1 = P1(apdu); uint8_t p1 = P1(apdu);
uint8_t p2 = P2(apdu); uint8_t p2 = P2(apdu);
if (p1 != 0x0 || (p2 & 0x60) != 0x0) if (p1 == 0xFF) {
if (apdu.cmd_apdu_data_len != 0)
return SW_WRONG_DATA();
if (p2 == 0x81)
has_pw1 = false;
else if (p2 == 0x82)
has_pw2 = false;
else if (p2 == 0x83)
has_pw3 = false;
return SW_OK();
}
else if (p1 != 0x0 || (p2 & 0x60) != 0x0)
return SW_WRONG_P1P2(); return SW_WRONG_P1P2();
uint8_t qualifier = p2&0x1f; uint8_t qualifier = p2&0x1f;
uint16_t fid = 0x1000 | p2; uint16_t fid = 0x1000 | p2;
if (fid == EF_RC && apdu.cmd_apdu_data_len > 0) if (fid == EF_RC && apdu.cmd_apdu_data_len > 0)
fid = EF_PW1; fid = EF_PW1;
file_t *pw, *retries; file_t *pw, *pw_status;
if (!(pw = search_by_fid(fid, NULL, SPECIFY_EF))) if (!(pw = search_by_fid(fid, NULL, SPECIFY_EF)))
return SW_REFERENCE_NOT_FOUND(); return SW_REFERENCE_NOT_FOUND();
if (!(retries = search_by_fid(fid+3, NULL, SPECIFY_EF))) if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF)))
return SW_REFERENCE_NOT_FOUND(); return SW_REFERENCE_NOT_FOUND();
if (file_read_uint8(pw->data+2) == 0) //not initialized if (file_read_uint8(pw->data+2) == 0) //not initialized
return SW_REFERENCE_NOT_FOUND(); return SW_REFERENCE_NOT_FOUND();
if (apdu.cmd_apdu_data_len > 0) { if (apdu.cmd_apdu_data_len > 0) {
return check_pin(pw, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len); return check_pin(pw, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
} }
if (file_read_uint8(retries->data+2) == 0) uint8_t retries = file_read_uint8(pw_status->data+2+3+(fid&0x3));
if (retries == 0)
return SW_PIN_BLOCKED(); return SW_PIN_BLOCKED();
return set_res_sw(0x63, 0xc0 | file_read_uint8(retries->data+2)); if ((p2 == 0x81 && has_pw1) || (p2 == 0x82 && has_pw2) || (p2 == 0x83 && has_pw3))
return SW_OK();
return set_res_sw(0x63, 0xc0 | retries);
} }
static int cmd_put_data() { static int cmd_put_data() {
@@ -735,6 +832,10 @@ static 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_PW_STATUS) {
fid = EF_PW_PRIV;
apdu.cmd_apdu_data_len = 4; //we silently ommit the reset parameters
}
if (apdu.cmd_apdu_data_len > 0 && (ef->type & FILE_DATA_FLASH)) { if (apdu.cmd_apdu_data_len > 0 && (ef->type & FILE_DATA_FLASH)) {
int r = flash_write_data_to_file(ef, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len); int r = flash_write_data_to_file(ef, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
if (r != CCID_OK) if (r != CCID_OK)
@@ -834,6 +935,10 @@ int store_keys(void *key_ctx, int type, uint16_t key_id) {
key_size = 32; key_size = 32;
memcpy(kdata, key_ctx, key_size); memcpy(kdata, key_ctx, key_size);
} }
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); //r = aes_encrypt_cfb_256(file_read(pw3->data+2), session_pw3, kdata, key_size);
//if (r != CCID_OK) //if (r != CCID_OK)
// return r; // return r;
@@ -848,15 +953,10 @@ int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) {
//wait_button(); //wait_button();
int key_size = file_read_uint16(fkey->data); int key_size = file_read_uint16(fkey->data);
uint8_t kdata[4096/8]; uint8_t kdata[4096/8];
//if (!has_pw3)
// return CCID_NO_LOGIN;
//file_t *pw3 = search_by_fid(EF_PW3, NULL, SPECIFY_EF);
//if (!pw3)
// return CCID_ERR_FILE_NOT_FOUND;
memcpy(kdata, file_read(fkey->data+2), key_size); memcpy(kdata, file_read(fkey->data+2), key_size);
//int r = aes_decrypt_cfb_256(file_read(pw3->data+2), session_pw3, kdata, key_size); if (dek_decrypt(kdata, key_size) != 0) {
//if (r != CCID_OK) return SW_EXEC_ERROR();
// return r; }
if (mbedtls_mpi_read_binary(&ctx->P, kdata, key_size/2) != 0) { if (mbedtls_mpi_read_binary(&ctx->P, kdata, key_size/2) != 0) {
mbedtls_rsa_free(ctx); mbedtls_rsa_free(ctx);
return CCID_WRONG_DATA; return CCID_WRONG_DATA;
@@ -889,6 +989,9 @@ int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey) {
int key_size = file_read_uint16(fkey->data); int key_size = file_read_uint16(fkey->data);
uint8_t kdata[67]; //Worst case, 521 bit + 1byte uint8_t kdata[67]; //Worst case, 521 bit + 1byte
memcpy(kdata, file_read(fkey->data+2), key_size); memcpy(kdata, file_read(fkey->data+2), key_size);
if (dek_decrypt(kdata, key_size) != 0) {
return SW_EXEC_ERROR();
}
mbedtls_ecp_group_id gid = kdata[0]; mbedtls_ecp_group_id gid = kdata[0];
int r = mbedtls_ecp_read_key(gid, ctx, kdata+1, key_size-1); int r = mbedtls_ecp_read_key(gid, ctx, kdata+1, key_size-1);
if (r != 0) { if (r != 0) {
@@ -951,6 +1054,8 @@ static int cmd_keypair_gen() {
return SW_INCORRECT_P1P2(); return SW_INCORRECT_P1P2();
if (apdu.cmd_apdu_data_len != 2 && apdu.cmd_apdu_data_len != 5) if (apdu.cmd_apdu_data_len != 2 && apdu.cmd_apdu_data_len != 5)
return SW_WRONG_LENGTH(); return SW_WRONG_LENGTH();
if (!has_pw3 && P1(apdu) == 0x80)
return SW_SECURITY_STATUS_NOT_SATISFIED();
uint16_t fid = 0x0; uint16_t fid = 0x0;
int r = CCID_OK; int r = CCID_OK;
@@ -1021,6 +1126,8 @@ static int cmd_keypair_gen() {
r = flash_write_data_to_file(pbef, res_APDU, res_APDU_size); r = flash_write_data_to_file(pbef, res_APDU, res_APDU_size);
if (r != CCID_OK) if (r != CCID_OK)
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
if (fid == EF_PK_SIG)
reset_sig_count();
low_flash_available(); low_flash_available();
return SW_OK(); return SW_OK();
} }
@@ -1035,13 +1142,99 @@ static int cmd_keypair_gen() {
return SW_INCORRECT_P1P2(); return SW_INCORRECT_P1P2();
} }
int rsa_sign(mbedtls_rsa_context *ctx, const uint8_t *data, size_t data_len, uint8_t *out, size_t *out_len) {
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;
if (mbedtls_asn1_get_tag(&d, end, &seq_len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) == 0) {
mbedtls_asn1_buf mdb;
r = mbedtls_asn1_get_alg_null(&d, end, &mdb);
if (r == 0) {
if (mbedtls_asn1_get_tag(&d, end, &hash_len, MBEDTLS_ASN1_OCTET_STRING) == 0) {
if (memcmp(mdb.p, "\x2B\x0E\x03\x02\x1A", 5) == 0)
md = MBEDTLS_MD_SHA1;
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x04", 9) == 0)
md = MBEDTLS_MD_SHA224;
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x01", 9) == 0)
md = MBEDTLS_MD_SHA256;
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x02", 9) == 0)
md = MBEDTLS_MD_SHA384;
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x03", 9) == 0)
md = MBEDTLS_MD_SHA512;
hsh = d;
}
}
}
if (md == MBEDTLS_MD_NONE) {
if (data_len == 32)
md = MBEDTLS_MD_SHA256;
else if (data_len == 20)
md = MBEDTLS_MD_SHA1;
else if (data_len == 28)
md = MBEDTLS_MD_SHA224;
else if (data_len == 48)
md = MBEDTLS_MD_SHA384;
else if (data_len == 64)
md = MBEDTLS_MD_SHA512;
hash_len = data_len;
hsh = (uint8_t *)data;
}
if (md == MBEDTLS_MD_NONE) {
if (data_len < key_size) //needs padding
memset((uint8_t *)data+data_len, 0, key_size-data_len);
r = mbedtls_rsa_private(ctx, random_gen, NULL, data, out);
}
else {
uint8_t *signature = (uint8_t *)calloc(key_size, sizeof(uint8_t));
r = mbedtls_rsa_pkcs1_sign(ctx, random_gen, NULL, md, hash_len, hsh, signature);
memcpy(out, signature, key_size);
free(signature);
}
*out_len = key_size;
inc_sig_count();
return r;
}
int ecdsa_sign(mbedtls_ecdsa_context *ctx, const uint8_t *data, size_t data_len, uint8_t *out, size_t *out_len) {
mbedtls_md_type_t md = MBEDTLS_MD_SHA256;
if (data_len == 32)
md = MBEDTLS_MD_SHA256;
else if (data_len == 20)
md = MBEDTLS_MD_SHA1;
else if (data_len == 28)
md = MBEDTLS_MD_SHA224;
else if (data_len == 48)
md = MBEDTLS_MD_SHA384;
else if (data_len == 64)
md = MBEDTLS_MD_SHA512;
mbedtls_mpi ri, si;
mbedtls_mpi_init(&ri);
mbedtls_mpi_init(&si);
int r = mbedtls_ecdsa_sign(&ctx->grp, &ri, &si, &ctx->d, data, data_len, random_gen, NULL);
if (r == 0) {
mbedtls_mpi_write_binary(&ri, out, mbedtls_mpi_size(&ri));
*out_len = mbedtls_mpi_size(&ri);
mbedtls_mpi_write_binary(&si, out+*out_len, mbedtls_mpi_size(&si));
*out_len += mbedtls_mpi_size(&si);
}
mbedtls_mpi_free(&ri);
mbedtls_mpi_free(&si);
inc_sig_count();
return r;
}
static int cmd_pso() { static int cmd_pso() {
uint16_t algo_fid = 0x0, pk_fid = 0x0; uint16_t algo_fid = 0x0, pk_fid = 0x0;
if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) { if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) {
if (!has_pw3 && !has_pw1)
return SW_SECURITY_STATUS_NOT_SATISFIED();
algo_fid = EF_ALGO_PRIV1; algo_fid = EF_ALGO_PRIV1;
pk_fid = EF_PK_SIG; pk_fid = EF_PK_SIG;
} }
else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) {
if (!has_pw3 && !has_pw2)
return SW_SECURITY_STATUS_NOT_SATISFIED();
algo_fid = EF_ALGO_PRIV2; algo_fid = EF_ALGO_PRIV2;
pk_fid = EF_PK_DEC; pk_fid = EF_PK_DEC;
} }
@@ -1068,30 +1261,12 @@ static int cmd_pso() {
if (r != CCID_OK) if (r != CCID_OK)
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) { if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) {
mbedtls_md_type_t md = MBEDTLS_MD_NONE; size_t olen = 0;
if (memcmp(apdu.cmd_apdu_data, "\x30\x51\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00",17)) r = rsa_sign(&ctx, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, res_APDU, &olen);
md = MBEDTLS_MD_SHA256;
else if (memcmp(apdu.cmd_apdu_data, "\x30\x51\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00",17))
md = MBEDTLS_MD_SHA384;
else if (memcmp(apdu.cmd_apdu_data, "\x30\x51\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00",17))
md = MBEDTLS_MD_SHA512;
uint8_t hash_len = apdu.cmd_apdu_data[18];
if (md == MBEDTLS_MD_SHA256 && hash_len != 32)
return SW_WRONG_DATA();
else if (md == MBEDTLS_MD_SHA384 && hash_len != 48)
return SW_WRONG_DATA();
else if (md == MBEDTLS_MD_SHA512 && hash_len != 64)
return SW_WRONG_DATA();
const uint8_t *hash = apdu.cmd_apdu_data+19;
uint8_t *signature = calloc( 1, ctx.len );
r = mbedtls_rsa_pkcs1_sign(&ctx, random_gen, NULL, md, hash_len, hash, signature);
memcpy(res_APDU, signature, key_size);
free(signature);
mbedtls_rsa_free(&ctx); mbedtls_rsa_free(&ctx);
if (r != 0) { if (r != 0)
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
} res_APDU_size = olen;
res_APDU_size = key_size;
//apdu.expected_res_size = key_size; //apdu.expected_res_size = key_size;
} }
else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) {
@@ -1099,76 +1274,44 @@ static int cmd_pso() {
memset(apdu.cmd_apdu_data+apdu.cmd_apdu_data_len, 0, key_size-apdu.cmd_apdu_data_len); memset(apdu.cmd_apdu_data+apdu.cmd_apdu_data_len, 0, key_size-apdu.cmd_apdu_data_len);
size_t olen = 0; size_t olen = 0;
r = mbedtls_rsa_pkcs1_decrypt(&ctx, random_gen, NULL, &olen, apdu.cmd_apdu_data+1, res_APDU, key_size); r = mbedtls_rsa_pkcs1_decrypt(&ctx, random_gen, NULL, &olen, apdu.cmd_apdu_data+1, res_APDU, key_size);
if (r != 0) {
mbedtls_rsa_free(&ctx); mbedtls_rsa_free(&ctx);
if (r != 0) {
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
} }
res_APDU_size = olen; res_APDU_size = olen;
mbedtls_rsa_free(&ctx);
} }
} }
else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA) { else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA) {
if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) { if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) {
mbedtls_ecdsa_context ctx; mbedtls_ecdsa_context ctx;
mbedtls_ecdsa_init(&ctx); mbedtls_ecdsa_init(&ctx);
mbedtls_md_type_t md = MBEDTLS_MD_SHA256;
if (apdu.cmd_apdu_data_len == 32)
md = MBEDTLS_MD_SHA256;
else if (apdu.cmd_apdu_data_len == 20)
md = MBEDTLS_MD_SHA1;
else if (apdu.cmd_apdu_data_len == 28)
md = MBEDTLS_MD_SHA224;
else if (apdu.cmd_apdu_data_len == 48)
md = MBEDTLS_MD_SHA384;
else if (apdu.cmd_apdu_data_len == 64)
md = MBEDTLS_MD_SHA512;
r = load_private_key_ecdsa(&ctx, ef); r = load_private_key_ecdsa(&ctx, ef);
if (r != CCID_OK) if (r != CCID_OK)
return SW_CONDITIONS_NOT_SATISFIED(); return SW_CONDITIONS_NOT_SATISFIED();
size_t olen = 0; size_t olen = 0;
uint8_t buf[MBEDTLS_ECDSA_MAX_LEN]; r = ecdsa_sign(&ctx, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, res_APDU, &olen);
mbedtls_mpi ri, si;
mbedtls_mpi_init(&ri);
mbedtls_mpi_init(&si);
r = mbedtls_ecdsa_sign(&ctx.grp, &ri, &si, &ctx.d, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, random_gen, NULL);
if (r != 0) {
mbedtls_mpi_free(&ri);
mbedtls_mpi_free(&si);
mbedtls_ecdsa_free(&ctx); mbedtls_ecdsa_free(&ctx);
if (r != 0)
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
} res_APDU_size = olen;
mbedtls_mpi_write_binary(&ri, res_APDU, mbedtls_mpi_size(&ri));
res_APDU_size = mbedtls_mpi_size(&ri);
mbedtls_mpi_write_binary(&si, res_APDU+res_APDU_size, mbedtls_mpi_size(&si));
res_APDU_size += mbedtls_mpi_size(&si);
mbedtls_mpi_free(&ri);
mbedtls_mpi_free(&si);
mbedtls_ecdsa_free(&ctx);
} }
else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) {
mbedtls_ecdh_context ctx; mbedtls_ecdh_context ctx;
uint8_t kdata[67]; uint8_t kdata[67];
uint8_t *data = apdu.cmd_apdu_data; uint8_t *data = apdu.cmd_apdu_data, *end = data+apdu.cmd_apdu_data_len;
if (*data++ != 0xA6) size_t len = 0;
if (mbedtls_asn1_get_tag(&data, end, &len, 0xA6) != 0)
return SW_WRONG_DATA(); return SW_WRONG_DATA();
if (*data == 0x82) data += 3; if (*data++ != 0x7f)
else if (*data == 0x81) data += 2;
else data++;
if (*data != 0x7f || *(data+1) != 0x49)
return SW_WRONG_DATA(); return SW_WRONG_DATA();
data += 2; if (mbedtls_asn1_get_tag(&data, end, &len, 0x49) != 0 || mbedtls_asn1_get_tag(&data, end, &len, 0x86) != 0)
if (*data == 0x82) data += 3;
else if (*data == 0x81) data += 2;
else data++;
if (*data++ != 0x86)
return SW_WRONG_DATA(); return SW_WRONG_DATA();
uint16_t dlen = 0; if (len != 2*key_size-1)
if (*data == 0x82) { dlen = (*(data+1) << 8) | *(data+2); data += 3; }
else if (*data == 0x81) { dlen = *(data+1); data += 2; }
else { dlen = *data++; }
if (dlen != 2*key_size-1)
return SW_WRONG_LENGTH(); return SW_WRONG_LENGTH();
memcpy(kdata, file_read(ef->data+2), key_size); memcpy(kdata, file_read(ef->data+2), key_size);
if (dek_decrypt(kdata, key_size) != 0) {
return SW_EXEC_ERROR();
}
mbedtls_ecdh_init(&ctx); mbedtls_ecdh_init(&ctx);
mbedtls_ecp_group_id gid = kdata[0]; mbedtls_ecp_group_id gid = kdata[0];
r = mbedtls_ecdh_setup(&ctx, gid); r = mbedtls_ecdh_setup(&ctx, gid);
@@ -1181,19 +1324,18 @@ static int cmd_pso() {
mbedtls_ecdh_free(&ctx); mbedtls_ecdh_free(&ctx);
return SW_DATA_INVALID(); return SW_DATA_INVALID();
} }
r = mbedtls_ecdh_read_public(&ctx, data-1, dlen+1); r = mbedtls_ecdh_read_public(&ctx, data-1, len+1);
if (r != 0) { if (r != 0) {
mbedtls_ecdh_free(&ctx); mbedtls_ecdh_free(&ctx);
return SW_DATA_INVALID(); return SW_DATA_INVALID();
} }
size_t olen = 0; size_t olen = 0;
res_APDU[0] = 0x04; r = mbedtls_ecdh_calc_secret(&ctx, &olen, res_APDU, MBEDTLS_ECP_MAX_BYTES, random_gen, NULL);
r = mbedtls_ecdh_calc_secret(&ctx, &olen, res_APDU+1, MBEDTLS_ECP_MAX_BYTES, random_gen, NULL);
if (r != 0) { if (r != 0) {
mbedtls_ecdh_free(&ctx); mbedtls_ecdh_free(&ctx);
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
} }
res_APDU_size = olen+1; res_APDU_size = olen;
mbedtls_ecdh_free(&ctx); mbedtls_ecdh_free(&ctx);
} }
} }
@@ -1203,6 +1345,11 @@ static int cmd_pso() {
static int cmd_terminate_df() { static int cmd_terminate_df() {
if (P1(apdu) != 0x0 && P2(apdu) != 0x0) if (P1(apdu) != 0x0 && P2(apdu) != 0x0)
return SW_INCORRECT_P1P2(); return SW_INCORRECT_P1P2();
file_t *retries;
if (!(retries = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF)))
return SW_REFERENCE_NOT_FOUND();
if (!has_pw3 && file_read_uint8(retries->data+2+6) > 0)
return SW_SECURITY_STATUS_NOT_SATISFIED();
if (apdu.cmd_apdu_data_len != 0) if (apdu.cmd_apdu_data_len != 0)
return SW_WRONG_LENGTH(); return SW_WRONG_LENGTH();
initialize_flash(true); initialize_flash(true);
@@ -1223,6 +1370,53 @@ static int cmd_challenge() {
return SW_OK(); return SW_OK();
} }
static int cmd_internal_aut() {
if (P1(apdu) != 0x00 || P2(apdu) != 0x00)
return SW_WRONG_P1P2();
if (!has_pw3 && !has_pw2)
return SW_SECURITY_STATUS_NOT_SATISFIED();
file_t *algo_ef = search_by_fid(EF_ALGO_PRIV3, NULL, SPECIFY_EF);
if (!algo_ef)
return SW_FILE_NOT_FOUND();
const uint8_t *algo = algorithm_attr_rsa2k+1;
uint16_t algo_len = algorithm_attr_rsa2k[0];
if (algo_ef && algo_ef->data) {
algo_len = file_read_uint16(algo_ef->data);
algo = file_read(algo_ef->data+2);
}
file_t *ef = search_by_fid(EF_PK_AUT, NULL, SPECIFY_EF);
if (!ef)
return SW_FILE_NOT_FOUND();
int r = CCID_OK;
if (algo[0] == ALGO_RSA) {
mbedtls_rsa_context ctx;
mbedtls_rsa_init(&ctx);
r = load_private_key_rsa(&ctx, ef);
if (r != CCID_OK)
return SW_EXEC_ERROR();
size_t olen = 0;
r = rsa_sign(&ctx, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, res_APDU, &olen);
mbedtls_rsa_free(&ctx);
if (r != 0)
return SW_EXEC_ERROR();
res_APDU_size = olen;
}
else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA) {
mbedtls_ecdsa_context ctx;
mbedtls_ecdsa_init(&ctx);
r = load_private_key_ecdsa(&ctx, ef);
if (r != CCID_OK)
return SW_CONDITIONS_NOT_SATISFIED();
size_t olen = 0;
r = ecdsa_sign(&ctx, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, res_APDU, &olen);
mbedtls_ecdsa_free(&ctx);
if (r != 0)
return SW_EXEC_ERROR();
res_APDU_size = olen;
}
return SW_OK();
}
typedef struct cmd typedef struct cmd
{ {
uint8_t ins; uint8_t ins;
@@ -1236,6 +1430,7 @@ typedef struct cmd
#define INS_ACTIVATE_FILE 0x44 #define INS_ACTIVATE_FILE 0x44
#define INS_KEYPAIR_GEN 0x47 #define INS_KEYPAIR_GEN 0x47
#define INS_CHALLENGE 0x84 #define INS_CHALLENGE 0x84
#define INS_INTERNAL_AUT 0x88
#define INS_SELECT 0xA4 #define INS_SELECT 0xA4
#define INS_GET_DATA 0xCA #define INS_GET_DATA 0xCA
#define INS_PUT_DATA 0xDA #define INS_PUT_DATA 0xDA
@@ -1253,6 +1448,7 @@ static const cmd_t cmds[] = {
{ INS_TERMINATE_DF, cmd_terminate_df }, { INS_TERMINATE_DF, cmd_terminate_df },
{ INS_ACTIVATE_FILE, cmd_activate_file }, { INS_ACTIVATE_FILE, cmd_activate_file },
{ INS_CHALLENGE, cmd_challenge }, { INS_CHALLENGE, cmd_challenge },
{ INS_INTERNAL_AUT, cmd_internal_aut },
{ 0x00, 0x0} { 0x00, 0x0}
}; };