67 Commits
v2.8 ... v2.10

Author SHA1 Message Date
Pol Henarejos
5e0b0bfe38 Upgrading to Version 2.10.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-17 12:01:08 +01:00
Pol Henarejos
9ac5200792 Moving Pico HSM SDK pointer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-19 22:56:35 +01:00
Pol Henarejos
7542fad31c Upgrade to Version 2.10
Update README also

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-19 22:50:56 +01:00
Pol Henarejos
fe95093484 Upgrading to Version 2.10.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-15 00:42:38 +01:00
Pol Henarejos
483a0931a6 Update README with new enhancements and features
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-15 00:42:13 +01:00
Pol Henarejos
6644b500fa Add write and read large blob test.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-15 00:31:21 +01:00
Pol Henarejos
c8775ec69f Fix computing sha256 of large blob array.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-15 00:00:29 +01:00
Pol Henarejos
9160bbb8fe Write default large blob array.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-15 00:00:07 +01:00
Pol Henarejos
19dd52f944 Fix with required parameters.
LB required parameters are not at the begining of map.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 23:59:54 +01:00
Pol Henarejos
4c724d0e8b Fix offset parameter.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 23:51:30 +01:00
Pol Henarejos
81d3da2645 Activating LBW permission.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 23:51:17 +01:00
Pol Henarejos
9bcfacfe08 Fix calling large blobs.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 23:51:03 +01:00
Pol Henarejos
641c2fb880 Minor indent changes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:42:24 +01:00
Pol Henarejos
cb24927a80 Update get info command to add max large blob array length.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:42:09 +01:00
Pol Henarejos
4ddd45f16e Add ef to large blob array.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:41:48 +01:00
Pol Henarejos
f39a51afca Add macro for large blob size.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:41:38 +01:00
Pol Henarejos
aa4255b875 Add large blob command to cbor.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:41:19 +01:00
Pol Henarejos
a4d82136c2 Compile large blob command.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:40:14 +01:00
Pol Henarejos
4a3f957fdf Add initial large blob command.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:40:02 +01:00
Pol Henarejos
24b66dcffc Added some largeBlobKey tests.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-12 00:48:17 +01:00
Pol Henarejos
703e4697ec Fix loading large blob key from a credential id.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-12 00:39:49 +01:00
Pol Henarejos
839562130a Zeroize large blob key.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-12 00:37:56 +01:00
Pol Henarejos
e87ae34ab5 Adde largeBlobs to get info.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-12 00:31:05 +01:00
Pol Henarejos
2431812a18 Return largeBlobKey on cred management.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-12 00:16:17 +01:00
Pol Henarejos
81717135f5 Add test for credBlob extension.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-11 21:04:55 +01:00
Pol Henarejos
1d1d8ce5c3 Adding info test.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-11 21:04:35 +01:00
Pol Henarejos
6030f33977 Added more tests
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-11 00:07:22 +01:00
Pol Henarejos
360b8eadaa Add minimal test for minPinLength extension.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-10 21:36:17 +01:00
Pol Henarejos
0d51d3c727 Number of credentials is always returned in GA, as Pico Fido does not have any display.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-10 20:49:09 +01:00
Pol Henarejos
d0924f5ecc Some optimizations to speed up tests.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 19:02:23 +01:00
Pol Henarejos
866d69a82d CredMgmt must be redeclared everytime, since PinToken might be changed due to underlaying doMC.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 18:42:59 +01:00
Pol Henarejos
00ba0db87a Test fixes for credMgmt.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 16:34:12 +01:00
Pol Henarejos
c5644d14b0 Fix token precedence
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 16:28:03 +01:00
Pol Henarejos
2cf211cbd0 Fix clearing token rp link.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 16:27:41 +01:00
Pol Henarejos
50418113a9 Authenticator may return 1 number of credentials (not None).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 14:21:04 +01:00
Pol Henarejos
3a92238c0c Fix returning numberOfCredentials based on up and uv flags in the request (not in the response).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 14:20:42 +01:00
Pol Henarejos
270a54f3b7 Adding parenthesis for clearer statement
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 14:19:28 +01:00
Pol Henarejos
a8364c281b When doing GA, GET permission is necessary.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 13:33:51 +01:00
Pol Henarejos
5dcf89cd66 Fix critical bug caused by double free().
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 13:13:47 +01:00
Pol Henarejos
5c7be811e8 Return largeBlobKey on getAssertion if credential has largeKeyBlob and if requested.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 21:03:30 +01:00
Pol Henarejos
1707430593 Return largeBlobKey on makeCredential if requested.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 21:02:52 +01:00
Pol Henarejos
a151dc72e4 Embed largeBlobKey presence in credId.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 21:02:23 +01:00
Pol Henarejos
315f01372e Adding largeBlobKey in getInfo.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 21:02:08 +01:00
Pol Henarejos
860cca53e0 Added key derivation for large blob.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 17:07:16 +01:00
Pol Henarejos
d7016f6065 Add MAX_MSG_SIZE in getInfo.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 17:01:01 +01:00
Pol Henarejos
8e9eafaec5 Fix important potential buffer overflow deriving the credential key.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 16:35:19 +01:00
Pol Henarejos
1376c51528 Fix credProtect should not be returned on getAssertion.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:57:41 +01:00
Pol Henarejos
fa5926a3cc credBlob is returned on getAssertion if requested.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:55:27 +01:00
Pol Henarejos
710e03f5a6 Process credBlob on makeCredential.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:54:57 +01:00
Pol Henarejos
9d79505c5a Embed credBlob onto credId.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:54:10 +01:00
Pol Henarejos
4cb0af5045 Defining max length for credBlobs
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:48:46 +01:00
Pol Henarejos
196430517f Added credBlob in getInfo.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:07:02 +01:00
Pol Henarejos
083f9bc787 Moving HSM pointer to support EA.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:06:29 +01:00
Pol Henarejos
eb66ec3064 Upgrade to v2.8
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 11:15:44 +01:00
Pol Henarejos
e5834ff7c4 Upgrading to v2.6
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 11:07:43 +01:00
Pol Henarejos
3f1aba889e Adding algorithms to get info.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-01 20:13:28 +01:00
Pol Henarejos
58fbea8929 Added a flag (--filename) to upload an enterprise attestation certificate.
If this flag is not provided, an enteprise attestation certificate is automatically requested and uploaded.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-01 19:30:00 +01:00
Pol Henarejos
8bf53a6497 Return EA certificate if present.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-01 19:22:05 +01:00
Pol Henarejos
c89b044825 Added a subcommand to upload an enterprise certificate for enterprise attestation.
If present, when requested enterpriseAttestation==2 for MC, this certificate is returned in the x5c field. If not present, a batch attestation is returned.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-01 19:21:45 +01:00
Pol Henarejos
004073c3dd Adding FID for Enterprise certificate.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-01 19:20:19 +01:00
Pol Henarejos
bae8450a8d Added first step to Enterprise Attestation.
Once enabled, it allows to generate a CSR in the device, which is sent to our PKI. If valid, it returns a signed certificate by an intermediate CA that will be used for attestation.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-30 23:41:05 +01:00
Pol Henarejos
a355f87f82 Fix freeing memory on x509.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-30 23:37:34 +01:00
Pol Henarejos
b023668788 Moving pointer of HSM SDK (again).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:55:51 +01:00
Pol Henarejos
1b6d1e4b7f Moving pointer of HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:52:05 +01:00
Pol Henarejos
3bea6adf7a Fix requesting CM permission in credMgmt preview.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:50:23 +01:00
Pol Henarejos
54c2df3570 Fix cred RP enumeration return value.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:41:51 +01:00
Pol Henarejos
ae42e28384 Added support for credMgmt preview, despite this info is not broadcasted.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:27:52 +01:00
27 changed files with 776 additions and 167 deletions

View File

@@ -64,6 +64,7 @@ target_sources(pico_fido PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_cred_mgmt.c
${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
)
set(HSM_DRIVER "hid")
include(pico-hsm-sdk/pico_hsm_sdk_import.cmake)

View File

@@ -19,10 +19,14 @@ Pico FIDO has implemented the following features:
- Support for vendor Config
- Backup with 24 words
- Secure lock to protect the device from flash dumpings
- Permissions support (MC, GA, CM, ACFG)
- Permissions support (MC, GA, CM, ACFG, LBW)
- Authenticator configuration
- minPinLength extension
- Self attestation
- Enterprise attestation
- credBlobs extension
- largeBlobKey extension
- largeBlobs support (2048 bytes máx.)
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.
@@ -34,10 +38,6 @@ 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 VID/PID patcher as follows: `./pico-fido-patch-vidpid.sh VID:PID input_fido_file.uf2 output_fido_file.uf2`
You can use whatever VID/PID, but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
## Build
Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive.
@@ -52,6 +52,8 @@ Note that PICO_BOARD, USB_VID and USB_PID are optional. If not provided, pico bo
After make ends, the binary file pico_fido.uf2 will be generated. Put your pico board into loading mode, by pushing BOOTSEL button while pluging on, and copy the UF2 to the new fresh usb mass storage Pico device. Once copied, the pico mass storage will be disconnected automatically and the pico board will reset with the new firmware. A blinking led will indicate the device is ready to work.
**Remark:** Pico Fido uses HID interface and thus, VID/PID values are irrelevant in terms of operativity. You can safely use any arbitrary value or the default ones.
## Led blink
Pico FIDO uses the led to indicate the current status. Four states are available:
### Press to confirm

View File

@@ -1,7 +1,7 @@
#!/bin/bash
VERSION_MAJOR="2"
VERSION_MINOR="4"
VERSION_MINOR="10"
rm -rf release/*
cd build_release

View File

@@ -37,6 +37,7 @@ int cbor_selection();
int cbor_cred_mgmt(const uint8_t *data, size_t len);
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);
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")
@@ -64,10 +65,12 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
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)
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);

View File

@@ -31,7 +31,6 @@
#include "hsm.h"
#include "apdu.h"
uint8_t permissions_rp_id = 0, permission_set = 0;
uint32_t usage_timer = 0, initial_usage_time_limit = 0;
uint32_t max_usage_time_period = 600*1000;
bool needs_power_cycle = false;
@@ -63,11 +62,11 @@ void clearPinUvAuthTokenPermissionsExceptLbw() {
}
void stopUsingPinUvAuthToken() {
permissions_rp_id = 0;
paut.permissions = 0;
usage_timer = 0;
paut.in_use = false;
memset(paut.rp_id_hash, 0, sizeof(paut.rp_id_hash));
paut.has_rp_id = false;
initial_usage_time_limit = 0;
paut.user_present = paut.user_verified = false;
user_present_time_limit = 0;
@@ -486,7 +485,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
if (subcommand == 0x9) {
if (permissions == 0)
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
if ((permissions & CTAP_PERMISSION_BE) || (permissions & CTAP_PERMISSION_LBW)) // Not supported yet
if ((permissions & CTAP_PERMISSION_BE)) // Not supported yet
CBOR_ERROR(CTAP2_ERR_UNAUTHORIZED_PERMISSION);
}
@@ -547,7 +546,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
mbedtls_sha256((uint8_t *)rpId.data, rpId.len, paut.rp_id_hash, 0);
paut.has_rp_id = true;
}
uint8_t pinUvAuthToken_enc[32+IV_SIZE];
else
paut.has_rp_id = false;
uint8_t pinUvAuthToken_enc[32 + IV_SIZE];
encrypt(pinUvAuthProtocol, sharedSecret, paut.data, 32, pinUvAuthToken_enc);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));

View File

@@ -95,7 +95,7 @@ int cbor_config(const uint8_t *data, size_t len) {
else if (val_u == 0x03) {
CBOR_FIELD_GET_UINT(pinUvAuthProtocol, 1);
}
else if (val_u == 0x04) { // pubKeyCredParams
else if (val_u == 0x04) {
CBOR_FIELD_GET_BYTES(pinUvAuthParam, 1);
}
}
@@ -204,12 +204,11 @@ int cbor_config(const uint8_t *data, size_t len) {
CBOR_FREE_BYTE_STRING(minPinLengthRPIDs[i]);
}
if (error != CborNoError)
{
if (error == CborErrorImproperValue)
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
return error;
}
if (error != CborNoError) {
if (error == CborErrorImproperValue)
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
return error;
}
res_APDU_size = resp_size;
return 0;
}

View File

@@ -43,7 +43,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CborEncoder encoder, mapEncoder, mapEncoder2;
uint8_t *raw_subpara = NULL;
size_t raw_subpara_len = 0;
bool asserted = false;
bool asserted = false, is_preview = *(data - 1) == 0x41; // Backwards compatibility
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
uint64_t val_c = 1;
@@ -115,7 +115,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
if(subcommand == 0x01) {
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *)"\x01", 1, pinUvAuthParam.data) != CborNoError)
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true))
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
uint8_t existing = 0;
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
@@ -133,7 +133,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
if (subcommand == 0x02) {
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *)"\x02", 1, pinUvAuthParam.data) != CborNoError)
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true))
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
rp_counter = 1;
rp_total = 0;
@@ -156,14 +156,14 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
rp_total++;
}
}
if (rp_ef == NULL) // should not happen
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
if (rp_ef == NULL)
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
rp_counter++;
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, subcommand == 0x02 ? 3 : 2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 1));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, file_get_data(rp_ef)+33, file_get_size(rp_ef)-33));
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, (char *)file_get_data(rp_ef)+33, file_get_size(rp_ef)-33));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(rp_ef)+1, 32));
@@ -179,7 +179,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
*(raw_subpara-1) = 0x04;
if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError)
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)))
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
cred_counter = 1;
cred_total = 0;
@@ -221,10 +221,20 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
cred_counter++;
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, subcommand == 0x04 ? 5 : 4));
uint8_t l = 4;
if (subcommand == 0x04)
l++;
if (cred.extensions.present == true) {
if (cred.extensions.credProtect > 0)
l++;
if (cred.extensions.largeBlobKey == ptrue)
l++;
}
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x06));
uint8_t l = 0;
l = 0;
if (cred.userId.present == true)
l++;
if (cred.userName.present == true)
@@ -259,7 +269,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
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, -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));
@@ -279,8 +289,22 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
asserted = true;
rpIdHashx = rpIdHash;
}
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0A));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, cred.extensions.credProtect));
if (cred.extensions.present == true) {
if (cred.extensions.credProtect > 0) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0A));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, cred.extensions.credProtect));
}
if (cred.extensions.largeBlobKey == ptrue) {
uint8_t largeBlobKey[32];
int ret = credential_derive_large_blob_key(cred.id.data, cred.id.len, largeBlobKey);
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0B));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
mbedtls_platform_zeroize(largeBlobKey, sizeof(largeBlobKey));
}
}
credential_free(&cred);
mbedtls_ecdsa_free(&key);
}
@@ -290,7 +314,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
*(raw_subpara - 1) = 0x06;
if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError)
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)))
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *ef = search_dynamic_file(EF_CRED + i);
@@ -324,7 +348,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
*(raw_subpara - 1) = 0x07;
if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError)
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)))
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *ef = search_dynamic_file(EF_CRED + i);

View File

@@ -85,9 +85,10 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
Credential creds[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
size_t allowList_len = 0, creds_len = 0;
uint8_t *aut_data = NULL;
bool asserted = false;
bool asserted = false, up = true, uv = false;
int64_t kty = 2, alg = 0, crv = 0;
CborByteString kax = {0}, kay = {0}, salt_enc = {0}, salt_auth = {0};
const bool *credBlob = NULL;
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
uint64_t val_c = 1;
@@ -173,7 +174,8 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_PARSE_MAP_END(_f2, 3);
continue;
}
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", extensions.credProtect);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "credBlob", credBlob);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "largeBlobKey", extensions.largeBlobKey);
CBOR_ADVANCE(2);
}
CBOR_PARSE_MAP_END(_f1, 2);
@@ -237,6 +239,10 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
//else if (options.up == NULL) //5.7
//rup = ptrue;
if (options.uv != NULL)
uv = *options.uv;
if (options.up != NULL)
up = *options.up;
}
if (pinUvAuthParam.present == true) { //6.1
@@ -332,7 +338,11 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
clearPinUvAuthTokenPermissionsExceptLbw();
}
if (!(flags & FIDO2_AUT_FLAG_UP) && !(flags & FIDO2_AUT_FLAG_UV)) {
if (extensions.largeBlobKey == pfalse) {
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
}
if (up == false && uv == false) {
selcred = &creds[0];
}
else {
@@ -368,6 +378,14 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
}
uint8_t largeBlobKey[32];
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey);
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
}
size_t ext_len = 0;
uint8_t ext [512];
if (extensions.present == true) {
@@ -377,12 +395,15 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
extensions.hmac_secret = NULL;
if (extensions.hmac_secret != NULL)
l++;
if (extensions.credProtect != 0)
if (credBlob == ptrue)
l++;
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
if (extensions.credProtect != 0) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, extensions.credProtect));
if (credBlob == ptrue) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
if (selcred->extensions.credBlob.present == true)
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, selcred->extensions.credBlob.data, selcred->extensions.credBlob.len));
else
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, NULL, 0));
}
if (extensions.hmac_secret != NULL) {
@@ -464,7 +485,9 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
uint8_t lfields = 3;
if (selcred->opts.present == true && selcred->opts.rk == ptrue)
lfields++;
if (numberOfCredentials > 1 && next == false && !(flags & FIDO2_AUT_FLAG_UP) && !(flags & FIDO2_AUT_FLAG_UV))
if (numberOfCredentials > 1 && next == false)
lfields++;
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue)
lfields++;
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, lfields));
@@ -506,10 +529,15 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
}
if (numberOfCredentials > 1 && next == false && !(flags & FIDO2_AUT_FLAG_UP) && !(flags & FIDO2_AUT_FLAG_UV)) {
if (numberOfCredentials > 1 && next == false) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, numberOfCredentials));
}
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
}
mbedtls_platform_zeroize(largeBlobKey, sizeof(largeBlobKey));
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
ctr++;

View File

@@ -23,10 +23,10 @@
#include "version.h"
int cbor_get_info() {
CborEncoder encoder, mapEncoder, arrayEncoder;
CborEncoder encoder, mapEncoder, arrayEncoder, mapEncoder2;
CborError error = CborNoError;
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 11));
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 15));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 3));
@@ -36,9 +36,11 @@ 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, 3));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 5));
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_encoder_close_container(&mapEncoder, &arrayEncoder));
@@ -46,7 +48,7 @@ int cbor_get_info() {
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, aaguid, sizeof(aaguid)));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 7));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 8));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "ep"));
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, get_opts() & FIDO2_OPT_EA));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "rk"));
@@ -60,12 +62,17 @@ int cbor_get_info() {
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
else
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, false));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "largeBlobs"));
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "pinUvAuthToken"));
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "setMinPINLength"));
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_MSG_SIZE));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x06));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 2));
CBOR_CHECK(cbor_encode_uint(&arrayEncoder, 1)); // PIN protocols
@@ -78,6 +85,31 @@ int cbor_get_info() {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
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_close_container(&mapEncoder, &arrayEncoder));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0B));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_LARGE_BLOB_SIZE)); // maxSerializedLargeBlobArray
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1)
@@ -93,6 +125,9 @@ int cbor_get_info() {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0E));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, PICO_FIDO_VERSION)); // firmwareVersion
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0F));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CREDBLOB_LENGTH)); // maxCredBlobLength
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x15));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 2));
CBOR_CHECK(cbor_encode_uint(&arrayEncoder, CTAP_CONFIG_AUT_ENABLE));

150
src/fido/cbor_large_blobs.c Normal file
View File

@@ -0,0 +1,150 @@
/*
* 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 "ctap2_cbor.h"
#include "fido.h"
#include "ctap.h"
#include "files.h"
#include "apdu.h"
#include "version.h"
#include "hsm.h"
#include "mbedtls/sha256.h"
static uint64_t expectedLength = 0, expectedNextOffset = 0;
uint8_t temp_lba[MAX_LARGE_BLOB_SIZE];
int cbor_large_blobs(const uint8_t *data, size_t len) {
CborParser parser;
CborValue map;
CborEncoder encoder, mapEncoder;
CborError error = CborNoError;
uint64_t get = 0, offset = UINT64_MAX, length = 0, pinUvAuthProtocol = 0;
CborByteString set = {0}, pinUvAuthParam = {0};
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
uint64_t val_c = 1;
CBOR_PARSE_MAP_START(map, 1) {
uint64_t val_u = 0;
CBOR_FIELD_GET_UINT(val_u, 1);
if (val_c <= 0 && val_c != val_u)
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (val_u < val_c)
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
val_c = val_u + 1;
if (val_u == 0x01) {
CBOR_FIELD_GET_UINT(get, 1);
}
else if (val_u == 0x02) {
CBOR_FIELD_GET_BYTES(set, 1);
}
else if (val_u == 0x03) {
CBOR_FIELD_GET_UINT(offset, 1);
}
else if (val_u == 0x04) {
CBOR_FIELD_GET_UINT(length, 1);
}
else if (val_u == 0x05) {
CBOR_FIELD_GET_BYTES(pinUvAuthParam, 1);
}
else if (val_u == 0x06) {
CBOR_FIELD_GET_UINT(pinUvAuthProtocol, 1);
}
}
CBOR_PARSE_MAP_END(map, 1);
if (offset == UINT64_MAX)
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
if (get == 0 && set.present == false)
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
if (get != 0 && set.present == true)
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
if (get > 0) {
if (length != 0)
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
if (length > MAX_FRAGMENT_LENGTH)
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
if (offset > file_get_size(ef_largeblob))
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(ef_largeblob)+offset, MIN(get, file_get_size(ef_largeblob)-offset)));
}
else {
if (set.len > MAX_FRAGMENT_LENGTH)
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
if (offset == 0) {
if (length == 0)
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
if (length > MAX_LARGE_BLOB_SIZE) {
CBOR_ERROR(CTAP2_ERR_LARGE_BLOB_STORAGE_FULL);
}
if (length < 17) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
expectedLength = length;
expectedNextOffset = 0;
}
else {
if (length != 0)
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (offset != expectedNextOffset)
CBOR_ERROR(CTAP1_ERR_INVALID_SEQ);
if (pinUvAuthParam.present == false)
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
if (pinUvAuthProtocol == 0)
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
uint8_t verify_data[70] = {0};
memset(verify_data, 0xff, 32);
verify_data[32] = 0x0C;
verify_data[34] = offset & 0xff;
verify_data[35] = offset >> 8;
verify_data[36] = offset >> 16;
verify_data[37] = offset >> 24;
mbedtls_sha256(set.data, set.len, verify_data+38, 0);
if (verify(pinUvAuthProtocol, paut.data, verify_data, sizeof(verify_data), pinUvAuthParam.data) != 0)
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (!(paut.permissions & CTAP_PERMISSION_LBW))
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (offset+set.len > expectedLength)
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
if (offset == 0)
memset(temp_lba, 0, sizeof(temp_lba));
memcpy(temp_lba+expectedNextOffset, set.data, set.len);
expectedNextOffset += set.len;
if (expectedNextOffset == expectedLength) {
uint8_t sha[32];
mbedtls_sha256(temp_lba, expectedLength-16, sha, 0);
if (expectedLength > 17 && memcmp(sha, temp_lba+expectedLength-16, 16) != 0)
CBOR_ERROR(CTAP2_ERR_INTEGRITY_FAILURE);
flash_write_data_to_file(ef_largeblob, temp_lba, expectedLength);
low_flash_available();
}
goto err;
}
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
err:
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
CBOR_FREE_BYTE_STRING(set);
if (error != CborNoError)
return -CTAP2_ERR_INVALID_CBOR;
res_APDU_size = cbor_encoder_get_buffer_size(&encoder, res_APDU + 1);
return 0;
}

View File

@@ -119,6 +119,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
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_ADVANCE(2);
}
CBOR_PARSE_MAP_END(_f1, 2);
@@ -238,10 +240,14 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
if (strcmp(excludeList[e].type.data, "public-key") != 0)
continue;
Credential ecred;
if (credential_load(excludeList[e].id.data, excludeList[e].id.len, rp_id_hash, &ecred) == 0 && (ecred.extensions.credProtect != CRED_PROT_UV_REQUIRED || flags & FIDO2_AUT_FLAG_UV))
if (credential_load(excludeList[e].id.data, excludeList[e].id.len, rp_id_hash, &ecred) == 0 && (ecred.extensions.credProtect != CRED_PROT_UV_REQUIRED || (flags & FIDO2_AUT_FLAG_UV)))
CBOR_ERROR(CTAP2_ERR_CREDENTIAL_EXCLUDED);
}
if (extensions.largeBlobKey == pfalse || (extensions.largeBlobKey == ptrue && options.rk != ptrue)) {
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
}
if (options.up == ptrue || options.up == NULL) { //14.1
if (pinUvAuthParam.present == true) {
if (getUserPresentFlagValue() == false) {
@@ -289,7 +295,13 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
}
}
if (extensions.credBlob.present == true)
l++;
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
if (extensions.credBlob.present == true) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, extensions.credBlob.len < MAX_CREDBLOB_LENGTH));
}
if (extensions.credProtect != 0) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, extensions.credProtect));
@@ -377,8 +389,16 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
ret = mbedtls_ecdsa_write_signature(&ekey, MBEDTLS_MD_SHA256, hash, 32, sig, sizeof(sig), &olen, random_gen, NULL);
mbedtls_ecdsa_free(&ekey);
uint8_t largeBlobKey[32];
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
ret = credential_derive_large_blob_key(cred_id, cred_id_len, largeBlobKey);
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
}
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 4));
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, extensions.largeBlobKey == ptrue && options.rk == ptrue ? 5 : 4));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "packed"));
@@ -393,15 +413,26 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, sig, olen));
if (self_attestation == false) {
CborEncoder arrEncoder;
file_t *ef_cert = NULL;
if (enterpriseAttestation == 2)
ef_cert = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
if (!file_has_data(ef_cert))
ef_cert = ef_certdev;
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "x5c"));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder2, &arrEncoder, 1));
CBOR_CHECK(cbor_encode_byte_string(&arrEncoder, file_get_data(ef_certdev), file_get_size(ef_certdev)));
CBOR_CHECK(cbor_encode_byte_string(&arrEncoder, file_get_data(ef_cert), file_get_size(ef_cert)));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder2, &arrEncoder));
}
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, enterpriseAttestation == 2));
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
}
mbedtls_platform_zeroize(largeBlobKey, sizeof(largeBlobKey));
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);

View File

@@ -26,6 +26,7 @@
#include "mbedtls/ecdh.h"
#include "mbedtls/chachapoly.h"
#include "mbedtls/hkdf.h"
#include "mbedtls/x509_csr.h"
extern uint8_t keydev_dec[32];
extern bool has_keydev_dec;
@@ -123,7 +124,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
}
else if (vendorCmd == 0x02) {
if (vendorParam.present == false)
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
uint8_t zeros[32];
memset(zeros, 0, sizeof(zeros));
flash_write_data_to_file(ef_keydev_enc, vendorParam.data, vendorParam.len);
@@ -226,6 +227,54 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
has_keydev_dec = true;
goto err;
}
else if (cmd == CTAP_VENDOR_EA) {
if (vendorCmd == 0x01) {
uint8_t buffer[1024];
mbedtls_ecdsa_context ekey;
mbedtls_ecdsa_init(&ekey);
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), file_get_size(ef_keydev));
if (ret != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
ret = mbedtls_ecp_mul(&ekey.grp, &ekey.Q, &ekey.d, &ekey.grp.G, random_gen, NULL);
if (ret != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
pico_unique_board_id_t rpiid;
pico_get_unique_board_id(&rpiid);
mbedtls_x509write_csr ctx;
mbedtls_x509write_csr_init(&ctx);
snprintf((char *)buffer, sizeof(buffer), "C=ES,O=Pico Keys,OU=Authenticator Attestation,CN=Pico Fido EE Serial %llu", ((uint64_t)rpiid.id[0] << 56) | ((uint64_t)rpiid.id[1] << 48) | ((uint64_t)rpiid.id[2] << 40) | ((uint64_t)rpiid.id[3] << 32) | (rpiid.id[4] << 24) | (rpiid.id[5] << 16) | (rpiid.id[6] << 8) | rpiid.id[7]);
mbedtls_x509write_csr_set_subject_name(&ctx, (char *)buffer);
mbedtls_pk_context key;
mbedtls_pk_init(&key);
mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
key.pk_ctx = &ekey;
mbedtls_x509write_csr_set_key(&ctx, &key);
mbedtls_x509write_csr_set_md_alg(&ctx, MBEDTLS_MD_SHA256);
mbedtls_x509write_csr_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xE5\x1C\x01\x01\x04", 0xB, 0, aaguid, sizeof(aaguid));
ret = mbedtls_x509write_csr_der(&ctx, buffer, sizeof(buffer), random_gen, NULL);
mbedtls_ecdsa_free(&ekey);
if (ret <= 0) {
mbedtls_x509write_csr_free(&ctx);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, buffer + sizeof(buffer) - ret, ret));
}
else if (vendorCmd == 0x02) {
if (vendorParam.present == false)
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
file_t *ef_ee_ea = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
if (ef_ee_ea)
flash_write_data_to_file(ef_ee_ea, vendorParam.data, vendorParam.len);
low_flash_available();
goto err;
}
}
else
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));

View File

@@ -60,6 +60,10 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
if (extensions->present == true) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, CborIndefiniteLength));
if (extensions->credBlob.present == true && extensions->credBlob.len < MAX_CREDBLOB_LENGTH) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "credBlob"));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, extensions->credBlob.data, extensions->credBlob.len));
}
if (extensions->credProtect != 0) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "credProtect"));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, extensions->credProtect));
@@ -68,6 +72,10 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "hmac-secret"));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, *extensions->hmac_secret));
}
if (extensions->largeBlobKey == ptrue) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "largeBlobKey"));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, true));
}
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
}
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
@@ -155,6 +163,8 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
CBOR_FIELD_GET_KEY_TEXT(2);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "hmac-secret", cred->extensions.hmac_secret);
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_ADVANCE(2);
}
CBOR_PARSE_MAP_END(_f1, 2);
@@ -309,10 +319,24 @@ int credential_derive_chacha_key(uint8_t *outk) {
int r = 0;
if ((r = load_keydev(outk)) != 0)
return r;
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"SLIP-0022", 9, outk);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)CRED_PROTO, 4, outk);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"Encryption key", 14, outk);
return 0;
}
int credential_derive_large_blob_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk) {
memset(outk, 0, 32);
int r = 0;
if ((r = load_keydev(outk)) != 0)
return r;
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"SLIP-0022", 9, outk);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)CRED_PROTO, 4, outk);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"largeBlobKey", 12, outk);
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
return 0;
}

View File

@@ -31,6 +31,8 @@ typedef struct CredExtensions {
const bool *hmac_secret;
uint64_t credProtect;
const bool *minPinLength;
CborByteString credBlob;
const bool *largeBlobKey;
bool present;
} CredExtensions;
@@ -62,5 +64,6 @@ extern void credential_free(Credential *cred);
extern int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash);
extern int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, Credential *cred);
extern int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk);
extern int credential_derive_large_blob_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk);
#endif // _CREDENTIAL_H_

View File

@@ -114,6 +114,7 @@ typedef struct {
#define CTAP_GET_NEXT_ASSERTION 0x08
#define CTAP_CREDENTIAL_MGMT 0x0A
#define CTAP_SELECTION 0x0B
#define CTAP_LARGE_BLOBS 0x0C
#define CTAP_CONFIG 0x0D
#define CTAP_CONFIG_AUT_ENABLE 0x03e43f56b34285e2
@@ -124,6 +125,7 @@ typedef struct {
#define CTAP_VENDOR_BACKUP 0x01
#define CTAP_VENDOR_MSE 0x02
#define CTAP_VENDOR_UNLOCK 0x03
#define CTAP_VENDOR_EA 0x04
#define CTAP_PERMISSION_MC 0x01 // MakeCredential
#define CTAP_PERMISSION_GA 0x02 // GetAssertion

View File

@@ -116,6 +116,8 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
mbedtls_x509write_crt_set_authority_key_identifier(&ctx);
mbedtls_x509write_crt_set_key_usage(&ctx, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN);
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, core1 ? random_gen : random_gen_core0, NULL);
/* pk cannot be freed, as it is freed later */
//mbedtls_pk_free(&key);
return ret;
}
@@ -241,11 +243,15 @@ int scan_files(bool core1) {
mbedtls_ecdsa_context key;
mbedtls_ecdsa_init(&key);
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, file_get_data(ef_keydev), file_get_size(ef_keydev));
if (ret != 0)
if (ret != 0) {
mbedtls_ecdsa_free(&key);
return ret;
}
ret = mbedtls_ecp_mul(&key.grp, &key.Q, &key.d, &key.grp.G, core1 ? random_gen : random_gen_core0, NULL);
if (ret != 0)
if (ret != 0) {
mbedtls_ecdsa_free(&key);
return ret;
}
ret = x509_create_cert(&key, cert, sizeof(cert), core1);
mbedtls_ecdsa_free(&key);
if (ret <= 0)
@@ -283,6 +289,10 @@ int scan_files(bool core1) {
else {
printf("FATAL ERROR: Auth Token not found in memory!\r\n");
}
ef_largeblob = search_by_fid(EF_LARGEBLOB, NULL, SPECIFY_EF);
if (!file_has_data(ef_largeblob)) {
flash_write_data_to_file(ef_largeblob, (const uint8_t *)"\x80\x76\xbe\x8b\x52\x8d\x00\x75\xf7\xaa\xe9\x8d\x6f\xa5\x7a\x6d\x3c", 17);
}
low_flash_available();
return CCID_OK;
}

View File

@@ -78,6 +78,10 @@ extern void set_opts(uint8_t);
#define MAX_CREDENTIAL_COUNT_IN_LIST 16
#define MAX_CRED_ID_LENGTH 1024
#define MAX_RESIDENT_CREDENTIALS 256
#define MAX_CREDBLOB_LENGTH 128
#define MAX_MSG_SIZE 1024
#define MAX_FRAGMENT_LENGTH (MAX_MSG_SIZE - 64)
#define MAX_LARGE_BLOB_SIZE 2048
typedef struct known_app {
const uint8_t *rp_id_hash;

View File

@@ -23,11 +23,13 @@ file_t file_entries[] = {
{.fid = EF_KEY_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Device Key
{.fid = EF_KEY_DEV_ENC, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Device Key Enc
{.fid = EF_EE_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // End Entity Certificate Device
{.fid = EF_EE_DEV_EA, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // End Entity Enterprise Attestation Certificate
{.fid = EF_COUNTER, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Global counter
{.fid = EF_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // PIN
{.fid = EF_AUTHTOKEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // AUTH TOKEN
{.fid = EF_MINPINLEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // MIN PIN LENGTH
{.fid = EF_OPTS, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Global options
{.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 = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = {0} } //end
};
@@ -39,3 +41,4 @@ file_t *ef_counter = NULL;
file_t *ef_pin = NULL;
file_t *ef_authtoken = NULL;
file_t *ef_keydev_enc = NULL;
file_t *ef_largeblob = NULL;

View File

@@ -23,6 +23,7 @@
#define EF_KEY_DEV 0xCC00
#define EF_KEY_DEV_ENC 0xCC01
#define EF_EE_DEV 0xCE00
#define EF_EE_DEV_EA 0xCE01
#define EF_COUNTER 0xC000
#define EF_OPTS 0xC001
#define EF_PIN 0x1080
@@ -30,6 +31,7 @@
#define EF_MINPINLEN 0x1100
#define EF_CRED 0xCF00 // Creds at 0xCF00 - 0xCFFF
#define EF_RP 0xD000 // RPs at 0xD000 - 0xD0FF
#define EF_LARGEBLOB 0x1101 // Large Blob Array
extern file_t *ef_keydev;
extern file_t *ef_certdev;
@@ -37,5 +39,6 @@ extern file_t *ef_counter;
extern file_t *ef_pin;
extern file_t *ef_authtoken;
extern file_t *ef_keydev_enc;
extern file_t *ef_largeblob;
#endif //_FILES_H_

View File

@@ -18,7 +18,7 @@
#ifndef __VERSION_H_
#define __VERSION_H_
#define PICO_FIDO_VERSION 0x0204
#define PICO_FIDO_VERSION 0x020A
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)

View File

@@ -296,7 +296,7 @@ class Device():
def doGA(self, client_data=Ellipsis, rp_id=Ellipsis, allow_list=None, extensions=None, user_verification=None, event=None, ctap1=False, check_only=False):
client_data = client_data if client_data is not Ellipsis else CollectedClientData.create(
type=CollectedClientData.TYPE.CREATE, origin=self.__origin, challenge=os.urandom(32)
type=CollectedClientData.TYPE.GET, origin=self.__origin, challenge=os.urandom(32)
)
rp_id = rp_id if rp_id is not Ellipsis else self.__rp['id']
if (ctap1 is True):

View File

@@ -76,7 +76,7 @@ def test_get_assertion_allow_list_filtering_and_buffering(device):
len(rp2_assertions)
)
assert counts in [(None, None), (l1, l2)]
assert counts in [(1, 1), (l1, l2)]
def test_corrupt_credId(device, MCRes):
# apply bit flip

View File

@@ -0,0 +1,130 @@
import pytest
from fido2.ctap import CtapError
from fido2.ctap2.pin import PinProtocolV2, ClientPin
from utils import verify
import os
PIN='12345678'
SMALL_BLOB=b"A"*32
LARGE_BLOB=b"B"*1024
@pytest.fixture(scope="function")
def MCCredBlob(device):
res = device.doMC(extensions={'credBlob': SMALL_BLOB})['res'].attestation_object
return res
@pytest.fixture(scope="function")
def GACredBlob(device, MCCredBlob):
res = device.doGA(allow_list=[
{"id": MCCredBlob.auth_data.credential_data.credential_id, "type": "public-key"}
], extensions={'getCredBlob': True})
assertions = res['res'].get_assertions()
for a in assertions:
verify(MCCredBlob, a, res['req']['client_data'].hash)
return assertions[0]
@pytest.fixture(scope="function")
def MCLBK(device):
res = device.doMC(
rk=True,
extensions={'largeBlob':{'support':'required'}}
)['res']
return res
@pytest.fixture(scope="function")
def GALBRead(device, MCLBK):
res = device.doGA(
allow_list=[
{"id": MCLBK.attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
],extensions={'largeBlob':{'read': True}}
)
assertions = res['res'].get_assertions()
for a in assertions:
verify(MCLBK.attestation_object, a, res['req']['client_data'].hash)
return res['res']
@pytest.fixture(scope="function")
def GALBReadLBK(GALBRead):
return GALBRead.get_assertions()[0]
@pytest.fixture(scope="function")
def GALBReadLB(GALBRead):
return GALBRead.get_response(0)
@pytest.fixture(scope="function")
def GALBWrite(device, MCLBK):
res = device.doGA(
allow_list=[
{"id": MCLBK.attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
],extensions={'largeBlob':{'write': LARGE_BLOB}}
)
assertions = res['res'].get_assertions()
for a in assertions:
verify(MCLBK.attestation_object, a, res['req']['client_data'].hash)
return res['res'].get_response(0)
def test_supports_credblob(info):
assert info.extensions
assert 'credBlob' in info.extensions
assert info.max_cred_blob_length
assert info.max_cred_blob_length > 0
def test_mc_credblob(MCCredBlob):
assert MCCredBlob.auth_data.extensions
assert "credBlob" in MCCredBlob.auth_data.extensions
assert MCCredBlob.auth_data.extensions['credBlob'] is True
def test_ga_credblob(GACredBlob):
assert GACredBlob.auth_data.extensions
assert "credBlob" in GACredBlob.auth_data.extensions
assert GACredBlob.auth_data.extensions['credBlob'] == SMALL_BLOB
def test_wrong_credblob(device, info):
device.reset()
cdh = os.urandom(32)
ClientPin(device.client()._backend.ctap2).set_pin(PIN)
pin_token = ClientPin(device.client()._backend.ctap2).get_pin_token(PIN, permissions=ClientPin.PERMISSION.MAKE_CREDENTIAL | ClientPin.PERMISSION.AUTHENTICATOR_CFG)
protocol = PinProtocolV2()
MC = device.MC(
client_data_hash=cdh,
extensions={'credBlob': b'A'*(info.max_cred_blob_length+1)},
pin_uv_protocol=protocol.VERSION,
pin_uv_param=protocol.authenticate(pin_token, cdh)
)['res']
assert MC.auth_data.extensions
assert "credBlob" in MC.auth_data.extensions
assert MC.auth_data.extensions['credBlob'] is False
res = device.doGA(allow_list=[
{"id": MC.auth_data.credential_data.credential_id, "type": "public-key"}
], extensions={'getCredBlob': True})
assertions = res['res'].get_assertions()
for a in assertions:
verify(MC, a, res['req']['client_data'].hash)
assert assertions[0].auth_data.extensions
assert "credBlob" in assertions[0].auth_data.extensions
assert len(assertions[0].auth_data.extensions['credBlob']) == 0
def test_supports_largeblobs(info):
assert info.extensions
assert 'largeBlobKey' in info.extensions
assert 'largeBlobs' in info.options
assert info.max_large_blob is None or (info.max_large_blob > 1024)
def test_get_largeblobkey_mc(MCLBK):
assert 'supported' in MCLBK.extension_results
assert MCLBK.extension_results['supported'] is True
def test_get_largeblobkey_ga(GALBReadLBK):
assert GALBReadLBK.large_blob_key is not None
def test_get_largeblob_rw(GALBWrite, GALBReadLB):
assert 'written' in GALBWrite.extension_results
assert GALBWrite.extension_results['written'] is True
assert 'blob' in GALBReadLB.extension_results
assert GALBReadLB.extension_results['blob'] == LARGE_BLOB

View File

@@ -3,76 +3,78 @@ import time
import random
from fido2.ctap import CtapError
from fido2.ctap2 import CredentialManagement
from fido2.utils import sha256, hmac_sha256
from fido2.ctap2.pin import PinProtocolV2
from fido2.utils import sha256
from fido2.ctap2.pin import PinProtocolV2, ClientPin
from binascii import hexlify
from utils import generate_random_user
PIN = "12345678"
@pytest.fixture(params=[PIN], scope = 'function')
def PinToken(request, device, client_pin):
def client_pin_set(request, device, client_pin):
#device.reboot()
device.reset()
pin = request.param
client_pin.set_pin(pin)
return client_pin.get_pin_token(pin)
@pytest.fixture(scope = 'function')
def MC_RK_Res(device, PinToken):
def MC_RK_Res(device, client_pin_set):
rp = {"id": "ssh:", "name": "Bate Goiko"}
device.doMC(rp=rp, rk=True)
rp = {"id": "xakcop.com", "name": "John Doe"}
device.doMC(rp=rp, rk=True)
def PinToken(device):
return ClientPin(device.client()._backend.ctap2).get_pin_token(PIN, permissions=ClientPin.PERMISSION.MAKE_CREDENTIAL | ClientPin.PERMISSION.CREDENTIAL_MGMT)
@pytest.fixture(scope = 'function')
def CredMgmt(device, PinToken):
def CredMgmt(device):
pt = PinToken(device)
pin_protocol = PinProtocolV2()
return CredentialManagement(device.client()._backend.ctap2, pin_protocol, PinToken)
return CredentialManagement(device.client()._backend.ctap2, pin_protocol, pt)
def _test_enumeration(CredMgmt, rp_map):
def _test_enumeration(device, rp_map):
"Enumerate credentials using BFS"
res = CredMgmt.enumerate_rps()
credMgmt = CredMgmt(device)
res = credMgmt.enumerate_rps()
assert len(rp_map.keys()) == len(res)
for rp in res:
creds = CredMgmt.enumerate_creds(sha256(rp[3]["id"]))
assert len(creds) == rp_map[rp[3]["id"].decode()]
creds = credMgmt.enumerate_creds(sha256(rp[3]["id"].encode()))
assert len(creds) == rp_map[rp[3]["id"]]
def _test_enumeration_interleaved(CredMgmt, rp_map):
def _test_enumeration_interleaved(device, rp_map):
"Enumerate credentials using DFS"
first_rp = CredMgmt.enumerate_rps_begin()
credMgmt = CredMgmt(device)
first_rp = credMgmt.enumerate_rps_begin()
assert len(rp_map.keys()) == first_rp[CredentialManagement.RESULT.TOTAL_RPS]
rk_count = 1
first_rk = CredMgmt.enumerate_creds_begin(sha256(first_rp[3]["id"]))
first_rk = credMgmt.enumerate_creds_begin(sha256(first_rp[3]["id"].encode()))
for i in range(1, first_rk[CredentialManagement.RESULT.TOTAL_CREDENTIALS]):
c = CredMgmt.enumerate_creds_next()
c = credMgmt.enumerate_creds_next()
rk_count += 1
assert rk_count == rp_map[first_rp[3]["id"].decode()]
assert rk_count == rp_map[first_rp[3]["id"]]
for i in range(1, first_rp[CredentialManagement.RESULT.TOTAL_RPS]):
next_rp = CredMgmt.enumerate_rps_next()
next_rp = credMgmt.enumerate_rps_next()
rk_count = 1
first_rk = CredMgmt.enumerate_creds_begin(
sha256(next_rp[3]["id"])
first_rk =credMgmt.enumerate_creds_begin(
sha256(next_rp[3]["id"].encode())
)
for i in range(1, first_rk[CredentialManagement.RESULT.TOTAL_CREDENTIALS]):
c = CredMgmt.enumerate_creds_next()
c = credMgmt.enumerate_creds_next()
rk_count += 1
assert rk_count == rp_map[next_rp[3]["id"].decode()]
assert rk_count == rp_map[next_rp[3]["id"]]
def CredMgmtWrongPinAuth(device, pin_token):
def CredMgmtWrongPinAuth(device):
pin_token = PinToken(device)
pin_protocol = PinProtocolV2()
wrong_pt = bytearray(pin_token)
wrong_pt[0] = (wrong_pt[0] + 1) % 256
@@ -98,55 +100,58 @@ def test_get_info(info):
assert 0x8 in info
assert info[0x8] > 1
def test_get_metadata(CredMgmt, MC_RK_Res):
metadata = CredMgmt.get_metadata()
def test_get_metadata_ok(MC_RK_Res, device):
metadata = CredMgmt(device).get_metadata()
assert metadata[CredentialManagement.RESULT.EXISTING_CRED_COUNT] == 2
assert metadata[CredentialManagement.RESULT.MAX_REMAINING_COUNT] >= 48
def test_enumerate_rps(CredMgmt, MC_RK_Res):
res = CredMgmt.enumerate_rps()
def test_enumerate_rps(MC_RK_Res, device):
res = CredMgmt(device).enumerate_rps()
assert len(res) == 2
assert res[0][CredentialManagement.RESULT.RP]["id"] == b"ssh:"
assert res[0][CredentialManagement.RESULT.RP]["id"] == "ssh:"
assert res[0][CredentialManagement.RESULT.RP_ID_HASH] == sha256(b"ssh:")
# Solo doesn't store rpId with the exception of "ssh:"
assert res[1][CredentialManagement.RESULT.RP]["id"] == b"xakcop.com"
assert res[1][CredentialManagement.RESULT.RP]["id"] == "xakcop.com"
assert res[1][CredentialManagement.RESULT.RP_ID_HASH] == sha256(b"xakcop.com")
def test_enumarate_creds(CredMgmt, MC_RK_Res):
res = CredMgmt.enumerate_creds(sha256(b"ssh:"))
def test_enumarate_creds(MC_RK_Res, device):
credMgmt = CredMgmt(device)
res = credMgmt.enumerate_creds(sha256(b"ssh:"))
assert len(res) == 1
assert_cred_response_has_all_fields(res[0])
res = CredMgmt.enumerate_creds(sha256(b"xakcop.com"))
res = credMgmt.enumerate_creds(sha256(b"xakcop.com"))
assert len(res) == 1
assert_cred_response_has_all_fields(res[0])
res = CredMgmt.enumerate_creds(sha256(b"missing.com"))
res = credMgmt.enumerate_creds(sha256(b"missing.com"))
assert not res
def test_get_metadata_wrong_pinauth(device, MC_RK_Res, PinToken):
def test_get_metadata_wrong_pinauth(device, MC_RK_Res):
cmd = lambda credMgmt: credMgmt.get_metadata()
_test_wrong_pinauth(device, cmd, PinToken)
_test_wrong_pinauth(device, cmd)
def test_rpbegin_wrong_pinauth(device, MC_RK_Res, PinToken):
def test_rpbegin_wrong_pinauth(device, MC_RK_Res):
cmd = lambda credMgmt: credMgmt.enumerate_rps_begin()
_test_wrong_pinauth(device, cmd, PinToken)
_test_wrong_pinauth(device, cmd)
def test_rkbegin_wrong_pinauth(device, MC_RK_Res, PinToken):
def test_rkbegin_wrong_pinauth(device, MC_RK_Res):
cmd = lambda credMgmt: credMgmt.enumerate_creds_begin(sha256(b"ssh:"))
_test_wrong_pinauth(device, cmd, PinToken)
_test_wrong_pinauth(device, cmd)
def test_rpnext_without_rpbegin(device, CredMgmt, MC_RK_Res):
CredMgmt.enumerate_creds_begin(sha256(b"ssh:"))
def test_rpnext_without_rpbegin(device, MC_RK_Res):
credMgmt = CredMgmt(device)
credMgmt.enumerate_creds_begin(sha256(b"ssh:"))
with pytest.raises(CtapError) as e:
CredMgmt.enumerate_rps_next()
credMgmt.enumerate_rps_next()
assert e.value.code == CtapError.ERR.NOT_ALLOWED
def test_rknext_without_rkbegin(device, CredMgmt, MC_RK_Res):
CredMgmt.enumerate_rps_begin()
def test_rknext_without_rkbegin(device, MC_RK_Res):
credMgmt = CredMgmt(device)
credMgmt.enumerate_rps_begin()
with pytest.raises(CtapError) as e:
CredMgmt.enumerate_creds_next()
credMgmt.enumerate_creds_next()
assert e.value.code == CtapError.ERR.NOT_ALLOWED
def test_delete(device, PinToken, CredMgmt):
def test_delete(device):
# create a new RK
rp = {"id": "example_3.com", "name": "John Doe 2"}
@@ -156,21 +161,22 @@ def test_delete(device, PinToken, CredMgmt):
auth = device.doGA(rp_id=rp['id'])
# get the ID from enumeration
creds = CredMgmt.enumerate_creds(reg.auth_data.rp_id_hash)
credMgmt = CredMgmt(device)
creds = credMgmt.enumerate_creds(reg.auth_data.rp_id_hash)
for cred in creds:
if cred[7]["id"] == reg.auth_data.credential_data.credential_id:
break
# delete it
cred = {"id": cred[7]["id"], "type": "public-key"}
CredMgmt.delete_cred(cred)
credMgmt.delete_cred(cred)
# make sure it doesn't work
with pytest.raises(CtapError) as e:
auth = device.doGA(rp_id=rp['id'])
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
def test_add_delete(device, PinToken, CredMgmt):
def test_add_delete(device):
""" Delete a credential in the 'middle' and ensure other credentials are not affected. """
rp = {"id": "example_4.com", "name": "John Doe 3"}
@@ -182,11 +188,12 @@ def test_add_delete(device, PinToken, CredMgmt):
regs.append(reg)
# Check they all enumerate
res = CredMgmt.enumerate_creds(regs[1].auth_data.rp_id_hash)
credMgmt = CredMgmt(device)
res = credMgmt.enumerate_creds(regs[1].auth_data.rp_id_hash)
assert len(res) == 3
# delete the middle one
creds = CredMgmt.enumerate_creds(reg.auth_data.rp_id_hash)
creds = credMgmt.enumerate_creds(reg.auth_data.rp_id_hash)
for cred in creds:
if cred[7]["id"] == regs[1].auth_data.credential_data.credential_id:
break
@@ -194,16 +201,16 @@ def test_add_delete(device, PinToken, CredMgmt):
assert cred[7]["id"] == regs[1].auth_data.credential_data.credential_id
cred = {"id": cred[7]["id"], "type": "public-key"}
CredMgmt.delete_cred(cred)
credMgmt.delete_cred(cred)
# Check one less enumerates
res = CredMgmt.enumerate_creds(regs[0].auth_data.rp_id_hash)
res = credMgmt.enumerate_creds(regs[0].auth_data.rp_id_hash)
assert len(res) == 2
def test_multiple_creds_per_multiple_rps(
device, PinToken, CredMgmt, MC_RK_Res
device, MC_RK_Res
):
res = CredMgmt.enumerate_rps()
res = CredMgmt(device).enumerate_rps()
assert len(res) == 2
new_rps = [
@@ -217,27 +224,26 @@ def test_multiple_creds_per_multiple_rps(
for i in range(0, 3):
reg = device.doMC(rp=rp, rk=True, user=generate_random_user())
res = CredMgmt.enumerate_rps()
credMgmt = CredMgmt(device)
res = credMgmt.enumerate_rps()
assert len(res) == 5
for rp in res:
if rp[3]["id"][:12] == "new_example_":
creds = CredMgmt.enumerate_creds(sha256(rp[3]["id"].encode("utf8")))
creds = credMgmt.enumerate_creds(sha256(rp[3]["id"].encode("utf8")))
assert len(creds) == 3
@pytest.mark.parametrize(
"enumeration_test", [_test_enumeration, _test_enumeration_interleaved]
)
def test_multiple_enumeration(
device, PinToken, MC_RK_Res, CredMgmt, enumeration_test
device, MC_RK_Res, enumeration_test
):
""" Test enumerate still works after different commands """
res = CredMgmt.enumerate_rps()
expected_enumeration = {"xakcop.com": 1, "ssh:": 1}
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(device, expected_enumeration)
new_rps = [
{"id": "example-2.com", "name": "Example-2-creds", "count": 2},
@@ -253,27 +259,19 @@ def test_multiple_enumeration(
# Now expect creds from this RP
expected_enumeration[rp["id"]] = rp["count"]
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(CredMgmt, expected_enumeration)
metadata = CredMgmt.get_metadata()
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(device, expected_enumeration)
@pytest.mark.parametrize(
"enumeration_test", [_test_enumeration, _test_enumeration_interleaved]
)
def test_multiple_enumeration_with_deletions(
device, PinToken, MC_RK_Res, CredMgmt, enumeration_test
device, MC_RK_Res, enumeration_test
):
""" Create each credential in random order. Test enumerate still works after randomly deleting each credential"""
res = CredMgmt.enumerate_rps()
expected_enumeration = {"xakcop.com": 1, "ssh:": 1}
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(device, expected_enumeration)
new_rps = [
{"id": "example-1.com", "name": "Example-1-creds"},
@@ -294,7 +292,7 @@ def test_multiple_enumeration_with_deletions(
else:
expected_enumeration[rp["id"]] += 1
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(device, expected_enumeration)
total_creds = len(new_rps)
@@ -304,10 +302,11 @@ def test_multiple_enumeration_with_deletions(
num = expected_enumeration[rp]
index = 0 if num == 1 else random.randint(0, num - 1)
cred = CredMgmt.enumerate_creds(sha256(rp.encode("utf8")))[index]
credMgmt = CredMgmt(device)
cred = credMgmt.enumerate_creds(sha256(rp.encode("utf8")))[index]
# print('Delete %d index (%d total) cred of %s' % (index, expected_enumeration[rp], rp))
CredMgmt.delete_cred({"id": cred[7]["id"], "type": "public-key"})
credMgmt.delete_cred({"id": cred[7]["id"], "type": "public-key"})
expected_enumeration[rp] -= 1
if expected_enumeration[rp] == 0:
@@ -316,43 +315,13 @@ def test_multiple_enumeration_with_deletions(
if len(list(expected_enumeration.keys())) == 0:
break
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(device, expected_enumeration)
def _test_wrong_pinauth(device, cmd, PinToken):
def _test_wrong_pinauth(device, cmd):
credMgmt = CredMgmtWrongPinAuth(device, PinToken)
credMgmt = CredMgmtWrongPinAuth(device)
for i in range(2):
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_AUTH_INVALID
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_AUTH_BLOCKED
#device.reboot()
credMgmt = CredMgmtWrongPinAuth(device, PinToken)
for i in range(2):
time.sleep(0.2)
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_AUTH_INVALID
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_AUTH_BLOCKED
#device.reboot()
credMgmt = CredMgmtWrongPinAuth(device, PinToken)
for i in range(1):
time.sleep(0.2)
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_AUTH_INVALID
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_BLOCKED

View File

@@ -0,0 +1,81 @@
import pytest
from fido2.ctap2.extensions import CredProtectExtension
from fido2.webauthn import UserVerificationRequirement
from fido2.ctap import CtapError
from fido2.ctap2.pin import PinProtocolV2, ClientPin
from fido2.ctap2 import Config
PIN='12345678'
MINPINLENGTH=6
@pytest.fixture(scope="function")
def MCMinPin(device):
res = device.doMC(rk=True, extensions={'minPinLength': True})['res'].attestation_object
return res
@pytest.fixture(scope="function")
def SetMinPin(device):
device.reset()
ClientPin(device.client()._backend.ctap2).set_pin(PIN)
cfg = FidoConfig(device)
cfg.set_min_pin_length(MINPINLENGTH,rp_ids=['example.com'])
@pytest.fixture(scope="function")
def SetMinPinWrongRpid(device):
device.reset()
ClientPin(device.client()._backend.ctap2).set_pin(PIN)
cfg = FidoConfig(device)
cfg.set_min_pin_length(MINPINLENGTH,rp_ids=['notanexample.com'])
def PinToken(device):
return ClientPin(device.client()._backend.ctap2).get_pin_token(PIN, permissions=ClientPin.PERMISSION.MAKE_CREDENTIAL | ClientPin.PERMISSION.AUTHENTICATOR_CFG)
def FidoConfig(device):
pt = PinToken(device)
pin_protocol = PinProtocolV2()
return Config(device.client()._backend.ctap2, pin_protocol, pt)
def test_supports_minpin(info):
assert info.extensions
assert 'minPinLength' in info.extensions
assert info.options
assert 'setMinPINLength' in info.options
assert info.options['setMinPINLength'] is True
def test_minpin(SetMinPin, MCMinPin):
assert MCMinPin.auth_data.extensions
assert "minPinLength" in MCMinPin.auth_data.extensions
assert MCMinPin.auth_data.extensions['minPinLength'] == MINPINLENGTH
def test_minpin_bad_rpid(SetMinPinWrongRpid, MCMinPin):
assert not MCMinPin.auth_data.extensions
assert "minPinLength" not in MCMinPin.auth_data.extensions
def test_setminpin(device, SetMinPin, MCMinPin):
cfg = FidoConfig(device)
cfg.set_min_pin_length(MINPINLENGTH+2,rp_ids=['example.com'])
res = device.doMC(rk=True, extensions={'minPinLength': True})['res'].attestation_object
assert res.auth_data.extensions
assert "minPinLength" in res.auth_data.extensions
assert res.auth_data.extensions['minPinLength'] == MINPINLENGTH+2
def test_no_setminpin(device, SetMinPin, MCMinPin):
cfg = FidoConfig(device)
with pytest.raises(CtapError) as e:
cfg.set_min_pin_length(MINPINLENGTH-2,rp_ids=['example.com'])
assert e.value.code == CtapError.ERR.PIN_POLICY_VIOLATION
def test_setminpin_check_force(device, SetMinPin, MCMinPin):
cfg = FidoConfig(device)
cfg.set_min_pin_length(len(PIN)+1,rp_ids=['example.com'])
info = device.client()._backend.ctap2.get_info()
assert info.force_pin_change == True
@pytest.mark.parametrize(
"force", [True, False]
)
def test_setminpin_set_forcee(device, SetMinPin, MCMinPin, force):
cfg = FidoConfig(device)
cfg.set_min_pin_length(MINPINLENGTH,rp_ids=['example.com'],force_change_pin=force)
info = device.client()._backend.ctap2.get_info()
assert info.force_pin_change == force

View File

@@ -27,6 +27,8 @@ from words import words
from threading import Event
from typing import Mapping, Any, Optional, Callable
import struct
import urllib.request
import json
from enum import IntEnum, unique
try:
@@ -48,6 +50,7 @@ try:
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
from cryptography.hazmat.primitives import hashes
from cryptography import x509
except:
print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`')
sys.exit(-1)
@@ -63,6 +66,21 @@ 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'
method = 'GET'
if (data is not None):
method = 'POST'
req = urllib.request.Request(f"https://www.picokeys.com/pico/pico-fido/{url}/",
method=method,
data=data,
headers={'User-Agent': user_agent, })
response = urllib.request.urlopen(req)
resp = response.read().decode('utf-8')
j = json.loads(resp)
return j
class VendorConfig(Config):
class PARAM(IntEnum):
@@ -179,6 +197,7 @@ class Vendor:
VENDOR_BACKUP = 0x01
VENDOR_MSE = 0x02
VENDOR_UNLOCK = 0x03
VENDOR_EA = 0x04
@unique
class PARAM(IntEnum):
@@ -189,6 +208,8 @@ class Vendor:
ENABLE = 0x01
DISABLE = 0x02
KEY_AGREEMENT = 0x01
EA_CSR = 0x01
EA_UPLOAD = 0x02
class RESP(IntEnum):
PARAM = 0x01
@@ -342,6 +363,21 @@ class Vendor:
def disable_device_aut(self):
self.vcfg.disable_device_aut()
def csr(self):
return self._call(
Vendor.CMD.VENDOR_EA,
Vendor.SUBCMD.EA_CSR,
)[Vendor.RESP.PARAM]
def upload_ea(self, der):
self._call(
Vendor.CMD.VENDOR_EA,
Vendor.SUBCMD.EA_UPLOAD,
{
Vendor.PARAM.PARAM: der
}
)
def parse_args():
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(title="commands", dest="command")
@@ -352,6 +388,10 @@ def parse_args():
parser_backup.add_argument('subcommand', choices=['save', 'load'], help='Saves or loads a backup.')
parser_backup.add_argument('filename', help='File to save or load the backup.')
parser_attestation = subparser.add_parser('attestation', help='Manages Enterprise Attestation')
parser_attestation.add_argument('subcommand', choices=['csr'])
parser_attestation.add_argument('--filename', help='Uploads the certificate filename to the device as enterprise attestation certificate. If not provided, it will generate an enterprise attestation certificate automatically.')
args = parser.parse_args()
return args
@@ -369,9 +409,24 @@ def backup(vdr, args):
elif (args.subcommand == 'load'):
vdr.backup_load(args.filename)
def attestation(vdr, args):
if (args.subcommand == 'csr'):
if (args.filename is None):
csr = x509.load_der_x509_csr(vdr.csr())
data = urllib.parse.urlencode({'csr': csr.public_bytes(Encoding.PEM)}).encode()
j = get_pki_data('csr', data=data)
cert = x509.load_pem_x509_certificate(j['x509'].encode())
else:
with open(args.filename, 'rb') as f:
dataf = f.read()
try:
cert = x509.load_der_x509_certificate(dataf)
except ValueError:
cert = x509.load_pem_x509_certificate(dataf)
vdr.upload_ea(cert.public_bytes(Encoding.DER))
def main(args):
print('Pico Fido Tool v1.2')
print('Pico Fido Tool v1.4')
print('Author: Pol Henarejos')
print('Report bugs to https://github.com/polhenarejos/pico-fido/issues')
print('')
@@ -385,6 +440,8 @@ def main(args):
secure(vdr, args)
elif (args.command == 'backup'):
backup(vdr, args)
elif (args.command == 'attestation'):
attestation(vdr, args)
def run():
args = parse_args()