mirror of
https://github.com/polhenarejos/pico-fido.git
synced 2025-12-19 02:48:04 +08:00
Add support for persistentPinUvAuthToken.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user