From d45fa9aae0a2f1dd07122fb68343e4004790b3c5 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 23 Nov 2022 17:01:18 +0100 Subject: [PATCH] Added support for setMinPinLength. Signed-off-by: Pol Henarejos --- src/fido/cbor_client_pin.c | 25 +++++++++++++-- src/fido/cbor_config.c | 63 +++++++++++++++++++++++++++++++++----- src/fido/cbor_get_info.c | 17 ++++++++-- src/fido/files.c | 1 + src/fido/files.h | 1 + 5 files changed, 94 insertions(+), 13 deletions(-) diff --git a/src/fido/cbor_client_pin.c b/src/fido/cbor_client_pin.c index 5051175..fe2014d 100644 --- a/src/fido/cbor_client_pin.c +++ b/src/fido/cbor_client_pin.c @@ -369,7 +369,11 @@ int cbor_client_pin(const uint8_t *data, size_t len) { uint8_t pin_len = 0; while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin)) pin_len++; - if (pin_len < 4) + uint8_t minPin = 4; + file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF); + if (file_has_data(ef_minpin)) + minPin = *file_get_data(ef_minpin); + if (pin_len < minPin) CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION); uint8_t hsh[34]; hsh[0] = MAX_PIN_RETRIES; @@ -448,13 +452,26 @@ int cbor_client_pin(const uint8_t *data, size_t len) { uint8_t pin_len = 0; while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin)) pin_len++; - if (pin_len < 4) + uint8_t minPin = 4; + file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF); + if (file_has_data(ef_minpin)) + minPin = *file_get_data(ef_minpin); + if (pin_len < minPin) CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION); uint8_t hsh[33]; hsh[0] = MAX_PIN_RETRIES; hsh[1] = pin_len; mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 2); + if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1 && memcmp(hsh+2, file_get_data(ef_pin)+2, 16) == 0) + CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION); flash_write_data_to_file(ef_pin, hsh, 2+16); + if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) { + uint8_t *tmp = (uint8_t *)calloc(1, file_get_size(ef_minpin)); + memcpy(tmp, file_get_data(ef_minpin), file_get_size(ef_minpin)); + tmp[1] = 0; + flash_write_data_to_file(ef_minpin, tmp, file_get_size(ef_minpin)); + free(tmp); + } low_flash_available(); resetPinUvAuthToken(); goto err; // No return @@ -518,6 +535,10 @@ int cbor_client_pin(const uint8_t *data, size_t len) { new_pin_mismatches = 0; flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data)); low_flash_available(); + file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF); + 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; diff --git a/src/fido/cbor_config.c b/src/fido/cbor_config.c index 5826f97..30a9080 100644 --- a/src/fido/cbor_config.c +++ b/src/fido/cbor_config.c @@ -27,7 +27,7 @@ #include "random.h" #include "mbedtls/ecdh.h" #include "mbedtls/chachapoly.h" -#include "mbedtls/hkdf.h" +#include "mbedtls/sha256.h" extern uint8_t keydev_dec[32]; extern bool has_keydev_dec; @@ -36,11 +36,13 @@ int cbor_config(const uint8_t *data, size_t len) { CborParser parser; CborValue map; CborError error = CborNoError; - uint64_t subcommand = 0, pinUvAuthProtocol = 0, vendorCommandId = 0; + uint64_t subcommand = 0, pinUvAuthProtocol = 0, vendorCommandId = 0, newMinPinLength = 0; CborByteString pinUvAuthParam = {0}, vendorAutCt = {0}; - size_t resp_size = 0, raw_subpara_len = 0; + CborCharString minPinLengthRPIDs[32] = {0}; + size_t resp_size = 0, raw_subpara_len = 0, minPinLengthRPIDs_len = 0; CborEncoder encoder, mapEncoder; uint8_t *raw_subpara = NULL; + const bool *forceChangePin = NULL; CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map)); uint64_t val_c = 1; @@ -68,6 +70,24 @@ int cbor_config(const uint8_t *data, size_t len) { CBOR_FIELD_GET_BYTES(vendorAutCt, 2); } } + else if (subcommand == 0x03) { + CBOR_FIELD_GET_UINT(subpara, 2); + if (subpara == 0x01) { + CBOR_FIELD_GET_UINT(newMinPinLength, 2); + } + else if (subpara == 0x02) { + CBOR_PARSE_ARRAY_START(_f2, 3) { + CBOR_FIELD_GET_TEXT(minPinLengthRPIDs[minPinLengthRPIDs_len], 3); + minPinLengthRPIDs_len++; + if (minPinLengthRPIDs_len >= 32) + CBOR_ERROR(CTAP2_ERR_KEY_STORE_FULL); + } + CBOR_PARSE_ARRAY_END(_f2, 3); + } + else if (subpara == 0x03) { + CBOR_FIELD_GET_BOOL(forceChangePin, 2); + } + } } CBOR_PARSE_MAP_END(_f1, 2); raw_subpara_len = cbor_value_get_next_byte(&_f1) - raw_subpara; @@ -145,6 +165,29 @@ int cbor_config(const uint8_t *data, size_t len) { } goto err; } + else if (subcommand == 0x03) { + uint8_t currentMinPinLen = 4; + file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF); + if (file_has_data(ef_minpin)) + currentMinPinLen = *file_get_data(ef_minpin); + if (newMinPinLength == 0) + newMinPinLength = currentMinPinLen; + else if (newMinPinLength > 0 && newMinPinLength < currentMinPinLen) + CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION); + if (forceChangePin == ptrue && !file_has_data(ef_pin)) + CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET); + if (file_has_data(ef_pin) && file_get_data(ef_pin)[1] < newMinPinLength) + forceChangePin = ptrue; + uint8_t *data = (uint8_t *)calloc(1, 2 + minPinLengthRPIDs_len * 32); + data[0] = newMinPinLength; + data[1] = forceChangePin == ptrue ? 1 : 0; + for (int m = 0; m < minPinLengthRPIDs_len; m++) { + mbedtls_sha256((uint8_t *)minPinLengthRPIDs[m].data, minPinLengthRPIDs[m].len, data + 2 + m*32, 0); + } + flash_write_data_to_file(ef_minpin, data, 2 + minPinLengthRPIDs_len * 32); + low_flash_available(); + goto err; //No return + } else CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION); CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); @@ -153,12 +196,16 @@ int cbor_config(const uint8_t *data, size_t len) { err: CBOR_FREE_BYTE_STRING(pinUvAuthParam); CBOR_FREE_BYTE_STRING(vendorAutCt); - - if (error != CborNoError) { - if (error == CborErrorImproperValue) - return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; - return error; + for (int i = 0; i < minPinLengthRPIDs_len; i++) { + CBOR_FREE_BYTE_STRING(minPinLengthRPIDs[i]); } + + if (error != CborNoError) + { + if (error == CborErrorImproperValue) + return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; + return error; + } res_APDU_size = resp_size; return 0; } diff --git a/src/fido/cbor_get_info.c b/src/fido/cbor_get_info.c index e83bcc1..656df99 100644 --- a/src/fido/cbor_get_info.c +++ b/src/fido/cbor_get_info.c @@ -26,7 +26,7 @@ int cbor_get_info() { CborEncoder encoder, mapEncoder, arrayEncoder; 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, 10)); + CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 11)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01)); CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 3)); @@ -45,7 +45,7 @@ int cbor_get_info() { CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, aaguid, sizeof(aaguid))); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04)); - CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 5)); + CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 6)); CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "rk")); CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true)); CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credMgmt")); @@ -59,6 +59,8 @@ int cbor_get_info() { CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, false)); CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "pinUvAuthToken")); CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true)); + CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "setMinPINLength")); + CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true)); CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x06)); @@ -73,8 +75,17 @@ 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 + 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) + CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true)); + else + CBOR_CHECK(cbor_encode_boolean(&mapEncoder, false)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0D)); - CBOR_CHECK(cbor_encode_uint(&mapEncoder, 4)); // minPINLength + if (file_has_data(ef_minpin)) + CBOR_CHECK(cbor_encode_uint(&mapEncoder, *file_get_data(ef_minpin))); // minPINLength + else + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 4)); // minPINLength CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0E)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, PICO_FIDO_VERSION)); // firmwareVersion diff --git a/src/fido/files.c b/src/fido/files.c index 01d7503..660c35f 100644 --- a/src/fido/files.c +++ b/src/fido/files.c @@ -26,6 +26,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_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 = 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 74f0e78..9182928 100644 --- a/src/fido/files.h +++ b/src/fido/files.h @@ -26,6 +26,7 @@ #define EF_COUNTER 0xC000 #define EF_PIN 0x1080 #define EF_AUTHTOKEN 0x1090 +#define EF_MINPINLEN 0x1100 #define EF_CRED 0xCF00 // Creds at 0xCF00 - 0xCFFF #define EF_RP 0xD000 // RPs at 0xD000 - 0xD0FF