From 72ebb2b596caa8e980be651d79e9888c73cce795 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 20 Sep 2022 17:31:09 +0200 Subject: [PATCH] Adding Credential management. Signed-off-by: Pol Henarejos --- CMakeLists.txt | 1 + src/fido/cbor_get_info.c | 4 +- src/fido/cbor_make_credential.c | 77 ++++-------------- src/fido/cbor_make_credential.h | 6 -- src/fido/credential.c | 137 ++++++++++++++++++++++++++++++++ src/fido/credential.h | 45 +++++++++++ src/fido/ctap2_cbor.h | 9 ++- src/fido/fido.h | 2 + 8 files changed, 210 insertions(+), 71 deletions(-) create mode 100644 src/fido/credential.c create mode 100644 src/fido/credential.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ab1e93e..12dffc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ target_sources(pico_fido PUBLIC ${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_make_credential.c ${CMAKE_CURRENT_LIST_DIR}/src/fido/known_apps.c ${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_client_pin.c + ${CMAKE_CURRENT_LIST_DIR}/src/fido/credential.c ) set(HSM_DRIVER "hid") include(pico-hsm-sdk/pico_hsm_sdk_import.cmake) diff --git a/src/fido/cbor_get_info.c b/src/fido/cbor_get_info.c index 5538fb9..277976a 100644 --- a/src/fido/cbor_get_info.c +++ b/src/fido/cbor_get_info.c @@ -66,10 +66,10 @@ int cbor_get_info() { CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07)); - CBOR_CHECK(cbor_encode_uint(&mapEncoder, 10)); // MAX_CRED_COUNT_IN_LIST + CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CREDENTIAL_COUNT_IN_LIST)); // MAX_CRED_COUNT_IN_LIST CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08)); - CBOR_CHECK(cbor_encode_uint(&mapEncoder, 1024)); // CRED_ID_MAX_LENGTH + CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CRED_ID_LENGTH)); // MAX_CRED_ID_MAX_LENGTH CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0D)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 4)); // minPINLength diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index 782ba22..e8b122e 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -16,7 +16,6 @@ */ #include "common.h" -#include "mbedtls/chachapoly.h" #include "ctap2_cbor.h" #include "cbor_make_credential.h" #include "fido.h" @@ -26,22 +25,7 @@ #include "hsm.h" #include #include "apdu.h" - -bool credential_verify(CborByteString *cred_id, const uint8_t *rp_id_hash) { - if (cred_id->len < 4+12+16) - return false; - size_t cipher_len = cred_id->len - (4 + 12 + 16); - uint8_t key[32], *iv = cred_id->data + 4, *cipher = cred_id->data + 4 + 12, *tag = cred_id->data - 16, *data = (uint8_t *)calloc(1, cipher_len); - memset(key, 0, sizeof(key)); - mbedtls_chachapoly_context chatx; - mbedtls_chachapoly_init(&chatx); - mbedtls_chachapoly_setkey(&chatx, key); - int ret = mbedtls_chachapoly_auth_decrypt(&chatx, cred_id->len - (4 + 12 + 16), iv, rp_id_hash, 32, tag, cipher, data); - free(data); - if (ret == 0) - return true; - return false; -} +#include "credential.h" int verify_user(CborByteString *clientDataHash, CborByteString *pinUvAuthParam) { return CborNoError; @@ -54,14 +38,14 @@ int cbor_make_credential(const uint8_t *data, size_t len) { CborByteString clientDataHash = {0}, pinUvAuthParam = {0}; PublicKeyCredentialRpEntity rp = {0}; PublicKeyCredentialUserEntity user = {0}; - PublicKeyCredentialParameters pubKeyCredParams[16] = {0}; + PublicKeyCredentialParameters pubKeyCredParams[MAX_CREDENTIAL_COUNT_IN_LIST] = {0}; size_t pubKeyCredParams_len = 0; - PublicKeyCredentialDescriptor excludeList[16] = {0}; + PublicKeyCredentialDescriptor excludeList[MAX_CREDENTIAL_COUNT_IN_LIST] = {0}; size_t excludeList_len = 0; CredOptions options = {0}; uint64_t pinUvAuthProtocol = 0, enterpriseAttestation = 0, credProtect = 0; const bool *hmac_secret = NULL; - uint8_t *cred_id = NULL, *aut_data = NULL; + uint8_t *aut_data = NULL; size_t resp_size = 0; CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map)); @@ -236,7 +220,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER); if (strcmp(excludeList[e].type.data, "public-key") != 0) continue; - if (credential_verify(&excludeList[e].id, rp_id_hash) == true) + if (credential_verify(&excludeList[e].id, rp_id_hash) == 0) CBOR_ERROR(CTAP2_ERR_CREDENTIAL_EXCLUDED); } @@ -247,41 +231,11 @@ int cbor_make_credential(const uint8_t *data, size_t len) { } const known_app_t *ka = find_app_by_rp_id_hash(rp_id_hash); - CborEncoder encoder, mapEncoder, mapEncoder2; - uint8_t cbor_buf[1024]; - cbor_encoder_init(&encoder, cbor_buf, sizeof(cbor_buf), 0); - CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, CborIndefiniteLength)); - CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x01, rp.id); - CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02)); - CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, rp_id_hash, 32)); - CBOR_APPEND_KEY_UINT_VAL_BYTES(mapEncoder, 0x03, user.id); - CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x04, user.displayName); - CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x05, user.displayName); - CBOR_APPEND_KEY_UINT_VAL_UINT(mapEncoder, 0x06, 1); - CBOR_APPEND_KEY_UINT_VAL_PBOOL(mapEncoder, 0x07, hmac_secret); - CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08)); - CBOR_CHECK(cbor_encode_boolean(&mapEncoder, (!ka || ka->use_sign_count == ptrue))); - if (alg != FIDO2_ALG_ES256 || curve != FIDO2_CURVE_P256) { - CBOR_APPEND_KEY_UINT_VAL_UINT(mapEncoder, 0x09, alg); - CBOR_APPEND_KEY_UINT_VAL_UINT(mapEncoder, 0x0A, curve); - } - CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); - size_t rs = cbor_encoder_get_buffer_size(&encoder, cbor_buf); - size_t cred_id_len = 4 + 12 + rs + 16; - cred_id = (uint8_t *)calloc(1, 4 + 12 + rs + 16); - uint8_t key[32]; - memset(key, 0, sizeof(key)); - uint8_t iv[12]; - random_gen(NULL, iv, sizeof(12)); - mbedtls_chachapoly_context chatx; - mbedtls_chachapoly_init(&chatx); - mbedtls_chachapoly_setkey(&chatx, key); - int ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, rs, iv, rp_id_hash, 32, cbor_buf, cred_id + 4 + 12, cred_id + 4 + 12 + rs); - if (ret != 0) { - CBOR_ERROR(CTAP1_ERR_OTHER); - } - memcpy(cred_id, "\xf1\xd0\x02\x00", 4); - memcpy(cred_id + 4, iv, 12); + + uint8_t cred_id[MAX_CRED_ID_LENGTH]; + size_t cred_id_len = 0; + + CBOR_ERROR(credential_create(&rp.id, &user.id, &user.parent.name, &user.displayName, hmac_secret, (!ka || ka->use_sign_count == ptrue), alg, curve, cred_id, &cred_id_len)); mbedtls_ecp_group_id mbedtls_curve = MBEDTLS_ECP_DP_SECP256R1; if (curve == FIDO2_CURVE_P256) @@ -306,7 +260,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { *(uint32_t *)key_path = 0x80000000 | 10022; for (int i = 1; i < KEY_PATH_ENTRIES; i++) *(uint32_t *)(key_path+i*sizeof(uint32_t)) |= 0x80000000; - ret = derive_key(NULL, false, key_path, mbedtls_curve, &ekey); + int ret = derive_key(NULL, false, key_path, mbedtls_curve, &ekey); if (ret != 0) { mbedtls_ecdsa_free(&ekey); CBOR_ERROR(CTAP1_ERR_OTHER); @@ -317,6 +271,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { flags |= FIDO2_AUT_FLAG_UV; size_t ext_len = 0; uint8_t ext [512]; + CborEncoder encoder, mapEncoder, mapEncoder2; if (hmac_secret != NULL || credProtect != 0) { cbor_encoder_init(&encoder, ext, sizeof(ext), 0); int l = 0; @@ -345,6 +300,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { CBOR_ERROR(CTAP1_ERR_OTHER); size_t olen = 0, pkey_len = ceil((float)cinfo->bit_size/8); uint32_t ctr = *(uint32_t *)file_get_data(ef_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)); @@ -361,7 +317,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pkey, pkey_len)); CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); - rs = cbor_encoder_get_buffer_size(&encoder, cbor_buf); + 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; aut_data = (uint8_t *)calloc(1, aut_data_len + clientDataHash.len); @@ -386,8 +342,7 @@ int cbor_make_credential(const uint8_t *data, size_t 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 (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); @@ -443,8 +398,6 @@ int cbor_make_credential(const uint8_t *data, size_t len) { } if (aut_data) free(aut_data); - if (cred_id) - free(cred_id); if (error != CborNoError) { if (error == CborErrorImproperValue) return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; diff --git a/src/fido/cbor_make_credential.h b/src/fido/cbor_make_credential.h index 4704905..52b3704 100644 --- a/src/fido/cbor_make_credential.h +++ b/src/fido/cbor_make_credential.h @@ -56,11 +56,5 @@ typedef struct PublicKeyCredentialDescriptor { size_t transports_len; } PublicKeyCredentialDescriptor; -typedef struct CredOptions { - const bool *rk; - const bool *up; - const bool *uv; - bool present; -} CredOptions; #endif //_CBOR_MAKE_CREDENTIAL_H_ diff --git a/src/fido/credential.c b/src/fido/credential.c new file mode 100644 index 0000000..71d4456 --- /dev/null +++ b/src/fido/credential.c @@ -0,0 +1,137 @@ +/* + * 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 . + */ + +#include "common.h" +#include "mbedtls/chachapoly.h" +#include "mbedtls/sha256.h" +#include "credential.h" +#include "bsp/board.h" +#include "fido.h" +#include "ctap.h" +#include "random.h" + +int credential_verify(CborByteString *cred_id, const uint8_t *rp_id_hash) { + if (cred_id->len < 4+12+16) + return -1; + uint8_t key[32], *iv = cred_id->data + 4, *cipher = cred_id->data + 4 + 12, *tag = cred_id->data - 16; + memset(key, 0, sizeof(key)); + mbedtls_chachapoly_context chatx; + mbedtls_chachapoly_init(&chatx); + mbedtls_chachapoly_setkey(&chatx, key); + return mbedtls_chachapoly_auth_decrypt(&chatx, cred_id->len - (4 + 12 + 16), iv, rp_id_hash, 32, tag, cipher, cipher); +} + +int credential_create(CborCharString *rpId, CborByteString *userId, CborCharString *userName, CborCharString *userDisplayName, const bool *hmac_secret, bool use_sign_count, int alg, int curve, uint8_t *cred_id, size_t *cred_id_len) { + CborEncoder encoder, mapEncoder; + CborError error = CborNoError; + uint8_t rp_id_hash[32]; + mbedtls_sha256((uint8_t *)rpId->data, rpId->len, rp_id_hash, 0); + cbor_encoder_init(&encoder, cred_id+4+12, sizeof(cred_id)-(4+12+16), 0); + CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, CborIndefiniteLength)); + CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x01, *rpId); + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02)); + CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, rp_id_hash, 32)); + CBOR_APPEND_KEY_UINT_VAL_BYTES(mapEncoder, 0x03, *userId); + CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x04, *userName); + CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x05, *userDisplayName); + CBOR_APPEND_KEY_UINT_VAL_UINT(mapEncoder, 0x06, board_millis()); + CBOR_APPEND_KEY_UINT_VAL_PBOOL(mapEncoder, 0x07, hmac_secret); + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08)); + CBOR_CHECK(cbor_encode_boolean(&mapEncoder, use_sign_count)); + if (alg != FIDO2_ALG_ES256 || curve != FIDO2_CURVE_P256) { + CBOR_APPEND_KEY_UINT_VAL_INT(mapEncoder, 0x09, alg); + CBOR_APPEND_KEY_UINT_VAL_INT(mapEncoder, 0x0A, curve); + } + CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); + size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id); + *cred_id_len = 4 + 12 + rs + 16; + uint8_t key[32]; + memset(key, 0, sizeof(key)); + uint8_t iv[12]; + random_gen(NULL, iv, sizeof(12)); + mbedtls_chachapoly_context chatx; + mbedtls_chachapoly_init(&chatx); + mbedtls_chachapoly_setkey(&chatx, key); + int ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, rs, iv, rp_id_hash, 32, cred_id + 4 + 12, cred_id + 4 + 12, cred_id + 4 + 12 + rs); + mbedtls_chachapoly_free(&chatx); + if (ret != 0) { + CBOR_ERROR(CTAP1_ERR_OTHER); + } + memcpy(cred_id, "\xf1\xd0\x02\x00", 4); + memcpy(cred_id + 4, iv, 12); + err: + if (error != CborNoError) { + if (error == CborErrorImproperValue) + return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; + return error; + } + return 0; +} + +int credential_load(CborByteString *cred_id, const uint8_t *rp_id_hash, Credential *cred) { + int ret = 0; + ret = credential_verify(cred_id, rp_id_hash); + if (ret != 0) + return ret; + CborParser parser; + CborValue map; + CborError error; + memset(cred, 0, sizeof(Credential)); + CBOR_CHECK(cbor_parser_init(cred_id->data + 4 + 12, cred_id->len - (4 + 12 + 16), 0, &parser, &map)); + CBOR_PARSE_MAP_START(map, 1) { + uint64_t val_u = 0; + CBOR_FIELD_GET_UINT(val_u, 1); + if (val_u == 0x01) { + CBOR_FIELD_GET_TEXT(cred->rpId, 1); + } + else if (val_u == 0x03) { + CBOR_FIELD_GET_BYTES(cred->userId, 1); + } + else if (val_u == 0x06) { + CBOR_FIELD_GET_UINT(cred->creation, 1); + } + else if (val_u == 0x07) { + CBOR_FIELD_GET_BOOL(cred->hmac_secret, 1); + } + else if (val_u == 0x08) { + CBOR_FIELD_GET_BOOL(cred->use_sign_count, 1); + } + else if (val_u == 0x09) { + CBOR_FIELD_GET_INT(cred->alg, 1); + } + else if (val_u == 0x0A) { + CBOR_FIELD_GET_INT(cred->curve, 1); + } + else { + CBOR_ADVANCE(1); + } + } + + cred->present = true; + err: + if (error != CborNoError) { + if (error == CborErrorImproperValue) + return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; + return error; + } + return 0; +} + +void credential_free(Credential *cred) { + CBOR_FREE_BYTE_STRING(cred->rpId); + CBOR_FREE_BYTE_STRING(cred->userId); +} diff --git a/src/fido/credential.h b/src/fido/credential.h new file mode 100644 index 0000000..28474df --- /dev/null +++ b/src/fido/credential.h @@ -0,0 +1,45 @@ +/* + * 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 _CREDENTIAL_H_ +#define _CREDENTIAL_H_ + +#include "ctap2_cbor.h" + +typedef struct CredOptions { + const bool *rk; + const bool *up; + const bool *uv; + bool present; +} CredOptions; + +typedef struct Credential { + CborCharString rpId; + CborByteString userId; + uint64_t creation; + const bool *hmac_secret; + const bool *use_sign_count; + int64_t alg; + int64_t curve; + bool present; +} Credential; + +extern int credential_verify(CborByteString *cred_id, const uint8_t *rp_id_hash); +extern int credential_create(CborCharString *rpId, CborByteString *userId, CborCharString *userName, CborCharString *userDisplayName, const bool *hmac_secret, bool use_sign_count, int alg, int curve, uint8_t *cred_id, size_t *cred_id_len); +extern void credential_free(Credential *cred); + +#endif // _CREDENTIAL_H_ diff --git a/src/fido/ctap2_cbor.h b/src/fido/ctap2_cbor.h index 8a897a0..78c2f17 100644 --- a/src/fido/ctap2_cbor.h +++ b/src/fido/ctap2_cbor.h @@ -104,7 +104,8 @@ typedef struct CborCharString { CBOR_FREE((v).data); \ else \ (v).data = NULL; \ - (v).len = 0; \ + (v).len = 0; \ + (v).present = false; \ } while(0) #define CBOR_PARSE_MAP_START(_p,_n) \ @@ -224,6 +225,12 @@ typedef struct CborCharString { CBOR_CHECK(cbor_encode_uint(&(p), (v))); \ } while(0) +#define CBOR_APPEND_KEY_UINT_VAL_INT(p, k, v) \ + do { \ + CBOR_CHECK(cbor_encode_int(&(p), (k))); \ + CBOR_CHECK(cbor_encode_int(&(p), (v))); \ + } while(0) + #define CBOR_APPEND_KEY_UINT_VAL_BOOL(p, k, v) \ do { \ CBOR_CHECK(cbor_encode_uint(&(p), (k))); \ diff --git a/src/fido/fido.h b/src/fido/fido.h index 765d45a..ce0ca08 100644 --- a/src/fido/fido.h +++ b/src/fido/fido.h @@ -65,6 +65,8 @@ extern void init_fido(); #define MAX_PIN_RETRIES 8 extern bool getUserVerifiedFlagValue(); +#define MAX_CREDENTIAL_COUNT_IN_LIST 16 +#define MAX_CRED_ID_LENGTH 1024 typedef struct known_app { const uint8_t *rp_id_hash;