Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71c0e865dc | ||
|
|
052ff2d60a | ||
|
|
8b70c864a4 | ||
|
|
765db0e98b | ||
|
|
6b2e95deb0 | ||
|
|
d45fa9aae0 | ||
|
|
23c7e16e6e | ||
|
|
5923f435fe | ||
|
|
04868f2d7b |
@@ -19,6 +19,10 @@ Pico FIDO has implemented the following features:
|
||||
- Support for vendor Config
|
||||
- Backup with 24 words
|
||||
- Secure lock to protect the device from flash dumpings
|
||||
- Permissions support (MC, GA, CM, ACFG)
|
||||
- Authenticator configuration
|
||||
- minPinLength extension
|
||||
- Self attestation
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Submodule pico-hsm-sdk updated: 4a8a6728c7...3def9bff4f
@@ -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() {
|
||||
@@ -369,12 +369,17 @@ 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];
|
||||
uint8_t hsh[34];
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 1);
|
||||
flash_write_data_to_file(ef_pin, hsh, 1+16);
|
||||
hsh[1] = pin_len;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 2);
|
||||
flash_write_data_to_file(ef_pin, hsh, 2+16);
|
||||
low_flash_available();
|
||||
goto err; //No return
|
||||
}
|
||||
@@ -408,16 +413,19 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
uint8_t retries = *file_get_data(ef_pin) - 1;
|
||||
flash_write_data_to_file(ef_pin, &retries, 1);
|
||||
uint8_t pin_data[18];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
pin_data[0] -= 1;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
uint8_t retries = pin_data[0];
|
||||
uint8_t paddedNewPin[64];
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
low_flash_available();
|
||||
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) {
|
||||
@@ -430,9 +438,10 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
else
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
retries = MAX_PIN_RETRIES;
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
new_pin_mismatches = 0;
|
||||
flash_write_data_to_file(ef_pin, &retries, 1);
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, paddedNewPin);
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (ret != 0) {
|
||||
@@ -443,23 +452,44 @@ 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;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 1);
|
||||
flash_write_data_to_file(ef_pin, hsh, 1+16);
|
||||
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
|
||||
}
|
||||
else if (subcommand == 0x9 || subcommand == 0x5) { //getUVRgetPinUvAuthTokenUsingPinWithPermissionsetries
|
||||
else if (subcommand == 0x9 || subcommand == 0x5) { //getPinUvAuthTokenUsingPinWithPermissions
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || alg == 0 || pinHashEnc.present == false)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if ((subcommand == 0x9 && permissions == 0) || (subcommand == 0x5 && (permissions != 0 || rpId.present == true)))
|
||||
if (subcommand == 0x5 && (permissions != 0 || rpId.present == true))
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (subcommand == 0x9) {
|
||||
if (permissions == 0)
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if ((permissions & CTAP_PERMISSION_BE) || (permissions & CTAP_PERMISSION_LBW)) // Not supported yet
|
||||
CBOR_ERROR(CTAP2_ERR_UNAUTHORIZED_PERMISSION);
|
||||
|
||||
}
|
||||
if (!file_has_data(ef_pin))
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
if (*file_get_data(ef_pin) == 0)
|
||||
@@ -476,16 +506,19 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t retries = *file_get_data(ef_pin) - 1;
|
||||
flash_write_data_to_file(ef_pin, &retries, 1);
|
||||
uint8_t pin_data[18];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
pin_data[0] -= 1;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
uint8_t retries = pin_data[0];
|
||||
uint8_t paddedNewPin[64], poff = (pinUvAuthProtocol-1)*IV_SIZE;
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
low_flash_available();
|
||||
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) {
|
||||
@@ -498,16 +531,22 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
else
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
retries = MAX_PIN_RETRIES;
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
new_pin_mismatches = 0;
|
||||
flash_write_data_to_file(ef_pin, &retries, 1);
|
||||
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;
|
||||
paut.permissions = permissions;
|
||||
if (rpId.present == true)
|
||||
memcpy(paut.rp_id_hash, rpId.data, 32);
|
||||
else
|
||||
memset(paut.rp_id_hash, 0, sizeof(paut.rp_id_hash));
|
||||
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];
|
||||
encrypt(pinUvAuthProtocol, sharedSecret, paut.data, 32, pinUvAuthToken_enc);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
|
||||
@@ -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,10 +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;
|
||||
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;
|
||||
@@ -56,6 +59,7 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
}
|
||||
else if (val_u == 0x02) {
|
||||
uint64_t subpara = 0;
|
||||
raw_subpara = (uint8_t *)cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
if (subcommand == 0xff) {
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
@@ -66,8 +70,27 @@ 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;
|
||||
}
|
||||
else if (val_u == 0x03) {
|
||||
CBOR_FIELD_GET_UINT(pinUvAuthProtocol, 1);
|
||||
@@ -80,6 +103,24 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
|
||||
if (pinUvAuthParam.present == false)
|
||||
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
|
||||
if (pinUvAuthProtocol == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
|
||||
uint8_t *verify_payload = (uint8_t *)calloc(1, 32 + 1 + 1 + raw_subpara_len);
|
||||
memset(verify_payload, 0xff, 32);
|
||||
verify_payload[32] = 0x0d;
|
||||
verify_payload[33] = subcommand;
|
||||
memcpy(verify_payload + 34, raw_subpara, raw_subpara_len);
|
||||
error = verify(pinUvAuthProtocol, paut.data, verify_payload, 32 + 1 + 1 + raw_subpara_len, pinUvAuthParam.data);
|
||||
free(verify_payload);
|
||||
if (error != CborNoError)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
|
||||
if (!(paut.permissions & CTAP_PERMISSION_ACFG))
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
|
||||
if (subcommand == 0xff) {
|
||||
if (vendorCommandId == CTAP_CONFIG_AUT_DISABLE) {
|
||||
if (!file_has_data(ef_keydev_enc))
|
||||
@@ -124,6 +165,33 @@ 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 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));
|
||||
@@ -132,12 +200,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;
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@ 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)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
uint8_t existing = 0;
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
if (file_has_data(search_dynamic_file(EF_CRED + i)))
|
||||
@@ -131,6 +133,8 @@ 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)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
rp_counter = 1;
|
||||
rp_total = 0;
|
||||
}
|
||||
@@ -175,6 +179,8 @@ 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))
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
cred_counter = 1;
|
||||
cred_total = 0;
|
||||
}
|
||||
@@ -284,6 +290,8 @@ 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))
|
||||
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);
|
||||
if (file_has_data(ef) && memcmp(file_get_data(ef)+32, credentialId.id.data, MIN(file_get_size(ef)-32, credentialId.id.len)) == 0) {
|
||||
@@ -316,6 +324,8 @@ 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))
|
||||
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);
|
||||
if (file_has_data(ef) && memcmp(file_get_data(ef)+32, credentialId.id.data, MIN(file_get_size(ef)-32, credentialId.id.len)) == 0) {
|
||||
|
||||
@@ -245,6 +245,10 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (getUserVerifiedFlagValue() == false)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (!(paut.permissions & CTAP_PERMISSION_GA))
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
flags |= FIDO2_AUT_FLAG_UV;
|
||||
// Check pinUvAuthToken permissions. See 6.2.2.4
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
@@ -36,16 +36,19 @@ 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, 2));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 3));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credProtect"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "hmac-secret"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "minPinLength"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
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, 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"));
|
||||
@@ -59,6 +62,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 +78,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
|
||||
|
||||
@@ -118,6 +118,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
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);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "minPinLength", extensions.minPinLength);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
@@ -206,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);
|
||||
}
|
||||
@@ -215,10 +219,17 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
int ret = verify(pinUvAuthProtocol, paut.data, clientDataHash.data, clientDataHash.len, pinUvAuthParam.data);
|
||||
if (ret != CborNoError)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (!(paut.permissions & CTAP_PERMISSION_MC))
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (getUserVerifiedFlagValue() == false)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
flags |= FIDO2_AUT_FLAG_UV;
|
||||
// Check pinUvAuthToken permissions. See 6.1.2.11
|
||||
if (paut.has_rp_id == false) {
|
||||
memcpy(paut.rp_id_hash, rp_id_hash, 32);
|
||||
paut.has_rp_id = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int e = 0; e < excludeList_len; e++) { //12.1
|
||||
@@ -251,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;
|
||||
@@ -267,10 +270,25 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
if (extensions.present == true) {
|
||||
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
|
||||
int l = 0;
|
||||
uint8_t minPinLen = 0;
|
||||
if (extensions.hmac_secret != NULL)
|
||||
l++;
|
||||
if (extensions.credProtect != 0)
|
||||
l++;
|
||||
if (extensions.minPinLength != NULL) {
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin)) {
|
||||
uint8_t *minpin_data = file_get_data(ef_minpin);
|
||||
for (int o = 2; o < file_get_size(ef_minpin); o += 32) {
|
||||
if (memcmp(minpin_data + o, rp_id_hash, 32) == 0) {
|
||||
minPinLen = minpin_data[0];
|
||||
if (minPinLen > 0)
|
||||
l++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
if (extensions.credProtect != 0) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
|
||||
@@ -281,15 +299,29 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "hmac-secret"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, *extensions.hmac_secret));
|
||||
}
|
||||
if (minPinLen > 0) {
|
||||
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "minPinLength"));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, minPinLen));
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
|
||||
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];
|
||||
@@ -326,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);
|
||||
@@ -344,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"));
|
||||
@@ -366,6 +400,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
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);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ typedef struct CredOptions {
|
||||
typedef struct CredExtensions {
|
||||
const bool *hmac_secret;
|
||||
uint64_t credProtect;
|
||||
const bool *minPinLength;
|
||||
bool present;
|
||||
} CredExtensions;
|
||||
|
||||
|
||||
@@ -125,6 +125,13 @@ typedef struct {
|
||||
#define CTAP_VENDOR_MSE 0x02
|
||||
#define CTAP_VENDOR_UNLOCK 0x03
|
||||
|
||||
#define CTAP_PERMISSION_MC 0x01 // MakeCredential
|
||||
#define CTAP_PERMISSION_GA 0x02 // GetAssertion
|
||||
#define CTAP_PERMISSION_CM 0x04 // CredentialManagement
|
||||
#define CTAP_PERMISSION_BE 0x08 // BioEnrollment
|
||||
#define CTAP_PERMISSION_LBW 0x10 // LargeBlobWrite
|
||||
#define CTAP_PERMISSION_ACFG 0x20 // AuthenticatorConfiguration
|
||||
|
||||
typedef struct mse {
|
||||
uint8_t Qpt[65];
|
||||
uint8_t key_enc[12 + 32];
|
||||
|
||||
@@ -325,6 +325,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;
|
||||
|
||||
@@ -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
|
||||
@@ -101,6 +98,7 @@ typedef struct pinUvAuthToken {
|
||||
bool in_use;
|
||||
uint8_t permissions;
|
||||
uint8_t rp_id_hash[32];
|
||||
bool has_rp_id;
|
||||
bool user_present;
|
||||
bool user_verified;
|
||||
} pinUvAuthToken_t;
|
||||
|
||||
@@ -26,6 +26,8 @@ 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 = 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
|
||||
};
|
||||
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
#define EF_KEY_DEV_ENC 0xCC01
|
||||
#define EF_EE_DEV 0xCE00
|
||||
#define EF_COUNTER 0xC000
|
||||
#define EF_OPTS 0xC001
|
||||
#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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user