Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
239e01c3f8 | ||
|
|
4a57698173 | ||
|
|
468051288c | ||
|
|
565ea12d88 | ||
|
|
1c7ef50568 | ||
|
|
878eae9787 | ||
|
|
24b1d6807b | ||
|
|
6bc081a1e1 | ||
|
|
afb16fff65 | ||
|
|
cf81a82645 | ||
|
|
dc820a60ae | ||
|
|
c57cc139f6 | ||
|
|
79426f35cd | ||
|
|
502a7ba81c | ||
|
|
deef209687 | ||
|
|
bb09f212d2 | ||
|
|
cfd86df45e | ||
|
|
d16c9b2324 | ||
|
|
f1630023c7 | ||
|
|
d41a488eda | ||
|
|
375a18ebac | ||
|
|
20216ac4ba | ||
|
|
d27d8b0c5b | ||
|
|
a619527482 | ||
|
|
85ff92c4de | ||
|
|
b1121718db | ||
|
|
2905dcc8c0 | ||
|
|
c9855f7214 | ||
|
|
853b8f29a2 | ||
|
|
d5378ffa41 | ||
|
|
4400eba974 | ||
|
|
0cc656c6c0 | ||
|
|
c9b32ab5d0 | ||
|
|
f9ffd39661 | ||
|
|
bfc12d6856 | ||
|
|
11874b52de | ||
|
|
33a2222cd8 | ||
|
|
923e05a36c | ||
|
|
ad66170379 | ||
|
|
86e38419ac |
@@ -51,7 +51,7 @@ target_sources(pico_hsm PUBLIC
|
||||
${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}/src/hsm/hash_utils.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/hsm/crypto_utils.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/hsm/dkek.c
|
||||
|
||||
${CMAKE_CURRENT_LIST_DIR}/mbedtls/library/sha256.c
|
||||
@@ -119,7 +119,7 @@ pico_add_extra_outputs(pico_hsm)
|
||||
|
||||
#target_compile_definitions(pico_hsm PRIVATE MBEDTLS_ECDSA_DETERMINISTIC=1)
|
||||
|
||||
target_link_libraries(pico_hsm 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 hardware_rtc)
|
||||
|
||||
#
|
||||
#project(flash_nuke C CXX ASM)
|
||||
|
||||
65
README.md
65
README.md
@@ -6,7 +6,7 @@ This is a project to create a Hardware Security Module (HSM) with a Raspberry Pi
|
||||
- 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.
|
||||
- SHA1, SHA224, SHA256, SHA384, SHA512 digests.
|
||||
- RSA-PSS, RSA-PKCS and raw RSA signature.
|
||||
- ECDSA raw and hash signature.
|
||||
- ECDH key derivation.
|
||||
@@ -23,10 +23,15 @@ This is a project to create a Hardware Security Module (HSM) with a Raspberry Pi
|
||||
- DKEK n-of-m threshold scheme.
|
||||
- USB/CCID support with OpenSC, openssl, etc.
|
||||
- Extended APDU support.
|
||||
- Private keys and certificates import from WKY or PKCS#12 files.[^2]
|
||||
- Private keys and certificates import from WKY or PKCS#12 files.[^2][^3]
|
||||
- Transport PIN for provisioning and forcing to set a new PIN.[^2]
|
||||
- Press-to-confirm button optional feature to authorize operations with private/secret keys.
|
||||
- Store and retrieve binary data.
|
||||
- Real time clock with external datetime setting and getting.
|
||||
|
||||
[^1]: PKCS11 modules (`pkcs11-tool` and `sc-tool`) do not support CMAC and key derivation. It must be processed through raw APDU command (`opensc-tool -s`).
|
||||
[^2]: Imports are available via SCS3 tool, and only if the Pico HSM is previously initialized with a DKEK and the DKEK shares are available during the import process. See [SCS3](/doc/rsa_4096_support.md "SCS3") for more information.
|
||||
[^2]: Available via SCS3 tool. See [SCS3](/doc/rsa_4096.md "SCS3") for more information.
|
||||
[^3]: Imports are available only if the Pico HSM is previously initialized with a DKEK and the DKEK shares are available during the import process.
|
||||
|
||||
## 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 and only during the process. All keys (including DKEK) are loaded and cleared every time to avoid potential security flaws.
|
||||
@@ -82,17 +87,59 @@ For backup, restore and DKEK share management, check [doc/backup-and-restore.md]
|
||||
|
||||
For AES key generation, encryption and decryption, check [doc/aes.md](/doc/aes.md).
|
||||
|
||||
For 4096 bits RSA support, check [doc/rsa_4096_support.md](/doc/rsa_4096_support.md).
|
||||
For 4096 bits RSA support, check [doc/rsa_4096_support.md](/doc/rsa_4096.md).
|
||||
|
||||
## Key generation time
|
||||
Generating EC keys is almost instant. RSA keypair generation takes some time, specially for `2048` and `4096` bits.
|
||||
For storing and retrieving arbitrary data, check [doc/store_data.md](/doc/store_data.md).
|
||||
|
||||
For extra options, such as set/get real datetime or enable/disable press-to-confirm button, check [doc/extra_command.md](/doc/extra_command.md).
|
||||
|
||||
## Operation time
|
||||
### 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 | N/A |
|
||||
| 4096 | N/A |
|
||||
| 3072 | 600 |
|
||||
| 4096 | ~1000 |
|
||||
|
||||
### Signature and decrypt
|
||||
| RSA key length (bits) | Average time (seconds) |
|
||||
| :---: | :---: |
|
||||
| 1024 | 1 |
|
||||
| 2048 | 3 |
|
||||
| 3072 | 7 |
|
||||
| 4096 | 15 |
|
||||
|
||||
## Press-to-confirm button
|
||||
Raspberry Pico comes with the BOOTSEL button to load the firmware. When this firmware is running, the button can be used for other purposes. Pico HSM uses this button to confirm private/secret operations. This feature is optional and it shall be enabled. For more information, see [doc/extra_command.md](/doc/extra_command.md).
|
||||
|
||||
With this feature enabled, everytime that a private/secret key is loaded, the Pico HSM awaits for the user confirmation by pressing the BOOTSEL button. The Led of the Pico HSM will remain almost illuminated, turning off quickly once a second, indicating that the user must press the button to confirm the operation. Otherwise, the Pico HSM waits indefinitely. See [Led blink](#press-to-confirm) for a picture of the blinking sequence. When in this mode, the Pico HSM sends periodic timeout commands to the host to do not trigger the timeout operation.
|
||||
|
||||
This feature is an extra layer of security, as it requires the user intervention to sign or decrypt and it ensures that any application will use the Pico HSM without user awareness. However, it is not recommended for servers or other environments where operations are authomatized, since it requires a physical access to the Pico HSM to push the button.
|
||||
|
||||
## Led blink
|
||||
Pico HSM 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.
|
||||
|
||||

|
||||
|
||||
### Idle mode
|
||||
In idle mode, the Pico HSM 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.
|
||||
|
||||

|
||||
|
||||
### Active mode
|
||||
In active mode, the Pico HSM is awaken and ready to receive a command. It blinks four times in a second.
|
||||
|
||||

|
||||
|
||||
### Processing
|
||||
While processing, the Pico HSM is busy and cannot receive additional commands until the current is processed. In this state, the Led blinks 20 times in a second.
|
||||
|
||||

|
||||
|
||||
## Driver
|
||||
|
||||
@@ -104,7 +151,7 @@ The way to communicate is exactly the same as with other cards, such as OpenPGP
|
||||
|
||||
For an advanced usage, see the docs and examples.
|
||||
|
||||
Pico HSM also supports SCS3 tool. See [SCS3](/doc/rsa_4096_support.md "SCS3") for more information.
|
||||
Pico HSM also supports SCS3 tool. See [SCS3](/doc/rsa_4096.md "SCS3") for more information.
|
||||
|
||||
### 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 or have an explicit authorization.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION_MAJOR="1"
|
||||
VERSION_MINOR="8"
|
||||
VERSION_MINOR="12"
|
||||
|
||||
rm -rf release/*
|
||||
cd build_release
|
||||
|
||||
86
doc/extra_command.md
Normal file
86
doc/extra_command.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Extra command
|
||||
|
||||
Pico HSM supports a customized extra command to use with different options. Since the drivers in the market do not support the following features, a raw APDU command shall be sent.
|
||||
|
||||
To send a raw APDU command, `opensc-tool -s <APDU>` can be used. The `APDU` parameter is a string of hexadecimal numbers and it takes the following form:
|
||||
```
|
||||
8054XX00YYZZZZRR
|
||||
```
|
||||
|
||||
It composed by the following fields:
|
||||
- `80` to indicate that it is a custom vendor type command.
|
||||
- `54` is the `INS` custom command.
|
||||
- `XX` is the command to execute. It varies depending on the targeted command.
|
||||
- `00` is the parameter of the command. At this moment, no commands support parameters.
|
||||
- `YY` is the length of the data. If no data is provided, this field is absent.
|
||||
- `ZZZZ` is the data to be sent. Optional. The length is variable.
|
||||
- `RR` is the length of the expected response. If no response is expected, this field is absent.
|
||||
|
||||
## Real time clock and datetime
|
||||
Pico HSM has an internal real time clock (RTC) which can track precisely the date and the time. However, when it is reset or powered down, the Pico HSM is reset to the initial datetime: 2020 January 1, 00:00:00.
|
||||
|
||||
### Getting the datetime
|
||||
To obtain the current datetime (referenced to 00:00:00 2020/01/01), the `XX` parameter must be set to `0A`. There is no data and, thus, `YY` and `ZZZZ` are absent. The expected response is 8 bytes length.
|
||||
|
||||
For example, to obtain the current datetime:
|
||||
|
||||
```
|
||||
$ opensc-tool -s 80540A0008
|
||||
Using reader with a card: Free Software Initiative of Japan Gnuk
|
||||
Sending: 80 54 0A 00 08
|
||||
Received (SW1=0x90, SW2=0x00):
|
||||
07 E6 04 06 03 13 29 1E ......).
|
||||
```
|
||||
|
||||
The response is composed by 8 bytes:
|
||||
- The first two bytes are the current year, MSB first. Hence, `07E6h` equals to `2022`.
|
||||
- 1 byte for the current month, `01h` is January and `0Ch` is December.
|
||||
- 1 byte for the current day, from `01h` (1) to `1Fh` (31).
|
||||
- 1 byte for the day of the week, `00h` is Sunday, `01h` is Monday, etc.
|
||||
- 1 byte for the hours, from `00h` (0) to `17h` (23).
|
||||
- 1 byte for the minutes, from `00h` (0) to `3Bh` (59).
|
||||
- 1 byte for the seconds, from `00h` (0) to `3Bh` (59).
|
||||
|
||||
If the command is correctly received, `SW1=0x90` and `SW2=0x00`. Other values mean that an error has ocurred.
|
||||
|
||||
### Setting the datetime
|
||||
To set the reference datetime, a datetime string must be provided. For example:
|
||||
|
||||
```
|
||||
$ opensc-tool -s 80540A000807E6040603132917
|
||||
Using reader with a card: Free Software Initiative of Japan Gnuk
|
||||
Sending: 80 54 0A 00 08 07 E6 04 06 03 13 29 17
|
||||
Received (SW1=0x90, SW2=0x00)
|
||||
```
|
||||
|
||||
will set the reference datetime to `Wednesday, 2022 April 6th, 19:41:23`.
|
||||
|
||||
## Dynamic options
|
||||
Pico HSM support initialize options, such as setting Transport PIN or reset retry counter options. However, once it is initialized, these options cannot be modified anymore, without a new initialization (loosing all stored keys). Pico HSM offers the chance to define a set of dynamic options that can be enabled/disabled dynamically without initializing the device at every moment.
|
||||
|
||||
To specify a set of options, the `XX` parameter shall be set to `06`. The data parameter shall be 1 byte, where the options are combined with the or operand `|`. The length `YY` shall be set to `01`.
|
||||
|
||||
### Press-to-confirm button
|
||||
Press-to-confirm button offers an extra security layer by requiring the user confirmation everytime that a private/secret key is loaded. This avoids ghost applications thay may perform hidden opperations without noticing the user, such as signing or decrypting. Pico HSM will inform the user that is awaiting for a confirmation by making almost a fixed Led blink.
|
||||
|
||||
This feature is disabled by default but can be enabled rapidly by setting the LSB bit to 1:
|
||||
|
||||
```
|
||||
$ opensc-tool -s 805406000101
|
||||
Using reader with a card: Free Software Initiative of Japan Gnuk
|
||||
Sending: 80 54 06 00 01 01
|
||||
Received (SW1=0x90, SW2=0x00)
|
||||
```
|
||||
|
||||
At this moment, when a private/secret key is loaded, the Pico HSM will wait for the pressed BOOTSEL button to confirm the operation.
|
||||
|
||||
To disable, the LSB bit must be set to 0:
|
||||
|
||||
```
|
||||
$ opensc-tool -s 805406000100
|
||||
Using reader with a card: Free Software Initiative of Japan Gnuk
|
||||
Sending: 80 54 06 00 01 00
|
||||
Received (SW1=0x90, SW2=0x00)
|
||||
```
|
||||
|
||||
|
||||
117
doc/store_data.md
Normal file
117
doc/store_data.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# Store binary data
|
||||
Pico HSM has a internal flash which can store binary data. With this approach, you can save different files, encrypt into the Pico HSM and retrieve them after.
|
||||
|
||||
## Maximum size
|
||||
Due to internal constraints with the flash components, the maximum file size is `4096` bytes. This mechanism is mainly used to store small files, such as keys in plain text, certificates, credentials, etc.
|
||||
|
||||
## Store a file
|
||||
Before writting a file into the Pico HSM, we generate the data file with the following text:
|
||||
|
||||
```
|
||||
$ echo 'Pico HSM is awesome!' > test
|
||||
```
|
||||
|
||||
Then, we can store the data file with the following command:
|
||||
|
||||
```
|
||||
$ pkcs11-tool --pin 648219 --write-object test --type data --id 1 --label 'test1'
|
||||
Using slot 0 with a present token (0x0)
|
||||
Created Data Object:
|
||||
Data object 1236368320
|
||||
label: 'test1'
|
||||
application: 'test1'
|
||||
app_id: <empty>
|
||||
flags: modifiable
|
||||
```
|
||||
|
||||
This file can also be protected with the PIN. In this case, use the previous command with the `--private` flag:
|
||||
|
||||
```
|
||||
$ pkcs11-tool --pin 648219 --write-object test --type data --id 2 --label 'test2' --private
|
||||
Using slot 0 with a present token (0x0)
|
||||
Created Data Object:
|
||||
Data object 1329612320
|
||||
label: 'test2'
|
||||
application: 'test2'
|
||||
app_id: <empty>
|
||||
flags: modifiable private
|
||||
```
|
||||
|
||||
Always provide a unique `--label`, as it will be used to index and reference the file for retrieving.
|
||||
|
||||
## Retrieve a file
|
||||
To view the stored file, we can use the following command with the same label we employed:
|
||||
|
||||
```
|
||||
$ pkcs11-tool --read-object --type data --label 'test1'
|
||||
Using slot 0 with a present token (0x0)
|
||||
Pico HSM is awesome!
|
||||
```
|
||||
|
||||
Note that if the `--private` flag is not provided during the writting stage, the file can be accessed without the PIN.
|
||||
|
||||
To retrieve a private file with the PIN:
|
||||
|
||||
```
|
||||
$ pkcs11-tool --read-object --type data --label 'test2' --pin 648219
|
||||
Using slot 0 with a present token (0x0)
|
||||
Pico HSM is awesome!
|
||||
```
|
||||
|
||||
## Using `pkcs15-tool`
|
||||
PKCS15 tool can be used to list the stored files. For instance:
|
||||
|
||||
```
|
||||
$ pkcs15-tool -D
|
||||
Using reader with a card: Free Software Initiative of Japan Gnuk
|
||||
PKCS#15 Card [Pico-HSM]:
|
||||
Version : 1
|
||||
Serial number : ESTERMHSM
|
||||
Manufacturer ID: Pol Henarejos
|
||||
Flags : PRN generation, EID compliant
|
||||
|
||||
|
||||
PIN [UserPIN]
|
||||
Object Flags : [0x03], private, modifiable
|
||||
Auth ID : 02
|
||||
ID : 01
|
||||
Flags : [0x812], local, initialized, exchangeRefData
|
||||
Length : min_len:6, max_len:15, stored_len:0
|
||||
Pad char : 0x00
|
||||
Reference : 129 (0x81)
|
||||
Type : ascii-numeric
|
||||
Path : e82b0601040181c31f0201::
|
||||
Tries left : 3
|
||||
|
||||
PIN [SOPIN]
|
||||
Object Flags : [0x01], private
|
||||
ID : 02
|
||||
Flags : [0x9A], local, unblock-disabled, initialized, soPin
|
||||
Length : min_len:16, max_len:16, stored_len:0
|
||||
Pad char : 0x00
|
||||
Reference : 136 (0x88)
|
||||
Type : bcd
|
||||
Path : e82b0601040181c31f0201::
|
||||
Tries left : 15
|
||||
|
||||
Data object 'test1'
|
||||
applicationName: test1
|
||||
Path: e82b0601040181c31f0201::cf00
|
||||
Data (21 bytes): 5069636F2048534D20697320617765736F6D65210A
|
||||
|
||||
Data object 'test2'
|
||||
applicationName: test2
|
||||
Path: e82b0601040181c31f0201::cd01
|
||||
Auth ID: 01
|
||||
```
|
||||
|
||||
As expected, the public file is displayed (in hexadecimal string). The private file contains the `Auth ID` flag and it is not displayed.
|
||||
|
||||
## Delete a file
|
||||
A stored file can be deleted with the following command:
|
||||
|
||||
```
|
||||
$ pkcs11-tool --login --pin 648219 --delete-object --type data --application-label test1
|
||||
```
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#
|
||||
|
||||
VERSION_MAJOR="1"
|
||||
VERSION_MINOR="8"
|
||||
VERSION_MINOR="0C"
|
||||
|
||||
echo "----------------------------"
|
||||
echo "VID/PID patcher for Pico HSM"
|
||||
|
||||
@@ -99,15 +99,16 @@ file_t file_entries[] = {
|
||||
/* 13 */ { .fid = 0x1089 , .parent = 5, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff} }, //max retries PIN (SOPIN)
|
||||
/* 14 */ { .fid = 0x108A , .parent = 5, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff} }, //retries PIN (SOPIN)
|
||||
/* 15 */ { .fid = EF_DKEK , .parent = 5, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff} }, //DKEK
|
||||
/* 16 */ { .fid = EF_PRKDFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.PrKDFs
|
||||
/* 17 */ { .fid = EF_PUKDFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.PuKDFs
|
||||
/* 18 */ { .fid = EF_CDFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.CDFs
|
||||
/* 19 */ { .fid = EF_AODFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.AODFs
|
||||
/* 20 */ { .fid = EF_DODFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.DODFs
|
||||
/* 21 */ { .fid = EF_SKDFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.SKDFs
|
||||
///* 22 */ { .fid = 0x0000, .parent = 0, .name = openpgpcard_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} },
|
||||
/* 23 */ { .fid = 0x0000, .parent = 5, .name = sc_hsm_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} },
|
||||
/* 24 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = {0} } //end
|
||||
/* 16 */ { .fid = EF_DEVOPS , .parent = 5, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff} }, //Device options
|
||||
/* 17 */ { .fid = EF_PRKDFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.PrKDFs
|
||||
/* 18 */ { .fid = EF_PUKDFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.PuKDFs
|
||||
/* 19 */ { .fid = EF_CDFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.CDFs
|
||||
/* 20 */ { .fid = EF_AODFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.AODFs
|
||||
/* 21 */ { .fid = EF_DODFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.DODFs
|
||||
/* 22 */ { .fid = EF_SKDFS , .parent = 5, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.SKDFs
|
||||
///* 23 */ { .fid = 0x0000, .parent = 0, .name = openpgpcard_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} },
|
||||
/* 24 */ { .fid = 0x0000, .parent = 5, .name = sc_hsm_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} },
|
||||
/* 25 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = {0} } //end
|
||||
};
|
||||
|
||||
const file_t *MF = &file_entries[0];
|
||||
@@ -261,19 +262,8 @@ void scan_flash() {
|
||||
file_t *file = (file_t *)search_by_fid(fid, NULL, SPECIFY_EF);
|
||||
if (!file) {
|
||||
file = file_new(fid);
|
||||
if ((fid & 0xff00) == (KEY_PREFIX << 8)) {
|
||||
//add_file_to_chain(file, &ef_kf);
|
||||
}
|
||||
else if ((fid & 0xff00) == (PRKD_PREFIX << 8)) {
|
||||
//add_file_to_chain(file, &ef_prkdf);
|
||||
}
|
||||
else if ((fid & 0xff00) == (CD_PREFIX << 8)) {
|
||||
//add_file_to_chain(file, &ef_cdf);
|
||||
}
|
||||
else if ((fid & 0xff00) == (EE_CERTIFICATE_PREFIX << 8)) {
|
||||
//add_file_to_chain(file, &ef_pukdf);
|
||||
}
|
||||
else {
|
||||
uint8_t pfx = fid >> 8;
|
||||
if (pfx != KEY_PREFIX && pfx != PRKD_PREFIX && pfx != CD_PREFIX && pfx != EE_CERTIFICATE_PREFIX && pfx != DCOD_PREFIX && pfx != PROT_DATA_PREFIX && pfx != DATA_PREFIX) {
|
||||
TU_LOG1("SCAN FOUND ORPHAN FILE: %x\r\n",fid);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
#define EF_AODFS 0x6043
|
||||
#define EF_DODFS 0x6044
|
||||
#define EF_SKDFS 0x6045
|
||||
#define EF_DEVOPS 0x100E
|
||||
|
||||
#define MAX_DEPTH 4
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "mbedtls/md.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "mbedtls/aes.h"
|
||||
#include "hash_utils.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "sc_hsm.h"
|
||||
#include "libopensc/card-sc-hsm.h"
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _HASH_UTILS_H_
|
||||
#define _HASH_UTILS_H_
|
||||
#ifndef _CRYPTO_UTILS_H_
|
||||
#define _CRYPTO_UTILS_H_
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "pico/stdlib.h"
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "stdlib.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "dkek.h"
|
||||
#include "hash_utils.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "random.h"
|
||||
#include "sc_hsm.h"
|
||||
#include "mbedtls/md.h"
|
||||
@@ -88,6 +88,7 @@ int dkek_kcv(uint8_t *kcv) { //kcv 8 bytes
|
||||
hash256(dkek+IV_SIZE, 32, hsh);
|
||||
release_dkek();
|
||||
memcpy(kcv, hsh, 8);
|
||||
return HSM_OK;
|
||||
}
|
||||
|
||||
int dkek_kenc(uint8_t *kenc) { //kenc 32 bytes
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
#include "pico/util/queue.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "random.h"
|
||||
#include "hsm2040.h"
|
||||
#include "hardware/rtc.h"
|
||||
|
||||
extern void do_flash();
|
||||
extern void low_flash_init();
|
||||
@@ -93,7 +93,8 @@ app_t *current_app = NULL;
|
||||
|
||||
extern void card_thread();
|
||||
|
||||
static queue_t *card_comm;
|
||||
queue_t *card_comm = NULL;
|
||||
queue_t *ccid_comm = NULL;
|
||||
extern void low_flash_init_core1();
|
||||
|
||||
int register_app(app_t * (*select_aid)()) {
|
||||
@@ -373,18 +374,28 @@ usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) {
|
||||
return &ccid_driver;
|
||||
}
|
||||
|
||||
enum {
|
||||
BLINK_NOT_MOUNTED = (250 << 16) | 250,
|
||||
BLINK_MOUNTED = (250 << 16) | 250,
|
||||
BLINK_SUSPENDED = (500 << 16) | 1000,
|
||||
BLINK_PROCESSING = (50 << 16) | 50,
|
||||
|
||||
BLINK_ALWAYS_ON = UINT32_MAX,
|
||||
BLINK_ALWAYS_OFF = 0
|
||||
};
|
||||
|
||||
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
|
||||
void led_set_blink(uint32_t mode) {
|
||||
blink_interval_ms = mode;
|
||||
}
|
||||
|
||||
void execute_tasks();
|
||||
|
||||
static void wait_button() {
|
||||
led_set_blink((1000 << 16) | 100);
|
||||
while (board_button_read() == false) {
|
||||
execute_tasks();
|
||||
//sleep_ms(10);
|
||||
}
|
||||
while (board_button_read() == true) {
|
||||
execute_tasks();
|
||||
//sleep_ms(10);
|
||||
}
|
||||
led_set_blink(BLINK_PROCESSING);
|
||||
}
|
||||
|
||||
void usb_tx_enable(const uint8_t *buf, uint32_t len)
|
||||
{
|
||||
if (len > 0) {
|
||||
@@ -484,7 +495,7 @@ static enum ccid_state ccid_power_on(struct ccid *c)
|
||||
|
||||
DEBUG_INFO("ON\r\n");
|
||||
c->tx_busy = 1;
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
led_set_blink(BLINK_MOUNTED);
|
||||
return CCID_STATE_WAIT;
|
||||
}
|
||||
|
||||
@@ -531,7 +542,7 @@ static enum ccid_state ccid_power_off(struct ccid *c)
|
||||
ccid_send_status (c);
|
||||
DEBUG_INFO ("OFF\r\n");
|
||||
c->tx_busy = 1;
|
||||
blink_interval_ms = BLINK_SUSPENDED;
|
||||
led_set_blink(BLINK_SUSPENDED);
|
||||
return CCID_STATE_START;
|
||||
}
|
||||
|
||||
@@ -1375,7 +1386,7 @@ void prepare_ccid()
|
||||
}
|
||||
|
||||
int process_apdu() {
|
||||
blink_interval_ms = BLINK_PROCESSING;
|
||||
led_set_blink(BLINK_PROCESSING);
|
||||
if (!current_app) {
|
||||
if (INS(apdu) == 0xA4 && P1(apdu) == 0x04 && (P2(apdu) == 0x00 || P2(apdu) == 0x4)) { //select by AID
|
||||
for (int a = 0; a < num_apps; a++) {
|
||||
@@ -1405,7 +1416,7 @@ static void card_init (void)
|
||||
|
||||
void card_thread()
|
||||
{
|
||||
queue_t *ccid_comm = (queue_t *)multicore_fifo_pop_blocking();
|
||||
ccid_comm = (queue_t *)multicore_fifo_pop_blocking();
|
||||
card_comm = (queue_t *)multicore_fifo_pop_blocking();
|
||||
|
||||
card_init ();
|
||||
@@ -1530,7 +1541,7 @@ void ccid_task(void)
|
||||
{
|
||||
DEBUG_INFO ("ERR05\r\n");
|
||||
}
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
led_set_blink(BLINK_MOUNTED);
|
||||
}
|
||||
else if (m == EV_TX_FINISHED)
|
||||
{
|
||||
@@ -1541,6 +1552,11 @@ void ccid_task(void)
|
||||
if (c->state == APDU_STATE_WAIT_COMMAND || c->state == APDU_STATE_COMMAND_CHAINING || c->state == APDU_STATE_RESULT_GET_RESPONSE)
|
||||
ccid_prepare_receive(c);
|
||||
}
|
||||
else if (m == EV_PRESS_BUTTON) {
|
||||
wait_button();
|
||||
uint32_t flag = EV_BUTTON_PRESSED;
|
||||
queue_try_add(&c->card_comm, &flag);
|
||||
}
|
||||
}
|
||||
else /* Timeout */
|
||||
{
|
||||
@@ -1604,10 +1620,32 @@ void led_off_all()
|
||||
#endif
|
||||
}
|
||||
|
||||
void init_rtc() {
|
||||
|
||||
rtc_init();
|
||||
datetime_t dt = {
|
||||
.year = 2020,
|
||||
.month = 1,
|
||||
.day = 1,
|
||||
.dotw = 3, // 0 is Sunday, so 5 is Friday
|
||||
.hour = 00,
|
||||
.min = 00,
|
||||
.sec = 00
|
||||
};
|
||||
rtc_set_datetime(&dt);
|
||||
}
|
||||
|
||||
extern void neug_task();
|
||||
|
||||
pico_unique_board_id_t unique_id;
|
||||
|
||||
void execute_tasks() {
|
||||
prev_millis = board_millis();
|
||||
ccid_task();
|
||||
tud_task(); // tinyusb device task
|
||||
led_blinking_task();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct apdu *a = &apdu;
|
||||
@@ -1628,6 +1666,7 @@ int main(void)
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
led_off_all();
|
||||
|
||||
@@ -1638,13 +1677,12 @@ int main(void)
|
||||
random_init();
|
||||
|
||||
low_flash_init();
|
||||
|
||||
init_rtc();
|
||||
|
||||
while (1)
|
||||
{
|
||||
prev_millis = board_millis();
|
||||
ccid_task();
|
||||
tud_task(); // tinyusb device task
|
||||
led_blinking_task();
|
||||
execute_tasks();
|
||||
neug_task();
|
||||
do_flash();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "tusb.h"
|
||||
#include "file.h"
|
||||
#include "pico/unique_id.h"
|
||||
#include "pico/util/queue.h"
|
||||
|
||||
#define USB_REQ_CCID 0xA1
|
||||
|
||||
@@ -79,13 +80,18 @@ struct apdu {
|
||||
#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 */
|
||||
#define EV_PRESS_BUTTON 32
|
||||
|
||||
/* OpenPGPcard thread */
|
||||
/* SC HSM 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
|
||||
#define EV_BUTTON_PRESSED 16
|
||||
|
||||
//Variables set by core1
|
||||
extern queue_t *ccid_comm;
|
||||
extern queue_t *card_comm;
|
||||
|
||||
enum ccid_state {
|
||||
CCID_STATE_NOCARD, /* No card available */
|
||||
@@ -157,4 +163,16 @@ extern void low_flash_available();
|
||||
extern int flash_clear_file(file_t *file);
|
||||
|
||||
extern pico_unique_board_id_t unique_id;
|
||||
|
||||
enum {
|
||||
BLINK_NOT_MOUNTED = (250 << 16) | 250,
|
||||
BLINK_MOUNTED = (250 << 16) | 250,
|
||||
BLINK_SUSPENDED = (500 << 16) | 1000,
|
||||
BLINK_PROCESSING = (50 << 16) | 50,
|
||||
|
||||
BLINK_ALWAYS_ON = UINT32_MAX,
|
||||
BLINK_ALWAYS_OFF = 0
|
||||
};
|
||||
extern void led_set_blink(uint32_t mode);
|
||||
|
||||
#endif
|
||||
229
src/hsm/sc_hsm.c
229
src/hsm/sc_hsm.c
@@ -30,8 +30,9 @@
|
||||
#include "mbedtls/hkdf.h"
|
||||
#include "version.h"
|
||||
#include "cvcerts.h"
|
||||
#include "hash_utils.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "dkek.h"
|
||||
#include "hardware/rtc.h"
|
||||
|
||||
const uint8_t sc_hsm_aid[] = {
|
||||
11,
|
||||
@@ -92,6 +93,28 @@ void select_file(file_t *pe) {
|
||||
//sc_hsm_unload(); //reset auth status
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t get_device_options() {
|
||||
file_t *ef = search_by_fid(EF_DEVOPS, NULL, SPECIFY_EF);
|
||||
if (ef && ef->data)
|
||||
return (file_read_uint8(ef->data+2) << 8) | file_read_uint8(ef->data+3);
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
extern uint32_t board_button_read(void);
|
||||
|
||||
static void wait_button() {
|
||||
uint16_t opts = get_device_options();
|
||||
if (opts & HSM_OPT_BOOTSEL_BUTTON) {
|
||||
uint32_t val = EV_PRESS_BUTTON;
|
||||
queue_try_add(ccid_comm, &val);
|
||||
do {
|
||||
queue_remove_blocking(card_comm, &val);
|
||||
}
|
||||
while (val != EV_BUTTON_PRESSED);
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_select() {
|
||||
uint8_t p1 = P1(apdu);
|
||||
uint8_t p2 = P2(apdu);
|
||||
@@ -108,16 +131,14 @@ static int cmd_select() {
|
||||
|
||||
//if ((fid & 0xff00) == (KEY_PREFIX << 8))
|
||||
// fid = (PRKD_PREFIX << 8) | (fid & 0xff);
|
||||
|
||||
if ((fid & 0xff00) == (PRKD_PREFIX << 8)) {
|
||||
if (!(pe = search_dynamic_file(fid)))
|
||||
return SW_FILE_NOT_FOUND();
|
||||
}
|
||||
else if ((fid & 0xff00) == (CD_PREFIX << 8)) {
|
||||
if (!(pe = search_dynamic_file(fid)))
|
||||
return SW_FILE_NOT_FOUND();
|
||||
}
|
||||
else if ((fid & 0xff00) == (EE_CERTIFICATE_PREFIX << 8)) {
|
||||
|
||||
uint8_t pfx = fid >> 8;
|
||||
if (pfx == PRKD_PREFIX ||
|
||||
pfx == CD_PREFIX ||
|
||||
pfx == EE_CERTIFICATE_PREFIX ||
|
||||
pfx == DCOD_PREFIX ||
|
||||
pfx == DATA_PREFIX ||
|
||||
pfx == PROT_DATA_PREFIX) {
|
||||
if (!(pe = search_dynamic_file(fid)))
|
||||
return SW_FILE_NOT_FOUND();
|
||||
}
|
||||
@@ -171,8 +192,9 @@ static int cmd_select() {
|
||||
if (pe == file_sc_hsm) {
|
||||
res_APDU[res_APDU_size++] = 0x85;
|
||||
res_APDU[res_APDU_size++] = 4;
|
||||
res_APDU[res_APDU_size++] = 0xff; //options
|
||||
res_APDU[res_APDU_size++] = 0xff;
|
||||
uint16_t opts = get_device_options();
|
||||
res_APDU[res_APDU_size++] = opts & 0xff;
|
||||
res_APDU[res_APDU_size++] = opts >> 8;
|
||||
res_APDU[res_APDU_size++] = HSM_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = HSM_VERSION_MINOR;
|
||||
res_APDU[1] = res_APDU_size-2;
|
||||
@@ -287,6 +309,14 @@ static int cmd_list_keys()
|
||||
res_APDU[res_APDU_size++] = f->fid & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < dynamic_files; i++) {
|
||||
file_t *f = &dynamic_file[i];
|
||||
if ((f->fid & 0xff00) == (DCOD_PREFIX << 8)) {
|
||||
res_APDU[res_APDU_size++] = DCOD_PREFIX;
|
||||
res_APDU[res_APDU_size++] = f->fid & 0xff;
|
||||
}
|
||||
}
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
@@ -400,11 +430,12 @@ int pin_wrong_retry(const file_t *pin) {
|
||||
|
||||
int check_pin(const file_t *pin, const uint8_t *data, size_t len) {
|
||||
if (!pin)
|
||||
return SW_FILE_NOT_FOUND();
|
||||
return SW_REFERENCE_NOT_FOUND();
|
||||
if (!pin->data) {
|
||||
return SW_REFERENCE_NOT_FOUND();
|
||||
}
|
||||
isUserAuthenticated = false;
|
||||
has_session_pin = has_session_sopin = false;
|
||||
uint8_t dhash[32];
|
||||
double_hash_pin(data, len, dhash);
|
||||
if (sizeof(dhash) != file_read_uint16(pin->data)-1) //1 byte for pin len
|
||||
@@ -422,7 +453,10 @@ int check_pin(const file_t *pin, const uint8_t *data, size_t len) {
|
||||
return SW_MEMORY_FAILURE();
|
||||
isUserAuthenticated = true;
|
||||
hash_multi(data, len, session_pin);
|
||||
has_session_pin = true;
|
||||
if (pin == file_pin1)
|
||||
has_session_pin = true;
|
||||
else if (pin == file_sopin)
|
||||
has_session_sopin = true;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
@@ -434,46 +468,94 @@ static int cmd_verify() {
|
||||
return SW_WRONG_P1P2();
|
||||
uint8_t qualifier = p2&0x1f;
|
||||
if (p2 == 0x81) { //UserPin
|
||||
uint16_t opts = get_device_options();
|
||||
if (opts & HSM_OPT_TRANSPORT_PIN)
|
||||
return SW_DATA_INVALID();
|
||||
if (file_read_uint8(file_pin1->data+2) == 0) //not initialized
|
||||
return SW_REFERENCE_NOT_FOUND();
|
||||
if (apdu.cmd_apdu_data_len > 0) {
|
||||
return check_pin(file_pin1, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
|
||||
}
|
||||
if (file_read_uint8(file_retries_pin1->data+2) == 0)
|
||||
return SW_PIN_BLOCKED();
|
||||
if (has_session_pin)
|
||||
return SW_OK();
|
||||
return set_res_sw(0x63, 0xc0 | file_read_uint8(file_retries_pin1->data+2));
|
||||
}
|
||||
else if (p2 == 0x88) { //SOPin
|
||||
if (file_read_uint8(file_sopin->data+2) == 0) //not initialized
|
||||
return SW_REFERENCE_NOT_FOUND();
|
||||
if (apdu.cmd_apdu_data_len > 0) {
|
||||
return check_pin(file_sopin, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
|
||||
}
|
||||
if (file_read_uint8(file_retries_sopin->data+2) == 0)
|
||||
return SW_PIN_BLOCKED();
|
||||
if (has_session_sopin)
|
||||
return SW_OK();
|
||||
return set_res_sw(0x63, 0xc0 | file_read_uint8(file_retries_sopin->data+2));
|
||||
}
|
||||
return SW_REFERENCE_NOT_FOUND();
|
||||
}
|
||||
|
||||
static int cmd_reset_retry() {
|
||||
if (P1(apdu) == 0x0) {
|
||||
if (P2(apdu) == 0x81) {
|
||||
if (!file_sopin || !file_pin1) {
|
||||
return SW_FILE_NOT_FOUND();
|
||||
}
|
||||
if (!file_sopin->data) {
|
||||
return SW_REFERENCE_NOT_FOUND();
|
||||
}
|
||||
if (P2(apdu) != 0x81)
|
||||
return SW_REFERENCE_NOT_FOUND();
|
||||
if (!file_sopin || !file_pin1) {
|
||||
return SW_FILE_NOT_FOUND();
|
||||
}
|
||||
if (!file_sopin->data) {
|
||||
return SW_REFERENCE_NOT_FOUND();
|
||||
}
|
||||
uint16_t opts = get_device_options();
|
||||
if (!(opts & HSM_OPT_RRC))
|
||||
return SW_COMMAND_NOT_ALLOWED();
|
||||
if (P1(apdu) == 0x0 || P1(apdu) == 0x2) {
|
||||
int newpin_len = 0;
|
||||
if (P1(apdu) == 0x0) {
|
||||
if (apdu.cmd_apdu_data_len <= 8)
|
||||
return SW_WRONG_LENGTH();
|
||||
uint16_t r = check_pin(file_sopin, apdu.cmd_apdu_data, 8);
|
||||
if (r != 0x9000)
|
||||
return r;
|
||||
uint8_t dhash[33];
|
||||
dhash[0] = apdu.cmd_apdu_data_len-8;
|
||||
double_hash_pin(apdu.cmd_apdu_data+8, apdu.cmd_apdu_data_len-8, dhash+1);
|
||||
flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
|
||||
if (pin_reset_retries(file_pin1, true) != HSM_OK)
|
||||
return SW_MEMORY_FAILURE();
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
newpin_len = apdu.cmd_apdu_data_len-8;
|
||||
}
|
||||
else if (P1(apdu) == 0x2) {
|
||||
if (!has_session_sopin)
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
if (apdu.cmd_apdu_data_len > 16)
|
||||
return SW_WRONG_LENGTH();
|
||||
newpin_len = apdu.cmd_apdu_data_len;
|
||||
}
|
||||
uint8_t dhash[33];
|
||||
dhash[0] = newpin_len;
|
||||
double_hash_pin(apdu.cmd_apdu_data+(apdu.cmd_apdu_data_len-newpin_len), newpin_len, dhash+1);
|
||||
flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
|
||||
if (pin_reset_retries(file_pin1, true) != HSM_OK)
|
||||
return SW_MEMORY_FAILURE();
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
else if (P1(apdu) == 0x1 || P1(apdu) == 0x3) {
|
||||
if (!(opts & HSM_OPT_RRC_RESET_ONLY))
|
||||
return SW_COMMAND_NOT_ALLOWED();
|
||||
if (P1(apdu) == 0x1) {
|
||||
if (apdu.cmd_apdu_data_len != 8)
|
||||
return SW_WRONG_LENGTH();
|
||||
uint16_t r = check_pin(file_sopin, apdu.cmd_apdu_data, 8);
|
||||
if (r != 0x9000)
|
||||
return r;
|
||||
}
|
||||
else if (P1(apdu) == 0x3) {
|
||||
if (!has_session_sopin)
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
if (apdu.cmd_apdu_data_len != 0)
|
||||
return SW_WRONG_LENGTH();
|
||||
}
|
||||
if (pin_reset_retries(file_pin1, true) != HSM_OK)
|
||||
return SW_MEMORY_FAILURE();
|
||||
return SW_OK();
|
||||
}
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
|
||||
static int cmd_challenge() {
|
||||
@@ -503,6 +585,8 @@ static int cmd_initialize() {
|
||||
uint8_t tag = *p++;
|
||||
uint8_t tag_len = *p++;
|
||||
if (tag == 0x80) { //options
|
||||
file_t *tf = search_by_fid(EF_DEVOPS, NULL, SPECIFY_EF);
|
||||
flash_write_data_to_file(tf, p, tag_len);
|
||||
}
|
||||
else if (tag == 0x81) { //user pin
|
||||
if (file_pin1 && file_pin1->data) {
|
||||
@@ -581,6 +665,7 @@ static int cmd_import_dkek() {
|
||||
|
||||
}
|
||||
}
|
||||
memset(res_APDU,0,10);
|
||||
res_APDU[0] = dkeks;
|
||||
res_APDU[1] = dkeks-current_dkeks;
|
||||
dkek_kcv(res_APDU+2);
|
||||
@@ -929,15 +1014,19 @@ static int cmd_keypair_gen() {
|
||||
|
||||
res_APDU[0] = 0x67;
|
||||
int outer_len = strlen(cvc.outer_car)+2+2+1+4;
|
||||
int bytes_length = (cvclen+outer_len)/256;
|
||||
if (cvclen%256 > 0)
|
||||
bytes_length++;
|
||||
if (cvclen < 128)
|
||||
int bytes_length = 0;
|
||||
if (cvclen+outer_len < 128)
|
||||
res_APDU[1] = cvclen+outer_len;
|
||||
else if (cvclen+outer_len < 256) {
|
||||
res_APDU[1] = 0x81;
|
||||
res_APDU[2] = cvclen+outer_len;
|
||||
bytes_length = 1;
|
||||
}
|
||||
else {
|
||||
res_APDU[1] = 0x80|bytes_length;
|
||||
for (int b = 1; b <= bytes_length; b++)
|
||||
res_APDU[1+b] = ((cvclen+outer_len)>>((bytes_length-b)*8))&0xff;
|
||||
res_APDU[1] = 0x82;
|
||||
res_APDU[2] = (cvclen+outer_len) >> 8;
|
||||
res_APDU[3] = (cvclen+outer_len) & 0xff;
|
||||
bytes_length = 2;
|
||||
}
|
||||
memcpy(res_APDU+bytes_length+2, cvcbin, cvclen);
|
||||
res_APDU[bytes_length+2+cvclen] = 0x42;
|
||||
@@ -967,7 +1056,7 @@ static int cmd_update_ef() {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
if (fid == 0x0)
|
||||
ef = currentEF;
|
||||
else if (p1 != EE_CERTIFICATE_PREFIX && p1 != PRKD_PREFIX && p1 != CA_CERTIFICATE_PREFIX && p1 != CD_PREFIX)
|
||||
else if (p1 != EE_CERTIFICATE_PREFIX && p1 != PRKD_PREFIX && p1 != CA_CERTIFICATE_PREFIX && p1 != CD_PREFIX && p1 != DATA_PREFIX && p1 != DCOD_PREFIX && p1 != PROT_DATA_PREFIX)
|
||||
return SW_INCORRECT_P1P2();
|
||||
|
||||
if (ef && !authenticate_action(ef, ACL_OP_UPDATE_ERASE))
|
||||
@@ -1036,7 +1125,7 @@ static int cmd_delete_file() {
|
||||
if (apdu.cmd_apdu_data_len == 0) {
|
||||
ef = currentEF;
|
||||
if (!(ef = search_dynamic_file(ef->fid)))
|
||||
return SW_FILE_INVALID();
|
||||
return SW_FILE_NOT_FOUND();
|
||||
}
|
||||
else {
|
||||
uint16_t fid = (apdu.cmd_apdu_data[0] << 8) | apdu.cmd_apdu_data[1];
|
||||
@@ -1116,6 +1205,7 @@ static int cmd_key_gen() {
|
||||
}
|
||||
|
||||
int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) {
|
||||
wait_button();
|
||||
int key_size = file_read_uint16(fkey->data);
|
||||
uint8_t kdata[4096/8];
|
||||
memcpy(kdata, file_read(fkey->data+2), key_size);
|
||||
@@ -1150,6 +1240,7 @@ int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) {
|
||||
}
|
||||
|
||||
int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey) {
|
||||
wait_button();
|
||||
int key_size = file_read_uint16(fkey->data);
|
||||
uint8_t kdata[67]; //Worst case, 521 bit + 1byte
|
||||
memcpy(kdata, file_read(fkey->data+2), key_size);
|
||||
@@ -1342,6 +1433,7 @@ static int cmd_key_wrap() {
|
||||
}
|
||||
else if (*dprkd == P15_KEYTYPE_AES) {
|
||||
uint8_t kdata[32]; //maximum AES key size
|
||||
wait_button();
|
||||
int key_size = file_read_uint16(ef->data), aes_type = HSM_KEY_AES;
|
||||
memcpy(kdata, file_read(ef->data+2), key_size);
|
||||
if (dkek_decrypt(kdata, key_size) != 0) {
|
||||
@@ -1451,6 +1543,7 @@ static int cmd_decrypt_asym() {
|
||||
}
|
||||
else if (P2(apdu) == ALGO_EC_DH) {
|
||||
mbedtls_ecdh_context ctx;
|
||||
wait_button();
|
||||
int key_size = file_read_uint16(ef->data);
|
||||
uint8_t *kdata = (uint8_t *)calloc(1,key_size);
|
||||
memcpy(kdata, file_read(ef->data+2), key_size);
|
||||
@@ -1505,6 +1598,7 @@ static int cmd_cipher_sym() {
|
||||
if ((apdu.cmd_apdu_data_len % 16) != 0) {
|
||||
return SW_WRONG_LENGTH();
|
||||
}
|
||||
wait_button();
|
||||
int key_size = file_read_uint16(ef->data);
|
||||
uint8_t kdata[32]; //maximum AES key size
|
||||
memcpy(kdata, file_read(ef->data+2), key_size);
|
||||
@@ -1649,6 +1743,59 @@ static int cmd_derive_asym() {
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
static int cmd_extras() {
|
||||
if (P2(apdu) != 0x0)
|
||||
return SW_INCORRECT_P1P2();
|
||||
if (P1(apdu) == 0xA) { //datetime operations
|
||||
if (apdu.cmd_apdu_data_len == 0) {
|
||||
datetime_t dt;
|
||||
if (!rtc_get_datetime(&dt))
|
||||
return SW_EXEC_ERROR();
|
||||
res_APDU[res_APDU_size++] = dt.year >> 8;
|
||||
res_APDU[res_APDU_size++] = dt.year & 0xff;
|
||||
res_APDU[res_APDU_size++] = dt.month;
|
||||
res_APDU[res_APDU_size++] = dt.day;
|
||||
res_APDU[res_APDU_size++] = dt.dotw;
|
||||
res_APDU[res_APDU_size++] = dt.hour;
|
||||
res_APDU[res_APDU_size++] = dt.min;
|
||||
res_APDU[res_APDU_size++] = dt.sec;
|
||||
}
|
||||
else {
|
||||
if (apdu.cmd_apdu_data_len != 8)
|
||||
return SW_WRONG_LENGTH();
|
||||
datetime_t dt;
|
||||
dt.year = (apdu.cmd_apdu_data[0] << 8) | (apdu.cmd_apdu_data[1]);
|
||||
dt.month = apdu.cmd_apdu_data[2];
|
||||
dt.day = apdu.cmd_apdu_data[3];
|
||||
dt.dotw = apdu.cmd_apdu_data[4];
|
||||
dt.hour = apdu.cmd_apdu_data[5];
|
||||
dt.min = apdu.cmd_apdu_data[6];
|
||||
dt.sec = apdu.cmd_apdu_data[7];
|
||||
if (!rtc_set_datetime(&dt))
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
}
|
||||
else if (P1(apdu) == 0x6) { //dynamic options
|
||||
if (apdu.cmd_apdu_data_len > sizeof(uint8_t))
|
||||
return SW_WRONG_LENGTH();
|
||||
uint16_t opts = get_device_options();
|
||||
if (apdu.cmd_apdu_data_len == 0) {
|
||||
res_APDU[res_APDU_size++] = opts >> 8;
|
||||
res_APDU[res_APDU_size++] = opts & 0xff;
|
||||
}
|
||||
else {
|
||||
uint8_t newopts[] = { apdu.cmd_apdu_data[0], (opts & 0xff) };
|
||||
file_t *tf = search_by_fid(EF_DEVOPS, NULL, SPECIFY_EF);
|
||||
flash_write_data_to_file(tf, newopts, sizeof(newopts));
|
||||
low_flash_available();
|
||||
}
|
||||
}
|
||||
else
|
||||
return SW_INCORRECT_P1P2();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
|
||||
typedef struct cmd
|
||||
{
|
||||
uint8_t ins;
|
||||
@@ -1662,6 +1809,7 @@ typedef struct cmd
|
||||
#define INS_KEY_GEN 0x48
|
||||
#define INS_INITIALIZE 0x50
|
||||
#define INS_IMPORT_DKEK 0x52
|
||||
#define INS_EXTRAS 0x54
|
||||
#define INS_LIST_KEYS 0x58
|
||||
#define INS_DECRYPT_ASYM 0x62
|
||||
#define INS_SIGNATURE 0x68
|
||||
@@ -1697,6 +1845,7 @@ static const cmd_t cmds[] = {
|
||||
{ INS_DECRYPT_ASYM, cmd_decrypt_asym },
|
||||
{ INS_CIPHER_SYM, cmd_cipher_sym },
|
||||
{ INS_DERIVE_ASYM, cmd_derive_asym },
|
||||
{ INS_EXTRAS, cmd_extras },
|
||||
{ 0x00, 0x0}
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ extern const uint8_t sc_hsm_aid[];
|
||||
|
||||
#define SW_BYTES_REMAINING_00() set_res_sw (0x61, 0x00)
|
||||
#define SW_WARNING_STATE_UNCHANGED() set_res_sw (0x62, 0x00)
|
||||
#define SW_PIN_BLOCKED() set_res_sw (0x63, 0x00)
|
||||
#define SW_EXEC_ERROR() set_res_sw (0x64, 0x00)
|
||||
#define SW_MEMORY_FAILURE() set_res_sw (0x65, 0x81)
|
||||
#define SW_WRONG_LENGTH() set_res_sw (0x67, 0x00)
|
||||
@@ -34,7 +33,7 @@ extern const uint8_t sc_hsm_aid[];
|
||||
#define SW_LOGICAL_CHANNEL_NOT_SUPPORTED() set_res_sw (0x68, 0x81)
|
||||
#define SW_SECURE_MESSAGING_NOT_SUPPORTED() set_res_sw (0x68, 0x82)
|
||||
#define SW_SECURITY_STATUS_NOT_SATISFIED() set_res_sw (0x69, 0x82)
|
||||
#define SW_FILE_INVALID() set_res_sw (0x69, 0x83)
|
||||
#define SW_PIN_BLOCKED() set_res_sw (0x69, 0x83)
|
||||
#define SW_DATA_INVALID() set_res_sw (0x69, 0x84)
|
||||
#define SW_CONDITIONS_NOT_SATISFIED() set_res_sw (0x69, 0x85)
|
||||
#define SW_COMMAND_NOT_ALLOWED() set_res_sw (0x69, 0x86)
|
||||
@@ -89,13 +88,14 @@ extern const uint8_t sc_hsm_aid[];
|
||||
#define ALGO_AES_CMAC 0x18
|
||||
#define ALGO_AES_DERIVE 0x99
|
||||
|
||||
#define HSM_OPT_RRC 0x1
|
||||
#define HSM_OPT_TRANSPORT_PIN 0x2
|
||||
#define HSM_OPT_SESSION_PIN 0x4
|
||||
#define HSM_OPT_SESSION_PIN_EXPL 0xC
|
||||
#define HSM_OPT_REPLACE_PKA 0x8
|
||||
#define HSM_OPT_COMBINED_AUTH 0x10
|
||||
#define HSM_OPT_RRC_RESET_ONLY 0x20
|
||||
#define HSM_OPT_RRC 0x0001
|
||||
#define HSM_OPT_TRANSPORT_PIN 0x0002
|
||||
#define HSM_OPT_SESSION_PIN 0x0004
|
||||
#define HSM_OPT_SESSION_PIN_EXPL 0x000C
|
||||
#define HSM_OPT_REPLACE_PKA 0x0008
|
||||
#define HSM_OPT_COMBINED_AUTH 0x0010
|
||||
#define HSM_OPT_RRC_RESET_ONLY 0x0020
|
||||
#define HSM_OPT_BOOTSEL_BUTTON 0x0100
|
||||
|
||||
#define P15_KEYTYPE_RSA 0x30
|
||||
#define P15_KEYTYPE_ECC 0xA0
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#ifndef __VERSION_H_
|
||||
#define __VERSION_H_
|
||||
|
||||
#define HSM_VERSION 0x0108
|
||||
#define HSM_VERSION 0x010C
|
||||
|
||||
#define HSM_VERSION_MAJOR ((HSM_VERSION >> 8) & 0xff)
|
||||
#define HSM_VERSION_MINOR (HSM_VERSION & 0xff)
|
||||
|
||||
@@ -31,40 +31,19 @@ static const struct ccid_class_descriptor desc_ccid = {
|
||||
0x02), // T=1
|
||||
.dwDefaultClock = (0xDFC),
|
||||
.dwMaximumClock = (0xDFC),
|
||||
.bNumClockSupport = 1,
|
||||
.bNumClockSupport = 0,
|
||||
.dwDataRate = (0x2580),
|
||||
.dwMaxDataRate = (0x2580),
|
||||
.bNumDataRatesSupported = 1,
|
||||
.dwMaxIFSD = (0xFF), // IFSD is handled by the real reader driver
|
||||
.bNumDataRatesSupported = 0,
|
||||
.dwMaxIFSD = (0xFE), // IFSD is handled by the real reader driver
|
||||
.dwSynchProtocols = (0),
|
||||
.dwMechanical = (0),
|
||||
.dwFeatures = (
|
||||
0x00000002| // Automatic parameter configuration based on ATR data
|
||||
0x00000004| // Automatic activation of ICC on inserting
|
||||
0x00000008| // Automatic ICC voltage selection
|
||||
0x00000010| // Automatic ICC clock frequency change
|
||||
0x00000020| // Automatic baud rate change
|
||||
0x00000040| // Automatic parameters negotiation
|
||||
0x00000080| // Automatic PPS
|
||||
0x00000400| // Automatic IFSD exchange as first exchange
|
||||
0x00040000| // Short and Extended APDU level exchange with CCID
|
||||
0x00100000), // USB Wake up signaling supported
|
||||
.dwMaxCCIDMessageLength = (CCID_EXT_APDU_MAX),
|
||||
.dwFeatures = 0x40840, //USB-ICC, short & extended APDU
|
||||
.dwMaxCCIDMessageLength = 65544+10,
|
||||
.bClassGetResponse = 0xFF,
|
||||
.bclassEnvelope = 0xFF,
|
||||
.wLcdLayout = 0x0,
|
||||
/*
|
||||
(
|
||||
0xFF00| // Number of lines for the LCD display
|
||||
0x00FF), // Number of characters per line
|
||||
*/
|
||||
.bPINSupport = 0x0,
|
||||
/*
|
||||
0x1| // PIN Verification supported
|
||||
0x2| // PIN Modification supported
|
||||
0x10| // PIN PACE Capabilities supported
|
||||
0x20, // PIN PACE Verification supported
|
||||
*/
|
||||
.bMaxCCIDBusySlots = 0x01,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user