From 24224b78dd5083650935c0fb1bf575c796262c64 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 15 May 2023 19:11:02 +0200 Subject: [PATCH 01/38] Added support to Nitrokey's nitropy tool. Signed-off-by: Pol Henarejos --- pico-hsm-sdk | 2 +- src/fido/cbor_make_credential.c | 6 +- src/fido/files.c | 2 + src/fido/files.h | 1 + src/fido/oath.c | 138 ++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 4 deletions(-) diff --git a/pico-hsm-sdk b/pico-hsm-sdk index 46e7d3a..2700e3a 160000 --- a/pico-hsm-sdk +++ b/pico-hsm-sdk @@ -1 +1 @@ -Subproject commit 46e7d3a181fab92fb8e394ed9b579b2b2f6a54a0 +Subproject commit 2700e3ae7a6bd6a43b8bdf39f243df8e8c52e7bf diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index 955ef61..4ffc8bd 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -464,12 +464,12 @@ 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) { diff --git a/src/fido/files.c b/src/fido/files.c index e32fafd..f072c5b 100644 --- a/src/fido/files.c +++ b/src/fido/files.c @@ -46,6 +46,8 @@ 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 }; diff --git a/src/fido/files.h b/src/fido/files.h index 5ef2dc7..ee8a211 100644 --- a/src/fido/files.h +++ b/src/fido/files.h @@ -36,6 +36,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; diff --git a/src/fido/oath.c b/src/fido/oath.c index c5c1f73..92a02b2 100644 --- a/src/fido/oath.c +++ b/src/fido/oath.c @@ -22,9 +22,11 @@ #include "random.h" #include "version.h" #include "asn1.h" +#include "crypto_utils.h" #define MAX_OATH_CRED 255 #define CHALLENGE_LEN 8 +#define MAX_OTP_COUNTER 3 #define TAG_NAME 0x71 #define TAG_NAME_LIST 0x72 @@ -38,6 +40,9 @@ #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 @@ -88,6 +93,16 @@ app_t *oath_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { 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; } @@ -252,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(); } @@ -479,6 +496,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 @@ -488,6 +618,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 }, @@ -499,6 +633,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 } }; From 016780b3de10fe696f0d3bd4e17e5150aad647a2 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 16 May 2023 09:23:16 +0200 Subject: [PATCH 02/38] Update pointer Signed-off-by: Pol Henarejos --- pico-hsm-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pico-hsm-sdk b/pico-hsm-sdk index 2700e3a..c7849e0 160000 --- a/pico-hsm-sdk +++ b/pico-hsm-sdk @@ -1 +1 @@ -Subproject commit 2700e3ae7a6bd6a43b8bdf39f243df8e8c52e7bf +Subproject commit c7849e0bdaa49ddd7ab9773875a975d71b991177 From c883083a75a24bb50a046f3ce7e18c54859cedcc Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 16 May 2023 09:41:11 +0200 Subject: [PATCH 03/38] Fix for mbedtls 3.4 build. Signed-off-by: Pol Henarejos --- src/fido/fido.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/fido/fido.c b/src/fido/fido.c index 98f091f..b9f7f7c 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -115,10 +115,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)); From c00c83dfe617c1b27e237bb918a3e215fce04787 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Sun, 13 Aug 2023 21:12:49 +0200 Subject: [PATCH 04/38] Added support for thirdPartyPayment extension. Signed-off-by: Pol Henarejos --- src/fido/cbor_get_assertion.c | 13 +++++++++++++ src/fido/cbor_make_credential.c | 1 + src/fido/credential.c | 5 +++++ src/fido/credential.h | 1 + 4 files changed, 20 insertions(+) diff --git a/src/fido/cbor_get_assertion.c b/src/fido/cbor_get_assertion.c index 5514c6e..7619884 100644 --- a/src/fido/cbor_get_assertion.c +++ b/src/fido/cbor_get_assertion.c @@ -193,6 +193,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); @@ -460,6 +461,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 +542,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); diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index 4ffc8bd..987f768 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -124,6 +124,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { 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); diff --git a/src/fido/credential.c b/src/fido/credential.c index bae5fb5..aba002c 100644 --- a/src/fido/credential.c +++ b/src/fido/credential.c @@ -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,7 @@ 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); diff --git a/src/fido/credential.h b/src/fido/credential.h index f883050..313077b 100644 --- a/src/fido/credential.h +++ b/src/fido/credential.h @@ -33,6 +33,7 @@ typedef struct CredExtensions { const bool *minPinLength; CborByteString credBlob; const bool *largeBlobKey; + const bool *thirdPartyPayment; bool present; } CredExtensions; From 30f51b84539ec45253925dc795b6fb56d2ce8fd2 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 14 Aug 2023 18:44:48 +0200 Subject: [PATCH 05/38] Add Nitrokey readme support. Signed-off-by: Pol Henarejos --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 39a5152..bd8bbd5 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Pico FIDO has implemented the following features: - 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. @@ -52,7 +53,7 @@ Alternatively you can use the legacy VID/PID patcher as follows: 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. +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. From a79842b33f351a243a3054786313fc88979b8f41 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 14 Aug 2023 18:45:36 +0200 Subject: [PATCH 06/38] Fix OTP slot deletion. Signed-off-by: Pol Henarejos --- src/fido/otp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fido/otp.c b/src/fido/otp.c index 9ea7820..d079612 100644 --- a/src/fido/otp.c +++ b/src/fido/otp.c @@ -337,9 +337,6 @@ int cmd_otp() { } if (p1 == 0x01 || p1 == 0x03) { // Configure slot 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 = 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); @@ -349,6 +346,9 @@ int cmd_otp() { } for (int c = 0; c < otp_config_size; c++) { if (apdu.data[c] != 0) { + 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(); From 2b1227b105dbe095cd381c54d7e4bfb1b8cd4643 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 14 Aug 2023 19:55:17 +0200 Subject: [PATCH 07/38] Added support for management via Yubikey Manager to enable/disable specific interfaces individually. All interfaces are enabled by default. Signed-off-by: Pol Henarejos --- src/fido/cmd_register.c | 3 +- src/fido/fido.c | 3 +- src/fido/files.h | 1 + src/fido/management.c | 84 ++++++++++++++++++++++++++++++----------- src/fido/management.h | 55 +++++++++++++++++++++++++++ src/fido/oath.c | 7 ++-- src/fido/otp.c | 8 ++-- 7 files changed, 132 insertions(+), 29 deletions(-) create mode 100644 src/fido/management.h diff --git a/src/fido/cmd_register.c b/src/fido/cmd_register.c index 81f5c38..806deea 100644 --- a/src/fido/cmd_register.c +++ b/src/fido/cmd_register.c @@ -22,6 +22,7 @@ #include "random.h" #include "files.h" #include "hid/ctap_hid.h" +#include "management.h" const uint8_t u2f_aid[] = { 7, @@ -32,7 +33,7 @@ int u2f_unload(); int u2f_process_apdu(); app_t *u2f_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { - if (!memcmp(aid, u2f_aid + 1, MIN(aid_len, u2f_aid[0]))) { + if (!memcmp(aid, u2f_aid + 1, MIN(aid_len, u2f_aid[0])) && cap_supported(CAP_U2F)) { a->aid = u2f_aid; a->process_apdu = u2f_process_apdu; a->unload = u2f_unload; diff --git a/src/fido/fido.c b/src/fido/fido.c index b9f7f7c..080246c 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -31,6 +31,7 @@ #include "bsp/board.h" #endif #include +#include "management.h" int fido_process_apdu(); int fido_unload(); @@ -52,7 +53,7 @@ const uint8_t atr_fido[] = { }; 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]))) { + if (!memcmp(aid, fido_aid + 1, MIN(aid_len, fido_aid[0])) && cap_supported(CAP_FIDO2)) { a->aid = fido_aid; a->process_apdu = fido_process_apdu; a->unload = fido_unload; diff --git a/src/fido/files.h b/src/fido/files.h index ee8a211..328eb13 100644 --- a/src/fido/files.h +++ b/src/fido/files.h @@ -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 diff --git a/src/fido/management.c b/src/fido/management.c index 1e42b5e..bd0be9a 100644 --- a/src/fido/management.c +++ b/src/fido/management.c @@ -19,6 +19,9 @@ #include "hsm.h" #include "apdu.h" #include "version.h" +#include "files.h" +#include "asn1.h" +#include "management.h" int man_process_apdu(); int man_unload(); @@ -27,7 +30,7 @@ const uint8_t man_aid[] = { 8, 0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 }; - +extern void scan_all(); app_t *man_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { if (!memcmp(aid, man_aid + 1, MIN(aid_len, man_aid[0]))) { a->aid = man_aid; @@ -36,6 +39,7 @@ app_t *man_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { 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(); return a; } return NULL; @@ -49,43 +53,69 @@ 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[1] << 8) | tag_data[0]; + } + 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++] = 0x01; + res_APDU[res_APDU_size++] = TAG_USB_SUPPORTED; res_APDU[res_APDU_size++] = 2; - res_APDU[res_APDU_size++] = 0x02; - res_APDU[res_APDU_size++] = 0x01 | 0x02 | 0x20; - res_APDU[res_APDU_size++] = 0x02; + 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++] = 0x03; - res_APDU[res_APDU_size++] = 2; - res_APDU[res_APDU_size++] = 0x02; - res_APDU[res_APDU_size++] = 0x01 | 0x02 | 0x20; - res_APDU[res_APDU_size++] = 0x04; + 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++] = 0x05; + 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++] = 0x08; - res_APDU[res_APDU_size++] = 1; - res_APDU[res_APDU_size++] = 0x80; - res_APDU[res_APDU_size++] = 0x0A; - res_APDU[res_APDU_size++] = 1; - res_APDU[res_APDU_size++] = 0x00; - res_APDU[res_APDU_size++] = 0x0D; - res_APDU[res_APDU_size++] = 1; - res_APDU[res_APDU_size++] = 0x00; - res_APDU[res_APDU_size++] = 0x0E; + 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; } @@ -95,10 +125,22 @@ int cmd_read_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 } }; diff --git a/src/fido/management.h b/src/fido/management.h new file mode 100644 index 0000000..6a5ff0d --- /dev/null +++ b/src/fido/management.h @@ -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 . + */ + +#ifndef _MANAGEMENT_H_ +#define _MANAGEMENT_H_ + +#include +#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 diff --git a/src/fido/oath.c b/src/fido/oath.c index 92a02b2..0e5be64 100644 --- a/src/fido/oath.c +++ b/src/fido/oath.c @@ -23,6 +23,7 @@ #include "version.h" #include "asn1.h" #include "crypto_utils.h" +#include "management.h" #define MAX_OATH_CRED 255 #define CHALLENGE_LEN 8 @@ -36,7 +37,7 @@ #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 @@ -68,12 +69,12 @@ const uint8_t oath_aid[] = { }; 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]))) { + if (!memcmp(aid, oath_aid + 1, MIN(aid_len, oath_aid[0])) && cap_supported(CAP_OATH)) { a->aid = oath_aid; 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; diff --git a/src/fido/otp.c b/src/fido/otp.c index d079612..30faf19 100644 --- a/src/fido/otp.c +++ b/src/fido/otp.c @@ -27,6 +27,7 @@ #include "bsp/board.h" #endif #include "mbedtls/aes.h" +#include "management.h" #define FIXED_SIZE 16 #define KEY_SIZE 16 @@ -114,7 +115,7 @@ const uint8_t otp_aid[] = { }; 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]))) { + if (!memcmp(aid, otp_aid + 1, MIN(aid_len, otp_aid[0]) && cap_supported(CAP_OTP))) { a->aid = otp_aid; a->process_apdu = otp_process_apdu; a->unload = otp_unload; @@ -175,6 +176,9 @@ extern int calculate_oath(uint8_t truncate, static uint8_t session_counter[2] = {0}; #endif int otp_button_pressed(uint8_t slot) { + if (!cap_supported(CAP_OTP)) { + return 3; + } init_otp(); #ifndef ENABLE_EMULATION file_t *ef = search_dynamic_file(slot == 1 ? EF_OTP_SLOT1 : EF_OTP_SLOT2); @@ -328,8 +332,6 @@ bool check_crc(const otp_config_t *data) { return crc == 0xF0B8; } -extern int man_get_config(); - int cmd_otp() { uint8_t p1 = P1(apdu), p2 = P2(apdu); if (p2 != 0x00) { From aa7362f88fe00973addec0859f2fdf5f73252203 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 14 Aug 2023 20:49:29 +0200 Subject: [PATCH 08/38] Fix enabled capabilities detection. Signed-off-by: Pol Henarejos --- src/fido/management.c | 2 +- src/fido/otp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fido/management.c b/src/fido/management.c index bd0be9a..483937f 100644 --- a/src/fido/management.c +++ b/src/fido/management.c @@ -63,7 +63,7 @@ bool cap_supported(uint16_t cap) { if (tag == TAG_USB_ENABLED) { uint16_t ecaps = tag_data[0]; if (tag_len == 2) { - ecaps = (tag_data[1] << 8) | tag_data[0]; + ecaps = (tag_data[0] << 8) | tag_data[1]; } return (ecaps & cap); } diff --git a/src/fido/otp.c b/src/fido/otp.c index 30faf19..cedcb71 100644 --- a/src/fido/otp.c +++ b/src/fido/otp.c @@ -176,10 +176,10 @@ extern int calculate_oath(uint8_t truncate, static uint8_t session_counter[2] = {0}; #endif int otp_button_pressed(uint8_t slot) { + init_otp(); if (!cap_supported(CAP_OTP)) { return 3; } - init_otp(); #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); From 51cbfe5fe98472135aa8dfded7aad5c97441d9ab Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 14 Aug 2023 21:09:54 +0200 Subject: [PATCH 09/38] Fix enabled cap detection when applet is already selected. Signed-off-by: Pol Henarejos --- src/fido/cbor.c | 69 +++++++++++++++++++++-------------------- src/fido/cmd_register.c | 10 +++--- src/fido/fido.c | 10 +++--- src/fido/oath.c | 10 +++--- src/fido/otp.c | 10 +++--- 5 files changed, 60 insertions(+), 49 deletions(-) diff --git a/src/fido/cbor.c b/src/fido/cbor.c index 4928626..818ddfe 100644 --- a/src/fido/cbor.c +++ b/src/fido/cbor.c @@ -23,6 +23,7 @@ #include "fido.h" #include "usb.h" #include "apdu.h" +#include "management.h" const bool _btrue = true, _bfalse = false; @@ -52,41 +53,43 @@ 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 (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; } diff --git a/src/fido/cmd_register.c b/src/fido/cmd_register.c index 806deea..837cb90 100644 --- a/src/fido/cmd_register.c +++ b/src/fido/cmd_register.c @@ -153,10 +153,12 @@ int u2f_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_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(); diff --git a/src/fido/fido.c b/src/fido/fido.c index 080246c..e86d7c6 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -415,10 +415,12 @@ int fido_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_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(); diff --git a/src/fido/oath.c b/src/fido/oath.c index 0e5be64..060ece3 100644 --- a/src/fido/oath.c +++ b/src/fido/oath.c @@ -645,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(); diff --git a/src/fido/otp.c b/src/fido/otp.c index cedcb71..eb12a31 100644 --- a/src/fido/otp.c +++ b/src/fido/otp.c @@ -463,10 +463,12 @@ 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(); From d0113145007b80dbfe526eb0eafca93f7bda9a07 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 14 Aug 2023 21:44:15 +0200 Subject: [PATCH 10/38] Add thirdPartyPayment extension to credential manager response. Signed-off-by: Pol Henarejos --- src/fido/cbor_cred_mgmt.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/fido/cbor_cred_mgmt.c b/src/fido/cbor_cred_mgmt.c index a5792eb..d9705fa 100644 --- a/src/fido/cbor_cred_mgmt.c +++ b/src/fido/cbor_cred_mgmt.c @@ -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++; } @@ -349,6 +349,12 @@ 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); From 5105545df0a63bfee0f2483c17cfb7fc7e7ed535 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 14 Aug 2023 21:44:34 +0200 Subject: [PATCH 11/38] Added thirdPartyPayment to supported extensions. Signed-off-by: Pol Henarejos --- src/fido/cbor_get_info.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fido/cbor_get_info.c b/src/fido/cbor_get_info.c index c29aeee..443c511 100644 --- a/src/fido/cbor_get_info.c +++ b/src/fido/cbor_get_info.c @@ -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)); From 8ffd1bfe3837df8e639cf88054cebad525b3bee1 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 16 Aug 2023 12:18:42 +0200 Subject: [PATCH 12/38] Added support for ES256K algorithm. It uses secp256k1 curve with SHA-256. Signed-off-by: Pol Henarejos --- src/fido/cbor_get_info.c | 8 +++++++- src/fido/cbor_make_credential.c | 3 +++ src/fido/fido.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/fido/cbor_get_info.c b/src/fido/cbor_get_info.c index 443c511..8812376 100644 --- a/src/fido/cbor_get_info.c +++ b/src/fido/cbor_get_info.c @@ -90,7 +90,7 @@ 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_array(&mapEncoder, &arrayEncoder, 4)); 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)); @@ -109,6 +109,12 @@ int cbor_get_info() { 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_ES256K)); + 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)); diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index 987f768..c630dcf 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -177,6 +177,9 @@ int cbor_make_credential(const uint8_t *data, size_t len) { else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512) { curve = FIDO2_CURVE_P521; } + else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256K) { + curve = FIDO2_CURVE_P256K1; + } else if (pubKeyCredParams[i].alg == 0) { // no present curve = -1; } diff --git a/src/fido/fido.h b/src/fido/fido.h index a063daf..06d547b 100644 --- a/src/fido/fido.h +++ b/src/fido/fido.h @@ -64,6 +64,7 @@ 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_CURVE_P256 1 #define FIDO2_CURVE_P384 2 From ce040a79f5f5fe2dee8bdba65a64d82252632aff Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 16 Aug 2023 12:39:53 +0200 Subject: [PATCH 13/38] Fix signature computation for algorithms ES384 and ES512. Signed-off-by: Pol Henarejos --- src/fido/cbor_get_assertion.c | 15 +++++++++++---- src/fido/cbor_make_credential.c | 16 ++++++++++++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/fido/cbor_get_assertion.c b/src/fido/cbor_get_assertion.c index 7619884..7967c28 100644 --- a/src/fido/cbor_get_assertion.c +++ b/src/fido/cbor_get_assertion.c @@ -574,16 +574,23 @@ 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), + 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); + } + ret = mbedtls_md(md, aut_data, aut_data_len + clientDataHash.len, hash); size_t olen = 0; ret = mbedtls_ecdsa_write_signature(&ekey, - MBEDTLS_MD_SHA256, + mbedtls_md_get_type(md), hash, - 32, + mbedtls_md_get_size(md), sig, sizeof(sig), &olen, diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index c630dcf..c176408 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -424,8 +424,15 @@ int cbor_make_credential(const uint8_t *data, size_t len) { } 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), + 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); + } + ret = mbedtls_md(md, aut_data, aut_data_len + clientDataHash.len, hash); @@ -435,12 +442,13 @@ int cbor_make_credential(const uint8_t *data, size_t len) { mbedtls_ecdsa_free(&ekey); mbedtls_ecdsa_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, + mbedtls_md_get_type(md), hash, - 32, + mbedtls_md_get_size(md), sig, sizeof(sig), &olen, From c258dad8e638c4de27abaafafe1928f14958fad2 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 16 Aug 2023 17:32:52 +0200 Subject: [PATCH 14/38] Fix OTP applet selection. Signed-off-by: Pol Henarejos --- src/fido/otp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fido/otp.c b/src/fido/otp.c index eb12a31..2f25ee1 100644 --- a/src/fido/otp.c +++ b/src/fido/otp.c @@ -115,7 +115,7 @@ const uint8_t otp_aid[] = { }; 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]) && cap_supported(CAP_OTP))) { + if (!memcmp(aid, otp_aid + 1, MIN(aid_len, otp_aid[0])) && cap_supported(CAP_OTP)) { a->aid = otp_aid; a->process_apdu = otp_process_apdu; a->unload = otp_unload; From bb20dd7a5325190f4b3f682abd65fc4206d93f4b Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 16 Aug 2023 19:16:54 +0200 Subject: [PATCH 15/38] First attempt to include CBOR as CCID. Signed-off-by: Pol Henarejos --- src/fido/fido.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/fido/fido.c b/src/fido/fido.c index e86d7c6..dc5f6ce 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -32,6 +32,7 @@ #endif #include #include "management.h" +#include "ctap_hid.h" int fido_process_apdu(); int fido_unload(); @@ -403,16 +404,32 @@ 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(); } if (cap_supported(CAP_U2F)) { From 2d81a3c472279dbe1d09d1337f88902a8c2990d3 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 16 Aug 2023 19:17:13 +0200 Subject: [PATCH 16/38] Update to pyfido2 1.1.2 Signed-off-by: Pol Henarejos --- tests/docker/fido2/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/docker/fido2/__init__.py b/tests/docker/fido2/__init__.py index e172b9d..83359b2 100644 --- a/tests/docker/fido2/__init__.py +++ b/tests/docker/fido2/__init__.py @@ -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 From b72c596aa6ee4f6a7e540c66a0a406b63f9ae45e Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 16 Aug 2023 19:20:11 +0200 Subject: [PATCH 17/38] Fix chained response. Signed-off-by: Pol Henarejos --- pico-hsm-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pico-hsm-sdk b/pico-hsm-sdk index c7849e0..e84258c 160000 --- a/pico-hsm-sdk +++ b/pico-hsm-sdk @@ -1 +1 @@ -Subproject commit c7849e0bdaa49ddd7ab9773875a975d71b991177 +Subproject commit e84258c434c6d90561df8b3d9c097bd12894a117 From b2c4e0e1c1352a6869347793c263d42a08fc9878 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Thu, 17 Aug 2023 01:19:13 +0200 Subject: [PATCH 18/38] Added curve to fido. Signed-off-by: Pol Henarejos --- src/fido/fido.c | 21 +++++++++++++++++++++ src/fido/fido.h | 1 + 2 files changed, 22 insertions(+) diff --git a/src/fido/fido.c b/src/fido/fido.c index dc5f6ce..abb0bf9 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -95,6 +95,27 @@ mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) { } 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; + } + return 0; +} int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key) { mbedtls_ecp_group_id mbedtls_curve = fido_curve_to_mbedtls(curve); diff --git a/src/fido/fido.h b/src/fido/fido.h index 06d547b..c22ed37 100644 --- a/src/fido/fido.h +++ b/src/fido/fido.h @@ -45,6 +45,7 @@ extern int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ec extern bool wait_button_pressed(); extern void init_fido(); extern mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve); +extern int mbedtls_curve_to_fido(mbedtls_ecp_group_id id); extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key); extern int load_keydev(uint8_t *key); extern int encrypt(uint8_t protocol, From 63e15b19bba2c817d2032b2a4da54890487befcb Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Thu, 17 Aug 2023 01:19:27 +0200 Subject: [PATCH 19/38] Added functions for writing COSE keys. Signed-off-by: Pol Henarejos --- src/fido/cbor.c | 77 +++++++++++++++++++++++++++++++++++++++++++ src/fido/ctap2_cbor.h | 7 ++++ 2 files changed, 84 insertions(+) diff --git a/src/fido/cbor.c b/src/fido/cbor.c index 818ddfe..548a96f 100644 --- a/src/fido/cbor.c +++ b/src/fido/cbor.c @@ -24,6 +24,7 @@ #include "usb.h" #include "apdu.h" #include "management.h" +#include "ctap2_cbor.h" const bool _btrue = true, _bfalse = false; @@ -127,3 +128,79 @@ 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, 5)); + + 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; + } + 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; +} diff --git a/src/fido/ctap2_cbor.h b/src/fido/ctap2_cbor.h index 65c038d..7d03fa8 100644 --- a/src/fido/ctap2_cbor.h +++ b/src/fido/ctap2_cbor.h @@ -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); @@ -237,4 +240,8 @@ 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); + #endif //_CTAP2_CBOR_H_ From b7ceec8d49d2f9e86aedaf34ae3a54522e0f0a77 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Thu, 17 Aug 2023 01:19:45 +0200 Subject: [PATCH 20/38] Using COSE keys write functions. Signed-off-by: Pol Henarejos --- src/fido/cbor_client_pin.c | 16 +--------------- src/fido/cbor_cred_mgmt.c | 16 +--------------- src/fido/cbor_get_info.c | 28 ++++------------------------ src/fido/cbor_make_credential.c | 17 +---------------- src/fido/cbor_vendor.c | 17 +---------------- 5 files changed, 8 insertions(+), 86 deletions(-) diff --git a/src/fido/cbor_client_pin.c b/src/fido/cbor_client_pin.c index d61133e..7ecbcbd 100644 --- a/src/fido/cbor_client_pin.c +++ b/src/fido/cbor_client_pin.c @@ -374,21 +374,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); diff --git a/src/fido/cbor_cred_mgmt.c b/src/fido/cbor_cred_mgmt.c index d9705fa..13ee3f6 100644 --- a/src/fido/cbor_cred_mgmt.c +++ b/src/fido/cbor_cred_mgmt.c @@ -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)); diff --git a/src/fido/cbor_get_info.c b/src/fido/cbor_get_info.c index 8812376..0864725 100644 --- a/src/fido/cbor_get_info.c +++ b/src/fido/cbor_get_info.c @@ -91,30 +91,10 @@ int cbor_get_info() { CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0A)); CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 4)); - 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_map(&arrayEncoder, &mapEncoder2, 2)); - CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg")); - CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES256K)); - 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(COSE_public_key(FIDO2_ALG_ES256, &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)); diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index c176408..d709d5c 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -369,7 +369,6 @@ 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); int ret = fido_load_key(curve, cred_id, &ekey); @@ -386,21 +385,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { 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; diff --git a/src/fido/cbor_vendor.c b/src/fido/cbor_vendor.c index 5ec5b28..466bc67 100644 --- a/src/fido/cbor_vendor.c +++ b/src/fido/cbor_vendor.c @@ -223,22 +223,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); } } From 4c3042a8bfcb725455200fd69bdb769ef1f4a8e7 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Thu, 17 Aug 2023 01:30:49 +0200 Subject: [PATCH 21/38] Added function for reading COSE keys. Signed-off-by: Pol Henarejos --- src/fido/cbor.c | 29 +++++++++++++++++++++++++++++ src/fido/cbor_client_pin.c | 25 +------------------------ src/fido/cbor_get_assertion.c | 25 +------------------------ src/fido/cbor_vendor.c | 25 +------------------------ src/fido/ctap2_cbor.h | 1 + 5 files changed, 33 insertions(+), 72 deletions(-) diff --git a/src/fido/cbor.c b/src/fido/cbor.c index 548a96f..d8ece1d 100644 --- a/src/fido/cbor.c +++ b/src/fido/cbor.c @@ -204,3 +204,32 @@ CborError COSE_public_key(int alg, CborEncoder *mapEncoderParent, CborEncoder *m 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; +} \ No newline at end of file diff --git a/src/fido/cbor_client_pin.c b/src/fido/cbor_client_pin.c index 7ecbcbd..f47c09e 100644 --- a/src/fido/cbor_client_pin.c +++ b/src/fido/cbor_client_pin.c @@ -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); diff --git a/src/fido/cbor_get_assertion.c b/src/fido/cbor_get_assertion.c index 7967c28..8dfb206 100644 --- a/src/fido/cbor_get_assertion.c +++ b/src/fido/cbor_get_assertion.c @@ -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); diff --git a/src/fido/cbor_vendor.c b/src/fido/cbor_vendor.c index 466bc67..f76de3c 100644 --- a/src/fido/cbor_vendor.c +++ b/src/fido/cbor_vendor.c @@ -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); diff --git a/src/fido/ctap2_cbor.h b/src/fido/ctap2_cbor.h index 7d03fa8..1706ed8 100644 --- a/src/fido/ctap2_cbor.h +++ b/src/fido/ctap2_cbor.h @@ -243,5 +243,6 @@ typedef struct CborCharString { 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_ From 0d280ca25265b2de8898ddaeaffddbac5ad2e5b0 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Fri, 18 Aug 2023 12:33:30 +0200 Subject: [PATCH 22/38] Moving pointer. Signed-off-by: Pol Henarejos --- pico-hsm-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pico-hsm-sdk b/pico-hsm-sdk index e84258c..a36a89c 160000 --- a/pico-hsm-sdk +++ b/pico-hsm-sdk @@ -1 +1 @@ -Subproject commit e84258c434c6d90561df8b3d9c097bd12894a117 +Subproject commit a36a89cc9555b9a9959218f011488c542aefc0b8 From be4494747537ecfe16d1744b92df922625788fba Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Fri, 18 Aug 2023 12:43:49 +0200 Subject: [PATCH 23/38] Fix writing COSE key when for curves with kty=1. Signed-off-by: Pol Henarejos --- src/fido/cbor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fido/cbor.c b/src/fido/cbor.c index d8ece1d..3cf70bf 100644 --- a/src/fido/cbor.c +++ b/src/fido/cbor.c @@ -136,7 +136,7 @@ CborError COSE_key_params(int crv, int alg, mbedtls_ecp_group *grp, mbedtls_ecp_ kty = 2; } - CBOR_CHECK(cbor_encoder_create_map(mapEncoderParent, mapEncoder, 5)); + 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)); From 05044b498dd5bc70ceef4aabbf85667d2b703c76 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Fri, 18 Aug 2023 13:06:51 +0200 Subject: [PATCH 24/38] Added test for testing algorithms on make credential. Signed-off-by: Pol Henarejos --- tests/pico-fido/test_020_register.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/pico-fido/test_020_register.py b/tests/pico-fido/test_020_register.py index 76cf631..d5e876a 100644 --- a/tests/pico-fido/test_020_register.py +++ b/tests/pico-fido/test_020_register.py @@ -19,7 +19,7 @@ from fido2.client import CtapError -from fido2.cose import ES256 +from fido2.cose import ES256, ES384, ES512, EdDSA import pytest @@ -31,7 +31,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,6 +120,13 @@ 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] +) +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}]) From 26148282e67a6de0afcaa418c9730ccd5836b234 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Fri, 18 Aug 2023 13:07:06 +0200 Subject: [PATCH 25/38] Fix credential creation for ES512. Signed-off-by: Pol Henarejos --- src/fido/fido.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fido/fido.c b/src/fido/fido.c index abb0bf9..885e58b 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -262,6 +262,9 @@ 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) { From 8e26ec8bcddc434d7af8f8dc0a2a44cbea6301b2 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Fri, 18 Aug 2023 14:10:17 +0200 Subject: [PATCH 26/38] Use python-fido2 from my repo, which contains some fixes. Signed-off-by: Pol Henarejos --- tests/docker/bullseye/Dockerfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/docker/bullseye/Dockerfile b/tests/docker/bullseye/Dockerfile index 65a92c5..f4e20f8 100644 --- a/tests/docker/bullseye/Dockerfile +++ b/tests/docker/bullseye/Dockerfile @@ -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 From 75771e5e464d4ea8a447179804730fa29ec8a12b Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Fri, 18 Aug 2023 14:10:24 +0200 Subject: [PATCH 27/38] Not used. Signed-off-by: Pol Henarejos --- tests/pico-fido/test_020_register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pico-fido/test_020_register.py b/tests/pico-fido/test_020_register.py index d5e876a..faad60f 100644 --- a/tests/pico-fido/test_020_register.py +++ b/tests/pico-fido/test_020_register.py @@ -19,7 +19,7 @@ from fido2.client import CtapError -from fido2.cose import ES256, ES384, ES512, EdDSA +from fido2.cose import ES256, ES384, ES512 import pytest From 539ea61436466451db4042e19d87421e76ec1be6 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Fri, 18 Aug 2023 14:10:49 +0200 Subject: [PATCH 28/38] Add get assertion test with different algorithms. Signed-off-by: Pol Henarejos --- tests/pico-fido/test_021_authenticate.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/pico-fido/test_021_authenticate.py b/tests/pico-fido/test_021_authenticate.py index 6223305..9de69bf 100644 --- a/tests/pico-fido/test_021_authenticate.py +++ b/tests/pico-fido/test_021_authenticate.py @@ -18,8 +18,9 @@ """ -from fido2.utils import sha256 from fido2.client import CtapError +from fido2.cose import ES256, ES384, ES512 +from utils import verify 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] +) +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"}}) From df26040838f0a1335ed1dde1e87e8d0b4847762d Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 21 Aug 2023 19:11:44 +0200 Subject: [PATCH 29/38] Fix loading SECP521R1 key. Signed-off-by: Pol Henarejos --- src/fido/fido.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fido/fido.c b/src/fido/fido.c index 885e58b..dc70e86 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -217,7 +217,7 @@ int derive_key(const uint8_t *app_id, uint8_t *key_handle, int curve, mbedtls_ecdsa_context *key) { - uint8_t outk[64] = { 0 }; + 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) { From 2bbaf7c274c4eeea6a10606ce4454632e893acfa Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 22 Aug 2023 13:22:16 +0200 Subject: [PATCH 30/38] Adapted pubKeyCredParams verification and return error messages to specs. Signed-off-by: Pol Henarejos --- src/fido/cbor_make_credential.c | 89 +++++++++++++++++---------------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index d709d5c..ceb1f22 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -156,48 +156,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 == FIDO2_ALG_ES256K) { - curve = FIDO2_CURVE_P256K1; - } - 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) { @@ -219,6 +177,53 @@ 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 { + 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); From 974868d8e4386fa6d3cc205d90e827bc35e54cdb Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 22 Aug 2023 13:22:38 +0200 Subject: [PATCH 31/38] FIDO2 Server only uses supported algorithms. Signed-off-by: Pol Henarejos --- tests/conftest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 43a65b3..eef3f8b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -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 From cac4ae1751190b92d5ee25ac2f1dd24fa42a058e Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 22 Aug 2023 13:23:22 +0200 Subject: [PATCH 32/38] Adapted test errors to specs. Signed-off-by: Pol Henarejos --- tests/pico-fido/test_020_register.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/pico-fido/test_020_register.py b/tests/pico-fido/test_020_register.py index faad60f..6a3ab73 100644 --- a/tests/pico-fido/test_020_register.py +++ b/tests/pico-fido/test_020_register.py @@ -131,14 +131,14 @@ 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, ] @@ -150,7 +150,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"}]) From 0c08590dccfe67fd15c1907415cd9bcd80373dbe Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 22 Aug 2023 13:23:42 +0200 Subject: [PATCH 33/38] Added support for ES256K tests. Signed-off-by: Pol Henarejos --- tests/utils.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/utils.py b/tests/utils.py index 55d76c9..5021ef3 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -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), + } + ) From 539420b996e701b2c85ddea85ec87d668b56bfab Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 22 Aug 2023 13:23:56 +0200 Subject: [PATCH 34/38] Added ES256K tests. Signed-off-by: Pol Henarejos --- tests/pico-fido/test_020_register.py | 3 ++- tests/pico-fido/test_021_authenticate.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/pico-fido/test_020_register.py b/tests/pico-fido/test_020_register.py index 6a3ab73..378a13e 100644 --- a/tests/pico-fido/test_020_register.py +++ b/tests/pico-fido/test_020_register.py @@ -20,6 +20,7 @@ from fido2.client import CtapError from fido2.cose import ES256, ES384, ES512 +from utils import ES256K import pytest @@ -121,7 +122,7 @@ def test_bad_type_pubKeyCredParams(device): device.doMC(key_params=["wrong"]) @pytest.mark.parametrize( - "alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM] + "alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, ES256K.ALGORITHM] ) def test_algorithms(device, info, alg): if ({'alg': alg, 'type': 'public-key'} in info.algorithms): diff --git a/tests/pico-fido/test_021_authenticate.py b/tests/pico-fido/test_021_authenticate.py index 9de69bf..433adca 100644 --- a/tests/pico-fido/test_021_authenticate.py +++ b/tests/pico-fido/test_021_authenticate.py @@ -20,7 +20,7 @@ from fido2.client import CtapError from fido2.cose import ES256, ES384, ES512 -from utils import verify +from utils import verify, ES256K import pytest def test_authenticate(device): @@ -49,7 +49,7 @@ def test_empty_allowList(device): assert e.value.code == CtapError.ERR.NO_CREDENTIALS @pytest.mark.parametrize( - "alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM] + "alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, ES256K.ALGORITHM] ) def test_algorithms(device, info, alg): if ({'alg': alg, 'type': 'public-key'} in info.algorithms): From bafede2ae599e78d26f56bddc321af9b90e2f7e5 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 22 Aug 2023 15:31:30 +0200 Subject: [PATCH 35/38] Add supported curves to README. Signed-off-by: Pol Henarejos --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bd8bbd5..e9b5963 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Pico FIDO has implemented the following features: - Discoverable credentials - Credential management - ECDSA authentication +- Authentication with SECP256R1, SECP384R1, SECP521R1 and SECP256K1 curves. - App registration and login - Device selection - Support for vendor Config From 332debea6d3869ebecb80c574a80559788faeaba Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Sun, 17 Sep 2023 19:11:39 +0200 Subject: [PATCH 36/38] Code style. Signed-off-by: Pol Henarejos --- src/fido/cbor.c | 42 ++++++++++++----- src/fido/cbor_cred_mgmt.c | 3 +- src/fido/cbor_make_credential.c | 30 ++++++++---- src/fido/credential.c | 4 +- src/fido/ctap2_cbor.h | 11 ++++- src/fido/files.c | 3 +- src/fido/management.c | 6 +-- src/fido/oath.c | 6 +-- src/fido/otp.c | 84 ++++++++++++++++++++------------- 9 files changed, 123 insertions(+), 66 deletions(-) diff --git a/src/fido/cbor.c b/src/fido/cbor.c index 3cf70bf..e80f513 100644 --- a/src/fido/cbor.c +++ b/src/fido/cbor.c @@ -129,10 +129,16 @@ int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len) { return 1; } -CborError COSE_key_params(int crv, int alg, mbedtls_ecp_group *grp, mbedtls_ecp_point *Q, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder) { +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) { + if (crv == FIDO2_CURVE_P256 || crv == FIDO2_CURVE_P384 || crv == FIDO2_CURVE_P521 || + crv == FIDO2_CURVE_P256K1) { kty = 2; } @@ -162,15 +168,17 @@ CborError COSE_key_params(int crv, int alg, mbedtls_ecp_group *grp, mbedtls_ecp_ } else { size_t olen = 0; - CBOR_CHECK(mbedtls_ecp_point_write_binary(grp, Q, MBEDTLS_ECP_PF_COMPRESSED, &olen, pkey, sizeof(pkey))); + 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: +err: return error; } -CborError COSE_key(mbedtls_ecp_keypair *key, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder) { +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; @@ -189,9 +197,16 @@ CborError COSE_key(mbedtls_ecp_keypair *key, CborEncoder *mapEncoderParent, Cbor } return COSE_key_params(crv, alg, &key->grp, &key->Q, mapEncoderParent, mapEncoder); } -CborError COSE_key_shared(mbedtls_ecdh_context *key, CborEncoder *mapEncoderParent, CborEncoder *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); + 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; @@ -201,10 +216,15 @@ CborError COSE_public_key(int alg, CborEncoder *mapEncoderParent, CborEncoder *m 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: +err: return error; } -CborError COSE_read_key(CborValue *f, int64_t *kty, int64_t *alg, int64_t *crv, CborByteString *kax, CborByteString *kay) { +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) @@ -230,6 +250,6 @@ CborError COSE_read_key(CborValue *f, int64_t *kty, int64_t *alg, int64_t *crv, } } CBOR_PARSE_MAP_END(*f, 0); - err: +err: return error; -} \ No newline at end of file +} diff --git a/src/fido/cbor_cred_mgmt.c b/src/fido/cbor_cred_mgmt.c index 13ee3f6..7f5bf3f 100644 --- a/src/fido/cbor_cred_mgmt.c +++ b/src/fido/cbor_cred_mgmt.c @@ -336,7 +336,8 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { mbedtls_platform_zeroize(largeBlobKey, sizeof(largeBlobKey)); } CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C)); - CBOR_CHECK(cbor_encode_boolean(&mapEncoder, cred.extensions.thirdPartyPayment == ptrue)); + CBOR_CHECK(cbor_encode_boolean(&mapEncoder, + cred.extensions.thirdPartyPayment == ptrue)); } else { CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C)); diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index ceb1f22..cf836d7 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -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,7 +124,8 @@ 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); @@ -131,7 +139,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) { } 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); @@ -468,7 +477,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) { CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, self_attestation == false || is_nitrokey ? 3 : 2)); CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg")); - CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, self_attestation || is_nitrokey ? -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 || is_nitrokey) { diff --git a/src/fido/credential.c b/src/fido/credential.c index aba002c..7e766c6 100644 --- a/src/fido/credential.c +++ b/src/fido/credential.c @@ -205,7 +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_FIELD_KEY_TEXT_VAL_BOOL(2, + "thirdPartyPayment", + cred->extensions.thirdPartyPayment); CBOR_ADVANCE(2); } CBOR_PARSE_MAP_END(_f1, 2); diff --git a/src/fido/ctap2_cbor.h b/src/fido/ctap2_cbor.h index 1706ed8..f7ee2d2 100644 --- a/src/fido/ctap2_cbor.h +++ b/src/fido/ctap2_cbor.h @@ -241,8 +241,15 @@ typedef struct CborCharString { } } 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_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); +extern CborError COSE_read_key(CborValue *f, + int64_t *kty, + int64_t *alg, + int64_t *crv, + CborByteString *kax, + CborByteString *kay); #endif //_CTAP2_CBOR_H_ diff --git a/src/fido/files.c b/src/fido/files.c index f072c5b..121f76d 100644 --- a/src/fido/files.c +++ b/src/fido/files.c @@ -46,7 +46,8 @@ 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, + { .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 diff --git a/src/fido/management.c b/src/fido/management.c index 483937f..f2f8df7 100644 --- a/src/fido/management.c +++ b/src/fido/management.c @@ -36,8 +36,8 @@ app_t *man_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { a->aid = man_aid; 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); + 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(); return a; @@ -65,7 +65,7 @@ bool cap_supported(uint16_t cap) { if (tag_len == 2) { ecaps = (tag_data[0] << 8) | tag_data[1]; } - return (ecaps & cap); + return ecaps & cap; } } } diff --git a/src/fido/oath.c b/src/fido/oath.c index 060ece3..e3d25bc 100644 --- a/src/fido/oath.c +++ b/src/fido/oath.c @@ -585,7 +585,7 @@ int cmd_verify_hotp() { return SW_DATA_INVALID(); } if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_IMF, &chal_len, - &chal) == false) { + &chal) == false) { return SW_INCORRECT_PARAMS(); } if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &code_len, &code) == true) { @@ -598,10 +598,10 @@ int cmd_verify_hotp() { } 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; + res_int %= (uint32_t) 1e6; } else { - res_int %= (uint32_t)1e8; + res_int %= (uint32_t) 1e8; } if (res_int != code_int) { return SW_WRONG_DATA(); diff --git a/src/fido/otp.c b/src/fido/otp.c index 2f25ee1..a38c287 100644 --- a/src/fido/otp.c +++ b/src/fido/otp.c @@ -51,7 +51,8 @@ #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) +#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 @@ -63,7 +64,8 @@ #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) +#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 @@ -135,7 +137,8 @@ app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { return NULL; } -uint8_t modhex_tab[] = {'c', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'r', 't', 'u', 'v'}; +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]; @@ -151,8 +154,9 @@ void init_otp() { 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)) { + 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]; @@ -173,7 +177,7 @@ extern int calculate_oath(uint8_t truncate, const uint8_t *chal, size_t chal_len); #ifndef ENABLE_EMULATION -static uint8_t session_counter[2] = {0}; +static uint8_t session_counter[2] = { 0 }; #endif int otp_button_pressed(uint8_t slot) { init_otp(); @@ -183,7 +187,7 @@ int otp_button_pressed(uint8_t slot) { #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; + otp_config_t *otp_config = (otp_config_t *) data; if (file_has_data(ef) == false) { return 1; } @@ -196,10 +200,10 @@ int otp_button_pressed(uint8_t slot) { 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 |= (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; @@ -207,24 +211,28 @@ int otp_button_pressed(uint8_t slot) { 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}; + 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]; + 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); + 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); + 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_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)); @@ -232,7 +240,7 @@ int otp_button_pressed(uint8_t slot) { low_flash_available(); } if (otp_config->tkt_flags & APPEND_CR) { - append_keyboard_buffer((const uint8_t *)"\r", 1); + append_keyboard_buffer((const uint8_t *) "\r", 1); } } else if (otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET) { @@ -241,7 +249,7 @@ int otp_button_pressed(uint8_t slot) { } add_keyboard_buffer(otp_config->fixed_data, otp_config->fixed_size, false); if (otp_config->tkt_flags & APPEND_CR) { - append_keyboard_buffer((const uint8_t *)"\x28", 1); + append_keyboard_buffer((const uint8_t *) "\x28", 1); } } else { @@ -276,9 +284,9 @@ int otp_button_pressed(uint8_t slot) { 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); + 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); + append_keyboard_buffer((const uint8_t *) "\r", 1); } if (++session_counter[slot - 1] == 0) { @@ -319,16 +327,16 @@ uint16_t otp_status() { 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); + (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); + uint16_t crc = calculate_crc((const uint8_t *) data, otp_config_size); return crc == 0xF0B8; } @@ -338,7 +346,7 @@ int cmd_otp() { return SW_INCORRECT_P1P2(); } if (p1 == 0x01 || p1 == 0x03) { // Configure slot - otp_config_t *odata = (otp_config_t *)apdu.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); @@ -367,7 +375,7 @@ int cmd_otp() { return otp_status(); } else if (p1 == 0x04 || p1 == 0x05) { - otp_config_t *odata = (otp_config_t *)apdu.data; + 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(); } @@ -379,9 +387,12 @@ int cmd_otp() { } 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); + 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(); } @@ -421,13 +432,18 @@ int cmd_otp() { 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); + 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); + 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; } From 1217d82361150b692dc82a54fac4400399fe2c80 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Sun, 17 Sep 2023 19:12:57 +0200 Subject: [PATCH 37/38] Add support for newer boards. Signed-off-by: Pol Henarejos --- build_pico_fido.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build_pico_fido.sh b/build_pico_fido.sh index fc7e24a..1e51c67 100755 --- a/build_pico_fido.sh +++ b/build_pico_fido.sh @@ -1,7 +1,7 @@ #!/bin/bash -VERSION_MAJOR="3" -VERSION_MINOR="0" +VERSION_MAJOR="5" +VERSION_MINOR="6" 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 \ From 45c2cf65feb28020b13aee32b82ebad7ed3cdea8 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Sun, 17 Sep 2023 19:13:07 +0200 Subject: [PATCH 38/38] Upgrade to version 5.6 Signed-off-by: Pol Henarejos --- src/fido/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fido/version.h b/src/fido/version.h index 4708ced..721a0bf 100644 --- a/src/fido/version.h +++ b/src/fido/version.h @@ -18,7 +18,7 @@ #ifndef __VERSION_H_ #define __VERSION_H_ -#define PICO_FIDO_VERSION 0x0504 +#define PICO_FIDO_VERSION 0x0506 #define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff) #define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)