diff --git a/src/fido/cbor_client_pin.c b/src/fido/cbor_client_pin.c index 1ceb701..ef2cd4d 100644 --- a/src/fido/cbor_client_pin.c +++ b/src/fido/cbor_client_pin.c @@ -105,11 +105,7 @@ int regenerate() { mbedtls_ecdh_init(&hkey); hkey_init = true; mbedtls_ecdh_setup(&hkey, MBEDTLS_ECP_DP_SECP256R1); - int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, - &hkey.ctx.mbed_ecdh.d, - &hkey.ctx.mbed_ecdh.Q, - random_gen, - NULL); + int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.d, &hkey.ctx.mbed_ecdh.Q, random_gen, NULL); mbedtls_mpi_lset(&hkey.ctx.mbed_ecdh.Qp.Z, 1); if (ret != 0) { return ret; @@ -125,34 +121,15 @@ int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) { return ret; } if (protocol == 1) { - return mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), - buf, - sizeof(buf), - sharedSecret); + return mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), buf, sizeof(buf), sharedSecret); } else if (protocol == 2) { const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); - ret = mbedtls_hkdf(md_info, - NULL, - 0, - buf, - sizeof(buf), - (uint8_t *) "CTAP2 HMAC key", - 14, - sharedSecret, - 32); + ret = mbedtls_hkdf(md_info, NULL, 0, buf, sizeof(buf), (uint8_t *) "CTAP2 HMAC key", 14, sharedSecret, 32); if (ret != 0) { return ret; } - return mbedtls_hkdf(md_info, - NULL, - 0, - buf, - sizeof(buf), - (uint8_t *) "CTAP2 AES key", - 13, - sharedSecret + 32, - 32); + return mbedtls_hkdf(md_info, NULL, 0, buf, sizeof(buf), (uint8_t *) "CTAP2 AES key", 13, sharedSecret + 32, 32); } return -1; } @@ -160,26 +137,38 @@ int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) { int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret) { mbedtls_mpi z; mbedtls_mpi_init(&z); - int ret = mbedtls_ecdh_compute_shared(&hkey.ctx.mbed_ecdh.grp, - &z, - Q, - &hkey.ctx.mbed_ecdh.d, - random_gen, - NULL); + int ret = mbedtls_ecdh_compute_shared(&hkey.ctx.mbed_ecdh.grp, &z, Q, &hkey.ctx.mbed_ecdh.d, random_gen, NULL); ret = kdf(protocol, &z, sharedSecret); mbedtls_mpi_free(&z); return ret; } -int resetPinUvAuthToken() { +void resetAuthToken(bool persistent) { + uint16_t fid = EF_AUTHTOKEN; + if (persistent) { + fid = EF_PAUTHTOKEN; + } + file_t *ef = search_by_fid(fid, NULL, SPECIFY_EF); uint8_t t[32]; random_gen(NULL, t, sizeof(t)); - file_put_data(ef_authtoken, t, sizeof(t)); + file_put_data(ef, t, sizeof(t)); + low_flash_available(); +} + +int resetPinUvAuthToken() { + resetAuthToken(false); paut.permissions = 0; paut.data = file_get_data(ef_authtoken); paut.len = file_get_size(ef_authtoken); + return 0; +} - low_flash_available(); +int resetPersistentPinUvAuthToken() { + resetAuthToken(true); + file_t *ef_pauthtoken = search_by_fid(EF_PAUTHTOKEN, NULL, SPECIFY_EF); + ppaut.permissions = 0; + ppaut.data = file_get_data(ef_pauthtoken); + ppaut.len = file_get_size(ef_pauthtoken); return 0; } @@ -578,6 +567,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) { } low_flash_available(); resetPinUvAuthToken(); + resetPersistentPinUvAuthToken(); goto err; // No return } else if (subcommand == 0x9 || subcommand == 0x5) { //getPinUvAuthTokenUsingPinWithPermissions @@ -598,7 +588,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) { if ((permissions & CTAP_PERMISSION_BE)) { // Not supported yet CBOR_ERROR(CTAP2_ERR_UNAUTHORIZED_PERMISSION); } - + if ((permissions & CTAP_PERMISSION_PCMR) && permissions != CTAP_PERMISSION_PCMR) { + CBOR_ERROR(CTAP2_ERR_UNAUTHORIZED_PERMISSION); + } } if (!file_has_data(ef_pin)) { CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET); @@ -664,21 +656,29 @@ int cbor_client_pin(const uint8_t *data, size_t len) { if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) { CBOR_ERROR(CTAP2_ERR_PIN_INVALID); } - resetPinUvAuthToken(); - beginUsingPinUvAuthToken(false); - if (subcommand == 0x05) { - permissions = CTAP_PERMISSION_MC | CTAP_PERMISSION_GA; - } - paut.permissions = (uint8_t)permissions; - if (rpId.present == true) { - 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], *pdata = NULL; + ; + if (permissions & CTAP_PERMISSION_PCMR) { + ppaut.permissions = CTAP_PERMISSION_PCMR; + pdata = ppaut.data; } else { - paut.has_rp_id = false; + resetPinUvAuthToken(); + beginUsingPinUvAuthToken(false); + if (subcommand == 0x05) { + permissions = CTAP_PERMISSION_MC | CTAP_PERMISSION_GA; + } + paut.permissions = (uint8_t)permissions; + if (rpId.present == true) { + mbedtls_sha256((uint8_t *) rpId.data, rpId.len, paut.rp_id_hash, 0); + paut.has_rp_id = true; + } + else { + paut.has_rp_id = false; + } + pdata = paut.data; } - uint8_t pinUvAuthToken_enc[32 + IV_SIZE]; - encrypt((uint8_t)pinUvAuthProtocol, sharedSecret, paut.data, 32, pinUvAuthToken_enc); + encrypt((uint8_t)pinUvAuthProtocol, sharedSecret, pdata, 32, pinUvAuthToken_enc); CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02)); CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pinUvAuthToken_enc, 32 + poff)); diff --git a/src/fido/cbor_config.c b/src/fido/cbor_config.c index a4afeaf..0661846 100644 --- a/src/fido/cbor_config.c +++ b/src/fido/cbor_config.c @@ -31,6 +31,8 @@ extern uint8_t keydev_dec[32]; extern bool has_keydev_dec; +extern void resetPersistentPinUvAuthToken(); +extern void resetPinUvAuthToken(); int cbor_config(const uint8_t *data, size_t len) { CborParser parser; @@ -207,6 +209,10 @@ int cbor_config(const uint8_t *data, size_t len) { if (file_has_data(ef_pin) && file_get_data(ef_pin)[1] < newMinPinLength) { forceChangePin = ptrue; } + if (forceChangePin) { + resetPersistentPinUvAuthToken(); + resetPinUvAuthToken(); + } uint8_t *dataf = (uint8_t *) calloc(1, 2 + minPinLengthRPIDs_len * 32); dataf[0] = (uint8_t)newMinPinLength; dataf[1] = forceChangePin == ptrue ? 1 : 0; diff --git a/src/fido/cbor_cred_mgmt.c b/src/fido/cbor_cred_mgmt.c index 5469326..5cd9f8a 100644 --- a/src/fido/cbor_cred_mgmt.c +++ b/src/fido/cbor_cred_mgmt.c @@ -79,8 +79,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { if (strcmp(_fd3, "transports") == 0) { CBOR_PARSE_ARRAY_START(_f3, 4) { - CBOR_FIELD_GET_TEXT(credentialId.transports[credentialId. - transports_len], 4); + CBOR_FIELD_GET_TEXT(credentialId.transports[credentialId.transports_len], 4); credentialId.transports_len++; } CBOR_PARSE_ARRAY_END(_f3, 4); @@ -122,12 +121,19 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0); if (subcommand == 0x01) { - if (verify((uint8_t)pinUvAuthProtocol, paut.data, (const uint8_t *) "\x01", 1, pinUvAuthParam.data) != CborNoError) { - CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + if (verify((uint8_t)pinUvAuthProtocol, ppaut.data, (const uint8_t *) "\x01", 1, pinUvAuthParam.data) == CborNoError) { + if (!(ppaut.permissions & CTAP_PERMISSION_PCMR)) { + CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + } } - if (is_preview == false && - (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)) { - CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + else { + if (verify((uint8_t)pinUvAuthProtocol, paut.data, (const uint8_t *) "\x01", 1, pinUvAuthParam.data) != CborNoError) { + CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + } + 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++) { @@ -144,11 +150,18 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { else if (subcommand == 0x02 || subcommand == 0x03) { file_t *rp_ef = NULL; if (subcommand == 0x02) { - if (verify((uint8_t)pinUvAuthProtocol, paut.data, (const uint8_t *) "\x02", 1, pinUvAuthParam.data) != CborNoError) { - CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + if (verify((uint8_t)pinUvAuthProtocol, ppaut.data, (const uint8_t *) "\x02", 1, pinUvAuthParam.data) == CborNoError) { + if (!(ppaut.permissions & CTAP_PERMISSION_PCMR)) { + CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + } } - if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)) { - CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + else { + if (verify((uint8_t)pinUvAuthProtocol, paut.data, (const uint8_t *) "\x02", 1, pinUvAuthParam.data) != CborNoError) { + CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + } + 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; @@ -199,13 +212,20 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) { } if (subcommand == 0x04) { *(raw_subpara - 1) = 0x04; - if (verify((uint8_t)pinUvAuthProtocol, paut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) != CborNoError) { - CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + if (verify((uint8_t)pinUvAuthProtocol, ppaut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) == CborNoError) { + if (!(ppaut.permissions & CTAP_PERMISSION_PCMR)) { + CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + } } - 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); + else { + if (verify((uint8_t)pinUvAuthProtocol, paut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) != CborNoError) { + CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + } + 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; diff --git a/src/fido/ctap.h b/src/fido/ctap.h index ce5617a..4c6aa31 100644 --- a/src/fido/ctap.h +++ b/src/fido/ctap.h @@ -134,6 +134,7 @@ typedef struct { #define CTAP_PERMISSION_BE 0x08 // BioEnrollment #define CTAP_PERMISSION_LBW 0x10 // LargeBlobWrite #define CTAP_PERMISSION_ACFG 0x20 // AuthenticatorConfiguration +#define CTAP_PERMISSION_PCMR 0x40 // PerCredentialManagementReadOnly typedef struct mse { uint8_t Qpt[65]; diff --git a/src/fido/fido.c b/src/fido/fido.c index b4e1d1c..799edbd 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -42,6 +42,7 @@ int fido_process_apdu(); int fido_unload(); pinUvAuthToken_t paut = { 0 }; +persistentPinUvAuthToken_t ppaut = { 0 }; uint8_t keydev_dec[32]; bool has_keydev_dec = false; @@ -419,6 +420,19 @@ int scan_files_fido() { else { printf("FATAL ERROR: Auth Token not found in memory!\r\n"); } + file_t *ef_pauthtoken = search_by_fid(EF_PAUTHTOKEN, NULL, SPECIFY_EF); + if (ef_pauthtoken) { + if (!file_has_data(ef_pauthtoken)) { + uint8_t t[32]; + random_gen(NULL, t, sizeof(t)); + file_put_data(ef_pauthtoken, t, sizeof(t)); + } + ppaut.data = file_get_data(ef_pauthtoken); + ppaut.len = file_get_size(ef_pauthtoken); + } + else { + printf("FATAL ERROR: Persistent Auth Token not found in memory!\r\n"); + } ef_largeblob = search_by_fid(EF_LARGEBLOB, NULL, SPECIFY_EF); if (!file_has_data(ef_largeblob)) { file_put_data(ef_largeblob, (const uint8_t *) "\x80\x76\xbe\x8b\x52\x8d\x00\x75\xf7\xaa\xe9\x8d\x6f\xa5\x7a\x6d\x3c", 17); diff --git a/src/fido/fido.h b/src/fido/fido.h index d2e2979..4bb2d9b 100644 --- a/src/fido/fido.h +++ b/src/fido/fido.h @@ -129,9 +129,17 @@ typedef struct pinUvAuthToken { bool user_verified; } pinUvAuthToken_t; +typedef struct persistentPinUvAuthToken { + uint8_t *data; + size_t len; + uint8_t permissions; +} persistentPinUvAuthToken_t; + extern uint32_t user_present_time_limit; extern pinUvAuthToken_t paut; +extern persistentPinUvAuthToken_t ppaut; + extern int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, uint16_t len, uint8_t *sign); extern uint8_t session_pin[32]; diff --git a/src/fido/files.c b/src/fido/files.c index bf403bd..63f4d5c 100644 --- a/src/fido/files.c +++ b/src/fido/files.c @@ -27,6 +27,7 @@ file_t file_entries[] = { { .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_PAUTHTOKEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // PERSISTENT AUTH TOKEN { .fid = EF_MINPINLEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // MIN PIN LENGTH { .fid = EF_OPTS, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Global options { .fid = EF_LARGEBLOB, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Large Blob diff --git a/src/fido/files.h b/src/fido/files.h index 93ceec9..176f1d5 100644 --- a/src/fido/files.h +++ b/src/fido/files.h @@ -29,6 +29,7 @@ #define EF_OPTS 0xC001 #define EF_PIN 0x1080 #define EF_AUTHTOKEN 0x1090 +#define EF_PAUTHTOKEN 0x1091 #define EF_MINPINLEN 0x1100 #define EF_DEV_CONF 0x1122 #define EF_CRED 0xCF00 // Creds at 0xCF00 - 0xCFFF