Upgrade to Version 2.10

Update README also

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos 2022-12-19 22:50:56 +01:00
commit 7542fad31c
No known key found for this signature in database
GPG Key ID: C0095B7870A4CCD3
16 changed files with 205 additions and 42 deletions

View File

@ -1,7 +1,7 @@
#!/bin/bash
VERSION_MAJOR="2"
VERSION_MINOR="6"
VERSION_MINOR="8"
rm -rf release/*
cd build_release

@ -1 +1 @@
Subproject commit 3def9bff4fb9e3e79e497392897deaf7c9bb5f29
Subproject commit 0bc13df1a22ab6c9cceb3b7a87b294e604e79c1e

View File

@ -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);

View File

@ -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];

View File

@ -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));

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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));

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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
};

View File

@ -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

View File

@ -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)

View File

@ -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()