50 Commits
v1.0 ... v1.4

Author SHA1 Message Date
Pol Henarejos
78d71a6d9c Upgrading to version 1.4.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-21 00:55:50 +01:00
Pol Henarejos
0a2740fbab Added AES derive support based on HKDF.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-21 00:54:59 +01:00
Pol Henarejos
3192e928ff Fixed a bug with deleting intermediate EF on flash. A new field on EF flash structure is added. Thus, the old structure must be erased.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-21 00:16:00 +01:00
Pol Henarejos
ae1e2ac111 Fix storing public key description when generating a new keypair.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-19 19:11:09 +01:00
Pol Henarejos
d87073f4cc Auth status should not be removed when apple is reselected. Auth status is removed when the reader disconnects the card (unloads it).
With this fix, it is possible to login first and send immediate low level APDU command that requires authentification (such as login+CMAC).

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-17 23:37:02 +01:00
Pol Henarejos
36a8f78313 Added support for AES-CMAC.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-17 23:35:07 +01:00
Pol Henarejos
0628d5015c Update asymmetric-ciphering.md 2022-03-17 00:44:38 +01:00
Pol Henarejos
daf0f98660 Update asymmetric-ciphering.md
Adding examples for ECDH key derivation.
2022-03-17 00:43:44 +01:00
Pol Henarejos
1f06c44a89 Adding ecdh support with MBEDTLS.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-17 00:28:40 +01:00
Pol Henarejos
ab1490a50b Added ECDH key derivation.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-17 00:28:16 +01:00
Pol Henarejos
23f53a6095 Added some free on bad return.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-16 23:40:09 +01:00
Pol Henarejos
920cf3a1c5 Upgrading to v1.2
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-16 23:31:17 +01:00
Pol Henarejos
74f2a80fb4 Update README.md 2022-03-16 18:51:16 +01:00
Pol Henarejos
29361fa110 Update usage.md 2022-03-16 18:50:29 +01:00
Pol Henarejos
679486d38c Update sign-verify.md
Added examples for ECDSA and ECDSA-SHA signature and verification.
2022-03-16 18:22:11 +01:00
Pol Henarejos
8988d1cf15 Fix support for ECDSA and ECDSA-SHAx signatures.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-16 18:20:17 +01:00
Pol Henarejos
693c890663 Update asymmetric-ciphering.md
Added OAEP encryption and decryption examples.
2022-03-16 15:13:23 +01:00
Pol Henarejos
591b02804e Fix for HASH PSS and HASH PKCS.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-16 12:02:49 +01:00
Pol Henarejos
37c3028b1c Adding code for AES derive
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-16 12:02:09 +01:00
Pol Henarejos
2cedf65f1a Update sign-verify.md 2022-03-16 11:33:00 +01:00
Pol Henarejos
c31e4f8c2b Update sign-verify.md 2022-03-16 10:04:04 +01:00
Pol Henarejos
c756e756b6 Update sign-verify.md
Added SHA1-RSA-PKCS-PSS and RSA-PKCS-PSS examples.
2022-03-16 10:03:24 +01:00
Pol Henarejos
73bc2ede6b Fixed a bug with RSA-PKCS-PSS. Surprisingly, PKCS_V21 signature takes in place (input buffer = output buffer) and, for a strange reason, it does not work for res_APDU.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-16 00:27:07 +01:00
Pol Henarejos
dcae71a4e8 Create asymmetric-ciphering.md 2022-03-15 01:04:27 +01:00
Pol Henarejos
71a5a456c5 Update sign-verify.md 2022-03-15 01:00:00 +01:00
Pol Henarejos
e1f88acb17 Create sign-verify.md 2022-03-15 00:53:18 +01:00
Pol Henarejos
5a2ec221b7 Update usage.md 2022-03-15 00:02:49 +01:00
Pol Henarejos
a018699283 Update README.md 2022-03-14 01:04:50 +01:00
Pol Henarejos
0a10fa4fbb Update README.md 2022-03-14 01:03:38 +01:00
Pol Henarejos
c609cec441 Update usage.md 2022-03-14 00:59:22 +01:00
Pol Henarejos
587ead4ad9 Update README.md 2022-03-14 00:58:42 +01:00
Pol Henarejos
e3d809ae7f Create aes.md 2022-03-14 00:55:32 +01:00
Pol Henarejos
4f142d1b93 Create backup-and-restore.md 2022-03-13 23:54:15 +01:00
Pol Henarejos
1f7e7aa14c Update usage.md 2022-03-13 22:14:56 +01:00
Pol Henarejos
a4baa99fce Update usage.md 2022-03-13 20:21:54 +01:00
Pol Henarejos
df020efa46 Update usage.md
Added keypair generation.
2022-03-12 20:24:55 +01:00
Pol Henarejos
c31dd26e22 Create usage.md 2022-03-12 01:29:25 +01:00
Pol Henarejos
6d22fc20d4 Update README.md 2022-03-10 18:56:51 +01:00
Pol Henarejos
3d74952c41 Update README.md
Adding usage section.
2022-03-10 18:47:01 +01:00
Pol Henarejos
51f574f9f6 Update README.md
Adding build section.
2022-03-10 18:36:59 +01:00
Pol Henarejos
1c6fb98350 Logout user when applet selected.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-10 00:48:47 +01:00
Pol Henarejos
f1c0b12f5c Increasing random buffer and checks.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-10 00:13:13 +01:00
Pol Henarejos
213b675b9f Fix returned error code for wrong pin.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-10 00:09:35 +01:00
Pol Henarejos
b701f639ac Increasing CCID buffer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-10 00:08:22 +01:00
Pol Henarejos
4a0144ed2a Adding version header to show in lsusb command (bcdDevice) and major version in pkcs15-tool -D.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-08 18:43:30 +01:00
Pol Henarejos
9be78aade6 Changing project name.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-08 00:37:27 +01:00
Pol Henarejos
b7ee325d4f Create README.md 2022-03-08 00:36:42 +01:00
Pol Henarejos
3e89e8f835 Updating submodule url
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-07 23:50:15 +01:00
Pol Henarejos
70f71e742e Not used anymore.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-07 23:49:08 +01:00
Pol Henarejos
7988083d6b Reorganization of file structure.
At this moment I disabled openpgp/gnuk due to missing deep tests.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-03-07 23:37:10 +01:00
67 changed files with 1189 additions and 793 deletions

2
.gitmodules vendored
View File

@@ -1,6 +1,6 @@
[submodule "OpenSC"]
path = OpenSC
url = https://github.com/OpenSC/OpenSC
url = https://github.com/polhenarejos/OpenSC
[submodule "mbedtls"]
path = mbedtls
url = https://github.com/ARMmbed/mbedtls

View File

@@ -2,14 +2,14 @@ cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(hsm2040 C CXX ASM)
project(pico_hsm C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(hsm2040)
add_executable(pico_hsm)
if (NOT DEFINED USB_VID)
set(USB_VID 0xFEFF)
@@ -25,24 +25,16 @@ set_source_files_properties(
PROPERTIES COMPILE_DEFINITIONS "PACKAGE_VERSION=\"0.22.0\";OPENSC_CONF_PATH=\".\""
)
target_sources(hsm2040 PUBLIC
${CMAKE_CURRENT_LIST_DIR}/hsm2040.c
${CMAKE_CURRENT_LIST_DIR}/usb_descriptors.c
${CMAKE_CURRENT_LIST_DIR}/openpgp.c
${CMAKE_CURRENT_LIST_DIR}/debug.c
${CMAKE_CURRENT_LIST_DIR}/openpgp-do.c
${CMAKE_CURRENT_LIST_DIR}/ac.c
${CMAKE_CURRENT_LIST_DIR}/file.c
${CMAKE_CURRENT_LIST_DIR}/flash.c
${CMAKE_CURRENT_LIST_DIR}/flash_openpgp.c
${CMAKE_CURRENT_LIST_DIR}/low_flash.c
${CMAKE_CURRENT_LIST_DIR}/call-rsa.c
${CMAKE_CURRENT_LIST_DIR}/call-ec_p256k1.c
${CMAKE_CURRENT_LIST_DIR}/ecc-ed25519.c
${CMAKE_CURRENT_LIST_DIR}/ecc-ed448.c
${CMAKE_CURRENT_LIST_DIR}/random.c
${CMAKE_CURRENT_LIST_DIR}/ecc-mont.c
${CMAKE_CURRENT_LIST_DIR}/ecc-x448.c
target_sources(pico_hsm PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/hsm/hsm2040.c
${CMAKE_CURRENT_LIST_DIR}/src/hsm/sc_hsm.c
${CMAKE_CURRENT_LIST_DIR}/src/usb/usb_descriptors.c
${CMAKE_CURRENT_LIST_DIR}/src/fs/file.c
${CMAKE_CURRENT_LIST_DIR}/src/fs/flash.c
${CMAKE_CURRENT_LIST_DIR}/src/fs/low_flash.c
${CMAKE_CURRENT_LIST_DIR}/src/rng/random.c
${CMAKE_CURRENT_LIST_DIR}/src/rng/neug.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/sha256.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/aes.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/sha512.c
@@ -61,17 +53,21 @@ target_sources(hsm2040 PUBLIC
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/md5.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/ripemd160.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/sha1.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/ecdh.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/cmac.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/cipher.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/cipher_wrap.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/chachapoly.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/camellia.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/chacha20.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/aria.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/poly1305.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/gcm.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/ccm.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/des.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/nist_kw.c
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/hkdf.c
${CMAKE_CURRENT_LIST_DIR}/shake256.c
${CMAKE_CURRENT_LIST_DIR}/neug.c
${CMAKE_CURRENT_LIST_DIR}/ec_p256k1.c
${CMAKE_CURRENT_LIST_DIR}/bn.c
${CMAKE_CURRENT_LIST_DIR}/mod.c
${CMAKE_CURRENT_LIST_DIR}/jpc_p256k1.c
${CMAKE_CURRENT_LIST_DIR}/modp256k1.c
${CMAKE_CURRENT_LIST_DIR}/p448.c
${CMAKE_CURRENT_LIST_DIR}/mod25638.c
${CMAKE_CURRENT_LIST_DIR}/sc_hsm.c
${CMAKE_CURRENT_LIST_DIR}/OpenSC/src/libopensc/pkcs15.c
${CMAKE_CURRENT_LIST_DIR}/OpenSC/src/libopensc/pkcs15-prkey.c
${CMAKE_CURRENT_LIST_DIR}/OpenSC/src/libopensc/pkcs15-algo.c
@@ -90,18 +86,21 @@ target_sources(hsm2040 PUBLIC
${CMAKE_CURRENT_LIST_DIR}/OpenSC/src/libopensc/padding.c
)
target_include_directories(hsm2040 PUBLIC
${CMAKE_CURRENT_LIST_DIR}
target_include_directories(pico_hsm PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/fs
${CMAKE_CURRENT_LIST_DIR}/src/hsm
${CMAKE_CURRENT_LIST_DIR}/src/rng
${CMAKE_CURRENT_LIST_DIR}/src/usb
${CMAKE_CURRENT_LIST_DIR}/opensc/src
${CMAKE_CURRENT_LIST_DIR}/mbedtls/include
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library
)
pico_add_extra_outputs(hsm2040)
pico_add_extra_outputs(pico_hsm)
#target_compile_definitions(hsm2040 PRIVATE MBEDTLS_ECDSA_DETERMINISTIC=1)
#target_compile_definitions(pico_hsm PRIVATE MBEDTLS_ECDSA_DETERMINISTIC=1)
target_link_libraries(hsm2040 PRIVATE pico_stdlib tinyusb_device tinyusb_board pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id)
target_link_libraries(pico_hsm PRIVATE pico_stdlib tinyusb_device tinyusb_board pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id)
#
#project(flash_nuke C CXX ASM)

97
README.md Normal file
View File

@@ -0,0 +1,97 @@
# Raspberry Pico HSM
This is a project to create a Hardware Security Module (HSM) with a Raspberry Pico. It converts your Pico board into a HSM which is able to generate and store private keys, encrypt or decrypt with AES or signing data without to disclose the private key. In detail, the private key never leaves the board and it cannot be retrieved as it is encrypted in the flash memory.
## Capabilities
- Key generation and protected storing.
- RSA key generation from 1024 to 4096 bits.
- ECDSA key generation from 192 to 521 bits.
- ECC curves secp192r1, secp256r1, secp384r1, secp521r1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1, secp192k1 (insecure), secp256k1.
- SHA1, SHA224, SHA256, SHA384, SHA256 digests.
- RSA-PSS, RSA-PKCS and raw RSA signature.
- ECDSA signature.
- RSA-OEP and RSA-X-509 decryption.
- AES key generation of 128, 192 and 256 bits.
- AES-CBC encryption/decryption.
- PIN authorization.
- PKCS11 compliant interface.
- HRNG (hardware random number generator).
- Device Key Encryption Key (DKEK) shares.
- DKEK n-of-m threshold scheme.
- USB/CCID support with OpenSC, openssl, etc.
- Extended APDU support.
## Security considerations
All secret keys (asymmetric and symmetric) are stored encrypted in the flash memory of the Raspberry Pico. DKEK 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. All keys (including DKEK) are loaded and cleared every time to avoid potential flaws.
At the same time, DKEK 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 HSM 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 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_hsm_file.uf2 output_hsm_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-hsm
cd pico-hsm
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_hsm.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.
## Usage
The firmware uploaded to the Pico contains a reader and a virtual smart card. It is like having a physical reader with an inserted SIM card.
We recommend the use of [OpenSC](http://github.com/opensc/opensc/ "OpenSC") to communicate with the reader. If it is not installed, you can download and build it or install the binaries for your system. The first command is to ensure that the Pico is detected as a HSM:
```
opensc-tool -an
````
It should return a text like the following:
```
Using reader with a card: Free Software Initiative of Japan Gnuk
3b:fe:18:00:00:81:31:fe:45:80:31:81:54:48:53:4d:31:73:80:21:40:81:07:fa
SmartCard-HSM
```
The name of the reader may vary if you modified the VID/PID.
For initialization and asymmetric operations, check [doc/usage.md](/doc/usage.md).
For signing and verification operations, check [doc/sign-verify.md](/doc/sign-verify.md).
For asymmetric encryption and decryption, check [doc/asymmetric-ciphering.md](/doc/asymmetric-ciphering.md).
For backup, restore and DKEK share management, check [doc/backup-and-restore.md](/doc/backup-and-restore.md).
For AES key generation, encryption and decryption, check [doc/aes.md](/doc/aes.md).
## Driver
Pico HSM uses the `sc-hsm` driver provided by [OpenSC](https://github.com/OpenSC/OpenSC/ "OpenSC") or the `sc-hsm-embedded` driver provided by [CardContact](https://github.com/CardContact/sc-hsm-embedded "CardContact"). 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.
Pico HSM 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 HSM.
The way to communicate is exactly the same as with other cards, such as OpenPGP or similar.
For an advanced usage, see the docs and examples.
### 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-hsm/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.
## Credits
Pico HSM uses the following libraries or portion of code:
- OpenSC for ASN1 manipulation.
- mbedTLS for cryptographic operations.
- gnuk for low level CCID procedures and OpenPGP support.
- TinyUSB for low level USB procedures.
In the case of gnuk, it is intended to work with STM32 processor and its family. Part of the code of CCID procedures are ported and adapted to run with Pico.

401
common.h
View File

@@ -1,401 +0,0 @@
/**
* \file common.h
*
* \brief Utility macros for internal use in the library
*/
/*
* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBEDTLS_LIBRARY_COMMON_H
#define MBEDTLS_LIBRARY_COMMON_H
#include "mbedtls/build_info.h"
#include <stdint.h>
/** Helper to define a function as static except when building invasive tests.
*
* If a function is only used inside its own source file and should be
* declared `static` to allow the compiler to optimize for code size,
* but that function has unit tests, define it with
* ```
* MBEDTLS_STATIC_TESTABLE int mbedtls_foo(...) { ... }
* ```
* and declare it in a header in the `library/` directory with
* ```
* #if defined(MBEDTLS_TEST_HOOKS)
* int mbedtls_foo(...);
* #endif
* ```
*/
#if defined(MBEDTLS_TEST_HOOKS)
#define MBEDTLS_STATIC_TESTABLE
#else
#define MBEDTLS_STATIC_TESTABLE static
#endif
#if defined(MBEDTLS_TEST_HOOKS)
extern void (*mbedtls_test_hook_test_fail)( const char * test, int line, const char * file );
#define MBEDTLS_TEST_HOOK_TEST_ASSERT( TEST ) \
do { \
if( ( ! ( TEST ) ) && ( ( *mbedtls_test_hook_test_fail ) != NULL ) ) \
{ \
( *mbedtls_test_hook_test_fail )( #TEST, __LINE__, __FILE__ ); \
} \
} while( 0 )
#else
#define MBEDTLS_TEST_HOOK_TEST_ASSERT( TEST )
#endif /* defined(MBEDTLS_TEST_HOOKS) */
/** Allow library to access its structs' private members.
*
* Although structs defined in header files are publicly available,
* their members are private and should not be accessed by the user.
*/
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
/** Byte Reading Macros
*
* Given a multi-byte integer \p x, MBEDTLS_BYTE_n retrieves the n-th
* byte from x, where byte 0 is the least significant byte.
*/
#define MBEDTLS_BYTE_0( x ) ( (uint8_t) ( ( x ) & 0xff ) )
#define MBEDTLS_BYTE_1( x ) ( (uint8_t) ( ( ( x ) >> 8 ) & 0xff ) )
#define MBEDTLS_BYTE_2( x ) ( (uint8_t) ( ( ( x ) >> 16 ) & 0xff ) )
#define MBEDTLS_BYTE_3( x ) ( (uint8_t) ( ( ( x ) >> 24 ) & 0xff ) )
#define MBEDTLS_BYTE_4( x ) ( (uint8_t) ( ( ( x ) >> 32 ) & 0xff ) )
#define MBEDTLS_BYTE_5( x ) ( (uint8_t) ( ( ( x ) >> 40 ) & 0xff ) )
#define MBEDTLS_BYTE_6( x ) ( (uint8_t) ( ( ( x ) >> 48 ) & 0xff ) )
#define MBEDTLS_BYTE_7( x ) ( (uint8_t) ( ( ( x ) >> 56 ) & 0xff ) )
/**
* Get the unsigned 32 bits integer corresponding to four bytes in
* big-endian order (MSB first).
*
* \param data Base address of the memory to get the four bytes from.
* \param offset Offset from \p data of the first and most significant
* byte of the four bytes to build the 32 bits unsigned
* integer from.
*/
#ifndef MBEDTLS_GET_UINT32_BE
#define MBEDTLS_GET_UINT32_BE( data , offset ) \
( \
( (uint32_t) ( data )[( offset ) ] << 24 ) \
| ( (uint32_t) ( data )[( offset ) + 1] << 16 ) \
| ( (uint32_t) ( data )[( offset ) + 2] << 8 ) \
| ( (uint32_t) ( data )[( offset ) + 3] ) \
)
#endif
/**
* Put in memory a 32 bits unsigned integer in big-endian order.
*
* \param n 32 bits unsigned integer to put in memory.
* \param data Base address of the memory where to put the 32
* bits unsigned integer in.
* \param offset Offset from \p data where to put the most significant
* byte of the 32 bits unsigned integer \p n.
*/
#ifndef MBEDTLS_PUT_UINT32_BE
#define MBEDTLS_PUT_UINT32_BE( n, data, offset ) \
{ \
( data )[( offset ) ] = MBEDTLS_BYTE_3( n ); \
( data )[( offset ) + 1] = MBEDTLS_BYTE_2( n ); \
( data )[( offset ) + 2] = MBEDTLS_BYTE_1( n ); \
( data )[( offset ) + 3] = MBEDTLS_BYTE_0( n ); \
}
#endif
/**
* Get the unsigned 32 bits integer corresponding to four bytes in
* little-endian order (LSB first).
*
* \param data Base address of the memory to get the four bytes from.
* \param offset Offset from \p data of the first and least significant
* byte of the four bytes to build the 32 bits unsigned
* integer from.
*/
#ifndef MBEDTLS_GET_UINT32_LE
#define MBEDTLS_GET_UINT32_LE( data, offset ) \
( \
( (uint32_t) ( data )[( offset ) ] ) \
| ( (uint32_t) ( data )[( offset ) + 1] << 8 ) \
| ( (uint32_t) ( data )[( offset ) + 2] << 16 ) \
| ( (uint32_t) ( data )[( offset ) + 3] << 24 ) \
)
#endif
/**
* Put in memory a 32 bits unsigned integer in little-endian order.
*
* \param n 32 bits unsigned integer to put in memory.
* \param data Base address of the memory where to put the 32
* bits unsigned integer in.
* \param offset Offset from \p data where to put the least significant
* byte of the 32 bits unsigned integer \p n.
*/
#ifndef MBEDTLS_PUT_UINT32_LE
#define MBEDTLS_PUT_UINT32_LE( n, data, offset ) \
{ \
( data )[( offset ) ] = MBEDTLS_BYTE_0( n ); \
( data )[( offset ) + 1] = MBEDTLS_BYTE_1( n ); \
( data )[( offset ) + 2] = MBEDTLS_BYTE_2( n ); \
( data )[( offset ) + 3] = MBEDTLS_BYTE_3( n ); \
}
#endif
/**
* Get the unsigned 16 bits integer corresponding to two bytes in
* little-endian order (LSB first).
*
* \param data Base address of the memory to get the two bytes from.
* \param offset Offset from \p data of the first and least significant
* byte of the two bytes to build the 16 bits unsigned
* integer from.
*/
#ifndef MBEDTLS_GET_UINT16_LE
#define MBEDTLS_GET_UINT16_LE( data, offset ) \
( \
( (uint16_t) ( data )[( offset ) ] ) \
| ( (uint16_t) ( data )[( offset ) + 1] << 8 ) \
)
#endif
/**
* Put in memory a 16 bits unsigned integer in little-endian order.
*
* \param n 16 bits unsigned integer to put in memory.
* \param data Base address of the memory where to put the 16
* bits unsigned integer in.
* \param offset Offset from \p data where to put the least significant
* byte of the 16 bits unsigned integer \p n.
*/
#ifndef MBEDTLS_PUT_UINT16_LE
#define MBEDTLS_PUT_UINT16_LE( n, data, offset ) \
{ \
( data )[( offset ) ] = MBEDTLS_BYTE_0( n ); \
( data )[( offset ) + 1] = MBEDTLS_BYTE_1( n ); \
}
#endif
/**
* Get the unsigned 16 bits integer corresponding to two bytes in
* big-endian order (MSB first).
*
* \param data Base address of the memory to get the two bytes from.
* \param offset Offset from \p data of the first and most significant
* byte of the two bytes to build the 16 bits unsigned
* integer from.
*/
#ifndef MBEDTLS_GET_UINT16_BE
#define MBEDTLS_GET_UINT16_BE( data, offset ) \
( \
( (uint16_t) ( data )[( offset ) ] << 8 ) \
| ( (uint16_t) ( data )[( offset ) + 1] ) \
)
#endif
/**
* Put in memory a 16 bits unsigned integer in big-endian order.
*
* \param n 16 bits unsigned integer to put in memory.
* \param data Base address of the memory where to put the 16
* bits unsigned integer in.
* \param offset Offset from \p data where to put the most significant
* byte of the 16 bits unsigned integer \p n.
*/
#ifndef MBEDTLS_PUT_UINT16_BE
#define MBEDTLS_PUT_UINT16_BE( n, data, offset ) \
{ \
( data )[( offset ) ] = MBEDTLS_BYTE_1( n ); \
( data )[( offset ) + 1] = MBEDTLS_BYTE_0( n ); \
}
#endif
/**
* Get the unsigned 24 bits integer corresponding to three bytes in
* big-endian order (MSB first).
*
* \param data Base address of the memory to get the three bytes from.
* \param offset Offset from \p data of the first and most significant
* byte of the three bytes to build the 24 bits unsigned
* integer from.
*/
#ifndef MBEDTLS_GET_UINT24_BE
#define MBEDTLS_GET_UINT24_BE( data , offset ) \
( \
( (uint32_t) ( data )[( offset ) ] << 16 ) \
| ( (uint32_t) ( data )[( offset ) + 1] << 8 ) \
| ( (uint32_t) ( data )[( offset ) + 2] ) \
)
#endif
/**
* Put in memory a 24 bits unsigned integer in big-endian order.
*
* \param n 24 bits unsigned integer to put in memory.
* \param data Base address of the memory where to put the 24
* bits unsigned integer in.
* \param offset Offset from \p data where to put the most significant
* byte of the 24 bits unsigned integer \p n.
*/
#ifndef MBEDTLS_PUT_UINT24_BE
#define MBEDTLS_PUT_UINT24_BE( n, data, offset ) \
{ \
( data )[( offset ) ] = MBEDTLS_BYTE_2( n ); \
( data )[( offset ) + 1] = MBEDTLS_BYTE_1( n ); \
( data )[( offset ) + 2] = MBEDTLS_BYTE_0( n ); \
}
#endif
/**
* Get the unsigned 24 bits integer corresponding to three bytes in
* little-endian order (LSB first).
*
* \param data Base address of the memory to get the three bytes from.
* \param offset Offset from \p data of the first and least significant
* byte of the three bytes to build the 24 bits unsigned
* integer from.
*/
#ifndef MBEDTLS_GET_UINT24_LE
#define MBEDTLS_GET_UINT24_LE( data, offset ) \
( \
( (uint32_t) ( data )[( offset ) ] ) \
| ( (uint32_t) ( data )[( offset ) + 1] << 8 ) \
| ( (uint32_t) ( data )[( offset ) + 2] << 16 ) \
)
#endif
/**
* Put in memory a 24 bits unsigned integer in little-endian order.
*
* \param n 24 bits unsigned integer to put in memory.
* \param data Base address of the memory where to put the 24
* bits unsigned integer in.
* \param offset Offset from \p data where to put the least significant
* byte of the 24 bits unsigned integer \p n.
*/
#ifndef MBEDTLS_PUT_UINT24_LE
#define MBEDTLS_PUT_UINT24_LE( n, data, offset ) \
{ \
( data )[( offset ) ] = MBEDTLS_BYTE_0( n ); \
( data )[( offset ) + 1] = MBEDTLS_BYTE_1( n ); \
( data )[( offset ) + 2] = MBEDTLS_BYTE_2( n ); \
}
#endif
/**
* Get the unsigned 64 bits integer corresponding to eight bytes in
* big-endian order (MSB first).
*
* \param data Base address of the memory to get the eight bytes from.
* \param offset Offset from \p data of the first and most significant
* byte of the eight bytes to build the 64 bits unsigned
* integer from.
*/
#ifndef MBEDTLS_GET_UINT64_BE
#define MBEDTLS_GET_UINT64_BE( data, offset ) \
( \
( (uint64_t) ( data )[( offset ) ] << 56 ) \
| ( (uint64_t) ( data )[( offset ) + 1] << 48 ) \
| ( (uint64_t) ( data )[( offset ) + 2] << 40 ) \
| ( (uint64_t) ( data )[( offset ) + 3] << 32 ) \
| ( (uint64_t) ( data )[( offset ) + 4] << 24 ) \
| ( (uint64_t) ( data )[( offset ) + 5] << 16 ) \
| ( (uint64_t) ( data )[( offset ) + 6] << 8 ) \
| ( (uint64_t) ( data )[( offset ) + 7] ) \
)
#endif
/**
* Put in memory a 64 bits unsigned integer in big-endian order.
*
* \param n 64 bits unsigned integer to put in memory.
* \param data Base address of the memory where to put the 64
* bits unsigned integer in.
* \param offset Offset from \p data where to put the most significant
* byte of the 64 bits unsigned integer \p n.
*/
#ifndef MBEDTLS_PUT_UINT64_BE
#define MBEDTLS_PUT_UINT64_BE( n, data, offset ) \
{ \
( data )[( offset ) ] = MBEDTLS_BYTE_7( n ); \
( data )[( offset ) + 1] = MBEDTLS_BYTE_6( n ); \
( data )[( offset ) + 2] = MBEDTLS_BYTE_5( n ); \
( data )[( offset ) + 3] = MBEDTLS_BYTE_4( n ); \
( data )[( offset ) + 4] = MBEDTLS_BYTE_3( n ); \
( data )[( offset ) + 5] = MBEDTLS_BYTE_2( n ); \
( data )[( offset ) + 6] = MBEDTLS_BYTE_1( n ); \
( data )[( offset ) + 7] = MBEDTLS_BYTE_0( n ); \
}
#endif
/**
* Get the unsigned 64 bits integer corresponding to eight bytes in
* little-endian order (LSB first).
*
* \param data Base address of the memory to get the eight bytes from.
* \param offset Offset from \p data of the first and least significant
* byte of the eight bytes to build the 64 bits unsigned
* integer from.
*/
#ifndef MBEDTLS_GET_UINT64_LE
#define MBEDTLS_GET_UINT64_LE( data, offset ) \
( \
( (uint64_t) ( data )[( offset ) + 7] << 56 ) \
| ( (uint64_t) ( data )[( offset ) + 6] << 48 ) \
| ( (uint64_t) ( data )[( offset ) + 5] << 40 ) \
| ( (uint64_t) ( data )[( offset ) + 4] << 32 ) \
| ( (uint64_t) ( data )[( offset ) + 3] << 24 ) \
| ( (uint64_t) ( data )[( offset ) + 2] << 16 ) \
| ( (uint64_t) ( data )[( offset ) + 1] << 8 ) \
| ( (uint64_t) ( data )[( offset ) ] ) \
)
#endif
/**
* Put in memory a 64 bits unsigned integer in little-endian order.
*
* \param n 64 bits unsigned integer to put in memory.
* \param data Base address of the memory where to put the 64
* bits unsigned integer in.
* \param offset Offset from \p data where to put the least significant
* byte of the 64 bits unsigned integer \p n.
*/
#ifndef MBEDTLS_PUT_UINT64_LE
#define MBEDTLS_PUT_UINT64_LE( n, data, offset ) \
{ \
( data )[( offset ) ] = MBEDTLS_BYTE_0( n ); \
( data )[( offset ) + 1] = MBEDTLS_BYTE_1( n ); \
( data )[( offset ) + 2] = MBEDTLS_BYTE_2( n ); \
( data )[( offset ) + 3] = MBEDTLS_BYTE_3( n ); \
( data )[( offset ) + 4] = MBEDTLS_BYTE_4( n ); \
( data )[( offset ) + 5] = MBEDTLS_BYTE_5( n ); \
( data )[( offset ) + 6] = MBEDTLS_BYTE_6( n ); \
( data )[( offset ) + 7] = MBEDTLS_BYTE_7( n ); \
}
#endif
/* Fix MSVC C99 compatible issue
* MSVC support __func__ from visual studio 2015( 1900 )
* Use MSVC predefine macro to avoid name check fail.
*/
#if (defined(_MSC_VER) && ( _MSC_VER <= 1900 ))
#define /*no-check-names*/ __func__ __FUNCTION__
#endif
#endif /* MBEDTLS_LIBRARY_COMMON_H */

58
doc/aes.md Normal file
View File

@@ -0,0 +1,58 @@
# AES
The Pico HSM supports AES secret key generation and CBC encryption/decryption. However, OpenSC does not provide AES support for sc-hsm driver. Instead, the `sc-hsm-embedded` module is used.
First, we setup the tool:
```
alias sc-tool=pkcs11-tool --module /path/to/libsc-hsm-pkcs11.so
```
## Secret key generation
Pico HSM supports AES keys with 128, 192 and 256 bits. To generate a secret 256 bits AES key:
```
$ sc-tool -l --pin 648219 --keygen --key-type AES:32 --id 12 --label "AES32"
Using slot 0 with a present token (0x1)
Key generated:
Secret Key Object; AES length 32
label: AES32
ID: 12
Usage: encrypt, decrypt
Access: sensitive, always sensitive, never extractable, local
```
For 128 bits, use the `--key-type aes:16`, for 192 bits, `aes:24`, and for 256 bits, `aes:32`.
For lack of AES support in AES, `pkcs15-tool -D` does not list AES keys. Instead, they can be listed with:
```
$ sc-tool -l --pin 648219 --list-object --type secrkey
Using slot 0 with a present token (0x1)
Secret Key Object; AES length 32
label: AES32
ID: 12
Usage: encrypt, decrypt
Access: sensitive, always sensitive, never extractable, local
```
## Encryption and decryption
Once a secret AES key is generated, a content can be encrypted and decrypted symmetrically:
```
$ echo "This is a text." | sc-tool -l --pin 648219 --encrypt --id 12 --mechanism aes-cbc > crypted.aes
````
The file `crypted.aes` contains the ciphered string with the AES key generated previously.
To decrypt the message, the inverse operation:
```
$ cat crypted.aes | sc-tool -l --pin 648219 --decrypt --id 12 --mechanism aes-cbc
Using slot 0 with a present token (0x1)
Using decrypt algorithm AES-CBC
This is a text.
```
AES-CBC it is a block operation and it requires an input size multiple of 16 bytes. Thus, for a trivial data, a padding operation has to be performed beforehand.

111
doc/asymmetric-ciphering.md Normal file
View File

@@ -0,0 +1,111 @@
# Asymmetric encryption and decryption
Pico HSM supports in place decryption with the following algorithms:
* RSA-PKCS
* RSA-X-509
* RSA-PKCS-OAEP
* ECDH-DERIVE
First, we generate the data:
```
$ echo "This is a test string. Be safe, be secure." > data
```
Obtain the public key and convert it to PEM format:
```
$ pkcs11-tool --read-object --pin 648219 --id 1 --type pubkey > 1.der
$ openssl rsa -inform DER -outform PEM -in 1.der -pubin > 1.pub
```
At this moment, you are able to verify with the public key in `1.pub`. The signature is computed inside the Pico HSM with the private key. It never leaves the device.
## RSA-PKCS
This algorithm uses the PKCSv1.5 padding. It is considered deprecated and insecure.
First, we encrypt the data with the public key:
```
$ openssl rsautl -encrypt -inkey 1.pub -in data -pubin -out data.crypt
```
Then, we decrypt with the private key inside the Pico HSM:
```
$ pkcs11-tool --id 1 --pin 648219 --decrypt --mechanism RSA-PKCS -i data.crypt
Using slot 0 with a present token (0x0)
Using decrypt algorithm RSA-PKCS
This is a test string. Be safe, be secure.
```
## RSA-X-509
In this algorithm, the data must be padded with a length equal to the size of private key (128, 256, 512 bytes for RSA-1024, RSA-2048 and RSA-4096, respectively).
First, we pad the data. The original data file occupies 29 bytes. Thus, for a 2048 bits key, a padding of 227 bytes is needed:
```
$ cp data data_pad
$ dd if=/dev/zero bs=1 count=227 >> data_pad
```
we encrypt the data with the public key:
```
$ openssl rsautl -encrypt -inkey 1.pub -in data_pad -pubin -out data.crypt -raw
```
Then, we decrypt with the private key inside the Pico HSM:
```
$ cat data.crypt|pkcs11-tool --id 4 --pin 648219 --decrypt --mechanism RSA-X-509
Using slot 0 with a present token (0x0)
Using decrypt algorithm RSA-X-509
This is a test string. Be safe, be secure.
```
## RSA-PKCS-OAEP
This algorithm is defined as PKCSv2.1 and it includes a padding mechanism to avoid garbage. Currently it only supports SHA256.
To encrypt the data:
```
$ openssl pkeyutl -encrypt -inkey 1.pub -pubin -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 -in data -out data.crypt
```
To decrypt with the private key inside the Pico HSM:
```
$ pkcs11-tool --id 1 --pin 648219 --decrypt --mechanism RSA-PKCS-OAEP -i data.crypt
Using slot 0 with a present token (0x0)
Using decrypt algorithm RSA-PKCS-OAEP
OAEP parameters: hashAlg=SHA256, mgf=MGF1-SHA256, source_type=0, source_ptr=0x0, source_len=0
This is a test string. Be safe, be secure.
```
## ECDH-DERIVE
ECC keys do not allow ciphering operations. Instead, the ECDH scheme provides a mechanism to exchange a shared symmetric key without transmitting it to the remote part. The shared key is composed by multiplying the local private key and the remote public key.
First, we create the remote part, Bob, by generating an ECC keypair and getting the public key:
```
$ openssl ecparam -genkey -name prime192v1 > bob.pem
$ openssl ec -in bob.pem -pubout -outform DER > bob.der
```
We derive the shared key by giving the Bob's public key to the Pico HSM:
```
$ pkcs11-tool --pin 648219 --id 11 --derive -i bob.der -o mine-bob.der
```
We compute the other shared key, with Bob's private key and our public key:
```
$ openssl pkeyutl -derive -out bob-mine.der -inkey bob.pem -peerkey 11.pub
```
Finally, we compare both shared keys:
```
$ cmp bob-mine.der mine-bob.der
```
No output is displayed if both are equal.
You can also view the contents of both keys:
```
$ xxd -p bob-mine.der
9874558aefa9d92cc051e5da6d1753987e5314925d6d78bf
$ xxd -p mine-bob.der
9874558aefa9d92cc051e5da6d1753987e5314925d6d78bf
```

218
doc/backup-and-restore.md Normal file
View File

@@ -0,0 +1,218 @@
# Backup and restore
Pico HSM supports secure backup and restore. This mechanism is used to export a private key securely and restore it into another Pico HSM or the same device. The exported key is encrypted with the Device Key Encryption Key (DKEK), an AES 256 bits key that is stored safely during the initialization.
## Initialization
It is highly recommended to initialize the Pico HSM with a known DKEK. You have multiple options:
* No DKEK (be careful!)
* Single DKEK share: the DKEK is stored safely with a passphrase outside the device and kept by one custodian. If the custodian looses the DKEK share or an attacker gets the share and the passphrase, the Pico HSM and all its contents will be compromised.
* Multiple DKEK shares: the DKEK is created from multiple portions of the original DKEK, kept by multiple custodians. For instance, a DKEK with 3 shares implies that the device cannot be fully initialized until all 3 custodians load their portion into the device. The order is irrelevant.
* DKEK n-of-m threshold scheme: the DKEK is created with at minimum of n of m portions. It adds more flexibility, as it does not require the availability of all custodians. For instance, an scheme of 3-of-5 implies that the DKEK can be created with the portions of 3 custodians of 5 in total. The order is irrelevant.
### No DKEK
If no DKEK is provided during the initialization, the Pico HSM will generate one randomly. Note that in this case, despite you still can export a private key but **you cannot import it into another Pico HSM**, since you do not know the DKEK. Furthermore, if you initialize again the device, another DKEK will be stored and the backups will not be restored, as they were encrypted with another DKEK.
Note that, even no DKEK is provided, the Pico HSM generates a DKEK internally but it is never exported for obvious reasons.
### Single DKEK
Before initializing the device, a DKEK is created with:
```
$ sc-hsm-tool --create-dkek-share dkek.pbe
Using reader with a card: Free Software Initiative of Japan Gnuk
The DKEK share will be enciphered using a key derived from a user supplied password.
The security of the DKEK share relies on a well chosen and sufficiently long password.
The recommended length is more than 10 characters, which are mixed letters, numbers and
symbols.
Please keep the generated DKEK share file in a safe location. We also recommend to keep a
paper printout, in case the electronic version becomes unavailable. A printable version
of the file can be generated using "openssl base64 -in <filename>".
Enter password to encrypt DKEK share :
Please retype password to confirm :
Enciphering DKEK share, please wait...
DKEK share created and saved to dkek.pbe
```
The generated file `dkek.pbe` contains the DKEK. Technically, it contains a share. But if a device is initialized with one share, it is equivalent to contain the full DKEK.
Keep these file in a safe place. If this file is lost, you can export the private keys but you will not be able to import into another device or in the same device if it is initialized again.
To initialize the device with a single share:
```
sc-hsm-tool --initialize --so-pin 3537363231383830 --pin 648219 --dkek-shares 1
```
At this moment, the Pico HSM expects the DKEK. It is loaded with the following command:
```
$ sc-hsm-tool --import-dkek-share dkek.pbe
Using reader with a card: Free Software Initiative of Japan Gnuk
Enter password to decrypt DKEK share :
Deciphering DKEK share, please wait...
DKEK share imported
DKEK shares : 1
DKEK key check value : 4B7DA256ACD4EF62
```
The Pico HSM is fully operative and you are ready to generate, export and import keys.
### Multiple DKEK
The process is similar with the [Single DKEK](#single-dkek), but it is repeated with multiple DKEK:
```
$ sc-hsm-tool --create-dkek-share dkek-share-1.pbe
$ sc-hsm-tool --create-dkek-share dkek-share-2.pbe
$ sc-hsm-tool --create-dkek-share dkek-share-3.pbe
```
The device is then initialized with 3 DKEK shares:
```
sc-hsm-tool --initialize --so-pin 3537363231383830 --pin 648219 --dkek-shares 3
```
And finally, all are imported one after the other, without special order:
```
$ sc-hsm-tool --import-dkek-share dkek-share-1.pbe
Using reader with a card: Free Software Initiative of Japan Gnuk
Enter password to decrypt DKEK share :
Deciphering DKEK share, please wait...
DKEK share imported
DKEK shares : 3
DKEK import pending, 2 share(s) still missing
$ sc-hsm-tool --import-dkek-share dkek-share-2.pbe
Using reader with a card: Free Software Initiative of Japan Gnuk
Enter password to decrypt DKEK share :
Deciphering DKEK share, please wait...
DKEK share imported
DKEK shares : 3
DKEK import pending, 1 share(s) still missing
$ sc-hsm-tool --import-dkek-share dkek-share-1.pbe
Using reader with a card: Free Software Initiative of Japan Gnuk
Enter password to decrypt DKEK share :
Deciphering DKEK share, please wait...
DKEK share imported
DKEK shares : 1
DKEK key check value : 4B7DA256ACD4EF62
```
### DKEK n-of-m threshold scheme
This scheme provides an extra level of flexiblity, as not all custodians are necessary to import the DKEK share. For instance, with the previous schemes, if a custodian gets unavailable, the initialization will block until the missing custodian can got to finalize the initialization.
With n-of-m threshold scheme, it flexibilizes the number of required custodians to reduce failure points. If a share is lost, the DKEK can still be recovered without major implications.
This scheme is not a replacement of DKEK shares. Instead, it splits the DKEK share encryption password amongst the n-of-m threshold scheme. For instance, if you define 2 shares and a scheme of 3-of-5 threshold for each share, it will imply 10 different custodians, where 6 are necessary to load both shares. You can also mix one share with traditional passphrase and the other with the n-of-m threshold scheme.
To generate a DKEK share with a 3-of-5 threshold scheme:
```
sc-hsm-tool --create-dkek-share dkek-share-1.pbe --pwd-shares-threshold 3 --pwd-shares-total 5
Using reader with a card:Free Software Initiative of Japan Gnuk
The DKEK will be enciphered using a randomly generated 64 bit password.
This password is split using a (3-of-5) threshold scheme.
Please keep the generated and encrypted DKEK file in a safe location. We also recommend
to keep a paper printout, in case the electronic version becomes unavailable. A printable version
of the file can be generated using "openssl base64 -in <filename>".
Press <enter> to continue
```
After enter, it will display 5 screens with the following information:
```
Share 1 of 5
Prime : f5:56:46:c9:a5:a1:01:87
Share ID : 1
Share value : 99:64:68:65:d8:8d:c0:5f
Please note ALL values above and press <enter> when finished
```
The `Prime` value is the same for all custodians. Only the first custodian is required to introduce it. Nevertheless, it is recommended that all custodians keep also the `Prime` value.
To import the DKEK share encrypted with this scheme:
```
$ sc-hsm-tool --import-dkek-share dkek-share-1.pbe --pwd-shares-total 3
Using reader with a card: Free Software Initiative of Japan Gnuk
Deciphering the DKEK for import into the SmartCard-HSM requires 3 key custodians
to present their share. Only the first key custodian needs to enter the public prime.
Please remember to present the share id as well as the share value.
Please enter prime: f5:56:46:c9:a5:a1:01:87
```
Then, all custodians introduce the `Share ID` and `Share value`:
```
Share 1 of 3
Please enter share ID: 1
Please enter share value: 99:64:68:65:d8:8d:c0:5f
```
After the 3 custodians introduce the share values, the share is successfully loaded.
## Backup
Once the Pico HSM is fully initialized, the device is ready to generate private keys and export them. To wrap a key and export them, the `Key Reference` field is necessary. To obtain it, you can list the objects with the `pkcs15-tool`:
```
$ pkcs15-tool -D
Using reader with a card: Free Software Initiative of Japan Gnuk
...
Private RSA Key [Certificate]
Object Flags : [0x03], private, modifiable
Usage : [0x2E], decrypt, sign, signRecover, unwrap
Access Flags : [0x1D], sensitive, alwaysSensitive, neverExtract, local
Algo_refs : 0
ModLength : 2048
Key ref : 1 (0x01)
Native : yes
Auth ID : 01
ID : 01
MD:guid : 748d16af-097a-cd84-2d62-92048f30f21d
...
```
Note that `Key ref` and `ID` may be different. Whilst different keys may share the same `ID` (highly discouraged), the `Key ref` is a value internally computed and unique.
To export and wrap the private key:
```
$ sc-hsm-tool --wrap-key wrap-key.bin --key-reference 1 --pin 648219
```
A file named `wrap-key.bin` is created with the private key encrypted securely with the DKEK.
## Restore
To restore the wraped key, a device initialized with the same DKEK is mandatory.
To unwrap the key:
```
$ sc-hsm-tool --unwrap-key wrap-key.bin --key-reference 10 --pin 648219
Using reader with a card: Free Software Initiative of Japan Gnuk
Wrapped key contains:
Key blob
Private Key Description (PRKD)
Certificate
Key successfully imported
```
Now, the key is restored in the device with the same `ID` as the original and with the specified `Key ref`.

153
doc/sign-verify.md Normal file
View File

@@ -0,0 +1,153 @@
# Sign and verify
Pico HSM supports in place signature of arbitrary data. It supports the following algorithms:
* RSA-PKCS
* RSA-X-509
* SHA1-RSA-PKCS
* SHA256-RSA-PKCS
* SHA224-RSA-PKCS
* SHA384-RSA-PKCS
* SHA512-RSA-PKCS
* RSA-PKCS-PSS
* SHA1-RSA-PKCS-PSS
* SHA256-RSA-PKCS-PSS
* SHA224-RSA-PKCS-PSS
* SHA384-RSA-PKCS-PSS
* SHA512-RSA-PKCS-PSS
* SHA1-ECDSA
* SHA224-ECDSA
* SHA256-ECDSA
First, we generate the data:
```
$ echo "This is a test string. Be safe, be secure." > data
```
Obtain the public key and convert it to PEM format:
```
$ pkcs11-tool --read-object --pin 648219 --id 1 --type pubkey > 1.der
$ openssl rsa -inform DER -outform PEM -in 1.der -pubin > 1.pub
```
At this moment, you are able to verify with the public key in `1.pub`. The signature is computed inside the Pico HSM with the private key. It never leaves the device.
## RSA-PKCS
This algorithm is used to sign raw data.
To sign the data:
```
$ pkcs11-tool --id 1 --sign --pin 648219 --mechanism RSA-PKCS -i data -o data.sig
```
To verify the signature:
```
$ openssl pkeyutl -verify -pubin -inkey 1.pub -in data -sigfile data.sig
Signature Verified Successfully
```
## SHA1-RSA-PKCS
This algorithm is used to sign digests computed outside. It supports SHA1, SHA224, SHA256, SHA384 and SHA512.
First, we generate a file with the digest:
```
openssl dgst -sha1 -binary -out data.sha1 data
```
To sign the data:
```
$ pkcs11-tool --id 1 --sign --pin 648219 --mechanism SHA1-RSA-PKCS -i data -o data.sig
```
To verify the signature:
```
$ openssl pkeyutl -verify -in data.sha1 -sigfile data.sig -pubin -inkey 1.pub -pkeyopt digest:sha1
Signature Verified Successfully
```
## RSA-X-509
This algorithm is used for signing raw data. In this algorithm, the data must be padded with a length equal to the size of private key (128, 256, 512 bytes for RSA-1024, RSA-2048 and RSA-4096, respectively).
First, we pad the data. The original data file occupies 29 bytes. Thus, for a 2048 bits key, a padding of 227 bytes is needed:
```
$ cp data data_pad
$ dd if=/dev/zero bs=1 count=227 >> data_pad
```
To sign the data:
```
$ pkcs11-tool --id 1 --sign --pin 648219 --mechanism RSA-X-509 -i data_pad -o data.sig
```
To verify the signature:
```
$ openssl rsautl -verify -inkey 1.pub -in data.sig -pubin -raw
This is a test string. Be safe, be secure.
```
## RSA-PKCS-PSS
This algorithm uses the RSA-PKCS with PSS salt to randomize the signature. Pico HSM does not support arbitrary salt lengths. Instead, it always uses the maximum salt length (the hash length). It uses the hash as the input.
To sign the data:
```
$ pkcs11-tool --id 1 --sign --pin 648219 --mechanism RSA-PKCS-PSS -i data.sha1 -o data.sig
```
To verify the signature:
```
$ openssl pkeyutl -verify -in data.sha1 -sigfile data.sig -pubin -inkey 1.pub -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:-1 -pkeyopt digest:sha1
Signature Verified Successfully
```
## SHA1-RSA-PKCS-PSS
This algorithm takes the file as the input and sends its hash for signing with the random salt.
To sign the data:
```
$ pkcs11-tool --id 1 --sign --pin 648219 --mechanism SHA1-RSA-PKCS-PSS -i data -o data.sig
```
To verify the signature:
```
$ openssl pkeyutl -verify -in data.sha1 -sigfile data.sig -pubin -inkey 1.pub -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:-1
Signature Verified Successfully
```
## ECDSA
This is a raw ECDSA signature, which is usually used to sign a hashed message. `pkcs11-tool` has the limit of the maximum supported length, which is the length in bytes of the ECC curve. For a 192 bits curve, it only supports hashed messages with SHA1 (20 bytes < 24 bytes). To support SHA256 hashed messages, a minimum of ECC curve of 256 bits is required. `sc-hsm-embedded` driver and `sc-tool` do not have this constraint and can be used with arbitrary data.
To sign the data:
```
$ pkcs11-tool --id 11 --sign --pin 648219 --mechanism ECDSA -i data.sha1 -o data.sig --signature-format openssl
Using slot 0 with a present token (0x0)
Using signature algorithm ECDSA
```
To verify the signature:
```
$ openssl pkeyutl -verify -pubin -inkey 11.pub -in data.sha1 -sigfile data.sig
Signature Verified Successfully
```
To sign raw data, use `sc-tool` of `sc-hsm-embedded` driver instead of `pkcs11-tool`.
## SHA1-ECDSA
For ECDSA signature, we employ a ECC key with the id `--id 11`. The signature is quite similar as with RSA.
To sign the data:
```
$ pkcs11-tool --id 11 --sign --pin 648219 --mechanism ECDSA-SHA1 -i data -o data.sig --signature-format openssl
Using slot 0 with a present token (0x0)
Using signature algorithm ECDSA-SHA256
```
The signature is verified with the hash:
```
$ openssl pkeyutl -verify -pubin -inkey 11.pub -in data.sha1 -sigfile data.sig
Signature Verified Successfully
```

220
doc/usage.md Normal file
View File

@@ -0,0 +1,220 @@
# Usage
## Tools
We use multiple tools and PKCS#11 drivers and modules, depending on the purpose.
* **pkcs11-tool**: from OpenSC. It interfaces with the HSM via PKCS#11 interface. It supports different drivers and modules.
* **sc-tool**: an alias of pkcs11-tool with the sc-hsm-embedded module. It is mainly used for AES management and it is defined as:
```
$ alias sc-tool=pkcs11-tool --module /path/to/libsc-hsm-pkcs11.so
```
* **openssl**: it used for certificate and X509 generation and management. It uses the pkcs11 engine. To configure the pkcs11 engine, add the following lines at the begining of `/etc/openssl.cnf` file[^1]:
```
openssl_conf = openssl_init
[openssl_init]
engines=engine_section
[engine_section]
pkcs11 = pkcs11_section
[pkcs11_section]
engine_id = pkcs11
dynamic_path = /path/to/ENGINESDIR/pkcs11.so
MODULE_PATH = /usr/local/lib/opensc-pkcs11.so
init=0
PIN=648219
```
`opensc-pkcs11.so` can be replaced by `libsc-hsm-pkcs11.so` if desired.
* **sc-hsm-tool**: from OpenSC. Used to initialize the device.
* **opensc-tool**: from OpenSC. Used to list and detect the reader with the HSM.
[^1]: `openssl version -a` will return the `OPENSSLDIR`, which contains `openssl.cnf` file and `ENGINESDIR`, which contains the p11 engine.
## Initialization
The first step is to initialize the HSM:
```
$ sc-hsm-tool --initialize --so-pin 3537363231383830 --pin 648219
```
The PIN number is used to manage all private keys in the device. It supports three attemps. After the third PIN failure, it gets blocked.
The PIN accepts from 6 to 16 characters.
The SO-PIN is used to unblock the PIN. It accepts 15 attemps. After 15 failed attempts, the device will be completely blocked and will be necessary to initialize again, erasing all private keys and losing the access. Therefore, keep the SO-PIN in a safe place.
The SO-PIN is always 16 hexadecimal characters.
## PIN and SO-PIN management
To change the SO-PIN:
```
$ pkcs11-tool --login --login-type so --so-pin 3537363231383830 --change-pin --new-pin 0123456789012345
```
To change the PIN:
```
$ pkcs11-tool --login --pin 648219 --change-pin --new-pin 123456
```
To unblock the PIN:
```
$ pkcs11-tool --login --login-type so --so-pin=3537363231383830 --init-pin --new-pin=648219
```
## Keypair generation
Pico HSM accepts internal keypair generation with RSA scheme. It generates a pair of private and public keys and stores both internally encrypted with a 256 bits AES key. The private key never leaves the device. It may be exported with wrap command but it will be encrypted with a passphrase and the AES key.
To generate a RSA 2048 bits, use the following command:
```
$ pkcs11-tool -l --pin 648219 --keypairgen --key-type rsa:2048 --id 1 --label "RSA2K"
Using slot 0 with a present token (0x0)
Key pair generated:
Private Key Object; RSA
label: RSA2K
ID: 1
Usage: decrypt, sign
Access: none
Public Key Object; RSA 2048 bits
label: RSA2K
ID: 1
Usage: encrypt, verify
Access: none
```
The ID parameter is an internal hexadecimal number for easy identification. The label is a string that also identifies the key. Despite it allows to store multiple keys with the same ID and/or same label, internally are stored with a unique index (the key reference). In any case, do not reuse the same ID/label to avoid future conflicts. Furthermore, it is highly recommended to use always the `--id` parameter, as it can be later referenced easily.
Pico HSM accepts RSA of 1024 (`rsa:1024`), 2048 (`rsa:2048`) and 4096 bits (`rsa:4096`).
**Caution**: RSA 2048 bits may take more than 20 seconds. RSA 4096 bits may take more than 20 minutes. The Pico HSM will work as normally and neither the HSM nor the host will block. But, in the meantime, the Pico HSM will not accept any command.
An alternative is to generate the private key locally and import it to the HSM. This approach, however, is less secure as it does not use a True RNG or HRNG like Pico HSM. Use this approach if you have plugged a TRNG or you are not worried about obtaining the highest entropy.
Pico HSM also accepts ECDSA keypairs:
* secp192r1 (prime192v1)
* secp256r1 (prime256v1)
* secp384r1 (prime384v1)
* secp521r1 (prime521v1)
* brainpoolP256r1
* brainpoolP384r1
* brainpoolP512r1
* secp192k1
* secp256k1
To use ECC keys, use the above command with the `--key-type` parameter with `EC:secp192r1`, `EC:secp256r1`, `EC:secp384r1`, `EC:secp521r1`, `EC:brainpoolP256r1`, `EC:brainpoolP384r1`, `EC:brainpoolP512r1`, `EC:secp192k1` and `EC:secp256r1`.
## Delete keys
To delete the previous generated key:
```
pkcs11-tool -l --pin 648219 --delete-object --type privkey --id 1
```
## Generate a certificate and sign it
Secret keys stored in the Pico HSM and can be used to sign data without leaving the device. To generate a certificate request and sign it with the private key stored in the device, use the following command:
```
$ openssl req -engine pkcs11 -new -key 0:1 -keyform engine -out cert.pem -text -x509 -days 365
```
The key is specified in the form of `slotid:keyid`. For Pico HSM, `slotid` is always `0` and the `keyid` is the id of the key specified with the key generation.
The `openssl.cnf` used by `openssl` command shall contain the blocks configured in [Tools section](#tools). The output will depend on your configuration, but for default configuration files it will prompt you something like this:
```
engine "pkcs11" set.
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:ES
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:patata.com
Email Address []:
```
The command terminates with success silently. Thus, if no additional output/errors are displayed, the certificate is properly generated and signed. You can check this with:
```
$ openssl x509 -in cert.pem -noout -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
01:3f:b4:5a:ac:7c:1a:e7:bc:37:e0:aa:f9:31:f4:68:90:08:fc:3d
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = ES, ST = Some-State, O = Internet Widgits Pty Ltd, CN = patata.com
Validity
Not Before: Mar 13 17:58:00 2022 GMT
Not After : Feb 29 17:58:00 2032 GMT
Subject: C = ES, ST = Some-State, O = Internet Widgits Pty Ltd, CN = patata.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (1024 bit)
Modulus:
00:91:85:89:5d:e0:fa:f3:2b:9e:85:75:c9:92:7d:
c5:18:16:c0:15:1b:4d:7e:af:1a:8c:ff:2e:39:74:
bb:b7:af:b4:ca:24:9d:80:c8:53:51:82:b5:c5:77:
0d:56:0a:08:99:84:8d:7a:28:6d:8e:c6:32:40:b0:
62:d6:e5:e6:28:35:08:32:d7:f7:d6:eb:10:a8:81:
43:9e:7c:51:b2:52:16:d2:fd:05:df:c3:dd:ee:c4:
dd:43:db:ca:ed:6f:10:ab:d4:59:dc:3a:2d:80:4b:
2c:37:75:14:df:62:e0:7a:b3:62:5b:80:5f:c5:9b:
a0:30:b2:ec:d3:d6:0d:58:f3
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
98:07:DA:13:B0:8E:A0:5C:97:83:68:FE:4A:25:8D:50:C4:DC:16:FA
X509v3 Authority Key Identifier:
keyid:98:07:DA:13:B0:8E:A0:5C:97:83:68:FE:4A:25:8D:50:C4:DC:16:FA
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
56:bc:32:c6:dc:4a:af:64:4e:27:1c:52:e2:9d:8a:d7:b9:e0:
7f:f0:3a:97:08:9a:5d:64:86:88:df:2f:c5:5d:ab:ae:00:ce:
db:13:fc:a0:a7:b3:13:4a:0b:2f:1d:9c:64:95:58:94:52:93:
81:18:32:a5:9d:5f:be:bd:b9:47:4d:67:b7:91:e6:10:a2:12:
3b:96:d3:8b:4d:1c:ef:12:81:63:97:85:9a:4c:04:d1:4c:da:
99:2b:b2:82:66:c1:06:a7:2c:62:af:e2:e4:93:42:36:66:8d:
c5:3f:e1:ec:5f:9a:f8:5f:b3:6a:8f:0e:12:5d:c9:46:38:ea:
0b:08
```
The resulting file `cert.pem` contains the signed certificate in PEM format. Convert it into DER format and load it into the Pico HSM:
```
$ openssl x509 -in cert.pem -out cert.der -outform der
$ pkcs11-tool -l --pin 648219 --write-object cert.der --type cert --id 1
Using slot 0 with a present token (0x0)
Created certificate:
Certificate Object; type = X.509 cert
label: Certificate
subject: DN: C=ES, ST=Some-State, O=Internet Widgits Pty Ltd, CN=patata.com
ID: 01
```
## Generate random numbers
To generate random numbers:
```
$ pkcs11-tool -l --pin 648219 --generate-random 64 | xxd -c 64 -p
Using slot 0 with a present token (0x0)
773ec49733435915f5cf056497d97d2b1e6a4af23e2851eb2adf75af40db6677115c401aa26d46677184f4cf878da6289cf3ff1a5192711377b869adbc7f2b6b
```
It supports up to $1024$ random bytes in a single call.
## Signing and verification
For signing and verification operations, check [doc/sign-verify.md](/doc/sign-verify.md).
## Asymmetric encryption and decryption
For asymmetric encryption and decryption, check [doc/asymmetric-ciphering.md](/doc/asymmetric-ciphering.md).
## Backup and restore
For backup, restore and DKEK share management, check [doc/backup-and-restore.md](/doc/backup-and-restore.md).
## AES operations
For AES key generation, encryption and decryption, check [doc/aes.md](/doc/aes.md).

View File

@@ -1,82 +0,0 @@
/**
* \file md_wrap.h
*
* \brief Message digest wrappers.
*
* \warning This in an internal header. Do not include directly.
*
* \author Adriaan de Jong <dejong@fox-it.com>
*/
/*
* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBEDTLS_MD_WRAP_H
#define MBEDTLS_MD_WRAP_H
#include "mbedtls/build_info.h"
#include "mbedtls/md.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Message digest information.
* Allows message digest functions to be called in a generic way.
*/
struct mbedtls_md_info_t
{
/** Name of the message digest */
const char * name;
/** Digest identifier */
mbedtls_md_type_t type;
/** Output length of the digest function in bytes */
unsigned char size;
/** Block length of the digest function in bytes */
unsigned char block_size;
};
#if defined(MBEDTLS_MD5_C)
extern const mbedtls_md_info_t mbedtls_md5_info;
#endif
#if defined(MBEDTLS_RIPEMD160_C)
extern const mbedtls_md_info_t mbedtls_ripemd160_info;
#endif
#if defined(MBEDTLS_SHA1_C)
extern const mbedtls_md_info_t mbedtls_sha1_info;
#endif
#if defined(MBEDTLS_SHA224_C)
extern const mbedtls_md_info_t mbedtls_sha224_info;
#endif
#if defined(MBEDTLS_SHA256_C)
extern const mbedtls_md_info_t mbedtls_sha256_info;
#endif
#if defined(MBEDTLS_SHA384_C)
extern const mbedtls_md_info_t mbedtls_sha384_info;
#endif
#if defined(MBEDTLS_SHA512_C)
extern const mbedtls_md_info_t mbedtls_sha512_info;
#endif
#ifdef __cplusplus
}
#endif
#endif /* MBEDTLS_MD_WRAP_H */

120
padlock.h
View File

@@ -1,120 +0,0 @@
/**
* \file padlock.h
*
* \brief VIA PadLock ACE for HW encryption/decryption supported by some
* processors
*
* \warning These functions are only for internal use by other library
* functions; you must not call them directly.
*/
/*
* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBEDTLS_PADLOCK_H
#define MBEDTLS_PADLOCK_H
#include "mbedtls/build_info.h"
#include "mbedtls/aes.h"
#define MBEDTLS_ERR_PADLOCK_DATA_MISALIGNED -0x0030 /**< Input data should be aligned. */
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
#define MBEDTLS_HAVE_ASAN
#endif
#endif
/* Some versions of ASan result in errors about not enough registers */
#if defined(MBEDTLS_HAVE_ASM) && defined(__GNUC__) && defined(__i386__) && \
!defined(MBEDTLS_HAVE_ASAN)
#ifndef MBEDTLS_HAVE_X86
#define MBEDTLS_HAVE_X86
#endif
#include <stdint.h>
#define MBEDTLS_PADLOCK_RNG 0x000C
#define MBEDTLS_PADLOCK_ACE 0x00C0
#define MBEDTLS_PADLOCK_PHE 0x0C00
#define MBEDTLS_PADLOCK_PMM 0x3000
#define MBEDTLS_PADLOCK_ALIGN16(x) (uint32_t *) (16 + ((int32_t) (x) & ~15))
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Internal PadLock detection routine
*
* \note This function is only for internal use by other library
* functions; you must not call it directly.
*
* \param feature The feature to detect
*
* \return non-zero if CPU has support for the feature, 0 otherwise
*/
int mbedtls_padlock_has_support( int feature );
/**
* \brief Internal PadLock AES-ECB block en(de)cryption
*
* \note This function is only for internal use by other library
* functions; you must not call it directly.
*
* \param ctx AES context
* \param mode MBEDTLS_AES_ENCRYPT or MBEDTLS_AES_DECRYPT
* \param input 16-byte input block
* \param output 16-byte output block
*
* \return 0 if success, 1 if operation failed
*/
int mbedtls_padlock_xcryptecb( mbedtls_aes_context *ctx,
int mode,
const unsigned char input[16],
unsigned char output[16] );
/**
* \brief Internal PadLock AES-CBC buffer en(de)cryption
*
* \note This function is only for internal use by other library
* functions; you must not call it directly.
*
* \param ctx AES context
* \param mode MBEDTLS_AES_ENCRYPT or MBEDTLS_AES_DECRYPT
* \param length length of the input data
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*
* \return 0 if success, 1 if operation failed
*/
int mbedtls_padlock_xcryptcbc( mbedtls_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output );
#ifdef __cplusplus
}
#endif
#endif /* HAVE_X86 */
#endif /* padlock.h */

View File

@@ -1,7 +1,7 @@
#!/bin/bash
echo "----------------------------"
echo "VID/PID patcher for HSM 2040"
echo "VID/PID patcher for Pico HSM"
echo "----------------------------"
echo ""

View File

@@ -1,5 +1,4 @@
#include "file.h"
#include "gnuk.h"
#include "tusb.h"
#include "hsm2040.h"
#include "sc_hsm.h"
@@ -280,8 +279,8 @@ void scan_flash() {
if (base == 0x0) //all is empty
break;
uint16_t fid = flash_read_uint16(base+sizeof(uintptr_t));
printf("scan fid %x\r\n",fid);
uint16_t fid = flash_read_uint16(base+sizeof(uintptr_t)+sizeof(uintptr_t));
printf("[%x] scan fid %x, len %d\r\n",base,fid,flash_read_uint16(base+sizeof(uintptr_t)+sizeof(uintptr_t)+sizeof(uint16_t)));
file_t *file = (file_t *)search_by_fid(fid, NULL, SPECIFY_EF);
if (!file) {
file = file_new(fid);
@@ -302,7 +301,7 @@ void scan_flash() {
continue;
}
}
file->data = (uint8_t *)(base+sizeof(uintptr_t)+sizeof(uint16_t));
file->data = (uint8_t *)(base+sizeof(uintptr_t)+sizeof(uintptr_t)+sizeof(uint16_t));
if (flash_read_uintptr(base) == 0x0) {
break;
}

View File

@@ -1,43 +1,6 @@
/*
* flash.c -- Data Objects (DO) and GPG Key handling on Flash ROM
*
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
* Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
*
* Gnuk 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, either version 3 of the License, or
* (at your option) any later version.
*
* Gnuk 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/>.
*
*/
/*
* We assume single DO size is less than 256.
*
* NOTE: "Card holder certificate" (which size is larger than 256) is
* not put into data pool, but is implemented by its own flash
* page(s).
*/
#include <stdint.h>
#include <string.h>
#include "config.h"
#include "sys.h"
#include "gnuk.h"
#include "pico/stdlib.h"
#include "hardware/flash.h"
#include "hsm2040.h"
@@ -45,10 +8,17 @@
#include "file.h"
#include "sc_hsm.h"
/*
* ------------------------------------------------------
* | |
* | next_addr | prev_addr | fid | data (len + payload) |
* | |
* ------------------------------------------------------
*/
#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES >> 1) // DATA starts at the mid of flash
#define FLASH_DATA_HEADER_SIZE (sizeof(uintptr_t)+sizeof(uint32_t))
//To avoid possible future allocations, data region starts at the begining of flash and goes upwards to the center region
//To avoid possible future allocations, data region starts at the end of flash and goes upwards to the center region
const uintptr_t start_data_pool = (XIP_BASE + FLASH_TARGET_OFFSET);
const uintptr_t end_data_pool = (XIP_BASE + PICO_FLASH_SIZE_BYTES)-FLASH_DATA_HEADER_SIZE; //This is a fixed value. DO NOT CHANGE
@@ -62,33 +32,10 @@ extern uint16_t flash_read_uint16(uintptr_t addr);
extern void low_flash_available();
/*
* Flash data pool managenent
*
* Flash data pool consists of two parts:
* 2-byte header
* contents
*
* Flash data pool objects:
* Data Object (DO) (of smart card)
* Internal objects:
* NONE (0x0000)
* 123-counter
* 14-bit counter
* bool object
* small enum
*
* Format of a Data Object:
* NR: 8-bit tag_number
* LEN: 8-bit length
* DATA: data * LEN
* PAD: optional byte for 16-bit alignment
*/
uintptr_t allocate_free_addr(uint16_t size) {
if (size > FLASH_SECTOR_SIZE)
return 0x0; //ERROR
size_t real_size = size+sizeof(uint16_t)+sizeof(uintptr_t)+sizeof(uint16_t); //len+len size+next address+fid
size_t real_size = size+sizeof(uint16_t)+sizeof(uintptr_t)+sizeof(uint16_t)+sizeof(uintptr_t); //len+len size+next address+fid+prev_addr size
uintptr_t next_base = 0x0;
for (uintptr_t base = end_data_pool; base >= start_data_pool; base = next_base) {
uintptr_t addr_alg = base & -FLASH_SECTOR_SIZE; //start address of sector
@@ -101,20 +48,23 @@ uintptr_t allocate_free_addr(uint16_t size) {
if (addr_alg <= potential_addr) //it fits in the current sector
{
flash_program_uintptr(potential_addr, 0x0);
flash_program_uintptr(potential_addr+sizeof(uintptr_t), base);
flash_program_uintptr(base, potential_addr);
return potential_addr;
}
else if (addr_alg-FLASH_SECTOR_SIZE >= start_data_pool) { //check whether it fits in the next sector, so we take addr_aligned as the base
potential_addr = addr_alg-real_size;
flash_program_uintptr(potential_addr, 0x0);
flash_program_uintptr(potential_addr+sizeof(uintptr_t), base);
flash_program_uintptr(base, potential_addr);
return potential_addr;
}
return 0x0;
}
//we check if |base-(next_addr+size_next_addr)| > |base-potential_addr| only if fid != 1xxx (not size blocked)
else if (addr_alg <= potential_addr && base-(next_base+flash_read_uint16(next_base+sizeof(uintptr_t)+sizeof(uint16_t))+2*sizeof(uint16_t)) > base-potential_addr && flash_read_uint16(next_base+sizeof(uintptr_t)) & 0x1000 != 0x1000) {
else if (addr_alg <= potential_addr && base-(next_base+flash_read_uint16(next_base+sizeof(uintptr_t)+sizeof(uintptr_t)+sizeof(uint16_t))+2*sizeof(uint16_t)+2*sizeof(uintptr_t)) > base-potential_addr && flash_read_uint16(next_base+sizeof(uintptr_t)) & 0x1000 != 0x1000) {
flash_program_uintptr(potential_addr, next_base);
flash_program_uintptr(potential_addr+sizeof(uintptr_t), base);
flash_program_uintptr(base, potential_addr);
return potential_addr;
}
@@ -123,13 +73,16 @@ uintptr_t allocate_free_addr(uint16_t size) {
}
int flash_clear_file(file_t *file) {
uintptr_t prev_addr = (uintptr_t)(file->data+flash_read_uint16((uintptr_t)file->data)+sizeof(uint16_t));
uintptr_t base_addr = (uintptr_t)(file->data-sizeof(uintptr_t)-sizeof(uint16_t));
uintptr_t base_addr = (uintptr_t)(file->data-sizeof(uintptr_t)-sizeof(uint16_t)-sizeof(uintptr_t));
uintptr_t prev_addr = flash_read_uintptr(base_addr+sizeof(uintptr_t));
uintptr_t next_addr = flash_read_uintptr(base_addr);
//printf("nc %x %x %x\r\n",prev_addr,base_addr,next_addr);
//printf("nc %x->%x %x->%x\r\n",prev_addr,flash_read_uintptr(prev_addr),base_addr,next_addr);
flash_program_uintptr(prev_addr, next_addr);
flash_program_halfword((uintptr_t)file->data, 0);
return 0;
if (next_addr > 0)
flash_program_uintptr(next_addr+sizeof(uintptr_t), prev_addr);
//printf("na %x->%x\r\n",prev_addr,flash_read_uintptr(prev_addr));
return HSM_OK;
}
int flash_write_data_to_file(file_t *file, const uint8_t *data, uint16_t len) {
@@ -153,8 +106,8 @@ int flash_write_data_to_file(file_t *file, const uint8_t *data, uint16_t len) {
//printf("na %x\r\n",new_addr);
if (new_addr == 0x0)
return HSM_ERR_NO_MEMORY;
file->data = (uint8_t *)new_addr+sizeof(uintptr_t)+sizeof(uint16_t); //next addr+fid
flash_program_halfword(new_addr+sizeof(uintptr_t), file->fid);
file->data = (uint8_t *)new_addr+sizeof(uintptr_t)+sizeof(uint16_t)+sizeof(uintptr_t); //next addr+fid+prev addr
flash_program_halfword(new_addr+sizeof(uintptr_t)+sizeof(uintptr_t), file->fid);
flash_program_halfword((uintptr_t)file->data, len);
if (data)
flash_program_block((uintptr_t)file->data+sizeof(uint16_t), data, len);

View File

@@ -8,7 +8,6 @@
#include "pico/mutex.h"
#include "pico/sem.h"
#include "pico/multicore.h"
#include "gnuk.h"
#include "hsm2040.h"
#include "sc_hsm.h"
#include <string.h>

View File

@@ -1,9 +1,3 @@
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
// Pico
@@ -19,11 +13,7 @@
#include "device/usbd_pvt.h"
#include "pico/util/queue.h"
#include "pico/multicore.h"
#include "gnuk.h"
#include "config.h"
#include "random.h"
// Device descriptors
#include "hsm2040.h"
extern void do_flash();
@@ -371,10 +361,6 @@ enum {
BLINK_SUSPENDED = (500 << 16) | 1000,
BLINK_PROCESSING = (50 << 16) | 50,
BLINK_RED = 18,
BLINK_GREEN = 19,
BLINK_BLUE = 20,
BLINK_ALWAYS_ON = UINT32_MAX,
BLINK_ALWAYS_OFF = 0
};
@@ -1568,8 +1554,12 @@ void led_blinking_task()
{
static uint32_t start_ms = 0;
static uint8_t led_state = false;
static uint8_t led_color = BLINK_RED;
static uint8_t led_color = PICO_DEFAULT_LED_PIN;
#ifdef PICO_DEFAULT_LED_PIN_INVERTED
uint32_t interval = !led_state ? blink_interval_ms & 0xffff : blink_interval_ms >> 16;
#else
uint32_t interval = led_state ? blink_interval_ms & 0xffff : blink_interval_ms >> 16;
#endif
// Blink every interval ms
@@ -1583,9 +1573,13 @@ void led_blinking_task()
void led_off_all()
{
gpio_put(18, 1);
gpio_put(19, 1);
gpio_put(20, 1);
#ifdef PIMORONI_TINY2040
gpio_put(TINY2040_LED_R_PIN, 1);
gpio_put(TINY2040_LED_G_PIN, 1);
gpio_put(TINY2040_LED_B_PIN, 1);
#else
gpio_put(PICO_DEFAULT_LED_PIN, 0);
#endif
}
extern void neug_task();
@@ -1597,15 +1591,19 @@ int main(void)
struct apdu *a = &apdu;
struct ccid *c = &ccid;
printf("BOARD INIT\r\n");
board_init();
gpio_init(18);
gpio_set_dir(18, GPIO_OUT);
gpio_init(19);
gpio_set_dir(19, GPIO_OUT);
gpio_init(20);
gpio_set_dir(20, GPIO_OUT);
#ifdef PIMORONI_TINY2040
gpio_init(TINY2040_LED_R_PIN);
gpio_set_dir(TINY2040_LED_R_PIN, GPIO_OUT);
gpio_init(TINY2040_LED_G_PIN);
gpio_set_dir(TINY2040_LED_G_PIN, GPIO_OUT);
gpio_init(TINY2040_LED_B_PIN);
gpio_set_dir(TINY2040_LED_B_PIN, GPIO_OUT);
#else
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
#endif
led_off_all();

View File

@@ -57,6 +57,37 @@ struct apdu {
uint8_t *res_apdu_data;
};
#define MAX_CMD_APDU_DATA_SIZE (24+4+512*4)
#define MAX_RES_APDU_DATA_SIZE (5+9+512*4)
#define CCID_MSG_HEADER_SIZE 10
#define USB_LL_BUF_SIZE 64
/* CCID thread */
#define EV_CARD_CHANGE 1
#define EV_TX_FINISHED 2 /* CCID Tx finished */
#define EV_EXEC_ACK_REQUIRED 4 /* OpenPGPcard Execution ACK required */
#define EV_EXEC_FINISHED 8 /* OpenPGPcard Execution finished */
#define EV_RX_DATA_READY 16 /* USB Rx data available */
/* OpenPGPcard thread */
#define EV_MODIFY_CMD_AVAILABLE 1
#define EV_VERIFY_CMD_AVAILABLE 2
#define EV_CMD_AVAILABLE 4
#define EV_EXIT 8
#define EV_PINPAD_INPUT_DONE 16
enum ccid_state {
CCID_STATE_NOCARD, /* No card available */
CCID_STATE_START, /* Initial */
CCID_STATE_WAIT, /* Waiting APDU */
CCID_STATE_EXECUTE, /* Executing command */
CCID_STATE_ACK_REQUIRED_0, /* Ack required (executing)*/
CCID_STATE_ACK_REQUIRED_1, /* Waiting user's ACK (execution finished) */
CCID_STATE_EXITED, /* CCID Thread Terminated */
CCID_STATE_EXEC_REQUESTED, /* Exec requested */
};
#define CLS(a) a.cmd_apdu_head[0]
#define INS(a) a.cmd_apdu_head[1]

View File

@@ -8,6 +8,10 @@
#include "mbedtls/rsa.h"
#include "mbedtls/ecp.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/cmac.h"
#include "mbedtls/hkdf.h"
#include "version.h"
const uint8_t sc_hsm_aid[] = {
11,
@@ -109,6 +113,7 @@ void select_file(file_t *pe) {
}
if (currentEF == file_openpgp || currentEF == file_sc_hsm) {
selected_applet = currentEF;
//sc_hsm_unload(); //reset auth status
}
}
static int cmd_select() {
@@ -192,10 +197,10 @@ static int cmd_select() {
}
int parse_token_info(const file_t *f, int mode) {
char *label = "HSM2040";
char *label = "PicoHSM";
char *manu = "Pol Henarejos";
sc_pkcs15_tokeninfo_t *ti = (sc_pkcs15_tokeninfo_t *)calloc(1, sizeof(sc_pkcs15_tokeninfo_t));
ti->version = 3;
ti->version = HSM_VERSION_MAJOR;
ti->flags = SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_EID_COMPLIANT;
ti->label = (char *)calloc(strlen(label)+1, sizeof(char));
strlcpy(ti->label, label, strlen(label)+1);
@@ -336,8 +341,12 @@ int pin_wrong_retry(const file_t *pin) {
if (retries > 0) {
retries -= 1;
int r = flash_write_data_to_file((file_t *)act, &retries, sizeof(retries));
if (r != HSM_OK)
return r;
low_flash_available();
return r;
if (retries == 0)
return HSM_ERR_BLOCKED;
return retries;
}
return HSM_ERR_BLOCKED;
}
@@ -354,9 +363,10 @@ int check_pin(const file_t *pin, const uint8_t *data, size_t len) {
if (sizeof(dhash) != file_read_uint16(pin->data)-1) //1 byte for pin len
return SW_CONDITIONS_NOT_SATISFIED();
if (memcmp(file_read(pin->data+3), dhash, sizeof(dhash)) != 0) {
if (pin_wrong_retry(pin) != HSM_OK)
uint8_t retries;
if ((retries = pin_wrong_retry(pin)) < HSM_OK)
return SW_PIN_BLOCKED();
return SW_SECURITY_STATUS_NOT_SATISFIED();
return set_res_sw(0x63, 0xc0 | retries);
}
int r = pin_reset_retries(pin, false);
if (r == HSM_ERR_BLOCKED)
@@ -382,7 +392,7 @@ static int cmd_verify() {
}
if (file_read_uint8(file_retries_pin1->data+2) == 0)
return SW_PIN_BLOCKED();
return set_res_sw (0x63, 0xc0 | file_read_uint8(file_retries_pin1->data+2));
return set_res_sw(0x63, 0xc0 | file_read_uint8(file_retries_pin1->data+2));
}
else if (p2 == 0x88) { //SOPin
}
@@ -414,7 +424,10 @@ static int cmd_reset_retry() {
}
static int cmd_challenge() {
memcpy(res_APDU, random_bytes_get(apdu.expected_res_size), apdu.expected_res_size);
uint8_t *rb = (uint8_t *)random_bytes_get(apdu.expected_res_size);
if (!rb)
return SW_WRONG_LENGTH();
res_APDU = rb;
res_APDU_size = apdu.expected_res_size;
return SW_OK();
}
@@ -473,8 +486,8 @@ static int cmd_initialize() {
encrypt(session_pin, tmp_dkek, tmp_dkek+IV_SIZE, 32);
file_t *tf = search_by_fid(EF_DKEK, NULL, SPECIFY_EF);
flash_write_data_to_file(tf, tmp_dkek, sizeof(tmp_dkek));
low_flash_available();
}
low_flash_available();
return SW_OK();
}
@@ -520,6 +533,17 @@ void hash(const uint8_t *input, size_t len, uint8_t output[32])
mbedtls_sha256_free (&ctx);
}
void generic_hash(mbedtls_md_type_t md, const uint8_t *input, size_t len, uint8_t *output) {
mbedtls_md_context_t ctx;
mbedtls_md_init(&ctx);
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md);
mbedtls_md_setup(&ctx, md_info, 0);
mbedtls_md_starts(&ctx);
mbedtls_md_update(&ctx, input, len);
mbedtls_md_finish(&ctx, output);
mbedtls_md_free(&ctx);
}
static int cmd_import_dkek() {
if (dkeks == 0)
return SW_COMMAND_NOT_ALLOWED();
@@ -658,7 +682,7 @@ int store_keys(void *key_ctx, int type, uint8_t key_id, sc_context_t *ctx) {
free(pukd);
free(p15o);
//sc_asn1_print_tags(asn1bin, asn1len);
fpk = file_new((CD_PREFIX << 8) | key_id);
fpk = file_new((EE_CERTIFICATE_PREFIX << 8) | key_id);
r = flash_write_data_to_file(fpk, asn1bin, asn1len);
free(asn1bin);
if (r != HSM_OK)
@@ -1197,21 +1221,23 @@ static int cmd_key_gen() {
key_size = 24;
else if (p2 == 0xB0)
key_size = 16;
if (!isUserAuthenticated)
return SW_SECURITY_STATUS_NOT_SATISFIED();
//at this moment, we do not use the template, as only CBC is supported by the driver (encrypt, decrypt and CMAC)
uint8_t aes_key[32]; //maximum AES key size
memcpy(aes_key, random_bytes_get(key_size), key_size);
if ((r = load_dkek()) != HSM_OK)
return r;
return SW_EXEC_ERROR() ;
if ((r = encrypt(tmp_dkek+IV_SIZE, tmp_dkek, aes_key, key_size)) != 0)
return r;
return SW_EXEC_ERROR() ;
release_dkek();
file_t *fpk = file_new((KEY_PREFIX << 8) | key_id);
if (!fpk)
return SW_MEMORY_FAILURE();
r = flash_write_data_to_file(fpk, aes_key, key_size);
if (r != HSM_OK)
return SW_MEMORY_FAILURE();
fpk = file_new((PRKD_PREFIX << 8) | key_id);
if (!fpk)
return SW_MEMORY_FAILURE();
r = flash_write_data_to_file(fpk, NULL, 0);
if (r != HSM_OK)
return SW_MEMORY_FAILURE();
@@ -1225,15 +1251,19 @@ int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) {
return SW_EXEC_ERROR();
uint8_t *kdata = (uint8_t *)calloc(1,key_size);
memcpy(kdata, file_read(fkey->data+2), key_size);
if (decrypt(tmp_dkek+IV_SIZE, tmp_dkek, kdata, key_size) != 0)
if (decrypt(tmp_dkek+IV_SIZE, tmp_dkek, kdata, key_size) != 0) {
free(kdata);
return SW_EXEC_ERROR();
}
release_dkek();
if (mbedtls_mpi_read_binary(&ctx->P, kdata, key_size/2) != 0) {
mbedtls_rsa_free(ctx);
free(kdata);
return SW_DATA_INVALID();
}
if (mbedtls_mpi_read_binary(&ctx->Q, kdata+key_size/2, key_size/2) != 0) {
mbedtls_rsa_free(ctx);
free(kdata);
return SW_DATA_INVALID();
}
free(kdata);
@@ -1262,15 +1292,19 @@ int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey) {
return SW_EXEC_ERROR();
uint8_t *kdata = (uint8_t *)calloc(1,key_size);
memcpy(kdata, file_read(fkey->data+2), key_size);
if (decrypt(tmp_dkek+IV_SIZE, tmp_dkek, kdata, key_size) != 0)
if (decrypt(tmp_dkek+IV_SIZE, tmp_dkek, kdata, key_size) != 0) {
free(kdata);
return SW_EXEC_ERROR();
}
release_dkek();
mbedtls_ecp_group_id gid = kdata[0];
if (mbedtls_ecp_group_load(&ctx->grp, gid) != 0) {
free(kdata);
mbedtls_ecdsa_free(ctx);
return SW_DATA_INVALID();
}
if (mbedtls_mpi_read_binary(&ctx->d, kdata+1, key_size-1) != 0) {
free(kdata);
mbedtls_ecdsa_free(ctx);
return SW_DATA_INVALID();
}
@@ -1294,13 +1328,21 @@ static int cmd_signature() {
md = MBEDTLS_MD_SHA256;
else if (p2 == ALGO_EC_SHA224)
md = MBEDTLS_MD_SHA224;
if (p2 == ALGO_RSA_PKCS1_SHA1 || p2 == ALGO_RSA_PSS_SHA1 || p2 == ALGO_EC_SHA1 || p2 == ALGO_RSA_PKCS1_SHA256 || p2 == ALGO_RSA_PSS_SHA256 || p2 == ALGO_EC_SHA256 || p2 == ALGO_EC_SHA224) {
generic_hash(md, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, apdu.cmd_apdu_data);
apdu.cmd_apdu_data_len = mbedtls_md_get_size(mbedtls_md_info_from_type(md));
}
if (p2 == ALGO_RSA_RAW || p2 == ALGO_RSA_PKCS1 || p2 == ALGO_RSA_PKCS1_SHA1 || p2 == ALGO_RSA_PKCS1_SHA256 || p2 == ALGO_RSA_PSS || p2 == ALGO_RSA_PSS_SHA1 || p2 == ALGO_RSA_PSS_SHA256) {
mbedtls_rsa_context ctx;
mbedtls_rsa_init(&ctx);
if (p2 == ALGO_RSA_PSS || p2 == ALGO_RSA_PSS_SHA1 || p2 == ALGO_RSA_PSS_SHA256) {
mbedtls_rsa_set_padding(&ctx, MBEDTLS_RSA_PKCS_V21, md);
}
else if (p2 == ALGO_RSA_PKCS1) { //DigestInfo attached
int r;
r = load_private_key_rsa(&ctx, fkey);
if (r != HSM_OK)
return r;
const uint8_t *hash = apdu.cmd_apdu_data;
size_t hash_len = apdu.cmd_apdu_data_len;
if (p2 == ALGO_RSA_PKCS1) { //DigestInfo attached
unsigned int algo;
if (sc_pkcs1_strip_digest_info_prefix(&algo, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, apdu.cmd_apdu_data, &apdu.cmd_apdu_data_len) != SC_SUCCESS) //gets the MD algo id and strips it off
return SW_EXEC_ERROR();
@@ -1315,18 +1357,51 @@ static int cmd_signature() {
else if (algo == SC_ALGORITHM_RSA_HASH_SHA512)
md = MBEDTLS_MD_SHA512;
}
int r;
r = load_private_key_rsa(&ctx, fkey);
if (r != HSM_OK)
return r;
else {
sc_asn1_print_tags(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
size_t tout = 0, oid_len = 0;
const uint8_t *p = sc_asn1_find_tag(NULL, (const uint8_t *)apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, 0x30, &tout), *oid = NULL;
if (p) {
size_t tout30 = 0;
const uint8_t *c30 = sc_asn1_find_tag(NULL, p, tout, 0x30, &tout30);
if (c30) {
oid = sc_asn1_find_tag(NULL, c30, tout30, 0x6, &oid_len);
}
hash = sc_asn1_find_tag(NULL, p, tout, 0x4, &hash_len);
}
if (oid && oid_len > 0) {
if (memcmp(oid, "\x2B\x0E\x03\x02\x1A", oid_len) == 0)
md = MBEDTLS_MD_SHA1;
else if (memcmp(oid, "\x60\x86\x48\x01\x65\x03\x04\x02\x04", oid_len) == 0)
md = MBEDTLS_MD_SHA224;
else if (memcmp(oid, "\x60\x86\x48\x01\x65\x03\x04\x02\x01", oid_len) == 0)
md = MBEDTLS_MD_SHA256;
else if (memcmp(oid, "\x60\x86\x48\x01\x65\x03\x04\x02\x02", oid_len) == 0)
md = MBEDTLS_MD_SHA384;
else if (memcmp(oid, "\x60\x86\x48\x01\x65\x03\x04\x02\x03", oid_len) == 0)
md = MBEDTLS_MD_SHA512;
}
if (p2 == ALGO_RSA_PSS || p2 == ALGO_RSA_PSS_SHA1 || p2 == ALGO_RSA_PSS_SHA256) {
if (p2 == ALGO_RSA_PSS && !oid) {
if (apdu.cmd_apdu_data_len == 20) //default is sha1
md = MBEDTLS_MD_SHA1;
else if (apdu.cmd_apdu_data_len == 32)
md = MBEDTLS_MD_SHA256;
}
mbedtls_rsa_set_padding(&ctx, MBEDTLS_RSA_PKCS_V21, md);
}
}
if (md == MBEDTLS_MD_NONE) {
if (apdu.cmd_apdu_data_len < key_size) //needs padding
memset(apdu.cmd_apdu_data+apdu.cmd_apdu_data_len, 0, key_size-apdu.cmd_apdu_data_len);
r = mbedtls_rsa_private(&ctx, random_gen, NULL, apdu.cmd_apdu_data, res_APDU);
}
else {
r = mbedtls_rsa_pkcs1_sign(&ctx, random_gen, NULL, md, apdu.cmd_apdu_data_len, apdu.cmd_apdu_data, res_APDU);
uint8_t *signature = (uint8_t *)calloc(key_size, sizeof(uint8_t));
DEBUG_PAYLOAD(hash,hash_len);
r = mbedtls_rsa_pkcs1_sign(&ctx, random_gen, NULL, md, hash_len, hash, signature);
memcpy(res_APDU, signature, key_size);
free(signature);
}
if (r != 0) {
mbedtls_rsa_free(&ctx);
@@ -1339,6 +1414,7 @@ static int cmd_signature() {
else if (p2 == ALGO_EC_RAW || p2 == ALGO_EC_SHA1 || p2 == ALGO_EC_SHA224 || p2 == ALGO_EC_SHA256) {
mbedtls_ecdsa_context ctx;
mbedtls_ecdsa_init(&ctx);
md = MBEDTLS_MD_SHA256;
if (p2 == ALGO_EC_RAW) {
if (apdu.cmd_apdu_data_len == 32)
md = MBEDTLS_MD_SHA256;
@@ -1351,6 +1427,12 @@ static int cmd_signature() {
else if (apdu.cmd_apdu_data_len == 64)
md = MBEDTLS_MD_SHA512;
}
if (p2 == ALGO_EC_SHA1)
md = MBEDTLS_MD_SHA1;
else if (p2 == ALGO_EC_SHA224)
md = MBEDTLS_MD_SHA224;
else if (p2 == ALGO_EC_SHA256)
md = MBEDTLS_MD_SHA256;
int r;
r = load_private_key_ecdsa(&ctx, fkey);
if (r != HSM_OK)
@@ -1400,28 +1482,73 @@ static int cmd_key_unwrap() {
static int cmd_decrypt_asym() {
int key_id = P1(apdu);
if (P2(apdu) != ALGO_RSA_DECRYPT)
return SW_WRONG_P1P2();
if (!isUserAuthenticated)
return SW_SECURITY_STATUS_NOT_SATISFIED();
file_t *ef = search_dynamic_file((KEY_PREFIX << 8) | key_id);
if (!ef)
return SW_FILE_NOT_FOUND();
mbedtls_rsa_context ctx;
mbedtls_rsa_init(&ctx);
int r = load_private_key_rsa(&ctx, ef);
if (r != HSM_OK)
return r;
int key_size = file_read_uint16(ef->data);
if (apdu.cmd_apdu_data_len < key_size) //needs padding
memset(apdu.cmd_apdu_data+apdu.cmd_apdu_data_len, 0, key_size-apdu.cmd_apdu_data_len);
r = mbedtls_rsa_private(&ctx, random_gen, NULL, apdu.cmd_apdu_data, res_APDU);
if (r != 0) {
if (P2(apdu) == ALGO_RSA_DECRYPT) {
mbedtls_rsa_context ctx;
mbedtls_rsa_init(&ctx);
int r = load_private_key_rsa(&ctx, ef);
if (r != HSM_OK)
return r;
int key_size = file_read_uint16(ef->data);
if (apdu.cmd_apdu_data_len < key_size) //needs padding
memset(apdu.cmd_apdu_data+apdu.cmd_apdu_data_len, 0, key_size-apdu.cmd_apdu_data_len);
r = mbedtls_rsa_private(&ctx, random_gen, NULL, apdu.cmd_apdu_data, res_APDU);
if (r != 0) {
mbedtls_rsa_free(&ctx);
return SW_EXEC_ERROR();
}
res_APDU_size = key_size;
mbedtls_rsa_free(&ctx);
return SW_EXEC_ERROR();
}
res_APDU_size = key_size;
mbedtls_rsa_free(&ctx);
else if (P2(apdu) == ALGO_EC_DH) {
mbedtls_ecdh_context ctx;
int key_size = file_read_uint16(ef->data);
if (load_dkek() != HSM_OK)
return SW_EXEC_ERROR();
uint8_t *kdata = (uint8_t *)calloc(1,key_size);
memcpy(kdata, file_read(ef->data+2), key_size);
if (decrypt(tmp_dkek+IV_SIZE, tmp_dkek, kdata, key_size) != 0) {
free(kdata);
return SW_EXEC_ERROR();
}
release_dkek();
mbedtls_ecdh_init(&ctx);
mbedtls_ecp_group_id gid = kdata[0];
int r = 0;
r = mbedtls_ecdh_setup(&ctx, gid);
if (r != 0) {
mbedtls_ecdh_free(&ctx);
free(kdata);
return SW_DATA_INVALID();
}
r = mbedtls_mpi_read_binary(&ctx.ctx.mbed_ecdh.d, kdata+1, key_size-1);
if (r != 0) {
mbedtls_ecdh_free(&ctx);
free(kdata);
return SW_DATA_INVALID();
}
free(kdata);
r = mbedtls_ecdh_read_public(&ctx, apdu.cmd_apdu_data-1, apdu.cmd_apdu_data_len+1);
if (r != 0) {
mbedtls_ecdh_free(&ctx);
return SW_DATA_INVALID();
}
size_t olen = 0;
res_APDU[0] = 0x04;
r = mbedtls_ecdh_calc_secret(&ctx, &olen, res_APDU+1, MBEDTLS_ECP_MAX_BYTES, random_gen, NULL);
if (r != 0) {
mbedtls_ecdh_free(&ctx);
return SW_EXEC_ERROR();
}
res_APDU_size = olen+1;
mbedtls_ecdh_free(&ctx);
}
else
return SW_WRONG_P1P2();
return SW_OK();
}
@@ -1433,6 +1560,9 @@ static int cmd_cipher_sym() {
file_t *ef = search_dynamic_file((KEY_PREFIX << 8) | key_id);
if (!ef)
return SW_FILE_NOT_FOUND();
if ((apdu.cmd_apdu_data_len % 16) != 0) {
return SW_WRONG_LENGTH();
}
int key_size = file_read_uint16(ef->data);
if (load_dkek() != HSM_OK)
return SW_EXEC_ERROR();
@@ -1443,9 +1573,6 @@ static int cmd_cipher_sym() {
}
release_dkek();
if (algo == ALGO_AES_CBC_ENCRYPT || algo == ALGO_AES_CBC_DECRYPT) {
if ((apdu.cmd_apdu_data_len % 16) != 0) {
return SW_WRONG_LENGTH();
}
mbedtls_aes_context aes;
mbedtls_aes_init(&aes);
uint8_t tmp_iv[IV_SIZE];
@@ -1477,6 +1604,27 @@ static int cmd_cipher_sym() {
res_APDU_size = apdu.cmd_apdu_data_len;
mbedtls_aes_free(&aes);
}
else if (algo == ALGO_AES_CMAC) {
const mbedtls_cipher_info_t *cipher_info;
if (key_size == 16)
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB);
else if (key_size == 24)
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_192_ECB);
else if (key_size == 32)
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_256_ECB);
else
return SW_WRONG_DATA();
int r = mbedtls_cipher_cmac(cipher_info, kdata, key_size*8, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, res_APDU);
if (r != 0)
return SW_EXEC_ERROR();
res_APDU_size = apdu.cmd_apdu_data_len;
}
else if (algo == ALGO_AES_DERIVE) {
int r = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, file_read(ef->data+2), key_size, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, res_APDU, apdu.cmd_apdu_data_len);
if (r != 0)
return SW_EXEC_ERROR();
res_APDU_size = apdu.cmd_apdu_data_len;
}
else {
return SW_WRONG_P1P2();
}

View File

@@ -60,11 +60,12 @@ extern const uint8_t sc_hsm_aid[];
#define ALGO_EC_SHA256 0x73 /* ECDSA signature with SHA-256 hash */
#define ALGO_EC_DH 0x80 /* ECDH key derivation */
#define ALGO_EC_DERIVE 0x98 /* Derive EC key from EC key */
#define ALGO_EC_DERIVE 0x98 /* Derive EC key from EC key */
#define ALGO_AES_CBC_ENCRYPT 0x10
#define ALGO_AES_CBC_DECRYPT 0x11
#define ALGO_AES_CMAC 0x18
#define ALGO_AES_CMAC 0x18
#define ALGO_AES_DERIVE 0x99
extern int pin_reset_retries(const file_t *pin, bool);
extern int pin_wrong_retry(const file_t *pin);

10
src/hsm/version.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef __VERSION_H_
#define __VERSION_H_
#define HSM_VERSION 0x0104
#define HSM_VERSION_MAJOR ((HSM_VERSION >> 8) & 0xff)
#define HSM_VERSION_MINOR (HSM_VERSION & 0xff)
#endif

View File

@@ -29,14 +29,13 @@
#include "pico/stdlib.h"
//#include <chopstx.h>
#include "sys.h"
#include "neug.h"
//#include "adc.h"
#include "gnuk.h"
#include "hardware/structs/rosc.h"
#include "hardware/gpio.h"
#include "hardware/adc.h"
#include "bsp/board.h"
#include "pico/unique_id.h"
void adc_start ()
{
@@ -198,7 +197,9 @@ void *neug_task ()
*/
void neug_init (uint32_t *buf, uint8_t size)
{
const uint32_t *u = (const uint32_t *)unique_device_id ();
pico_unique_board_id_t unique_id;
pico_get_unique_board_id(&unique_id);
const uint32_t *u = (const uint32_t *)unique_id.id;
struct rng_rb *rb = &the_ring_buffer;
int i;

View File

@@ -25,7 +25,6 @@
#include <stdint.h>
#include <string.h>
#include "gnuk.h"
#include "neug.h"
#define RANDOM_BYTES_LENGTH 32
@@ -50,9 +49,12 @@ void random_fini (void)
* Return pointer to random 32-byte
*/
void random_bytes_free (const uint8_t *p);
#define MAX_RANDOM_BUFFER 1024
const uint8_t * random_bytes_get (size_t len)
{
static uint32_t return_word[512/sizeof(uint32_t)];
if (len > MAX_RANDOM_BUFFER)
return NULL;
static uint32_t return_word[MAX_RANDOM_BUFFER/sizeof(uint32_t)];
for (int ix = 0; ix < len; ix += RANDOM_BYTES_LENGTH) {
neug_wait_full ();
memcpy(return_word+ix/sizeof(uint32_t), random_word, RANDOM_BYTES_LENGTH);

View File

@@ -27,6 +27,7 @@
#include "usb_descriptors.h"
#include "ccid.h"
#include "pico/unique_id.h"
#include "version.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
@@ -67,7 +68,7 @@ tusb_desc_device_t const desc_device =
.idVendor = (USB_VID),
.idProduct = (USB_PID),
.bcdDevice = (0x0301),
.bcdDevice = HSM_VERSION,
.iManufacturer = 1,
.iProduct = 2,
@@ -183,10 +184,10 @@ char const* string_desc_arr [] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"Pol Henarejos", // 1: Manufacturer
"HSM 2040", // 2: Product
"Pico HSM", // 2: Product
"11223344", // 3: Serials, should use chip ID
"HSM 2040 Config", // 4: Vendor Interface
"HSM 2040 Interface"
"Pico HSM Config", // 4: Vendor Interface
"Pico HSM Interface"
};
static uint16_t _desc_str[32];

0
sys.h
View File