diff --git a/build_pico_fido.sh b/build_pico_fido.sh index 9a67f1d..18984c1 100755 --- a/build_pico_fido.sh +++ b/build_pico_fido.sh @@ -1,7 +1,7 @@ #!/bin/bash VERSION_MAJOR="2" -VERSION_MINOR="6" +VERSION_MINOR="8" rm -rf release/* cd build_release diff --git a/pico-hsm-sdk b/pico-hsm-sdk index 3def9bf..0bc13df 160000 --- a/pico-hsm-sdk +++ b/pico-hsm-sdk @@ -1 +1 @@ -Subproject commit 3def9bff4fb9e3e79e497392897deaf7c9bb5f29 +Subproject commit 0bc13df1a22ab6c9cceb3b7a87b294e604e79c1e diff --git a/src/fido/cbor.c b/src/fido/cbor.c index ff26b24..3896916 100644 --- a/src/fido/cbor.c +++ b/src/fido/cbor.c @@ -64,7 +64,7 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) { return cbor_get_next_assertion(data + 1, len - 1); else if (data[0] == CTAP_SELECTION) return cbor_selection(); - else if (data[0] == CTAP_CREDENTIAL_MGMT) + else if (data[0] == CTAP_CREDENTIAL_MGMT || data[0] == 0x41) return cbor_cred_mgmt(data + 1, len - 1); else if (data[0] == CTAP_CONFIG) return cbor_config(data + 1, len - 1); diff --git a/src/fido/cbor_client_pin.c b/src/fido/cbor_client_pin.c index fe2014d..bf6a21e 100644 --- a/src/fido/cbor_client_pin.c +++ b/src/fido/cbor_client_pin.c @@ -59,7 +59,7 @@ void clearUserVerifiedFlag() { void clearPinUvAuthTokenPermissionsExceptLbw() { if (paut.in_use == true) - paut.permissions = FIDO2_PERMISSION_LBW; + paut.permissions = CTAP_PERMISSION_LBW; } void stopUsingPinUvAuthToken() { @@ -518,7 +518,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) { mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret)); CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); } - if (memcmp(paddedNewPin, file_get_data(ef_pin)+1, 16) != 0) { + if (memcmp(paddedNewPin, file_get_data(ef_pin)+2, 16) != 0) { regenerate(); mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret)); if (retries == 0) { @@ -544,7 +544,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) { permissions = CTAP_PERMISSION_MC | CTAP_PERMISSION_GA; paut.permissions = permissions; if (rpId.present == true) { - memcpy(paut.rp_id_hash, rpId.data, 32); + mbedtls_sha256((uint8_t *)rpId.data, rpId.len, paut.rp_id_hash, 0); paut.has_rp_id = true; } uint8_t pinUvAuthToken_enc[32+IV_SIZE]; diff --git a/src/fido/cbor_config.c b/src/fido/cbor_config.c index 30a9080..402a931 100644 --- a/src/fido/cbor_config.c +++ b/src/fido/cbor_config.c @@ -188,6 +188,10 @@ int cbor_config(const uint8_t *data, size_t len) { low_flash_available(); goto err; //No return } + else if (subcommand == 0x01) { + set_opts(get_opts() | FIDO2_OPT_EA); + goto err; + } else CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION); CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); diff --git a/src/fido/cbor_cred_mgmt.c b/src/fido/cbor_cred_mgmt.c index fb9a488..74523ba 100644 --- a/src/fido/cbor_cred_mgmt.c +++ b/src/fido/cbor_cred_mgmt.c @@ -43,7 +43,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { CborEncoder encoder, mapEncoder, mapEncoder2; uint8_t *raw_subpara = NULL; size_t raw_subpara_len = 0; - bool asserted = false; + bool asserted = false, is_preview = *(data - 1) == 0x41; // Backwards compatibility CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map)); uint64_t val_c = 1; @@ -115,7 +115,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { if(subcommand == 0x01) { if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *)"\x01", 1, pinUvAuthParam.data) != CborNoError) CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); - if (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true) + if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)) CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); uint8_t existing = 0; for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) { @@ -133,7 +133,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { if (subcommand == 0x02) { if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *)"\x02", 1, pinUvAuthParam.data) != CborNoError) CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); - if (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true) + if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)) CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); rp_counter = 1; rp_total = 0; @@ -156,14 +156,14 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { rp_total++; } } - if (rp_ef == NULL) // should not happen - CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED); + if (rp_ef == NULL) + CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS); rp_counter++; CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, subcommand == 0x02 ? 3 : 2)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03)); CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 1)); CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id")); - CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, file_get_data(rp_ef)+33, file_get_size(rp_ef)-33)); + CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, (char *)file_get_data(rp_ef)+33, file_get_size(rp_ef)-33)); CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04)); CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(rp_ef)+1, 32)); @@ -179,7 +179,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { *(raw_subpara-1) = 0x04; if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError) CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); - if (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)) + if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); cred_counter = 1; cred_total = 0; @@ -259,7 +259,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1)); CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2)); CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3)); - CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, cred.alg)); + CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -cred.alg)); CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1)); CBOR_CHECK(cbor_encode_uint(&mapEncoder2, cred.curve)); CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2)); @@ -290,7 +290,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { *(raw_subpara - 1) = 0x06; if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError) CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); - if (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)) + if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) { file_t *ef = search_dynamic_file(EF_CRED + i); @@ -324,7 +324,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { *(raw_subpara - 1) = 0x07; if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError) CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); - if (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)) + if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) { file_t *ef = search_dynamic_file(EF_CRED + i); diff --git a/src/fido/cbor_get_info.c b/src/fido/cbor_get_info.c index f213d1a..8764f60 100644 --- a/src/fido/cbor_get_info.c +++ b/src/fido/cbor_get_info.c @@ -23,10 +23,10 @@ #include "version.h" int cbor_get_info() { - CborEncoder encoder, mapEncoder, arrayEncoder; + CborEncoder encoder, mapEncoder, arrayEncoder, mapEncoder2; CborError error = CborNoError; cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0); - CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 11)); + CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 12)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01)); CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 3)); @@ -46,7 +46,9 @@ int cbor_get_info() { CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, aaguid, sizeof(aaguid))); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04)); - CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 6)); + CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 7)); + CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "ep")); + CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, get_opts() & FIDO2_OPT_EA)); CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "rk")); CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true)); CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credMgmt")); @@ -76,6 +78,28 @@ int cbor_get_info() { CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CRED_ID_LENGTH)); // MAX_CRED_ID_MAX_LENGTH + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0A)); + CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 3)); + CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2)); + CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg")); + CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES256)); + CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type")); + CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key")); + CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2)); + CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2)); + CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg")); + CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES384)); + CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type")); + CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key")); + CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2)); + CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2)); + CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg")); + CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES512)); + CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type")); + CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key")); + CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2)); + CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder)); + file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C)); if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index 99be982..b9de407 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -207,6 +207,9 @@ int cbor_make_credential(const uint8_t *data, size_t len) { CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED); } if (enterpriseAttestation > 0) { + if (!(get_opts() & FIDO2_OPT_EA)) { + CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER); + } if (enterpriseAttestation != 1 && enterpriseAttestation != 2) { //9.2.1 CBOR_ERROR(CTAP2_ERR_INVALID_OPTION); } @@ -259,14 +262,6 @@ int cbor_make_credential(const uint8_t *data, size_t len) { CBOR_CHECK(credential_create(&rp.id, &user.id, &user.parent.name, &user.displayName, &options, &extensions, (!ka || ka->use_sign_count == ptrue), alg, curve, cred_id, &cred_id_len)); - mbedtls_ecdsa_context ekey; - mbedtls_ecdsa_init(&ekey); - int ret = fido_load_key(curve, cred_id, &ekey); - if (ret != 0) { - mbedtls_ecdsa_free(&ekey); - CBOR_ERROR(CTAP1_ERR_OTHER); - } - if (getUserVerifiedFlagValue()) flags |= FIDO2_AUT_FLAG_UV; size_t ext_len = 0; @@ -315,9 +310,18 @@ int cbor_make_credential(const uint8_t *data, size_t len) { flags |= FIDO2_AUT_FLAG_ED; } uint8_t pkey[66]; - const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(ekey.grp.id); - if (cinfo == NULL) + mbedtls_ecdsa_context ekey; + mbedtls_ecdsa_init(&ekey); + int ret = fido_load_key(curve, cred_id, &ekey); + if (ret != 0) { + mbedtls_ecdsa_free(&ekey); CBOR_ERROR(CTAP1_ERR_OTHER); + } + const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(ekey.grp.id); + if (cinfo == NULL) { + mbedtls_ecdsa_free(&ekey); + CBOR_ERROR(CTAP1_ERR_OTHER); + } size_t olen = 0; uint32_t ctr = get_sign_counter(); uint8_t cbor_buf[1024]; @@ -354,15 +358,17 @@ int cbor_make_credential(const uint8_t *data, size_t len) { memcpy(pa, cred_id, cred_id_len); pa += cred_id_len; memcpy(pa, cbor_buf, rs); pa += rs; memcpy(pa, ext, ext_len); pa += ext_len; - if (pa-aut_data != aut_data_len) + if (pa-aut_data != aut_data_len) { + mbedtls_ecdsa_free(&ekey); CBOR_ERROR(CTAP1_ERR_OTHER); + } memcpy(pa, clientDataHash.data, clientDataHash.len); uint8_t hash[32], sig[MBEDTLS_ECDSA_MAX_LEN]; ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), aut_data, aut_data_len+clientDataHash.len, hash); bool self_attestation = true; - if (ka && ka->use_self_attestation == pfalse) { + if (enterpriseAttestation == 2 || (ka && ka->use_self_attestation == pfalse)) { mbedtls_ecdsa_free(&ekey); mbedtls_ecdsa_init(&ekey); ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), 32); @@ -372,7 +378,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { mbedtls_ecdsa_free(&ekey); cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0); - CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 3)); + CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 4)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01)); CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "packed")); @@ -387,13 +393,20 @@ int cbor_make_credential(const uint8_t *data, size_t len) { CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, sig, olen)); if (self_attestation == false) { CborEncoder arrEncoder; + file_t *ef_cert = NULL; + if (enterpriseAttestation == 2) + ef_cert = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF); + if (!file_has_data(ef_cert)) + ef_cert = ef_certdev; CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "x5c")); CBOR_CHECK(cbor_encoder_create_array(&mapEncoder2, &arrEncoder, 1)); - CBOR_CHECK(cbor_encode_byte_string(&arrEncoder, file_get_data(ef_certdev), file_get_size(ef_certdev))); + CBOR_CHECK(cbor_encode_byte_string(&arrEncoder, file_get_data(ef_cert), file_get_size(ef_cert))); CBOR_CHECK(cbor_encoder_close_container(&mapEncoder2, &arrEncoder)); } CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2)); + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04)); + CBOR_CHECK(cbor_encode_boolean(&mapEncoder, enterpriseAttestation == 2)); CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1); diff --git a/src/fido/cbor_vendor.c b/src/fido/cbor_vendor.c index 6d4ea11..0252d1b 100644 --- a/src/fido/cbor_vendor.c +++ b/src/fido/cbor_vendor.c @@ -26,6 +26,7 @@ #include "mbedtls/ecdh.h" #include "mbedtls/chachapoly.h" #include "mbedtls/hkdf.h" +#include "mbedtls/x509_csr.h" extern uint8_t keydev_dec[32]; extern bool has_keydev_dec; @@ -123,7 +124,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) { } else if (vendorCmd == 0x02) { if (vendorParam.present == false) - CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER); + CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER); uint8_t zeros[32]; memset(zeros, 0, sizeof(zeros)); flash_write_data_to_file(ef_keydev_enc, vendorParam.data, vendorParam.len); @@ -226,6 +227,54 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) { has_keydev_dec = true; goto err; } + else if (cmd == CTAP_VENDOR_EA) { + if (vendorCmd == 0x01) { + uint8_t buffer[1024]; + mbedtls_ecdsa_context ekey; + mbedtls_ecdsa_init(&ekey); + int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), file_get_size(ef_keydev)); + if (ret != 0) { + mbedtls_ecdsa_free(&ekey); + CBOR_ERROR(CTAP2_ERR_PROCESSING); + } + ret = mbedtls_ecp_mul(&ekey.grp, &ekey.Q, &ekey.d, &ekey.grp.G, random_gen, NULL); + if (ret != 0) { + mbedtls_ecdsa_free(&ekey); + CBOR_ERROR(CTAP2_ERR_PROCESSING); + } + pico_unique_board_id_t rpiid; + pico_get_unique_board_id(&rpiid); + mbedtls_x509write_csr ctx; + mbedtls_x509write_csr_init(&ctx); + snprintf((char *)buffer, sizeof(buffer), "C=ES,O=Pico Keys,OU=Authenticator Attestation,CN=Pico Fido EE Serial %llu", ((uint64_t)rpiid.id[0] << 56) | ((uint64_t)rpiid.id[1] << 48) | ((uint64_t)rpiid.id[2] << 40) | ((uint64_t)rpiid.id[3] << 32) | (rpiid.id[4] << 24) | (rpiid.id[5] << 16) | (rpiid.id[6] << 8) | rpiid.id[7]); + mbedtls_x509write_csr_set_subject_name(&ctx, (char *)buffer); + mbedtls_pk_context key; + mbedtls_pk_init(&key); + mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); + key.pk_ctx = &ekey; + mbedtls_x509write_csr_set_key(&ctx, &key); + mbedtls_x509write_csr_set_md_alg(&ctx, MBEDTLS_MD_SHA256); + mbedtls_x509write_csr_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xE5\x1C\x01\x01\x04", 0xB, 0, aaguid, sizeof(aaguid)); + ret = mbedtls_x509write_csr_der(&ctx, buffer, sizeof(buffer), random_gen, NULL); + mbedtls_ecdsa_free(&ekey); + if (ret <= 0) { + mbedtls_x509write_csr_free(&ctx); + CBOR_ERROR(CTAP2_ERR_PROCESSING); + } + CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1)); + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01)); + CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, buffer + sizeof(buffer) - ret, ret)); + } + else if (vendorCmd == 0x02) { + if (vendorParam.present == false) + CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER); + file_t *ef_ee_ea = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF); + if (ef_ee_ea) + flash_write_data_to_file(ef_ee_ea, vendorParam.data, vendorParam.len); + low_flash_available(); + goto err; + } + } else CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION); CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); diff --git a/src/fido/ctap.h b/src/fido/ctap.h index 4614349..db09d8f 100644 --- a/src/fido/ctap.h +++ b/src/fido/ctap.h @@ -124,6 +124,7 @@ typedef struct { #define CTAP_VENDOR_BACKUP 0x01 #define CTAP_VENDOR_MSE 0x02 #define CTAP_VENDOR_UNLOCK 0x03 +#define CTAP_VENDOR_EA 0x04 #define CTAP_PERMISSION_MC 0x01 // MakeCredential #define CTAP_PERMISSION_GA 0x02 // GetAssertion diff --git a/src/fido/fido.c b/src/fido/fido.c index e53512b..8b44acf 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -116,6 +116,7 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe mbedtls_x509write_crt_set_authority_key_identifier(&ctx); mbedtls_x509write_crt_set_key_usage(&ctx, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN); int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, core1 ? random_gen : random_gen_core0, NULL); + mbedtls_pk_free(&key); return ret; } @@ -325,6 +326,19 @@ uint32_t get_sign_counter() { return (*caddr) | (*(caddr + 1) << 8) | (*(caddr + 2) << 16) | (*(caddr + 3) << 24); } +uint8_t get_opts() { + file_t *ef = search_by_fid(EF_OPTS, NULL, SPECIFY_EF); + if (file_has_data(ef)) + return *file_get_data(ef); + return 0; +} + +void set_opts(uint8_t opts) { + file_t *ef = search_by_fid(EF_OPTS, NULL, SPECIFY_EF); + flash_write_data_to_file(ef, &opts, sizeof(uint8_t)); + low_flash_available(); +} + typedef struct cmd { uint8_t ins; diff --git a/src/fido/fido.h b/src/fido/fido.h index 0762e0c..c1d54e7 100644 --- a/src/fido/fido.h +++ b/src/fido/fido.h @@ -63,12 +63,7 @@ extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSec #define FIDO2_AUT_FLAG_AT 0x40 #define FIDO2_AUT_FLAG_ED 0x80 -#define FIDO2_PERMISSION_MC 0x1 -#define FIDO2_PERMISSION_GA 0x2 -#define FIDO2_PERMISSION_CM 0x4 -#define FIDO2_PERMISSION_BE 0x8 -#define FIDO2_PERMISSION_LBW 0x10 -#define FIDO2_PERMISSION_ACFG 0x20 +#define FIDO2_OPT_EA 0x01 // Enterprise Attestation #define MAX_PIN_RETRIES 8 extern bool getUserPresentFlagValue(); @@ -78,6 +73,8 @@ extern void clearUserVerifiedFlag(); extern void clearPinUvAuthTokenPermissionsExceptLbw(); extern void send_keepalive(); extern uint32_t get_sign_counter(); +extern uint8_t get_opts(); +extern void set_opts(uint8_t); #define MAX_CREDENTIAL_COUNT_IN_LIST 16 #define MAX_CRED_ID_LENGTH 1024 #define MAX_RESIDENT_CREDENTIALS 256 diff --git a/src/fido/files.c b/src/fido/files.c index 660c35f..0e7e52e 100644 --- a/src/fido/files.c +++ b/src/fido/files.c @@ -23,10 +23,12 @@ file_t file_entries[] = { {.fid = EF_KEY_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Device Key {.fid = EF_KEY_DEV_ENC, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Device Key Enc {.fid = EF_EE_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // End Entity Certificate Device + {.fid = EF_EE_DEV_EA, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // End Entity Enterprise Attestation Certificate {.fid = EF_COUNTER, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Global counter {.fid = EF_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // PIN {.fid = EF_AUTHTOKEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // AUTH TOKEN {.fid = EF_MINPINLEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // MIN PIN LENGTH + {.fid = EF_OPTS, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Global options { .fid = 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 9182928..ad96ca0 100644 --- a/src/fido/files.h +++ b/src/fido/files.h @@ -23,7 +23,9 @@ #define EF_KEY_DEV 0xCC00 #define EF_KEY_DEV_ENC 0xCC01 #define EF_EE_DEV 0xCE00 +#define EF_EE_DEV_EA 0xCE01 #define EF_COUNTER 0xC000 +#define EF_OPTS 0xC001 #define EF_PIN 0x1080 #define EF_AUTHTOKEN 0x1090 #define EF_MINPINLEN 0x1100 diff --git a/src/fido/version.h b/src/fido/version.h index 432e026..f72ed36 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 0x0206 +#define PICO_FIDO_VERSION 0x0208 #define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff) #define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff) diff --git a/tools/pico-fido-tool.py b/tools/pico-fido-tool.py index 51d49a5..9971387 100644 --- a/tools/pico-fido-tool.py +++ b/tools/pico-fido-tool.py @@ -27,6 +27,8 @@ from words import words from threading import Event from typing import Mapping, Any, Optional, Callable import struct +import urllib.request +import json from enum import IntEnum, unique try: @@ -48,6 +50,7 @@ try: from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 from cryptography.hazmat.primitives import hashes + from cryptography import x509 except: print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`') sys.exit(-1) @@ -63,6 +66,21 @@ else: print('ERROR: platform not supported') sys.exit(-1) +def get_pki_data(url, data=None, method='GET'): + user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; ' + 'rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7' + method = 'GET' + if (data is not None): + method = 'POST' + req = urllib.request.Request(f"https://www.picokeys.com/pico/pico-fido/{url}/", + method=method, + data=data, + headers={'User-Agent': user_agent, }) + response = urllib.request.urlopen(req) + resp = response.read().decode('utf-8') + j = json.loads(resp) + return j + class VendorConfig(Config): class PARAM(IntEnum): @@ -179,6 +197,7 @@ class Vendor: VENDOR_BACKUP = 0x01 VENDOR_MSE = 0x02 VENDOR_UNLOCK = 0x03 + VENDOR_EA = 0x04 @unique class PARAM(IntEnum): @@ -189,6 +208,8 @@ class Vendor: ENABLE = 0x01 DISABLE = 0x02 KEY_AGREEMENT = 0x01 + EA_CSR = 0x01 + EA_UPLOAD = 0x02 class RESP(IntEnum): PARAM = 0x01 @@ -342,6 +363,21 @@ class Vendor: def disable_device_aut(self): self.vcfg.disable_device_aut() + def csr(self): + return self._call( + Vendor.CMD.VENDOR_EA, + Vendor.SUBCMD.EA_CSR, + )[Vendor.RESP.PARAM] + + def upload_ea(self, der): + self._call( + Vendor.CMD.VENDOR_EA, + Vendor.SUBCMD.EA_UPLOAD, + { + Vendor.PARAM.PARAM: der + } + ) + def parse_args(): parser = argparse.ArgumentParser() subparser = parser.add_subparsers(title="commands", dest="command") @@ -352,6 +388,10 @@ def parse_args(): parser_backup.add_argument('subcommand', choices=['save', 'load'], help='Saves or loads a backup.') parser_backup.add_argument('filename', help='File to save or load the backup.') + parser_attestation = subparser.add_parser('attestation', help='Manages Enterprise Attestation') + parser_attestation.add_argument('subcommand', choices=['csr']) + parser_attestation.add_argument('--filename', help='Uploads the certificate filename to the device as enterprise attestation certificate. If not provided, it will generate an enterprise attestation certificate automatically.') + args = parser.parse_args() return args @@ -369,9 +409,24 @@ def backup(vdr, args): elif (args.subcommand == 'load'): vdr.backup_load(args.filename) +def attestation(vdr, args): + if (args.subcommand == 'csr'): + if (args.filename is None): + csr = x509.load_der_x509_csr(vdr.csr()) + data = urllib.parse.urlencode({'csr': csr.public_bytes(Encoding.PEM)}).encode() + j = get_pki_data('csr', data=data) + cert = x509.load_pem_x509_certificate(j['x509'].encode()) + else: + with open(args.filename, 'rb') as f: + dataf = f.read() + try: + cert = x509.load_der_x509_certificate(dataf) + except ValueError: + cert = x509.load_pem_x509_certificate(dataf) + vdr.upload_ea(cert.public_bytes(Encoding.DER)) def main(args): - print('Pico Fido Tool v1.2') + print('Pico Fido Tool v1.4') print('Author: Pol Henarejos') print('Report bugs to https://github.com/polhenarejos/pico-fido/issues') print('') @@ -385,6 +440,8 @@ def main(args): secure(vdr, args) elif (args.command == 'backup'): backup(vdr, args) + elif (args.command == 'attestation'): + attestation(vdr, args) def run(): args = parse_args()