Compare commits
128 Commits
v3.0
...
v5.8-eddsa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21765a6f10 | ||
|
|
eb2c92bc5c | ||
|
|
20a8ef08f0 | ||
|
|
e757ad2945 | ||
|
|
1ce0d98c34 | ||
|
|
96de6efed6 | ||
|
|
195096ad52 | ||
|
|
1ee86f8634 | ||
|
|
ffb3beb84a | ||
|
|
d78d9d10aa | ||
|
|
f8d4f1d02e | ||
|
|
b493a81ddc | ||
|
|
5c20909b03 | ||
|
|
27b9e3954a | ||
|
|
440ec5c854 | ||
|
|
cb2744cab3 | ||
|
|
5db1014850 | ||
|
|
421bea6421 | ||
|
|
65039c0959 | ||
|
|
8e36b4c379 | ||
|
|
3652368542 | ||
|
|
e5d1ef29a4 | ||
|
|
0fd36806cc | ||
|
|
7bf26b28fc | ||
|
|
da94a82487 | ||
|
|
c24be5a631 | ||
|
|
46ce9390bf | ||
|
|
c1fd5736f9 | ||
|
|
b1c4ff877e | ||
|
|
6c85d57412 | ||
|
|
233c5a7c7d | ||
|
|
3b4ac12d0f | ||
|
|
2e5b8f4c71 | ||
|
|
a9697ba4e0 | ||
|
|
aec488f070 | ||
|
|
7c5bab8b05 | ||
|
|
21035d649d | ||
|
|
9c90095e96 | ||
|
|
7c5f2cee4b | ||
|
|
5e0c42a9f9 | ||
|
|
da7b918dc4 | ||
|
|
abe91823c0 | ||
|
|
91e049b997 | ||
|
|
cfcfb941e0 | ||
|
|
45c2cf65fe | ||
|
|
1217d82361 | ||
|
|
332debea6d | ||
|
|
8836902dc1 | ||
|
|
bafede2ae5 | ||
|
|
a019b54d69 | ||
|
|
539420b996 | ||
|
|
0c08590dcc | ||
|
|
cac4ae1751 | ||
|
|
974868d8e4 | ||
|
|
2bbaf7c274 | ||
|
|
3adb1a8422 | ||
|
|
df26040838 | ||
|
|
95a9fe4214 | ||
|
|
8af7cac57a | ||
|
|
7997eefdc8 | ||
|
|
e18f841a34 | ||
|
|
73b51cabfc | ||
|
|
539ea61436 | ||
|
|
75771e5e46 | ||
|
|
8e26ec8bcd | ||
|
|
ad3b2bbe4b | ||
|
|
b9ad8f4745 | ||
|
|
26148282e6 | ||
|
|
05044b498d | ||
|
|
8242dc8d80 | ||
|
|
be44947475 | ||
|
|
0d280ca252 | ||
|
|
2f6e4d5568 | ||
|
|
911dab031e | ||
|
|
4c3042a8bf | ||
|
|
b7ceec8d49 | ||
|
|
63e15b19bb | ||
|
|
b2c4e0e1c1 | ||
|
|
b72c596aa6 | ||
|
|
2d81a3c472 | ||
|
|
bb20dd7a53 | ||
|
|
3a71275bc8 | ||
|
|
c258dad8e6 | ||
|
|
9f1e879efe | ||
|
|
57bf97196d | ||
|
|
e8c8ce4d15 | ||
|
|
69d618cc6b | ||
|
|
e057f17180 | ||
|
|
ce040a79f5 | ||
|
|
8ffd1bfe38 | ||
|
|
5105545df0 | ||
|
|
d011314500 | ||
|
|
51cbfe5fe9 | ||
|
|
aa7362f88f | ||
|
|
2b1227b105 | ||
|
|
a79842b33f | ||
|
|
30f51b8453 | ||
|
|
c00c83dfe6 | ||
|
|
cda97259b3 | ||
|
|
c883083a75 | ||
|
|
016780b3de | ||
|
|
24224b78dd | ||
|
|
07729f807b | ||
|
|
e0c793dd0a | ||
|
|
9d6003d1e5 | ||
|
|
147a93d7fb | ||
|
|
f12c55805c | ||
|
|
7e10e25f96 | ||
|
|
9052c66a7f | ||
|
|
443ca69547 | ||
|
|
415c1b2e9c | ||
|
|
d87c1530c7 | ||
|
|
f90baaf095 | ||
|
|
1d7bdb0861 | ||
|
|
fa811e2a0f | ||
|
|
ff498ebfdf | ||
|
|
cceb735cc0 | ||
|
|
5a9de32e02 | ||
|
|
c9eacc4a3d | ||
|
|
c23d92ea28 | ||
|
|
da04fbb824 | ||
|
|
0bfa760903 | ||
|
|
bd9d4286d5 | ||
|
|
3d1c68fa40 | ||
|
|
26ac66e813 | ||
|
|
05afcd706e | ||
|
|
8c90dd55bd | ||
|
|
c6c1d0c6eb |
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: polhenarejos
|
||||
custom: ["https://www.paypal.me/polhenarejos"]
|
||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -13,10 +13,10 @@ name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "development" ]
|
||||
branches: [ "main", "development", "eddsa" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main", "development" ]
|
||||
branches: [ "main", "development", "eddsa" ]
|
||||
schedule:
|
||||
- cron: '23 5 * * 4'
|
||||
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -13,10 +13,10 @@ name: "Emulation and test"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "development" ]
|
||||
branches: [ "main", "development", "eddsa" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main", "development" ]
|
||||
branches: [ "main", "development", "eddsa" ]
|
||||
schedule:
|
||||
- cron: '23 5 * * 4'
|
||||
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "pico-hsm-sdk"]
|
||||
path = pico-hsm-sdk
|
||||
url = ../pico-hsm-sdk
|
||||
[submodule "pico-keys-sdk"]
|
||||
path = pico-keys-sdk
|
||||
url = https://github.com/polhenarejos/pico-keys-sdk
|
||||
|
||||
@@ -95,6 +95,7 @@ set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_config.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_vendor.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_large_blobs.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/management.c
|
||||
)
|
||||
if (${ENABLE_OATH_APP})
|
||||
set(SOURCES ${SOURCES}
|
||||
@@ -108,7 +109,7 @@ set(SOURCES ${SOURCES}
|
||||
endif()
|
||||
|
||||
set(USB_ITF_HID 1)
|
||||
include(pico-hsm-sdk/pico_hsm_sdk_import.cmake)
|
||||
include(pico-keys-sdk/pico_keys_sdk_import.cmake)
|
||||
|
||||
set(INCLUDES ${INCLUDES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido
|
||||
@@ -146,5 +147,5 @@ target_compile_options(pico_fido PUBLIC
|
||||
endif (APPLE)
|
||||
else()
|
||||
pico_add_extra_outputs(pico_fido)
|
||||
target_link_libraries(pico_fido PRIVATE pico_hsm_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board)
|
||||
target_link_libraries(pico_fido PRIVATE pico_keys_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board)
|
||||
endif()
|
||||
|
||||
20
README.md
20
README.md
@@ -13,7 +13,8 @@ Pico FIDO has implemented the following features:
|
||||
- User Verification with PIN
|
||||
- Discoverable credentials
|
||||
- Credential management
|
||||
- ECDSA authentication
|
||||
- ECDSA and EDDSA authentication
|
||||
- Authentication with SECP256R1, SECP384R1, SECP521R1, SECP256K1 and Ed25519 curves
|
||||
- App registration and login
|
||||
- Device selection
|
||||
- Support for vendor Config
|
||||
@@ -27,6 +28,14 @@ Pico FIDO has implemented the following features:
|
||||
- credBlobs extension
|
||||
- largeBlobKey extension
|
||||
- largeBlobs support (2048 bytes máx.)
|
||||
- OATH (based on YKOATH protocol specification)
|
||||
- TOTP / HOTP
|
||||
- Yubikey OTP
|
||||
- Challenge-response generation
|
||||
- Emulated keyboard interface
|
||||
- Button press generates an OTP that is written directly is it was typed
|
||||
- Yubico YKMAN compatible
|
||||
- Nitrokey nitropy and nitroapp compatible
|
||||
|
||||
All these features are compliant with the specification. Therefore, if you detect some behaviour that is not expected or it does not follow the rules of specs, please open an issue.
|
||||
|
||||
@@ -38,6 +47,15 @@ If the Pico is stolen the contents of private and secret keys can be read.
|
||||
## Download
|
||||
Please, go to the [Release page](https://github.com/polhenarejos/pico-fido/releases "Release page") and download the UF2 file for your board.
|
||||
|
||||
Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you are planning to use it with OpenSC or similar, you should modify Info.plist of CCID driver to add these VID/PID or use the [Pico Patcher tool](https://www.picokeys.com/pico-patcher/).
|
||||
|
||||
Alternatively you can use the legacy 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.
|
||||
|
||||
Note that the pure-browser option [Pico Patcher tool](https://www.picokeys.com/pico-patcher/) is the most recommended.
|
||||
|
||||
## Build
|
||||
Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION_MAJOR="3"
|
||||
VERSION_MINOR="0"
|
||||
VERSION_MAJOR="5"
|
||||
VERSION_MINOR="8-eddsa1"
|
||||
|
||||
rm -rf release/*
|
||||
cd build_release
|
||||
@@ -17,6 +17,7 @@ for board in adafruit_feather_rp2040 \
|
||||
eetree_gamekit_rp2040 \
|
||||
garatronic_pybstick26_rp2040 \
|
||||
melopero_shake_rp2040 \
|
||||
nullbits_bit_c_pro \
|
||||
pico \
|
||||
pico_w \
|
||||
pimoroni_badger2040 \
|
||||
@@ -31,6 +32,7 @@ for board in adafruit_feather_rp2040 \
|
||||
pimoroni_servo2040 \
|
||||
pimoroni_tiny2040 \
|
||||
pimoroni_tiny2040_2mb \
|
||||
pololu_3pi_2040_robot \
|
||||
seeed_xiao_rp2040 \
|
||||
solderparty_rp2040_stamp \
|
||||
solderparty_rp2040_stamp_carrier \
|
||||
|
||||
Submodule pico-hsm-sdk deleted from b12e66a057
1
pico-keys-sdk
Submodule
1
pico-keys-sdk
Submodule
Submodule pico-keys-sdk added at e5e2169a47
209
src/fido/cbor.c
209
src/fido/cbor.c
@@ -23,6 +23,9 @@
|
||||
#include "fido.h"
|
||||
#include "usb.h"
|
||||
#include "apdu.h"
|
||||
#include "management.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "version.h"
|
||||
|
||||
const bool _btrue = true, _bfalse = false;
|
||||
|
||||
@@ -38,6 +41,8 @@ int cbor_config(const uint8_t *data, size_t len);
|
||||
int cbor_vendor(const uint8_t *data, size_t len);
|
||||
int cbor_large_blobs(const uint8_t *data, size_t len);
|
||||
|
||||
extern int cmd_read_config();
|
||||
|
||||
const uint8_t aaguid[16] =
|
||||
{ 0x89, 0xFB, 0x94, 0xB7, 0x06, 0xC9, 0x36, 0x73, 0x9B, 0x7E, 0x30, 0x52, 0x6D, 0x96, 0x81, 0x45 }; // First 16 bytes of SHA256("Pico FIDO2")
|
||||
|
||||
@@ -52,41 +57,49 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
if (len > 0) {
|
||||
DEBUG_DATA(data + 1, len - 1);
|
||||
}
|
||||
driver_prepare_response_hid();
|
||||
if (cmd == CTAPHID_CBOR) {
|
||||
if (data[0] == CTAP_MAKE_CREDENTIAL) {
|
||||
return cbor_make_credential(data + 1, len - 1);
|
||||
if (cap_supported(CAP_FIDO2)) {
|
||||
driver_prepare_response_hid();
|
||||
if (cmd == CTAPHID_CBOR) {
|
||||
if (data[0] == CTAP_MAKE_CREDENTIAL) {
|
||||
return cbor_make_credential(data + 1, len - 1);
|
||||
}
|
||||
if (data[0] == CTAP_GET_INFO) {
|
||||
return cbor_get_info();
|
||||
}
|
||||
else if (data[0] == CTAP_RESET) {
|
||||
return cbor_reset();
|
||||
}
|
||||
else if (data[0] == CTAP_CLIENT_PIN) {
|
||||
return cbor_client_pin(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_GET_ASSERTION) {
|
||||
return cbor_get_assertion(data + 1, len - 1, false);
|
||||
}
|
||||
else if (data[0] == CTAP_GET_NEXT_ASSERTION) {
|
||||
return cbor_get_next_assertion(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_SELECTION) {
|
||||
return cbor_selection();
|
||||
}
|
||||
else if (data[0] == CTAP_CREDENTIAL_MGMT || data[0] == 0x41) {
|
||||
return cbor_cred_mgmt(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_CONFIG) {
|
||||
return cbor_config(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_LARGE_BLOBS) {
|
||||
return cbor_large_blobs(data + 1, len - 1);
|
||||
}
|
||||
}
|
||||
if (data[0] == CTAP_GET_INFO) {
|
||||
return cbor_get_info();
|
||||
else if (cmd == CTAP_VENDOR_CBOR) {
|
||||
return cbor_vendor(data, len);
|
||||
}
|
||||
else if (data[0] == CTAP_RESET) {
|
||||
return cbor_reset();
|
||||
else if (cmd == 0xC2) {
|
||||
if (cmd_read_config() == 0x9000) {
|
||||
res_APDU_size -= 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (data[0] == CTAP_CLIENT_PIN) {
|
||||
return cbor_client_pin(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_GET_ASSERTION) {
|
||||
return cbor_get_assertion(data + 1, len - 1, false);
|
||||
}
|
||||
else if (data[0] == CTAP_GET_NEXT_ASSERTION) {
|
||||
return cbor_get_next_assertion(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_SELECTION) {
|
||||
return cbor_selection();
|
||||
}
|
||||
else if (data[0] == CTAP_CREDENTIAL_MGMT || data[0] == 0x41) {
|
||||
return cbor_cred_mgmt(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_CONFIG) {
|
||||
return cbor_config(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_LARGE_BLOBS) {
|
||||
return cbor_large_blobs(data + 1, len - 1);
|
||||
}
|
||||
}
|
||||
else if (cmd == CTAP_VENDOR_CBOR) {
|
||||
return cbor_vendor(data, len);
|
||||
}
|
||||
return CTAP1_ERR_INVALID_CMD;
|
||||
}
|
||||
@@ -107,6 +120,10 @@ void cbor_thread() {
|
||||
if (apdu.sw == 0) {
|
||||
DEBUG_DATA(res_APDU + 1, res_APDU_size);
|
||||
}
|
||||
else {
|
||||
res_APDU[0] = apdu.sw;
|
||||
apdu.sw = 0;
|
||||
}
|
||||
|
||||
finished_data_size = res_APDU_size + 1;
|
||||
|
||||
@@ -124,3 +141,131 @@ int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len) {
|
||||
res_APDU_size = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
CborError COSE_key_params(int crv,
|
||||
int alg,
|
||||
mbedtls_ecp_group *grp,
|
||||
mbedtls_ecp_point *Q,
|
||||
CborEncoder *mapEncoderParent,
|
||||
CborEncoder *mapEncoder) {
|
||||
CborError error = CborNoError;
|
||||
int kty = 1;
|
||||
if (crv == FIDO2_CURVE_P256 || crv == FIDO2_CURVE_P384 || crv == FIDO2_CURVE_P521 ||
|
||||
crv == FIDO2_CURVE_P256K1) {
|
||||
kty = 2;
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(mapEncoderParent, mapEncoder, kty == 2 ? 5 : 4));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(mapEncoder, kty));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(mapEncoder, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, -alg));
|
||||
|
||||
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(mapEncoder, crv));
|
||||
|
||||
|
||||
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, 2));
|
||||
uint8_t pkey[67];
|
||||
if (kty == 2) {
|
||||
size_t plen = mbedtls_mpi_size(&grp->P);
|
||||
CBOR_CHECK(mbedtls_mpi_write_binary(&Q->X, pkey, plen));
|
||||
CBOR_CHECK(cbor_encode_byte_string(mapEncoder, pkey, plen));
|
||||
|
||||
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, 3));
|
||||
|
||||
CBOR_CHECK(mbedtls_mpi_write_binary(&Q->Y, pkey, plen));
|
||||
CBOR_CHECK(cbor_encode_byte_string(mapEncoder, pkey, plen));
|
||||
}
|
||||
else {
|
||||
size_t olen = 0;
|
||||
CBOR_CHECK(mbedtls_ecp_point_write_binary(grp, Q, MBEDTLS_ECP_PF_COMPRESSED, &olen, pkey,
|
||||
sizeof(pkey)));
|
||||
CBOR_CHECK(cbor_encode_byte_string(mapEncoder, pkey, olen));
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(mapEncoderParent, mapEncoder));
|
||||
err:
|
||||
return error;
|
||||
}
|
||||
CborError COSE_key(mbedtls_ecp_keypair *key, CborEncoder *mapEncoderParent,
|
||||
CborEncoder *mapEncoder) {
|
||||
int crv = mbedtls_curve_to_fido(key->grp.id), alg = 0;
|
||||
if (key->grp.id == MBEDTLS_ECP_DP_SECP256R1) {
|
||||
alg = FIDO2_ALG_ES256;
|
||||
}
|
||||
else if (key->grp.id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
alg = FIDO2_ALG_ES384;
|
||||
}
|
||||
else if (key->grp.id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
alg = FIDO2_ALG_ES512;
|
||||
}
|
||||
else if (key->grp.id == MBEDTLS_ECP_DP_SECP256K1) {
|
||||
alg = FIDO2_ALG_ES256K;
|
||||
}
|
||||
else if (key->grp.id == MBEDTLS_ECP_DP_CURVE25519) {
|
||||
alg = FIDO2_ALG_ECDH_ES_HKDF_256;
|
||||
}
|
||||
else if (key->grp.id == MBEDTLS_ECP_DP_ED25519) {
|
||||
alg = FIDO2_ALG_EDDSA;
|
||||
}
|
||||
return COSE_key_params(crv, alg, &key->grp, &key->Q, mapEncoderParent, mapEncoder);
|
||||
}
|
||||
CborError COSE_key_shared(mbedtls_ecdh_context *key,
|
||||
CborEncoder *mapEncoderParent,
|
||||
CborEncoder *mapEncoder) {
|
||||
int crv = mbedtls_curve_to_fido(key->ctx.mbed_ecdh.grp.id), alg = FIDO2_ALG_ECDH_ES_HKDF_256;
|
||||
return COSE_key_params(crv,
|
||||
alg,
|
||||
&key->ctx.mbed_ecdh.grp,
|
||||
&key->ctx.mbed_ecdh.Q,
|
||||
mapEncoderParent,
|
||||
mapEncoder);
|
||||
}
|
||||
CborError COSE_public_key(int alg, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder) {
|
||||
CborError error = CborNoError;
|
||||
CBOR_CHECK(cbor_encoder_create_map(mapEncoderParent, mapEncoder, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(mapEncoder, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, -alg));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(mapEncoder, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(mapEncoder, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(mapEncoderParent, mapEncoder));
|
||||
err:
|
||||
return error;
|
||||
}
|
||||
CborError COSE_read_key(CborValue *f,
|
||||
int64_t *kty,
|
||||
int64_t *alg,
|
||||
int64_t *crv,
|
||||
CborByteString *kax,
|
||||
CborByteString *kay) {
|
||||
int64_t kkey = 0;
|
||||
CborError error = CborNoError;
|
||||
CBOR_PARSE_MAP_START(*f, 0)
|
||||
{
|
||||
CBOR_FIELD_GET_INT(kkey, 0);
|
||||
if (kkey == 1) {
|
||||
CBOR_FIELD_GET_INT(*kty, 0);
|
||||
}
|
||||
else if (kkey == 3) {
|
||||
CBOR_FIELD_GET_INT(*alg, 0);
|
||||
}
|
||||
else if (kkey == -1) {
|
||||
CBOR_FIELD_GET_INT(*crv, 0);
|
||||
}
|
||||
else if (kkey == -2) {
|
||||
CBOR_FIELD_GET_BYTES(*kax, 0);
|
||||
}
|
||||
else if (kkey == -3) {
|
||||
CBOR_FIELD_GET_BYTES(*kay, 0);
|
||||
}
|
||||
else {
|
||||
CBOR_ADVANCE(0);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(*f, 0);
|
||||
err:
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
|
||||
uint32_t usage_timer = 0, initial_usage_time_limit = 0;
|
||||
@@ -181,12 +181,12 @@ int resetPinUvAuthToken() {
|
||||
int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out) {
|
||||
if (protocol == 1) {
|
||||
memcpy(out, in, in_len);
|
||||
return aes_encrypt(key, NULL, 32 * 8, HSM_AES_MODE_CBC, out, in_len);
|
||||
return aes_encrypt(key, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
random_gen(NULL, out, IV_SIZE);
|
||||
memcpy(out + IV_SIZE, in, in_len);
|
||||
return aes_encrypt(key + 32, out, 32 * 8, HSM_AES_MODE_CBC, out + IV_SIZE, in_len);
|
||||
return aes_encrypt(key + 32, out, 32 * 8, PICO_KEYS_AES_MODE_CBC, out + IV_SIZE, in_len);
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -195,11 +195,11 @@ int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_l
|
||||
int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out) {
|
||||
if (protocol == 1) {
|
||||
memcpy(out, in, in_len);
|
||||
return aes_decrypt(key, NULL, 32 * 8, HSM_AES_MODE_CBC, out, in_len);
|
||||
return aes_decrypt(key, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
memcpy(out, in + IV_SIZE, in_len);
|
||||
return aes_decrypt(key + 32, in, 32 * 8, HSM_AES_MODE_CBC, out, in_len - IV_SIZE);
|
||||
return aes_decrypt(key + 32, in, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len - IV_SIZE);
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -312,30 +312,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_FIELD_GET_UINT(subcommand, 1);
|
||||
}
|
||||
else if (val_u == 0x03) {
|
||||
int64_t key = 0;
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_INT(key, 2);
|
||||
if (key == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 2);
|
||||
}
|
||||
else if (key == 3) {
|
||||
CBOR_FIELD_GET_INT(alg, 2);
|
||||
}
|
||||
else if (key == -1) {
|
||||
CBOR_FIELD_GET_INT(crv, 2);
|
||||
}
|
||||
else if (key == -2) {
|
||||
CBOR_FIELD_GET_BYTES(kax, 2);
|
||||
}
|
||||
else if (key == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 2);
|
||||
}
|
||||
else {
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
CBOR_CHECK(COSE_read_key(&_f1, &kty, &alg, &crv, &kax, &kay));
|
||||
}
|
||||
else if (val_u == 0x04) {
|
||||
CBOR_FIELD_GET_BYTES(pinUvAuthParam, 1);
|
||||
@@ -374,21 +351,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ECDH_ES_HKDF_256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, FIDO2_CURVE_P256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2));
|
||||
uint8_t pkey[32];
|
||||
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.X, pkey, 32);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 3));
|
||||
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.Y, pkey, 32);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_key_shared(&hkey, &mapEncoder, &mapEncoder2));
|
||||
}
|
||||
else if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "credential.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "random.h"
|
||||
#include "mbedtls/ecdh.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
@@ -64,7 +64,7 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
raw_subpara = (uint8_t *) cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
if (subcommand == 0xff) {
|
||||
if (subcommand == 0x7f) {
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(vendorCommandId, 2);
|
||||
@@ -134,7 +134,7 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
|
||||
if (subcommand == 0xff) {
|
||||
if (subcommand == 0x7f) {
|
||||
if (vendorCommandId == CTAP_CONFIG_AUT_DISABLE) {
|
||||
if (!file_has_data(ef_keydev_enc)) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "credential.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
|
||||
uint8_t rp_counter = 1;
|
||||
uint8_t rp_total = 0;
|
||||
@@ -258,7 +258,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
|
||||
cred_counter++;
|
||||
|
||||
uint8_t l = 3;
|
||||
uint8_t l = 4;
|
||||
if (subcommand == 0x04) {
|
||||
l++;
|
||||
}
|
||||
@@ -309,21 +309,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -cred.alg));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, cred.curve));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2));
|
||||
uint8_t pkey[66];
|
||||
mbedtls_mpi_write_binary(&key.Q.X, pkey, mbedtls_mpi_size(&key.Q.X));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, mbedtls_mpi_size(&key.Q.X)));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 3));
|
||||
mbedtls_mpi_write_binary(&key.Q.Y, pkey, mbedtls_mpi_size(&key.Q.Y));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, mbedtls_mpi_size(&key.Q.Y)));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_key(&key, &mapEncoder, &mapEncoder2));
|
||||
|
||||
if (subcommand == 0x04) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x09));
|
||||
@@ -349,6 +335,13 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
sizeof(largeBlobKey)));
|
||||
mbedtls_platform_zeroize(largeBlobKey, sizeof(largeBlobKey));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder,
|
||||
cred.extensions.thirdPartyPayment == ptrue));
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, false));
|
||||
}
|
||||
credential_free(&cred);
|
||||
mbedtls_ecdsa_free(&key);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "fido.h"
|
||||
#include "files.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "cbor_make_credential.h"
|
||||
#include "credential.h"
|
||||
@@ -150,30 +150,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
{
|
||||
CBOR_FIELD_GET_UINT(ukey, 3);
|
||||
if (ukey == 0x01) {
|
||||
int64_t kkey = 0;
|
||||
CBOR_PARSE_MAP_START(_f3, 4)
|
||||
{
|
||||
CBOR_FIELD_GET_INT(kkey, 4);
|
||||
if (kkey == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 4);
|
||||
}
|
||||
else if (kkey == 3) {
|
||||
CBOR_FIELD_GET_INT(alg, 4);
|
||||
}
|
||||
else if (kkey == -1) {
|
||||
CBOR_FIELD_GET_INT(crv, 4);
|
||||
}
|
||||
else if (kkey == -2) {
|
||||
CBOR_FIELD_GET_BYTES(kax, 4);
|
||||
}
|
||||
else if (kkey == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 4);
|
||||
}
|
||||
else {
|
||||
CBOR_ADVANCE(4);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f3, 4);
|
||||
CBOR_CHECK(COSE_read_key(&_f3, &kty, &alg, &crv, &kax, &kay));
|
||||
}
|
||||
else if (ukey == 0x02) {
|
||||
CBOR_FIELD_GET_BYTES(salt_enc, 3);
|
||||
@@ -193,6 +170,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "credBlob", credBlob);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "largeBlobKey", extensions.largeBlobKey);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "thirdPartyPayment", extensions.thirdPartyPayment);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
@@ -428,12 +406,12 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
flags = flagsx;
|
||||
selcred = &credsx[credentialCounter];
|
||||
}
|
||||
mbedtls_ecdsa_context ekey;
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
mbedtls_ecp_keypair ekey;
|
||||
mbedtls_ecp_keypair_init(&ekey);
|
||||
int ret = fido_load_key(selcred->curve, selcred->id.data, &ekey);
|
||||
if (ret != 0) {
|
||||
if (derive_key(rp_id_hash, false, selcred->id.data, MBEDTLS_ECP_DP_SECP256R1, &ekey) != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
}
|
||||
@@ -460,6 +438,9 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
if (credBlob == ptrue) {
|
||||
l++;
|
||||
}
|
||||
if (extensions.thirdPartyPayment != NULL) {
|
||||
l++;
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
if (credBlob == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
|
||||
@@ -538,6 +519,15 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
encrypt(hmacSecretPinUvAuthProtocol, sharedSecret, out1, salt_enc.len - poff, hmac_res);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, hmac_res, salt_enc.len));
|
||||
}
|
||||
if (extensions.thirdPartyPayment != NULL) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "thirdPartyPayment"));
|
||||
if (selcred->extensions.thirdPartyPayment == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, false));
|
||||
}
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
|
||||
@@ -561,22 +551,50 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
|
||||
memcpy(pa, clientDataHash.data, clientDataHash.len);
|
||||
uint8_t hash[32], sig[MBEDTLS_ECDSA_MAX_LEN];
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
aut_data,
|
||||
aut_data_len + clientDataHash.len,
|
||||
hash);
|
||||
uint8_t hash[64], sig[MBEDTLS_ECDSA_MAX_LEN];
|
||||
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
if (ekey.grp.id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
|
||||
}
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
}
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_ED25519) {
|
||||
md = NULL;
|
||||
}
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey,
|
||||
MBEDTLS_MD_SHA256,
|
||||
hash,
|
||||
32,
|
||||
sig,
|
||||
sizeof(sig),
|
||||
&olen,
|
||||
random_gen,
|
||||
NULL);
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
if (md != NULL) {
|
||||
ret = mbedtls_md(md,
|
||||
aut_data,
|
||||
aut_data_len + clientDataHash.len,
|
||||
hash);
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey,
|
||||
mbedtls_md_get_type(md),
|
||||
hash,
|
||||
mbedtls_md_get_size(md),
|
||||
sig,
|
||||
sizeof(sig),
|
||||
&olen,
|
||||
random_gen,
|
||||
NULL);
|
||||
}
|
||||
else {
|
||||
ret = mbedtls_eddsa_write_signature(&ekey,
|
||||
aut_data,
|
||||
aut_data_len + clientDataHash.len,
|
||||
sig,
|
||||
sizeof(sig),
|
||||
&olen,
|
||||
MBEDTLS_EDDSA_PURE,
|
||||
NULL,
|
||||
0,
|
||||
random_gen,
|
||||
NULL);
|
||||
}
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
|
||||
uint8_t lfields = 3;
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
|
||||
@@ -37,12 +37,13 @@ int cbor_get_info() {
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 5));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 6));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credBlob"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credProtect"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "hmac-secret"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "largeBlobKey"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "minPinLength"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "thirdPartyPayment"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
@@ -89,25 +90,14 @@ int cbor_get_info() {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CRED_ID_LENGTH)); // MAX_CRED_ID_MAX_LENGTH
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0A));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 3));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES256));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES384));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES512));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 5));
|
||||
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES256, &arrayEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_public_key(FIDO2_ALG_EDDSA, &arrayEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES384, &arrayEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES512, &arrayEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES256K, &arrayEncoder, &mapEncoder2));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0B));
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
|
||||
static uint64_t expectedLength = 0, expectedNextOffset = 0;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "credential.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "random.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
|
||||
int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CborParser parser;
|
||||
@@ -65,7 +65,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_FIELD_GET_BYTES(clientDataHash, 1);
|
||||
}
|
||||
else if (val_u == 0x02) { // rp
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "id", rp.id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "name", rp.parent.name);
|
||||
@@ -73,7 +74,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x03) { // user
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(2, "id", user.id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "name", user.parent.name);
|
||||
@@ -83,9 +85,11 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x04) { // pubKeyCredParams
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2) {
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2)
|
||||
{
|
||||
PublicKeyCredentialParameters *pk = &pubKeyCredParams[pubKeyCredParams_len];
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", pk->type);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_INT(3, "alg", pk->alg);
|
||||
@@ -96,14 +100,17 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_ARRAY_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x05) { // excludeList
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2) {
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2)
|
||||
{
|
||||
PublicKeyCredentialDescriptor *pc = &excludeList[excludeList_len];
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", pc->id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", pc->type);
|
||||
if (strcmp(_fd3, "transports") == 0) {
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4) {
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4)
|
||||
{
|
||||
CBOR_FIELD_GET_TEXT(pc->transports[pc->transports_len], 4);
|
||||
pc->transports_len++;
|
||||
}
|
||||
@@ -117,20 +124,23 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
else if (val_u == 0x06) { // extensions
|
||||
extensions.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "hmac-secret", extensions.hmac_secret);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", extensions.credProtect);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "minPinLength", extensions.minPinLength);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(2, "credBlob", extensions.credBlob);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "largeBlobKey", extensions.largeBlobKey);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "thirdPartyPayment", extensions.thirdPartyPayment);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x07) { // options
|
||||
options.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "rk", options.rk);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "up", options.up);
|
||||
@@ -155,45 +165,6 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
uint8_t rp_id_hash[32];
|
||||
mbedtls_sha256((uint8_t *) rp.id.data, rp.id.len, rp_id_hash, 0);
|
||||
|
||||
int curve = -1, alg = 0;
|
||||
if (pubKeyCredParams_len == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
for (int i = 0; i < pubKeyCredParams_len; i++) {
|
||||
if (pubKeyCredParams[i].type.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0) {
|
||||
continue;
|
||||
}
|
||||
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256) {
|
||||
curve = FIDO2_CURVE_P256;
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384) {
|
||||
curve = FIDO2_CURVE_P384;
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512) {
|
||||
curve = FIDO2_CURVE_P521;
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == 0) { // no present
|
||||
curve = -1;
|
||||
}
|
||||
else {
|
||||
curve = 0;
|
||||
}
|
||||
if (curve > 0) {
|
||||
alg = pubKeyCredParams[i].alg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (curve == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
}
|
||||
else if (curve == -1) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) {
|
||||
if (check_user_presence() == false) {
|
||||
@@ -215,6 +186,61 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int curve = -1, alg = 0;
|
||||
if (pubKeyCredParams_len == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
for (int i = 0; i < pubKeyCredParams_len; i++) {
|
||||
if (pubKeyCredParams[i].type.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
if (pubKeyCredParams[i].alg == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
|
||||
}
|
||||
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256) {
|
||||
if (curve <= 0) {
|
||||
curve = FIDO2_CURVE_P256;
|
||||
}
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384) {
|
||||
if (curve <= 0) {
|
||||
curve = FIDO2_CURVE_P384;
|
||||
}
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512) {
|
||||
if (curve <= 0) {
|
||||
curve = FIDO2_CURVE_P521;
|
||||
}
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256K) {
|
||||
if (curve <= 0) {
|
||||
curve = FIDO2_CURVE_P256K1;
|
||||
}
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_EDDSA) {
|
||||
if (curve <= 0) {
|
||||
curve = FIDO2_CURVE_ED25519;
|
||||
}
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg <= FIDO2_ALG_RS256 && pubKeyCredParams[i].alg >= FIDO2_ALG_RS512) {
|
||||
// pass
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
|
||||
}
|
||||
if (curve > 0 && alg == 0) {
|
||||
alg = pubKeyCredParams[i].alg;
|
||||
}
|
||||
}
|
||||
if (curve <= 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
}
|
||||
|
||||
if (options.present) {
|
||||
if (options.uv == ptrue) { //5.3
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
@@ -365,38 +391,23 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
|
||||
flags |= FIDO2_AUT_FLAG_ED;
|
||||
}
|
||||
uint8_t pkey[66];
|
||||
mbedtls_ecdsa_context ekey;
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
mbedtls_ecp_keypair ekey;
|
||||
mbedtls_ecp_keypair_init(&ekey);
|
||||
int ret = fido_load_key(curve, cred_id, &ekey);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(ekey.grp.id);
|
||||
if (cinfo == NULL) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
size_t olen = 0;
|
||||
uint32_t ctr = get_sign_counter();
|
||||
uint8_t cbor_buf[1024];
|
||||
cbor_encoder_init(&encoder, cbor_buf, sizeof(cbor_buf), 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, -alg));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, curve));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 2));
|
||||
mbedtls_mpi_write_binary(&ekey.Q.X, pkey, mbedtls_mpi_size(&ekey.Q.X));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pkey, mbedtls_mpi_size(&ekey.Q.X)));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 3));
|
||||
mbedtls_mpi_write_binary(&ekey.Q.Y, pkey, mbedtls_mpi_size(&ekey.Q.Y));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pkey, mbedtls_mpi_size(&ekey.Q.Y)));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
CBOR_CHECK(COSE_key(&ekey, &encoder, &mapEncoder));
|
||||
size_t rs = cbor_encoder_get_buffer_size(&encoder, cbor_buf);
|
||||
|
||||
size_t aut_data_len = 32 + 1 + 4 + (16 + 2 + cred_id_len + rs) + ext_len;
|
||||
@@ -415,34 +426,64 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
memcpy(pa, cbor_buf, rs); pa += rs;
|
||||
memcpy(pa, ext, ext_len); pa += ext_len;
|
||||
if (pa - aut_data != aut_data_len) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
|
||||
memcpy(pa, clientDataHash.data, clientDataHash.len);
|
||||
uint8_t hash[32], sig[MBEDTLS_ECDSA_MAX_LEN];
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
aut_data,
|
||||
aut_data_len + clientDataHash.len,
|
||||
hash);
|
||||
|
||||
uint8_t hash[64], sig[MBEDTLS_ECDSA_MAX_LEN];
|
||||
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
if (ekey.grp.id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
|
||||
}
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
}
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_ED25519) {
|
||||
md = NULL;
|
||||
}
|
||||
if (md != NULL) {
|
||||
ret = mbedtls_md(md,
|
||||
aut_data,
|
||||
aut_data_len + clientDataHash.len,
|
||||
hash);
|
||||
}
|
||||
bool self_attestation = true;
|
||||
if (enterpriseAttestation == 2 || (ka && ka->use_self_attestation == pfalse)) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
mbedtls_ecp_keypair_init(&ekey);
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), 32);
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
self_attestation = false;
|
||||
}
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey,
|
||||
MBEDTLS_MD_SHA256,
|
||||
hash,
|
||||
32,
|
||||
sig,
|
||||
sizeof(sig),
|
||||
&olen,
|
||||
random_gen,
|
||||
NULL);
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
if (md != NULL) {
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey,
|
||||
mbedtls_md_get_type(md),
|
||||
hash,
|
||||
mbedtls_md_get_size(md),
|
||||
sig,
|
||||
sizeof(sig),
|
||||
&olen,
|
||||
random_gen,
|
||||
NULL);
|
||||
}
|
||||
else {
|
||||
ret = mbedtls_eddsa_write_signature(&ekey,
|
||||
aut_data,
|
||||
aut_data_len + clientDataHash.len,
|
||||
sig,
|
||||
sizeof(sig),
|
||||
&olen,
|
||||
MBEDTLS_EDDSA_PURE,
|
||||
NULL,
|
||||
0,
|
||||
random_gen,
|
||||
NULL);
|
||||
}
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
|
||||
uint8_t largeBlobKey[32];
|
||||
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
|
||||
@@ -464,12 +505,13 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2,
|
||||
self_attestation == false ? 3 : 2));
|
||||
self_attestation == false || is_nitrokey ? 3 : 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, self_attestation ? -alg : -FIDO2_ALG_ES256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2,
|
||||
self_attestation || is_nitrokey ? -alg : -FIDO2_ALG_ES256));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "sig"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, sig, olen));
|
||||
if (self_attestation == false) {
|
||||
if (self_attestation == false || is_nitrokey) {
|
||||
CborEncoder arrEncoder;
|
||||
file_t *ef_cert = NULL;
|
||||
if (enterpriseAttestation == 2) {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "random.h"
|
||||
#include "mbedtls/ecdh.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
@@ -84,30 +84,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
CBOR_FIELD_GET_BYTES(vendorParam, 2);
|
||||
}
|
||||
else if (subpara == 0x02) {
|
||||
int64_t key = 0;
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_INT(key, 3);
|
||||
if (key == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 3);
|
||||
}
|
||||
else if (key == 3) {
|
||||
CBOR_FIELD_GET_INT(alg, 3);
|
||||
}
|
||||
else if (key == -1) {
|
||||
CBOR_FIELD_GET_INT(crv, 3);
|
||||
}
|
||||
else if (key == -2) {
|
||||
CBOR_FIELD_GET_BYTES(kax, 3);
|
||||
}
|
||||
else if (key == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 3);
|
||||
}
|
||||
else {
|
||||
CBOR_ADVANCE(3);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f2, 3);
|
||||
CBOR_CHECK(COSE_read_key(&_f2, &kty, &alg, &crv, &kax, &kay));
|
||||
}
|
||||
else {
|
||||
CBOR_ADVANCE(2);
|
||||
@@ -223,22 +200,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ECDH_ES_HKDF_256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, FIDO2_CURVE_P256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2));
|
||||
uint8_t pkey[32];
|
||||
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.X, pkey, 32);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 3));
|
||||
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.Y, pkey, 32);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_key_shared(&hkey, &mapEncoder, &mapEncoder2));
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#include "random.h"
|
||||
|
||||
@@ -16,12 +16,38 @@
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "management.h"
|
||||
|
||||
const uint8_t u2f_aid[] = {
|
||||
7,
|
||||
0xA0, 0x00, 0x00, 0x05, 0x27, 0x10, 0x02
|
||||
};
|
||||
|
||||
int u2f_unload();
|
||||
int u2f_process_apdu();
|
||||
|
||||
int u2f_select(app_t *a) {
|
||||
if (cap_supported(CAP_U2F)) {
|
||||
a->process_apdu = u2f_process_apdu;
|
||||
a->unload = u2f_unload;
|
||||
return CCID_OK;
|
||||
}
|
||||
return CCID_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
void __attribute__((constructor)) u2f_ctor() {
|
||||
register_app(u2f_select, u2f_aid);
|
||||
}
|
||||
|
||||
int u2f_unload() {
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
const uint8_t *bogus_firefox =
|
||||
(const uint8_t *)
|
||||
@@ -110,3 +136,29 @@ int cmd_register() {
|
||||
ef_certdev_size + olen;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
extern int cmd_register();
|
||||
extern int cmd_authenticate();
|
||||
extern int cmd_version();
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ CTAP_REGISTER, cmd_register },
|
||||
{ CTAP_AUTHENTICATE, cmd_authenticate },
|
||||
{ CTAP_VERSION, cmd_version },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int u2f_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
if (cap_supported(CAP_U2F)) {
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "apdu.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
|
||||
int cmd_version() {
|
||||
memcpy(res_APDU, "U2F_V2", strlen("U2F_V2"));
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "ctap.h"
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
|
||||
int credential_derive_chacha_key(uint8_t *outk);
|
||||
|
||||
@@ -98,6 +98,10 @@ int credential_create(CborCharString *rpId,
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "largeBlobKey"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, true));
|
||||
}
|
||||
if (extensions->thirdPartyPayment == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "thirdPartyPayment"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, true));
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
|
||||
@@ -201,6 +205,9 @@ int credential_load(const uint8_t *cred_id,
|
||||
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", cred->extensions.credProtect);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(2, "credBlob", cred->extensions.credBlob);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "largeBlobKey", cred->extensions.largeBlobKey);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2,
|
||||
"thirdPartyPayment",
|
||||
cred->extensions.thirdPartyPayment);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
|
||||
@@ -33,6 +33,7 @@ typedef struct CredExtensions {
|
||||
const bool *minPinLength;
|
||||
CborByteString credBlob;
|
||||
const bool *largeBlobKey;
|
||||
const bool *thirdPartyPayment;
|
||||
bool present;
|
||||
} CredExtensions;
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
#define _CTAP2_CBOR_H_
|
||||
|
||||
#include "cbor.h"
|
||||
#include "common.h"
|
||||
#include "mbedtls/ecp.h"
|
||||
#include "mbedtls/ecdh.h"
|
||||
|
||||
extern uint8_t *driver_prepare_response();
|
||||
extern void driver_exec_finished(size_t size_next);
|
||||
@@ -54,7 +57,7 @@ extern const bool _btrue, _bfalse;
|
||||
do \
|
||||
{ \
|
||||
error = e; \
|
||||
printf("Cbor ERROR [%s:%d]: %d\n", __FILE__, __LINE__, e); \
|
||||
printf("Cbor ERROR [%s:%d]: %x\n", __FILE__, __LINE__, e); \
|
||||
goto err; \
|
||||
} while (0)
|
||||
|
||||
@@ -237,4 +240,16 @@ typedef struct CborCharString {
|
||||
CBOR_CHECK(cbor_encode_boolean(&(p), v == ptrue ? true : false)); \
|
||||
} } while (0)
|
||||
|
||||
extern CborError COSE_key(mbedtls_ecp_keypair *, CborEncoder *, CborEncoder *);
|
||||
extern CborError COSE_key_shared(mbedtls_ecdh_context *key,
|
||||
CborEncoder *mapEncoderParent,
|
||||
CborEncoder *mapEncoder);
|
||||
extern CborError COSE_public_key(int alg, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder);
|
||||
extern CborError COSE_read_key(CborValue *f,
|
||||
int64_t *kty,
|
||||
int64_t *alg,
|
||||
int64_t *crv,
|
||||
CborByteString *kax,
|
||||
CborByteString *kay);
|
||||
|
||||
#endif //_CTAP2_CBOR_H_
|
||||
|
||||
126
src/fido/fido.c
126
src/fido/fido.c
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#include "files.h"
|
||||
@@ -31,6 +31,9 @@
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include "management.h"
|
||||
#include "ctap_hid.h"
|
||||
#include "version.h"
|
||||
|
||||
int fido_process_apdu();
|
||||
int fido_unload();
|
||||
@@ -40,7 +43,7 @@ pinUvAuthToken_t paut = { 0 };
|
||||
uint8_t keydev_dec[32];
|
||||
bool has_keydev_dec = false;
|
||||
|
||||
const uint8_t fido_aid[] = {
|
||||
const uint8_t _fido_aid[] = {
|
||||
8,
|
||||
0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01
|
||||
};
|
||||
@@ -51,21 +54,44 @@ const uint8_t atr_fido[] = {
|
||||
0x75, 0x62, 0x69, 0x4b, 0x65, 0x79, 0x40
|
||||
};
|
||||
|
||||
app_t *fido_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
|
||||
if (!memcmp(aid, fido_aid + 1, MIN(aid_len, fido_aid[0]))) {
|
||||
a->aid = fido_aid;
|
||||
uint8_t fido_get_version_major() {
|
||||
return PICO_FIDO_VERSION_MAJOR;
|
||||
}
|
||||
uint8_t fido_get_version_minor() {
|
||||
return PICO_FIDO_VERSION_MINOR;
|
||||
}
|
||||
|
||||
int fido_select(app_t *a) {
|
||||
if (cap_supported(CAP_FIDO2)) {
|
||||
a->process_apdu = fido_process_apdu;
|
||||
a->unload = fido_unload;
|
||||
return a;
|
||||
return CCID_OK;
|
||||
}
|
||||
return NULL;
|
||||
return CCID_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
extern uint8_t (*get_version_major)();
|
||||
extern uint8_t (*get_version_minor)();
|
||||
extern const uint8_t *fido_aid;
|
||||
extern void (*init_fido_cb)();
|
||||
extern void (*cbor_thread_func)();
|
||||
extern int (*cbor_process_cb)(uint8_t, const uint8_t *, size_t);
|
||||
extern void cbor_thread();
|
||||
extern int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len);
|
||||
|
||||
void __attribute__((constructor)) fido_ctor() {
|
||||
#if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION)
|
||||
ccid_atr = atr_fido;
|
||||
#endif
|
||||
register_app(fido_select);
|
||||
get_version_major = fido_get_version_major;
|
||||
get_version_minor = fido_get_version_minor;
|
||||
fido_aid = _fido_aid;
|
||||
init_fido_cb = init_fido;
|
||||
#ifndef ENABLE_EMULATION
|
||||
cbor_thread_func = cbor_thread;
|
||||
#endif
|
||||
cbor_process_cb = cbor_process;
|
||||
register_app(fido_select, fido_aid);
|
||||
}
|
||||
|
||||
int fido_unload() {
|
||||
@@ -91,10 +117,43 @@ mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) {
|
||||
else if (curve == FIDO2_CURVE_X448) {
|
||||
return MBEDTLS_ECP_DP_CURVE448;
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_ED25519) {
|
||||
return MBEDTLS_ECP_DP_ED25519;
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_ED448) {
|
||||
return MBEDTLS_ECP_DP_ED448;
|
||||
}
|
||||
return MBEDTLS_ECP_DP_NONE;
|
||||
}
|
||||
int mbedtls_curve_to_fido(mbedtls_ecp_group_id id) {
|
||||
if (id == MBEDTLS_ECP_DP_SECP256R1) {
|
||||
return FIDO2_CURVE_P256;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
return FIDO2_CURVE_P384;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
return FIDO2_CURVE_P521;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_SECP256K1) {
|
||||
return FIDO2_CURVE_P256K1;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_CURVE25519) {
|
||||
return MBEDTLS_ECP_DP_CURVE25519;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_CURVE448) {
|
||||
return FIDO2_CURVE_X448;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_ED25519) {
|
||||
return FIDO2_CURVE_ED25519;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_ED448) {
|
||||
return FIDO2_CURVE_ED448;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key) {
|
||||
int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecp_keypair *key) {
|
||||
mbedtls_ecp_group_id mbedtls_curve = fido_curve_to_mbedtls(curve);
|
||||
if (mbedtls_curve == MBEDTLS_ECP_DP_NONE) {
|
||||
return CTAP2_ERR_UNSUPPORTED_ALGORITHM;
|
||||
@@ -115,10 +174,9 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
|
||||
mbedtls_x509write_crt_set_validity(&ctx, "20220901000000", "20720831235959");
|
||||
mbedtls_x509write_crt_set_issuer_name(&ctx, "C=ES,O=Pico HSM,CN=Pico FIDO");
|
||||
mbedtls_x509write_crt_set_subject_name(&ctx, "C=ES,O=Pico HSM,CN=Pico FIDO");
|
||||
mbedtls_mpi serial;
|
||||
mbedtls_mpi_init(&serial);
|
||||
mbedtls_mpi_fill_random(&serial, 32, random_gen, NULL);
|
||||
mbedtls_x509write_crt_set_serial(&ctx, &serial);
|
||||
uint8_t serial[20];
|
||||
random_gen(NULL, serial, sizeof(serial));
|
||||
mbedtls_x509write_crt_set_serial_raw(&ctx, serial, sizeof(serial));
|
||||
mbedtls_pk_context key;
|
||||
mbedtls_pk_init(&key);
|
||||
mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
|
||||
@@ -152,7 +210,7 @@ int load_keydev(uint8_t *key) {
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_context *key) {
|
||||
int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecp_keypair *key) {
|
||||
for (int i = 0; i < KEY_PATH_ENTRIES; i++) {
|
||||
uint32_t k = *(uint32_t *) &keyHandle[i * sizeof(uint32_t)];
|
||||
if (!(k & 0x80000000)) {
|
||||
@@ -194,8 +252,8 @@ int derive_key(const uint8_t *app_id,
|
||||
bool new_key,
|
||||
uint8_t *key_handle,
|
||||
int curve,
|
||||
mbedtls_ecdsa_context *key) {
|
||||
uint8_t outk[64] = { 0 };
|
||||
mbedtls_ecp_keypair *key) {
|
||||
uint8_t outk[67] = { 0 }; //SECP521R1 key is 66 bytes length
|
||||
int r = 0;
|
||||
memset(outk, 0, sizeof(outk));
|
||||
if ((r = load_keydev(outk)) != CCID_OK) {
|
||||
@@ -240,11 +298,17 @@ int derive_key(const uint8_t *app_id,
|
||||
if (cinfo == NULL) {
|
||||
return 1;
|
||||
}
|
||||
if (cinfo->bit_size % 8 != 0) {
|
||||
outk[0] >>= 8 - (cinfo->bit_size % 8);
|
||||
}
|
||||
r = mbedtls_ecp_read_key(curve, key, outk, ceil((float) cinfo->bit_size / 8));
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
if (curve == MBEDTLS_ECP_DP_ED25519) {
|
||||
return mbedtls_ecp_point_edwards(&key->grp, &key->Q, &key->d, random_gen, NULL);
|
||||
}
|
||||
return mbedtls_ecp_mul(&key->grp, &key->Q, &key->d, &key->grp.G, random_gen, NULL);
|
||||
}
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
@@ -349,8 +413,10 @@ void scan_all() {
|
||||
scan_files();
|
||||
}
|
||||
|
||||
extern void init_otp();
|
||||
void init_fido() {
|
||||
scan_all();
|
||||
init_otp();
|
||||
}
|
||||
|
||||
bool wait_button_pressed() {
|
||||
@@ -403,22 +469,40 @@ void set_opts(uint8_t opts) {
|
||||
extern int cmd_register();
|
||||
extern int cmd_authenticate();
|
||||
extern int cmd_version();
|
||||
extern int cbor_parse(int, uint8_t *, size_t);
|
||||
|
||||
#define CTAP_CBOR 0x10
|
||||
|
||||
int cmd_cbor() {
|
||||
uint8_t *old_buf = res_APDU;
|
||||
int ret = cbor_parse(0x90, apdu.data, apdu.nc);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
res_APDU = old_buf;
|
||||
res_APDU_size += 1;
|
||||
memcpy(res_APDU, ctap_resp->init.data, res_APDU_size);
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ CTAP_REGISTER, cmd_register },
|
||||
{ CTAP_AUTHENTICATE, cmd_authenticate },
|
||||
{ CTAP_VERSION, cmd_version },
|
||||
{ CTAP_CBOR, cmd_cbor },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int fido_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
if (CLA(apdu) != 0x00 && CLA(apdu) != 0x80) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
if (cap_supported(CAP_U2F)) {
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#endif
|
||||
#include "common.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "mbedtls/eddsa.h"
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "ctap_hid.h"
|
||||
#else
|
||||
@@ -40,12 +41,13 @@ extern int derive_key(const uint8_t *app_id,
|
||||
bool new_key,
|
||||
uint8_t *key_handle,
|
||||
int,
|
||||
mbedtls_ecdsa_context *key);
|
||||
extern int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_context *);
|
||||
mbedtls_ecp_keypair *key);
|
||||
extern int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecp_keypair *);
|
||||
extern bool wait_button_pressed();
|
||||
extern void init_fido();
|
||||
extern mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve);
|
||||
extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key);
|
||||
extern int mbedtls_curve_to_fido(mbedtls_ecp_group_id id);
|
||||
extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecp_keypair *key);
|
||||
extern int load_keydev(uint8_t *key);
|
||||
extern int encrypt(uint8_t protocol,
|
||||
const uint8_t *key,
|
||||
@@ -64,6 +66,10 @@ extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSec
|
||||
#define FIDO2_ALG_ES384 -35 //ECDSA-SHA384 P384
|
||||
#define FIDO2_ALG_ES512 -36 //ECDSA-SHA512 P521
|
||||
#define FIDO2_ALG_ECDH_ES_HKDF_256 -25 //ECDH-ES + HKDF-256
|
||||
#define FIDO2_ALG_ES256K -47
|
||||
#define FIDO2_ALG_RS256 -257
|
||||
#define FIDO2_ALG_RS384 -258
|
||||
#define FIDO2_ALG_RS512 -259
|
||||
|
||||
#define FIDO2_CURVE_P256 1
|
||||
#define FIDO2_CURVE_P384 2
|
||||
|
||||
@@ -46,6 +46,9 @@ file_t file_entries[] = {
|
||||
{ .fid = EF_LARGEBLOB, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
|
||||
.ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Large Blob
|
||||
{ .fid = EF_OTP_PIN, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH,
|
||||
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } },
|
||||
{ .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL,
|
||||
.ef_structure = 0, .acl = { 0 } } //end
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#define EF_PIN 0x1080
|
||||
#define EF_AUTHTOKEN 0x1090
|
||||
#define EF_MINPINLEN 0x1100
|
||||
#define EF_DEV_CONF 0x1122
|
||||
#define EF_CRED 0xCF00 // Creds at 0xCF00 - 0xCFFF
|
||||
#define EF_RP 0xD000 // RPs at 0xD000 - 0xD0FF
|
||||
#define EF_LARGEBLOB 0x1101 // Large Blob Array
|
||||
@@ -36,6 +37,7 @@
|
||||
#define EF_OATH_CODE 0xBAFF
|
||||
#define EF_OTP_SLOT1 0xBB00
|
||||
#define EF_OTP_SLOT2 0xBB01
|
||||
#define EF_OTP_PIN 0x10A0 // Nitrokey OTP PIN
|
||||
|
||||
extern file_t *ef_keydev;
|
||||
extern file_t *ef_certdev;
|
||||
|
||||
156
src/fido/management.c
Normal file
156
src/fido/management.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "version.h"
|
||||
#include "files.h"
|
||||
#include "asn1.h"
|
||||
#include "management.h"
|
||||
|
||||
int man_process_apdu();
|
||||
int man_unload();
|
||||
|
||||
const uint8_t man_aid[] = {
|
||||
8,
|
||||
0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17
|
||||
};
|
||||
extern void scan_all();
|
||||
extern void init_otp();
|
||||
int man_select(app_t *a) {
|
||||
a->process_apdu = man_process_apdu;
|
||||
a->unload = man_unload;
|
||||
sprintf((char *) res_APDU, "%d.%d.0", PICO_FIDO_VERSION_MAJOR, PICO_FIDO_VERSION_MINOR);
|
||||
res_APDU_size = strlen((char *) res_APDU);
|
||||
apdu.ne = res_APDU_size;
|
||||
scan_all();
|
||||
init_otp();
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
void __attribute__((constructor)) man_ctor() {
|
||||
register_app(man_select, man_aid);
|
||||
}
|
||||
|
||||
int man_unload() {
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
bool cap_supported(uint16_t cap) {
|
||||
file_t *ef = search_dynamic_file(EF_DEV_CONF);
|
||||
if (file_has_data(ef)) {
|
||||
uint16_t tag = 0x0, data_len = file_get_size(ef);
|
||||
uint8_t *tag_data = NULL, *p = NULL, *data = file_get_data(ef);
|
||||
size_t tag_len = 0;
|
||||
while (walk_tlv(data, data_len, &p, &tag, &tag_len, &tag_data)) {
|
||||
if (tag == TAG_USB_ENABLED) {
|
||||
uint16_t ecaps = tag_data[0];
|
||||
if (tag_len == 2) {
|
||||
ecaps = (tag_data[0] << 8) | tag_data[1];
|
||||
}
|
||||
return ecaps & cap;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int man_get_config() {
|
||||
file_t *ef = search_dynamic_file(EF_DEV_CONF);
|
||||
res_APDU_size = 0;
|
||||
res_APDU[res_APDU_size++] = 0; // Overall length. Filled later
|
||||
res_APDU[res_APDU_size++] = TAG_USB_SUPPORTED;
|
||||
res_APDU[res_APDU_size++] = 2;
|
||||
res_APDU[res_APDU_size++] = CAP_FIDO2 >> 8;
|
||||
res_APDU[res_APDU_size++] = CAP_OTP | CAP_U2F | CAP_OATH;
|
||||
res_APDU[res_APDU_size++] = TAG_SERIAL;
|
||||
res_APDU[res_APDU_size++] = 4;
|
||||
#ifndef ENABLE_EMULATION
|
||||
pico_get_unique_board_id_string((char *) res_APDU + res_APDU_size, 4);
|
||||
#endif
|
||||
res_APDU_size += 4;
|
||||
res_APDU[res_APDU_size++] = TAG_FORM_FACTOR;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x01;
|
||||
res_APDU[res_APDU_size++] = TAG_VERSION;
|
||||
res_APDU[res_APDU_size++] = 3;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
res_APDU[res_APDU_size++] = TAG_NFC_SUPPORTED;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x00;
|
||||
if (!file_has_data(ef)) {
|
||||
res_APDU[res_APDU_size++] = TAG_USB_ENABLED;
|
||||
res_APDU[res_APDU_size++] = 2;
|
||||
res_APDU[res_APDU_size++] = CAP_FIDO2 >> 8;
|
||||
res_APDU[res_APDU_size++] = CAP_OTP | CAP_U2F | CAP_OATH;
|
||||
res_APDU[res_APDU_size++] = TAG_DEVICE_FLAGS;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = FLAG_EJECT;
|
||||
res_APDU[res_APDU_size++] = TAG_CONFIG_LOCK;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x00;
|
||||
res_APDU[res_APDU_size++] = TAG_NFC_ENABLED;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x00;
|
||||
}
|
||||
else {
|
||||
memcpy(res_APDU + res_APDU_size, file_get_data(ef), file_get_size(ef));
|
||||
res_APDU_size += file_get_size(ef);
|
||||
}
|
||||
res_APDU[0] = res_APDU_size - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_read_config() {
|
||||
man_get_config();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_write_config() {
|
||||
if (apdu.data[0] != apdu.nc - 1) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
file_t *ef = file_new(EF_DEV_CONF);
|
||||
flash_write_data_to_file(ef, apdu.data + 1, apdu.nc - 1);
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_READ_CONFIG 0x1D
|
||||
#define INS_WRITE_CONFIG 0x1C
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ INS_READ_CONFIG, cmd_read_config },
|
||||
{ INS_WRITE_CONFIG, cmd_write_config },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int man_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
}
|
||||
55
src/fido/management.h
Normal file
55
src/fido/management.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _MANAGEMENT_H_
|
||||
#define _MANAGEMENT_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "pico/stdlib.h"
|
||||
#endif
|
||||
|
||||
#define TAG_USB_SUPPORTED 0x01
|
||||
#define TAG_SERIAL 0x02
|
||||
#define TAG_USB_ENABLED 0x03
|
||||
#define TAG_FORM_FACTOR 0x04
|
||||
#define TAG_VERSION 0x05
|
||||
#define TAG_AUTO_EJECT_TIMEOUT 0x06
|
||||
#define TAG_CHALRESP_TIMEOUT 0x07
|
||||
#define TAG_DEVICE_FLAGS 0x08
|
||||
#define TAG_APP_VERSIONS 0x09
|
||||
#define TAG_CONFIG_LOCK 0x0A
|
||||
#define TAG_UNLOCK 0x0B
|
||||
#define TAG_REBOOT 0x0C
|
||||
#define TAG_NFC_SUPPORTED 0x0D
|
||||
#define TAG_NFC_ENABLED 0x0E
|
||||
|
||||
#define CAP_OTP 0x01
|
||||
#define CAP_U2F 0x02
|
||||
#define CAP_FIDO2 0x200
|
||||
#define CAP_OATH 0x20
|
||||
#define CAP_PIV 0x10
|
||||
#define CAP_OPENPGP 0x08
|
||||
#define CAP_HSMAUTH 0x100
|
||||
|
||||
#define FLAG_REMOTE_WAKEUP 0x40
|
||||
#define FLAG_EJECT 0x80
|
||||
|
||||
extern bool cap_supported(uint16_t cap);
|
||||
extern int man_get_config();
|
||||
|
||||
#endif //_MANAGEMENT_H
|
||||
174
src/fido/oath.c
174
src/fido/oath.c
@@ -16,15 +16,18 @@
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "version.h"
|
||||
#include "asn1.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "management.h"
|
||||
|
||||
#define MAX_OATH_CRED 255
|
||||
#define CHALLENGE_LEN 8
|
||||
#define MAX_OTP_COUNTER 3
|
||||
|
||||
#define TAG_NAME 0x71
|
||||
#define TAG_NAME_LIST 0x72
|
||||
@@ -34,10 +37,13 @@
|
||||
#define TAG_T_RESPONSE 0x76
|
||||
#define TAG_NO_RESPONSE 0x77
|
||||
#define TAG_PROPERTY 0x78
|
||||
#define TAG_VERSION 0x79
|
||||
#define TAG_T_VERSION 0x79
|
||||
#define TAG_IMF 0x7a
|
||||
#define TAG_ALGO 0x7b
|
||||
#define TAG_TOUCH_RESPONSE 0x7c
|
||||
#define TAG_PASSWORD 0x80
|
||||
#define TAG_NEW_PASSWORD 0x81
|
||||
#define TAG_PIN_COUNTER 0x82
|
||||
|
||||
#define ALG_HMAC_SHA1 0x01
|
||||
#define ALG_HMAC_SHA256 0x02
|
||||
@@ -62,13 +68,12 @@ const uint8_t oath_aid[] = {
|
||||
0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01
|
||||
};
|
||||
|
||||
app_t *oath_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
|
||||
if (!memcmp(aid, oath_aid + 1, MIN(aid_len, oath_aid[0]))) {
|
||||
a->aid = oath_aid;
|
||||
int oath_select(app_t *a) {
|
||||
if (cap_supported(CAP_OATH)) {
|
||||
a->process_apdu = oath_process_apdu;
|
||||
a->unload = oath_unload;
|
||||
res_APDU_size = 0;
|
||||
res_APDU[res_APDU_size++] = TAG_VERSION;
|
||||
res_APDU[res_APDU_size++] = TAG_T_VERSION;
|
||||
res_APDU[res_APDU_size++] = 3;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
|
||||
@@ -76,25 +81,36 @@ app_t *oath_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
|
||||
res_APDU[res_APDU_size++] = TAG_NAME;
|
||||
res_APDU[res_APDU_size++] = 8;
|
||||
#ifndef ENABLE_EMULATION
|
||||
pico_get_unique_board_id((pico_unique_board_id_t *) (res_APDU + res_APDU_size));
|
||||
res_APDU_size += 8;
|
||||
pico_get_unique_board_id((pico_unique_board_id_t *) (res_APDU + res_APDU_size));
|
||||
res_APDU_size += 8;
|
||||
#else
|
||||
memset(res_APDU + res_APDU_size, 0, 8); res_APDU_size += 8;
|
||||
memset(res_APDU + res_APDU_size, 0, 8); res_APDU_size += 8;
|
||||
#endif
|
||||
if (file_has_data(search_dynamic_file(EF_OATH_CODE)) == true) {
|
||||
random_gen(NULL, challenge, sizeof(challenge));
|
||||
res_APDU[res_APDU_size++] = TAG_CHALLENGE;
|
||||
res_APDU[res_APDU_size++] = sizeof(challenge);
|
||||
memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge));
|
||||
res_APDU_size += sizeof(challenge);
|
||||
}
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_otp_pin)) {
|
||||
const uint8_t *pin_data = file_get_data(ef_otp_pin);
|
||||
res_APDU[res_APDU_size++] = TAG_PIN_COUNTER;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = *pin_data;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = TAG_ALGO;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
|
||||
apdu.ne = res_APDU_size;
|
||||
return a;
|
||||
return CCID_OK;
|
||||
}
|
||||
return NULL;
|
||||
return CCID_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
void __attribute__((constructor)) oath_ctor() {
|
||||
register_app(oath_select);
|
||||
register_app(oath_select, oath_aid);
|
||||
}
|
||||
|
||||
int oath_unload() {
|
||||
@@ -251,6 +267,8 @@ int cmd_reset() {
|
||||
}
|
||||
}
|
||||
delete_file(search_dynamic_file(EF_OATH_CODE));
|
||||
flash_clear_file(search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF));
|
||||
low_flash_available();
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
@@ -436,6 +454,7 @@ int cmd_calculate_all() {
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
res_APDU_size = 0;
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
|
||||
if (file_has_data(ef)) {
|
||||
@@ -478,6 +497,119 @@ int cmd_send_remaining() {
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_set_otp_pin() {
|
||||
size_t pw_len = 0;
|
||||
uint8_t *pw = NULL, hsh[33] = { 0 };
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_otp_pin)) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
hsh[0] = MAX_OTP_COUNTER;
|
||||
double_hash_pin(pw, pw_len, hsh + 1);
|
||||
flash_write_data_to_file(ef_otp_pin, hsh, sizeof(hsh));
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_change_otp_pin() {
|
||||
size_t pw_len = 0, new_pw_len = 0;
|
||||
uint8_t *pw = NULL, *new_pw = NULL, hsh[33] = { 0 };
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_otp_pin)) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
double_hash_pin(pw, pw_len, hsh + 1);
|
||||
if (memcmp(file_get_data(ef_otp_pin) + 1, hsh + 1, 32) != 0) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NEW_PASSWORD, &new_pw_len, &new_pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
hsh[0] = MAX_OTP_COUNTER;
|
||||
double_hash_pin(new_pw, new_pw_len, hsh + 1);
|
||||
flash_write_data_to_file(ef_otp_pin, hsh, sizeof(hsh));
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_verify_otp_pin() {
|
||||
size_t pw_len = 0;
|
||||
uint8_t *pw = NULL, hsh[33] = { 0 }, data_hsh[33];
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_otp_pin)) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
double_hash_pin(pw, pw_len, hsh + 1);
|
||||
memcpy(data_hsh, file_get_data(ef_otp_pin), sizeof(data_hsh));
|
||||
if (data_hsh[0] == 0 || memcmp(data_hsh + 1, hsh + 1, 32) != 0) {
|
||||
if (data_hsh[0] > 0) {
|
||||
data_hsh[0] -= 1;
|
||||
}
|
||||
flash_write_data_to_file(ef_otp_pin, data_hsh, sizeof(data_hsh));
|
||||
low_flash_available();
|
||||
validated = false;
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
data_hsh[0] = MAX_OTP_COUNTER;
|
||||
flash_write_data_to_file(ef_otp_pin, data_hsh, sizeof(data_hsh));
|
||||
low_flash_available();
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_verify_hotp() {
|
||||
size_t key_len = 0, chal_len = 0, name_len = 0, code_len = 0;
|
||||
uint8_t *key = NULL, *chal = NULL, *name = NULL, *code = NULL;
|
||||
uint32_t code_int = 0;
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
file_t *ef = find_oath_cred(name, name_len);
|
||||
if (file_has_data(ef) == false) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_KEY, &key_len, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
if ((key[0] & OATH_TYPE_MASK) != OATH_TYPE_HOTP) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_IMF, &chal_len,
|
||||
&chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &code_len, &code) == true) {
|
||||
code_int = (code[0] << 24) | (code[1] << 16) | (code[2] << 8) | code[3];
|
||||
}
|
||||
|
||||
int ret = calculate_oath(0x01, key, key_len, chal, chal_len);
|
||||
if (ret != CCID_OK) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
uint32_t res_int = (res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5];
|
||||
if (res_APDU[1] == 6) {
|
||||
res_int %= (uint32_t) 1e6;
|
||||
}
|
||||
else {
|
||||
res_int %= (uint32_t) 1e8;
|
||||
}
|
||||
if (res_int != code_int) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
res_APDU_size = apdu.ne = 0;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_PUT 0x01
|
||||
#define INS_DELETE 0x02
|
||||
#define INS_SET_CODE 0x03
|
||||
@@ -487,6 +619,10 @@ int cmd_send_remaining() {
|
||||
#define INS_VALIDATE 0xa3
|
||||
#define INS_CALC_ALL 0xa4
|
||||
#define INS_SEND_REMAINING 0xa5
|
||||
#define INS_VERIFY_CODE 0xb1
|
||||
#define INS_VERIFY_PIN 0xb2
|
||||
#define INS_CHANGE_PIN 0xb3
|
||||
#define INS_SET_PIN 0xb4
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ INS_PUT, cmd_put },
|
||||
@@ -498,6 +634,10 @@ static const cmd_t cmds[] = {
|
||||
{ INS_CALCULATE, cmd_calculate },
|
||||
{ INS_CALC_ALL, cmd_calculate_all },
|
||||
{ INS_SEND_REMAINING, cmd_send_remaining },
|
||||
{ INS_SET_PIN, cmd_set_otp_pin },
|
||||
{ INS_CHANGE_PIN, cmd_change_otp_pin },
|
||||
{ INS_VERIFY_PIN, cmd_verify_otp_pin },
|
||||
{ INS_VERIFY_CODE, cmd_verify_hotp },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
@@ -505,10 +645,12 @@ int oath_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
if (cap_supported(CAP_OATH)) {
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
|
||||
521
src/fido/otp.c
521
src/fido/otp.c
@@ -16,12 +16,18 @@
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "version.h"
|
||||
#include "asn1.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include "mbedtls/aes.h"
|
||||
#include "management.h"
|
||||
|
||||
#define FIXED_SIZE 16
|
||||
#define KEY_SIZE 16
|
||||
@@ -36,6 +42,54 @@
|
||||
#define CONFIG_LED_INV 0x10
|
||||
#define CONFIG_STATUS_MASK 0x1f
|
||||
|
||||
/* EXT Flags */
|
||||
#define SERIAL_BTN_VISIBLE 0x01 // Serial number visible at startup (button press)
|
||||
#define SERIAL_USB_VISIBLE 0x02 // Serial number visible in USB iSerial field
|
||||
#define SERIAL_API_VISIBLE 0x04 // Serial number visible via API call
|
||||
#define USE_NUMERIC_KEYPAD 0x08 // Use numeric keypad for digits
|
||||
#define FAST_TRIG 0x10 // Use fast trig if only cfg1 set
|
||||
#define ALLOW_UPDATE 0x20 // Allow update of existing configuration (selected flags + access code)
|
||||
#define DORMANT 0x40 // Dormant config (woken up, flag removed, requires update flag)
|
||||
#define LED_INV 0x80 // LED idle state is off rather than on
|
||||
#define EXTFLAG_UPDATE_MASK (SERIAL_BTN_VISIBLE | SERIAL_USB_VISIBLE | SERIAL_API_VISIBLE | \
|
||||
USE_NUMERIC_KEYPAD | FAST_TRIG | ALLOW_UPDATE | DORMANT | LED_INV)
|
||||
|
||||
/* TKT Flags */
|
||||
#define TAB_FIRST 0x01 // Send TAB before first part
|
||||
#define APPEND_TAB1 0x02 // Send TAB after first part
|
||||
#define APPEND_TAB2 0x04 // Send TAB after second part
|
||||
#define APPEND_DELAY1 0x08 // Add 0.5s delay after first part
|
||||
#define APPEND_DELAY2 0x10 // Add 0.5s delay after second part
|
||||
#define APPEND_CR 0x20 // Append CR as final character
|
||||
#define OATH_HOTP 0x40 // OATH HOTP mode
|
||||
#define CHAL_RESP 0x40 // Challenge-response enabled (both must be set)
|
||||
#define PROTECT_CFG2 0x80 // Block update of config 2 unless config 2 is configured and has this bit set
|
||||
#define TKTFLAG_UPDATE_MASK (TAB_FIRST | APPEND_TAB1 | APPEND_TAB2 | APPEND_DELAY1 | APPEND_DELAY2 | \
|
||||
APPEND_CR)
|
||||
|
||||
/* CFG Flags */
|
||||
#define SEND_REF 0x01 // Send reference string (0..F) before data
|
||||
#define PACING_10MS 0x04 // Add 10ms intra-key pacing
|
||||
#define PACING_20MS 0x08 // Add 20ms intra-key pacing
|
||||
#define STATIC_TICKET 0x20 // Static ticket generation
|
||||
// Static
|
||||
#define SHORT_TICKET 0x02 // Send truncated ticket (half length)
|
||||
#define STRONG_PW1 0x10 // Strong password policy flag #1 (mixed case)
|
||||
#define STRONG_PW2 0x40 // Strong password policy flag #2 (subtitute 0..7 to digits)
|
||||
#define MAN_UPDATE 0x80 // Allow manual (local) update of static OTP
|
||||
// Challenge (no keyboard)
|
||||
#define HMAC_LT64 0x04 // Set when HMAC message is less than 64 bytes
|
||||
#define CHAL_BTN_TRIG 0x08 // Challenge-response operation requires button press
|
||||
#define CHAL_YUBICO 0x20 // Challenge-response enabled - Yubico OTP mode
|
||||
#define CHAL_HMAC 0x22 // Challenge-response enabled - HMAC-SHA1
|
||||
// OATH
|
||||
#define OATH_HOTP8 0x02 // Generate 8 digits HOTP rather than 6 digits
|
||||
#define OATH_FIXED_MODHEX1 0x10 // First byte in fixed part sent as modhex
|
||||
#define OATH_FIXED_MODHEX2 0x40 // First two bytes in fixed part sent as modhex
|
||||
#define OATH_FIXED_MODHEX 0x50 // Fixed part sent as modhex
|
||||
#define OATH_FIXED_MASK 0x50 // Mask to get out fixed flags
|
||||
#define CFGFLAG_UPDATE_MASK (PACING_10MS | PACING_20MS)
|
||||
|
||||
static uint8_t config_seq = { 1 };
|
||||
|
||||
typedef struct otp_config {
|
||||
@@ -57,14 +111,20 @@ uint16_t otp_status();
|
||||
int otp_process_apdu();
|
||||
int otp_unload();
|
||||
|
||||
#ifndef ENABLE_EMULATION
|
||||
extern int (*hid_set_report_cb)(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t);
|
||||
extern uint16_t (*hid_get_report_cb)(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t);
|
||||
int otp_hid_set_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t);
|
||||
uint16_t otp_hid_get_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t);
|
||||
#endif
|
||||
|
||||
const uint8_t otp_aid[] = {
|
||||
7,
|
||||
0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
|
||||
};
|
||||
|
||||
app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
|
||||
if (!memcmp(aid, otp_aid + 1, MIN(aid_len, otp_aid[0]))) {
|
||||
a->aid = otp_aid;
|
||||
int otp_select(app_t *a) {
|
||||
if (cap_supported(CAP_OTP)) {
|
||||
a->process_apdu = otp_process_apdu;
|
||||
a->unload = otp_unload;
|
||||
if (file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ||
|
||||
@@ -75,14 +135,208 @@ app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
|
||||
config_seq = 0;
|
||||
}
|
||||
otp_status();
|
||||
memmove(res_APDU, res_APDU + 1, 6);
|
||||
res_APDU_size = 6;
|
||||
apdu.ne = res_APDU_size;
|
||||
return a;
|
||||
return CCID_OK;
|
||||
}
|
||||
return NULL;
|
||||
return CCID_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint8_t modhex_tab[] =
|
||||
{ 'c', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'r', 't', 'u', 'v' };
|
||||
int encode_modhex(const uint8_t *in, size_t len, uint8_t *out) {
|
||||
for (int l = 0; l < len; l++) {
|
||||
*out++ = modhex_tab[in[l] >> 4];
|
||||
*out++ = modhex_tab[in[l] & 0xf];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static bool scanned = false;
|
||||
extern void scan_all();
|
||||
void init_otp() {
|
||||
if (scanned == false) {
|
||||
scan_all();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_OTP_SLOT1 + i);
|
||||
uint8_t *data = file_get_data(ef);
|
||||
otp_config_t *otp_config = (otp_config_t *) data;
|
||||
if (file_has_data(ef) && !(otp_config->tkt_flags & OATH_HOTP) &&
|
||||
!(otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET)) {
|
||||
uint16_t counter = (data[otp_config_size] << 8) | data[otp_config_size + 1];
|
||||
if (++counter <= 0x7fff) {
|
||||
uint8_t new_data[otp_config_size + 8];
|
||||
memcpy(new_data, data, sizeof(new_data));
|
||||
new_data[otp_config_size] = counter >> 8;
|
||||
new_data[otp_config_size + 1] = counter & 0xff;
|
||||
flash_write_data_to_file(ef, new_data, sizeof(new_data));
|
||||
}
|
||||
}
|
||||
}
|
||||
scanned = true;
|
||||
low_flash_available();
|
||||
}
|
||||
}
|
||||
extern int calculate_oath(uint8_t truncate,
|
||||
const uint8_t *key,
|
||||
size_t key_len,
|
||||
const uint8_t *chal,
|
||||
size_t chal_len);
|
||||
|
||||
uint16_t calculate_crc(const uint8_t *data, size_t data_len) {
|
||||
uint16_t crc = 0xFFFF;
|
||||
for (size_t idx = 0; idx < data_len; idx++) {
|
||||
crc ^= data[idx];
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
uint16_t j = crc & 0x1;
|
||||
crc >>= 1;
|
||||
if (j == 1) {
|
||||
crc ^= 0x8408;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc & 0xFFFF;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_EMULATION
|
||||
static uint8_t session_counter[2] = { 0 };
|
||||
#endif
|
||||
int otp_button_pressed(uint8_t slot) {
|
||||
init_otp();
|
||||
if (!cap_supported(CAP_OTP)) {
|
||||
return 3;
|
||||
}
|
||||
#ifndef ENABLE_EMULATION
|
||||
file_t *ef = search_dynamic_file(slot == 1 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
||||
const uint8_t *data = file_get_data(ef);
|
||||
otp_config_t *otp_config = (otp_config_t *) data;
|
||||
if (file_has_data(ef) == false) {
|
||||
return 1;
|
||||
}
|
||||
if (otp_config->cfg_flags & CHAL_YUBICO && otp_config->tkt_flags & CHAL_RESP) {
|
||||
return 2;
|
||||
}
|
||||
if (otp_config->tkt_flags & OATH_HOTP) {
|
||||
uint8_t tmp_key[KEY_SIZE + 2];
|
||||
tmp_key[0] = 0x01;
|
||||
memcpy(tmp_key + 2, otp_config->aes_key, KEY_SIZE);
|
||||
uint64_t imf = 0;
|
||||
const uint8_t *p = data + otp_config_size;
|
||||
imf |= (uint64_t) *p++ << 56;
|
||||
imf |= (uint64_t) *p++ << 48;
|
||||
imf |= (uint64_t) *p++ << 40;
|
||||
imf |= (uint64_t) *p++ << 32;
|
||||
imf |= *p++ << 24;
|
||||
imf |= *p++ << 16;
|
||||
imf |= *p++ << 8;
|
||||
imf |= *p++;
|
||||
if (imf == 0) {
|
||||
imf = ((otp_config->uid[4] << 8) | otp_config->uid[5]) << 4;
|
||||
}
|
||||
uint8_t chal[8] =
|
||||
{ imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8, imf & 0xff };
|
||||
res_APDU_size = 0;
|
||||
int ret = calculate_oath(1, tmp_key, sizeof(tmp_key), chal, sizeof(chal));
|
||||
if (ret == CCID_OK) {
|
||||
uint32_t base = otp_config->cfg_flags & OATH_HOTP8 ? 1e8 : 1e6;
|
||||
uint32_t number =
|
||||
(res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5];
|
||||
number %= base;
|
||||
char number_str[9];
|
||||
if (otp_config->cfg_flags & OATH_HOTP8) {
|
||||
sprintf(number_str, "%08lu", (long unsigned int) number);
|
||||
add_keyboard_buffer((const uint8_t *) number_str, 8, true);
|
||||
}
|
||||
else {
|
||||
sprintf(number_str, "%06lu", (long unsigned int) number);
|
||||
add_keyboard_buffer((const uint8_t *) number_str, 6, true);
|
||||
}
|
||||
imf++;
|
||||
uint8_t new_chal[8] =
|
||||
{ imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8,
|
||||
imf & 0xff };
|
||||
uint8_t new_otp_config[otp_config_size + sizeof(new_chal)];
|
||||
memcpy(new_otp_config, otp_config, otp_config_size);
|
||||
memcpy(new_otp_config + otp_config_size, new_chal, sizeof(new_chal));
|
||||
flash_write_data_to_file(ef, new_otp_config, sizeof(new_otp_config));
|
||||
low_flash_available();
|
||||
}
|
||||
if (otp_config->tkt_flags & APPEND_CR) {
|
||||
append_keyboard_buffer((const uint8_t *) "\r", 1);
|
||||
}
|
||||
}
|
||||
else if (otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET) {
|
||||
uint8_t fixed_size = FIXED_SIZE + UID_SIZE + KEY_SIZE;
|
||||
if (otp_config->cfg_flags & SHORT_TICKET) { // Not clear which is the purpose of SHORT_TICKET
|
||||
//fixed_size /= 2;
|
||||
}
|
||||
add_keyboard_buffer(otp_config->fixed_data, fixed_size, false);
|
||||
if (otp_config->tkt_flags & APPEND_CR) {
|
||||
append_keyboard_buffer((const uint8_t *) "\x28", 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint8_t otpk[22], *po = otpk;
|
||||
bool update_counter = false;
|
||||
uint16_t counter = (data[otp_config_size] << 8) | data[otp_config_size + 1], crc = 0;
|
||||
uint32_t ts = board_millis() / 1000;
|
||||
if (counter == 0) {
|
||||
update_counter = true;
|
||||
counter = 1;
|
||||
}
|
||||
memcpy(po, otp_config->fixed_data, 6);
|
||||
po += 6;
|
||||
memcpy(po, otp_config->uid, UID_SIZE);
|
||||
po += UID_SIZE;
|
||||
*po++ = counter & 0xff;
|
||||
*po++ = counter >> 8;
|
||||
ts >>= 3;
|
||||
*po++ = ts & 0xff;
|
||||
*po++ = ts >> 8;
|
||||
*po++ = ts >> 16;
|
||||
*po++ = session_counter[slot - 1];
|
||||
random_gen(NULL, po, 2);
|
||||
po += 2;
|
||||
crc = calculate_crc(otpk + 6, 14);
|
||||
*po++ = ~crc & 0xff;
|
||||
*po++ = ~crc >> 8;
|
||||
mbedtls_aes_context ctx;
|
||||
mbedtls_aes_init(&ctx);
|
||||
mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128);
|
||||
mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, otpk + 6, otpk + 6);
|
||||
mbedtls_aes_free(&ctx);
|
||||
uint8_t otp_out[44];
|
||||
encode_modhex(otpk, sizeof(otpk), otp_out);
|
||||
add_keyboard_buffer((const uint8_t *) otp_out, sizeof(otp_out), true);
|
||||
if (otp_config->tkt_flags & APPEND_CR) {
|
||||
append_keyboard_buffer((const uint8_t *) "\r", 1);
|
||||
}
|
||||
|
||||
if (++session_counter[slot - 1] == 0) {
|
||||
if (++counter <= 0x7fff) {
|
||||
update_counter = true;
|
||||
}
|
||||
}
|
||||
if (update_counter == true) {
|
||||
uint8_t new_data[otp_config_size + 8];
|
||||
memcpy(new_data, data, sizeof(new_data));
|
||||
new_data[otp_config_size] = counter >> 8;
|
||||
new_data[otp_config_size + 1] = counter & 0xff;
|
||||
flash_write_data_to_file(ef, new_data, sizeof(new_data));
|
||||
low_flash_available();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __attribute__((constructor)) otp_ctor() {
|
||||
register_app(otp_select);
|
||||
register_app(otp_select, otp_aid);
|
||||
button_pressed_cb = otp_button_pressed;
|
||||
#ifndef ENABLE_EMULATION
|
||||
hid_set_report_cb = otp_hid_set_report_cb;
|
||||
hid_get_report_cb = otp_hid_get_report_cb;
|
||||
#endif
|
||||
}
|
||||
|
||||
int otp_unload() {
|
||||
@@ -90,31 +344,36 @@ int otp_unload() {
|
||||
}
|
||||
|
||||
uint16_t otp_status() {
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
res_APDU[res_APDU_size++] = config_seq;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
res_APDU[res_APDU_size++] = (CONFIG2_TOUCH | CONFIG1_TOUCH) |
|
||||
(file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ? CONFIG1_VALID :
|
||||
0x00) |
|
||||
(file_has_data(search_dynamic_file(EF_OTP_SLOT2)) ? CONFIG2_VALID :
|
||||
0x00);
|
||||
if (scanned == false) {
|
||||
scan_all();
|
||||
scanned = true;
|
||||
}
|
||||
res_APDU_size = 0;
|
||||
res_APDU[1] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[2] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[3] = 0;
|
||||
res_APDU[4] = config_seq;
|
||||
res_APDU[5] = (CONFIG2_TOUCH | CONFIG1_TOUCH) |
|
||||
(file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ? CONFIG1_VALID :
|
||||
0x00) |
|
||||
(file_has_data(search_dynamic_file(EF_OTP_SLOT2)) ? CONFIG2_VALID :
|
||||
0x00);
|
||||
res_APDU[6] = 0;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
bool check_crc(const otp_config_t *data) {
|
||||
uint16_t crc = calculate_crc((const uint8_t *) data, otp_config_size);
|
||||
return crc == 0xF0B8;
|
||||
}
|
||||
|
||||
int cmd_otp() {
|
||||
uint8_t p1 = P1(apdu), p2 = P2(apdu);
|
||||
if (p2 != 0x00) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
if (p1 == 0x01 || p1 == 0x03) { // Configure slot
|
||||
if (apdu.nc != otp_config_size + ACC_CODE_SIZE) {
|
||||
return SW_WRONG_LENGTH();
|
||||
}
|
||||
if (apdu.data[48] != 0 || apdu.data[49] != 0) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
otp_config_t *odata = (otp_config_t *) apdu.data;
|
||||
file_t *ef = file_new(p1 == 0x01 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
||||
if (file_has_data(ef)) {
|
||||
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
|
||||
@@ -124,7 +383,11 @@ int cmd_otp() {
|
||||
}
|
||||
for (int c = 0; c < otp_config_size; c++) {
|
||||
if (apdu.data[c] != 0) {
|
||||
flash_write_data_to_file(ef, apdu.data, otp_config_size);
|
||||
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
memset(apdu.data + otp_config_size, 0, 8); // Add 8 bytes extra
|
||||
flash_write_data_to_file(ef, apdu.data, otp_config_size + 8);
|
||||
low_flash_available();
|
||||
config_seq++;
|
||||
return otp_status();
|
||||
@@ -138,24 +401,101 @@ int cmd_otp() {
|
||||
}
|
||||
return otp_status();
|
||||
}
|
||||
else if (p1 == 0x04 || p1 == 0x05) {
|
||||
otp_config_t *odata = (otp_config_t *) apdu.data;
|
||||
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
file_t *ef = search_dynamic_file(p1 == 0x04 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
||||
if (file_has_data(ef)) {
|
||||
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
|
||||
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
memcpy(apdu.data, file_get_data(ef), FIXED_SIZE + UID_SIZE + KEY_SIZE);
|
||||
odata->fixed_size = otpc->fixed_size;
|
||||
odata->ext_flags = (otpc->ext_flags & ~EXTFLAG_UPDATE_MASK) |
|
||||
(odata->ext_flags & EXTFLAG_UPDATE_MASK);
|
||||
odata->tkt_flags = (otpc->tkt_flags & ~TKTFLAG_UPDATE_MASK) |
|
||||
(odata->tkt_flags & TKTFLAG_UPDATE_MASK);
|
||||
odata->cfg_flags = (otpc->cfg_flags & ~CFGFLAG_UPDATE_MASK) |
|
||||
(odata->cfg_flags & CFGFLAG_UPDATE_MASK);
|
||||
flash_write_data_to_file(ef, apdu.data, otp_config_size);
|
||||
low_flash_available();
|
||||
}
|
||||
}
|
||||
else if (p1 == 0x06) {
|
||||
uint8_t tmp[otp_config_size + 8];
|
||||
bool ef1_data = false;
|
||||
file_t *ef1 = file_new(EF_OTP_SLOT1);
|
||||
file_t *ef2 = file_new(EF_OTP_SLOT2);
|
||||
if (file_has_data(ef1)) {
|
||||
memcpy(tmp, file_get_data(ef1), file_get_size(ef1));
|
||||
ef1_data = true;
|
||||
}
|
||||
if (file_has_data(ef2)) {
|
||||
flash_write_data_to_file(ef1, file_get_data(ef2), file_get_size(ef2));
|
||||
}
|
||||
else {
|
||||
delete_file(ef1);
|
||||
}
|
||||
if (ef1_data) {
|
||||
flash_write_data_to_file(ef2, tmp, sizeof(tmp));
|
||||
}
|
||||
else {
|
||||
delete_file(ef2);
|
||||
}
|
||||
low_flash_available();
|
||||
}
|
||||
else if (p1 == 0x10) {
|
||||
#ifndef ENABLE_EMULATION
|
||||
pico_get_unique_board_id_string((char *) res_APDU, 4);
|
||||
#endif
|
||||
res_APDU_size = 4;
|
||||
}
|
||||
else if (p1 == 0x13) {
|
||||
man_get_config();
|
||||
}
|
||||
else if (p1 == 0x30 || p1 == 0x38 || p1 == 0x20 || p1 == 0x28) {
|
||||
file_t *ef = search_dynamic_file(p1 == 0x30 || p1 == 0x20 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
||||
if (file_has_data(ef)) {
|
||||
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
|
||||
if (!(otp_config->cfg_flags & CHAL_YUBICO && otp_config->tkt_flags & CHAL_RESP)) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
int ret = 0;
|
||||
if (p1 == 0x30 || p1 == 0x38) {
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1),
|
||||
otp_config->aes_key,
|
||||
KEY_SIZE,
|
||||
apdu.data,
|
||||
8,
|
||||
res_APDU);
|
||||
if (ret == 0) {
|
||||
res_APDU_size = 20;
|
||||
}
|
||||
}
|
||||
else if (p1 == 0x20 || p1 == 0x28) {
|
||||
uint8_t challenge[16];
|
||||
memcpy(challenge, apdu.data, 6);
|
||||
#ifndef ENABLE_EMULATION
|
||||
pico_get_unique_board_id_string((char *) challenge + 6, 10);
|
||||
#endif
|
||||
mbedtls_aes_context ctx;
|
||||
mbedtls_aes_init(&ctx);
|
||||
mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128);
|
||||
ret = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, challenge, res_APDU);
|
||||
mbedtls_aes_free(&ctx);
|
||||
if (ret == 0) {
|
||||
res_APDU_size = 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_OTP 0x01
|
||||
#define INS_DELETE 0x02
|
||||
#define INS_SET_CODE 0x03
|
||||
#define INS_RESET 0x04
|
||||
#define INS_LIST 0xa1
|
||||
#define INS_CALCULATE 0xa2
|
||||
#define INS_VALIDATE 0xa3
|
||||
#define INS_CALC_ALL 0xa4
|
||||
#define INS_SEND_REMAINING 0xa5
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ INS_OTP, cmd_otp },
|
||||
@@ -166,11 +506,120 @@ int otp_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
if (cap_supported(CAP_OTP)) {
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
}
|
||||
|
||||
#ifndef ENABLE_EMULATION
|
||||
|
||||
uint8_t otp_frame_rx[70] = {0};
|
||||
uint8_t otp_frame_tx[70] = {0};
|
||||
uint8_t otp_exp_seq = 0, otp_curr_seq = 0;
|
||||
uint8_t otp_header[4] = {0};
|
||||
|
||||
extern uint16_t *get_send_buffer_size(uint8_t itf);
|
||||
|
||||
int otp_send_frame(uint8_t *frame, size_t frame_len) {
|
||||
uint16_t crc = calculate_crc(frame, frame_len);
|
||||
frame[frame_len] = ~crc & 0xff;
|
||||
frame[frame_len + 1] = ~crc >> 8;
|
||||
frame_len += 2;
|
||||
*get_send_buffer_size(ITF_KEYBOARD) = frame_len;
|
||||
otp_exp_seq = (frame_len / 7);
|
||||
if (frame_len % 7) {
|
||||
otp_exp_seq++;
|
||||
}
|
||||
otp_curr_seq = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otp_hid_set_report_cb(uint8_t itf,
|
||||
uint8_t report_id,
|
||||
hid_report_type_t report_type,
|
||||
uint8_t const *buffer,
|
||||
uint16_t bufsize)
|
||||
{
|
||||
if (report_type == 3) {
|
||||
DEBUG_PAYLOAD(buffer, bufsize);
|
||||
if (itf == ITF_KEYBOARD && buffer[7] == 0xFF) { // reset
|
||||
*get_send_buffer_size(ITF_KEYBOARD) = 0;
|
||||
otp_curr_seq = otp_exp_seq = 0;
|
||||
memset(otp_frame_tx, 0, sizeof(otp_frame_tx));
|
||||
}
|
||||
else if (buffer[7] & 0x80) { // a frame
|
||||
uint8_t rseq = buffer[7] & 0x1F;
|
||||
if (rseq < 10) {
|
||||
if (rseq == 0) {
|
||||
memset(otp_frame_rx, 0, sizeof(otp_frame_rx));
|
||||
}
|
||||
memcpy(otp_frame_rx + rseq * 7, buffer, 7);
|
||||
if (rseq == 9) {
|
||||
DEBUG_DATA(otp_frame_rx, sizeof(otp_frame_rx));
|
||||
uint16_t residual_crc = calculate_crc(otp_frame_rx, 64), rcrc = (otp_frame_rx[66] << 8 | otp_frame_rx[65]);
|
||||
uint8_t slot_id = otp_frame_rx[64];
|
||||
if (residual_crc == rcrc) {
|
||||
apdu.data = otp_frame_rx;
|
||||
apdu.nc = 64;
|
||||
apdu.rdata = otp_frame_tx;
|
||||
apdu.header[0] = 0;
|
||||
apdu.header[1] = 0x01;
|
||||
apdu.header[2] = slot_id;
|
||||
apdu.header[3] = 0;
|
||||
int ret = otp_process_apdu();
|
||||
if (ret == 0x9000 && res_APDU_size > 0) {
|
||||
otp_send_frame(apdu.rdata, apdu.rlen);
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("[OTP] Bad CRC!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t otp_hid_get_report_cb(uint8_t itf,
|
||||
uint8_t report_id,
|
||||
hid_report_type_t report_type,
|
||||
uint8_t *buffer,
|
||||
uint16_t reqlen) {
|
||||
// TODO not Implemented
|
||||
(void) itf;
|
||||
(void) report_id;
|
||||
(void) report_type;
|
||||
(void) buffer;
|
||||
(void) reqlen;
|
||||
uint16_t send_buffer_size = *get_send_buffer_size(ITF_KEYBOARD);
|
||||
if (send_buffer_size > 0) {
|
||||
uint8_t seq = otp_curr_seq++;
|
||||
memset(buffer, 0, 8);
|
||||
memcpy(buffer, otp_frame_tx + 7 * seq, MIN(7, send_buffer_size));
|
||||
buffer[7] = 0x40 | seq;
|
||||
DEBUG_DATA(buffer, 8);
|
||||
*get_send_buffer_size(ITF_KEYBOARD) -= MIN(7, send_buffer_size);
|
||||
}
|
||||
else if (otp_curr_seq == otp_exp_seq && otp_exp_seq > 0) {
|
||||
memset(buffer, 0, 7);
|
||||
buffer[7] = 0x40;
|
||||
DEBUG_DATA(buffer,8);
|
||||
otp_curr_seq = otp_exp_seq = 0;
|
||||
}
|
||||
else {
|
||||
otp_status();
|
||||
memcpy(buffer, res_APDU, 7);
|
||||
}
|
||||
|
||||
return reqlen;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#ifndef __VERSION_H_
|
||||
#define __VERSION_H_
|
||||
|
||||
#define PICO_FIDO_VERSION 0x0300
|
||||
#define PICO_FIDO_VERSION 0x0508
|
||||
|
||||
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
||||
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||
|
||||
@@ -25,7 +25,7 @@ from fido2.attestation import FidoU2FAttestation
|
||||
from fido2.ctap2.pin import ClientPin
|
||||
from fido2.server import Fido2Server
|
||||
from fido2.ctap import CtapError
|
||||
from fido2.webauthn import CollectedClientData, AttestedCredentialData
|
||||
from fido2.webauthn import CollectedClientData, PublicKeyCredentialParameters, PublicKeyCredentialType
|
||||
from utils import *
|
||||
from fido2.cose import ES256
|
||||
import sys
|
||||
@@ -116,6 +116,10 @@ class Device():
|
||||
self.__rp = rp
|
||||
self.__attestation = attestation
|
||||
self.__server = Fido2Server(self.__rp, attestation=self.__attestation)
|
||||
self.__server.allowed_algorithms = [
|
||||
PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, p['alg'])
|
||||
for p in self.__client._backend.info.algorithms
|
||||
]
|
||||
|
||||
def client(self):
|
||||
return self.__client
|
||||
|
||||
@@ -22,7 +22,12 @@ RUN apt install -y libccid \
|
||||
cmake \
|
||||
libfuse-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN pip3 install pytest pycvc cryptography pyscard fido2 inputimeout
|
||||
RUN pip3 install pytest pycvc cryptography pyscard inputimeout
|
||||
RUN git clone https://github.com/polhenarejos/python-fido2.git
|
||||
WORKDIR /python-fido2
|
||||
RUN git checkout development
|
||||
RUN pip3 install .
|
||||
WORKDIR /
|
||||
RUN git clone https://github.com/frankmorgner/vsmartcard.git
|
||||
WORKDIR /vsmartcard/virtualsmartcard
|
||||
RUN autoreconf --verbose --install
|
||||
|
||||
@@ -49,13 +49,14 @@ elif sys.platform.startswith("darwin"):
|
||||
from . import macos as backend
|
||||
elif sys.platform.startswith("freebsd"):
|
||||
from . import freebsd as backend
|
||||
elif sys.platform.startswith("netbsd"):
|
||||
from . import netbsd as backend
|
||||
elif sys.platform.startswith("openbsd"):
|
||||
from . import openbsd as backend
|
||||
else:
|
||||
raise Exception("Unsupported platform")
|
||||
from . import emulation as backend
|
||||
|
||||
|
||||
list_descriptors = backend.list_descriptors
|
||||
get_descriptor = backend.get_descriptor
|
||||
open_connection = backend.open_connection
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
|
||||
|
||||
from fido2.client import CtapError
|
||||
from fido2.cose import ES256
|
||||
from fido2.cose import ES256, ES384, ES512, EdDSA
|
||||
from utils import ES256K
|
||||
import pytest
|
||||
|
||||
|
||||
@@ -31,7 +32,7 @@ def test_make_credential():
|
||||
pass
|
||||
|
||||
def test_attestation_format(MCRes):
|
||||
assert MCRes['res'].attestation_object.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"]
|
||||
assert MCRes['res'].attestation_object.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"]
|
||||
|
||||
def test_authdata_length(MCRes):
|
||||
assert len(MCRes['res'].attestation_object.auth_data) >= 77
|
||||
@@ -120,18 +121,25 @@ def test_bad_type_pubKeyCredParams(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=["wrong"])
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, ES256K.ALGORITHM, EdDSA.ALGORITHM]
|
||||
)
|
||||
def test_algorithms(device, info, alg):
|
||||
if ({'alg': alg, 'type': 'public-key'} in info.algorithms):
|
||||
device.doMC(key_params=[{"alg": alg, "type": "public-key"}])
|
||||
|
||||
def test_missing_pubKeyCredParams_type(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=[{"alg": ES256.ALGORITHM}])
|
||||
|
||||
assert e.value.code == CtapError.ERR.MISSING_PARAMETER
|
||||
assert e.value.code == CtapError.ERR.INVALID_CBOR
|
||||
|
||||
def test_missing_pubKeyCredParams_alg(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=[{"type": "public-key"}])
|
||||
|
||||
assert e.value.code in [
|
||||
CtapError.ERR.MISSING_PARAMETER,
|
||||
CtapError.ERR.INVALID_CBOR,
|
||||
CtapError.ERR.UNSUPPORTED_ALGORITHM,
|
||||
]
|
||||
|
||||
@@ -143,7 +151,7 @@ def test_unsupported_algorithm(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=[{"alg": 1337, "type": "public-key"}])
|
||||
|
||||
assert e.value.code == CtapError.ERR.UNSUPPORTED_ALGORITHM
|
||||
assert e.value.code == CtapError.ERR.CBOR_UNEXPECTED_TYPE
|
||||
|
||||
def test_exclude_list(resetdevice):
|
||||
resetdevice.doMC(exclude_list=[{"id": b"1234", "type": "rot13"}])
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
"""
|
||||
|
||||
|
||||
from fido2.utils import sha256
|
||||
from fido2.client import CtapError
|
||||
from fido2.cose import ES256, ES384, ES512, EdDSA
|
||||
from utils import verify, ES256K
|
||||
import pytest
|
||||
|
||||
def test_authenticate(device):
|
||||
@@ -47,6 +48,17 @@ def test_empty_allowList(device):
|
||||
device.doGA(allow_list=[])
|
||||
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, ES256K.ALGORITHM, EdDSA.ALGORITHM]
|
||||
)
|
||||
def test_algorithms(device, info, alg):
|
||||
if ({'alg': alg, 'type': 'public-key'} in info.algorithms):
|
||||
MCRes = device.doMC(key_params=[{"alg": alg, "type": "public-key"}])
|
||||
res = device.GA(allow_list=[
|
||||
{"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
])
|
||||
verify(MCRes['res'].attestation_object, res['res'], res['req']['client_data_hash'])
|
||||
|
||||
def test_get_assertion_allow_list_filtering_and_buffering(device):
|
||||
""" Check that authenticator filters and stores items in allow list correctly """
|
||||
allow_list = []
|
||||
@@ -124,7 +136,6 @@ def test_missing_rp(device):
|
||||
assert e.value.code == CtapError.ERR.MISSING_PARAMETER
|
||||
|
||||
def test_bad_rp(device):
|
||||
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doGA(rp_id={"id": {"type": "wrong"}})
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
|
||||
|
||||
from fido2.webauthn import AttestedCredentialData
|
||||
from fido2.cose import CoseKey
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from fido2.utils import bytes2int, int2bytes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
import random
|
||||
import string
|
||||
import secrets
|
||||
@@ -175,3 +180,29 @@ class Timeout(object):
|
||||
if self.timer:
|
||||
self.timer.cancel()
|
||||
self.timer.join()
|
||||
|
||||
class ES256K(CoseKey):
|
||||
ALGORITHM = -47
|
||||
_HASH_ALG = hashes.SHA256()
|
||||
|
||||
def verify(self, message, signature):
|
||||
if self[-1] != 8:
|
||||
raise ValueError("Unsupported elliptic curve")
|
||||
ec.EllipticCurvePublicNumbers(
|
||||
bytes2int(self[-2]), bytes2int(self[-3]), ec.SECP256K1()
|
||||
).public_key(default_backend()).verify(
|
||||
signature, message, ec.ECDSA(self._HASH_ALG)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_cryptography_key(cls, public_key):
|
||||
pn = public_key.public_numbers()
|
||||
return cls(
|
||||
{
|
||||
1: 2,
|
||||
3: cls.ALGORITHM,
|
||||
-1: 8,
|
||||
-2: int2bytes(pn.x, 32),
|
||||
-3: int2bytes(pn.y, 32),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -23,7 +23,6 @@ import sys
|
||||
import argparse
|
||||
import platform
|
||||
from binascii import hexlify
|
||||
from words import words
|
||||
from threading import Event
|
||||
from typing import Mapping, Any, Optional, Callable
|
||||
import struct
|
||||
@@ -33,7 +32,7 @@ from enum import IntEnum, unique
|
||||
|
||||
try:
|
||||
from fido2.ctap2.config import Config
|
||||
from fido2.ctap2 import Ctap2
|
||||
from fido2.ctap2 import Ctap2, ClientPin, PinProtocolV2
|
||||
from fido2.hid import CtapHidDevice, CTAPHID
|
||||
from fido2.utils import bytes2int, int2bytes
|
||||
from fido2 import cbor
|
||||
@@ -58,14 +57,6 @@ except:
|
||||
from enum import IntEnum
|
||||
from binascii import hexlify
|
||||
|
||||
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
|
||||
from secure_key import windows as skey
|
||||
elif (platform.system() == 'Darwin'):
|
||||
from secure_key import macos as skey
|
||||
else:
|
||||
print('ERROR: platform not supported')
|
||||
sys.exit(-1)
|
||||
|
||||
def get_pki_data(url, data=None, method='GET'):
|
||||
user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; '
|
||||
'rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7'
|
||||
@@ -90,6 +81,7 @@ class VendorConfig(Config):
|
||||
class CMD(IntEnum):
|
||||
CONFIG_AUT_ENABLE = 0x03e43f56b34285e2
|
||||
CONFIG_AUT_DISABLE = 0x1831a40f04a25ed9
|
||||
CONFIG_VENDOR_PROTOTYPE = 0x7f
|
||||
|
||||
class RESP(IntEnum):
|
||||
KEY_AGREEMENT = 0x01
|
||||
@@ -99,7 +91,7 @@ class VendorConfig(Config):
|
||||
|
||||
def enable_device_aut(self, ct):
|
||||
self._call(
|
||||
Config.CMD.VENDOR_PROTOTYPE,
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PROTOTYPE,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_AUT_ENABLE,
|
||||
VendorConfig.PARAM.VENDOR_AUT_CT: ct
|
||||
@@ -108,7 +100,7 @@ class VendorConfig(Config):
|
||||
|
||||
def disable_device_aut(self):
|
||||
self._call(
|
||||
Config.CMD.VENDOR_PROTOTYPE,
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PROTOTYPE,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_AUT_DISABLE
|
||||
},
|
||||
@@ -230,7 +222,7 @@ class Vendor:
|
||||
self.__key_enc = None
|
||||
self.__iv = None
|
||||
|
||||
self.vcfg = VendorConfig(ctap)
|
||||
self.vcfg = VendorConfig(ctap, pin_uv_protocol=pin_uv_protocol, pin_uv_token=pin_uv_token)
|
||||
|
||||
def _call(self, cmd, sub_cmd, params=None):
|
||||
if params:
|
||||
@@ -252,6 +244,14 @@ class Vendor:
|
||||
return self.ctap.vendor(cmd, sub_cmd, params, pin_uv_protocol, pin_uv_param)
|
||||
|
||||
def backup_save(self, filename):
|
||||
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
|
||||
from secure_key import windows as skey
|
||||
elif (platform.system() == 'Darwin'):
|
||||
from secure_key import macos as skey
|
||||
else:
|
||||
print('ERROR: platform not supported')
|
||||
sys.exit(-1)
|
||||
from words import words
|
||||
ret = self._call(
|
||||
Vendor.CMD.VENDOR_BACKUP,
|
||||
Vendor.SUBCMD.ENABLE,
|
||||
@@ -270,6 +270,14 @@ class Vendor:
|
||||
print(f'{(c+1):02d} - {words[coef]}')
|
||||
|
||||
def backup_load(self, filename):
|
||||
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
|
||||
from secure_key import windows as skey
|
||||
elif (platform.system() == 'Darwin'):
|
||||
from secure_key import macos as skey
|
||||
else:
|
||||
print('ERROR: platform not supported')
|
||||
sys.exit(-1)
|
||||
from words import words
|
||||
d = 0
|
||||
if (d == 0):
|
||||
for c in range(24):
|
||||
@@ -349,6 +357,13 @@ class Vendor:
|
||||
)
|
||||
|
||||
def _get_key_device(self):
|
||||
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
|
||||
from secure_key import windows as skey
|
||||
elif (platform.system() == 'Darwin'):
|
||||
from secure_key import macos as skey
|
||||
else:
|
||||
print('ERROR: platform not supported')
|
||||
sys.exit(-1)
|
||||
return skey.get_secure_key()
|
||||
|
||||
def get_skey(self):
|
||||
@@ -381,6 +396,7 @@ class Vendor:
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
subparser = parser.add_subparsers(title="commands", dest="command")
|
||||
parser.add_argument('-p','--pin', help='Specify the PIN of the device.', required=True)
|
||||
parser_secure = subparser.add_parser('secure', help='Manages security of Pico Fido.')
|
||||
parser_secure.add_argument('subcommand', choices=['enable', 'disable', 'unlock'], help='Enables, disables or unlocks the security.')
|
||||
|
||||
@@ -426,15 +442,17 @@ def attestation(vdr, args):
|
||||
vdr.upload_ea(cert.public_bytes(Encoding.DER))
|
||||
|
||||
def main(args):
|
||||
print('Pico Fido Tool v1.4')
|
||||
print('Pico Fido Tool v1.6')
|
||||
print('Author: Pol Henarejos')
|
||||
print('Report bugs to https://github.com/polhenarejos/pico-fido/issues')
|
||||
print('')
|
||||
print('')
|
||||
|
||||
dev = next(CtapHidDevice.list_devices(), None)
|
||||
|
||||
vdr = Vendor(Ctap2Vendor(dev))
|
||||
ctap = Ctap2Vendor(dev)
|
||||
client_pin = ClientPin(ctap)
|
||||
token = client_pin.get_pin_token(args.pin, permissions=ClientPin.PERMISSION.AUTHENTICATOR_CFG)
|
||||
vdr = Vendor(ctap, pin_uv_protocol=PinProtocolV2(), pin_uv_token=token)
|
||||
|
||||
if (args.command == 'secure'):
|
||||
secure(vdr, args)
|
||||
|
||||
@@ -51,7 +51,9 @@ def get_secure_key():
|
||||
try:
|
||||
backend = get_backend(False)
|
||||
key = backend.get_password(DOMAIN, USERNAME)[0]
|
||||
except keyring.errors.KeyringError:
|
||||
if (key is None):
|
||||
raise TypeError
|
||||
except (keyring.errors.KeyringError, TypeError):
|
||||
try:
|
||||
key = generate_secure_key(False)[0] # It should be True, but secure enclave causes python segfault
|
||||
except keyring.errors.PasswordSetError:
|
||||
|
||||
44
tools/secure_key/windows.py
Normal file
44
tools/secure_key/windows.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import sys
|
||||
|
||||
DOMAIN = "PicoKeys.com"
|
||||
USERNAME = "Pico-Fido"
|
||||
|
||||
try:
|
||||
import keyring
|
||||
except:
|
||||
print('ERROR: keyring module not found! Install keyring package.\nTry with `pip install keyring`')
|
||||
sys.exit(-1)
|
||||
|
||||
try:
|
||||
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption, load_pem_private_key
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
except:
|
||||
print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`')
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
|
||||
def generate_secure_key():
|
||||
pkey = ec.generate_private_key(ec.SECP256R1())
|
||||
set_secure_key(pkey)
|
||||
return keyring.get_password(DOMAIN, USERNAME)
|
||||
|
||||
def get_d(key):
|
||||
return load_pem_private_key(key, password=None).private_numbers().private_value.to_bytes(32, 'big')
|
||||
|
||||
def set_secure_key(pk):
|
||||
try:
|
||||
keyring.delete_password(DOMAIN, USERNAME)
|
||||
except:
|
||||
pass
|
||||
keyring.set_password(DOMAIN, USERNAME, pk.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()).decode())
|
||||
|
||||
def get_secure_key():
|
||||
key = None
|
||||
try:
|
||||
key = keyring.get_password(DOMAIN, USERNAME)
|
||||
if (key is None):
|
||||
raise TypeError
|
||||
except (keyring.errors.KeyringError, TypeError):
|
||||
key = generate_secure_key()
|
||||
return get_d(key.encode())
|
||||
Reference in New Issue
Block a user