16#include <esp_system.h>
17#include <esp_random.h>
18#include <mbedtls/ecdsa.h>
19#include <mbedtls/ecp.h>
20#include <mbedtls/ecdh.h>
21#include <mbedtls/md.h>
22#include <mbedtls/sha256.h>
23#include <mbedtls/aes.h>
25#include <freertos/FreeRTOS.h>
26#include <freertos/task.h>
33static const char*
TAG =
"CTAP2";
40#ifndef CTAP2_DEBUG_COMMANDS
41#define CTAP2_DEBUG_COMMANDS 0
46 0xCD, 0xCB, 0xAD, 0x6E,
49 0xBA, 0xD6, 0xE0, 0x01,
50 0x00, 0x00, 0x00, 0x01
56#define USER_PRESENCE_TIMEOUT_MS 30000
78#define PIN_PROTOCOL_VERSION 2
79#define PIN_TOKEN_SIZE 32
80#define PIN_RETRIES_MAX 8
81#define PIN_UV_RETRIES_MAX 3
84#define PIN_CMD_GET_RETRIES 0x01
85#define PIN_CMD_GET_KEY_AGREEMENT 0x02
86#define PIN_CMD_SET_PIN 0x03
87#define PIN_CMD_CHANGE_PIN 0x04
88#define PIN_CMD_GET_PIN_TOKEN 0x05
89#define PIN_CMD_GET_PIN_UV_TOKEN 0x09
92#define PIN_PERM_MAKE_CREDENTIAL 0x01
93#define PIN_PERM_GET_ASSERTION 0x02
94#define PIN_PERM_CRED_MGMT 0x04
95#define PIN_PERM_BIO_ENROLLMENT 0x08
96#define PIN_PERM_LARGE_BLOB_WRITE 0x10
97#define PIN_PERM_AUTHN_CONFIG 0x20
121#define CRED_MGMT_GET_CREDS_METADATA 0x01
122#define CRED_MGMT_ENUMERATE_RPS_BEGIN 0x02
123#define CRED_MGMT_ENUMERATE_RPS_GET_NEXT 0x03
124#define CRED_MGMT_ENUMERATE_CREDS_BEGIN 0x04
125#define CRED_MGMT_ENUMERATE_CREDS_GET_NEXT 0x05
126#define CRED_MGMT_DELETE_CREDENTIAL 0x06
148 if (se && se->isSessionActive() && se->getRandom(out,
static_cast<uint16_t
>(len))) {
151 esp_fill_random(out, len);
158 const uint8_t *attested_cred_data,
159 uint16_t attested_cred_len,
160 const uint8_t *ext_data,
192 uint16_t cred_id_len,
193 const uint8_t *pubkey,
198 if (!out || !out_len)
return false;
200 const size_t fixed_prefix = 16 + 2 + cred_id_len;
201 if (out_size < fixed_prefix)
return false;
205 memcpy(out + off,
AAGUID, 16);
208 out[off++] = (cred_id_len >> 8) & 0xFF;
209 out[off++] = cred_id_len & 0xFF;
211 memcpy(out + off, cred_id, cred_id_len);
214 cbor_writer_t cose_w;
235 if (level == 0 || !out || out_size < 20)
return 0;
258 const uint8_t *attested_cred,
259 uint16_t attested_len,
262 uint16_t *auth_data_len) {
264 uint8_t
flags = 0x01 | 0x40;
273 uint8_t ext_data[32];
274 uint16_t ext_len = 0;
283 attested_cred, attested_len,
284 ext_len > 0 ? ext_data : NULL, ext_len,
285 auth_data, auth_data_len) ==
CTAP2_OK;
319 uint16_t auth_data_len,
325 uint16_t *response_len) {
333 if (sig_len == 0 && (cert == NULL || cert_len == 0)) {
346 if (sig_len == 0 && (cert == NULL || cert_len == 0)) {
349 }
else if (cert && cert_len > 0) {
386 if (!key || !pubkey)
return false;
387 mbedtls_ecp_keypair_init(key);
389 int rc = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, key,
ctap2_random, NULL);
391 mbedtls_ecp_keypair_free(key);
395#if defined(MBEDTLS_PRIVATE)
396#define CTAP2_ECP_GRP(k) (k).MBEDTLS_PRIVATE(grp)
397#define CTAP2_ECP_Q(k) (k).MBEDTLS_PRIVATE(Q)
399#define CTAP2_ECP_GRP(k) (k).grp
400#define CTAP2_ECP_Q(k) (k).Q
406 MBEDTLS_ECP_PF_UNCOMPRESSED,
407 &olen, buf,
sizeof(buf));
410 if (rc != 0 || olen !=
sizeof(buf)) {
411 mbedtls_ecp_keypair_free(key);
414 memcpy(pubkey, buf + 1, 64);
430 const uint8_t *msg,
size_t msg_len,
431 uint8_t *sig,
size_t sig_size,
size_t *sig_len) {
432 if (!key || !msg || !sig || !sig_len)
return false;
434 sha256(msg, msg_len, hash);
436 mbedtls_ecdsa_context ecdsa;
437 mbedtls_ecdsa_init(&ecdsa);
438 int rc = mbedtls_ecdsa_from_keypair(&ecdsa, key);
440 mbedtls_ecdsa_free(&ecdsa);
444 rc = mbedtls_ecdsa_write_signature(&ecdsa, MBEDTLS_MD_SHA256,
446 sig, sig_size, sig_len,
448 mbedtls_ecdsa_free(&ecdsa);
470 const uint8_t *attested_cred_data,
471 uint16_t attested_cred_len,
472 const uint8_t *ext_data,
484 if (ext_data && ext_len > 0) {
487 out[offset++] =
flags;
496 if (attested_cred_data && attested_cred_len > 0) {
497 memcpy(out + offset, attested_cred_data, attested_cred_len);
498 offset += attested_cred_len;
501 if (ext_data && ext_len > 0) {
502 memcpy(out + offset, ext_data, ext_len);
518 LOG_I(
TAG,
"User presence required for %s at %s",
531 LOG_I(
TAG,
"User presence approved");
537 LOG_I(
TAG,
"User presence timeout");
540 LOG_W(
TAG,
"User presence unknown state: %d", result);
648static void dump_get_info_response(
const uint8_t *response, uint16_t len) {
649 LOG_I(
TAG,
"getInfo response len=%u", len);
650 for (uint16_t offset = 0; offset < len; offset += 16) {
652 int dump_len = ((len - offset) < 16) ? (len - offset) : 16;
653 for (
int i = 0; i < dump_len; i++) {
654 sprintf(hex + (i * 3),
"%02X ", response[offset + i]);
656 LOG_D(
TAG,
"%03u: %s", offset, hex);
694 dump_get_info_response(response, *response_len);
728 memset(
this, 0,
sizeof(*
this));
743 for (
int j = 0; j <
rp_count; j++) {
750 if (strcmp(rp_key,
"id") == 0) {
770 if (user_count < 0)
return false;
772 for (
int j = 0; j < user_count; j++) {
779 if (strcmp(user_key,
"id") == 0) {
784 }
else if (strcmp(user_key,
"name") == 0) {
802 if (params_count < 0)
return false;
804 for (
int j = 0; j < params_count; j++) {
806 int64_t param_alg = 0;
807 for (
int k = 0; k < param_count; k++) {
814 if (strcmp(param_key,
"alg") == 0) {
837 if (ext_count < 0)
return false;
839 for (
int j = 0; j < ext_count; j++) {
846 if (strcmp(ext_key,
"appidExclude") == 0) {
851 }
else if (strcmp(ext_key,
"credProtect") == 0) {
872 if (opt_count < 0)
return false;
874 for (
int j = 0; j < opt_count; j++) {
881 if (strcmp(opt_key,
"rk") == 0) {
883 }
else if (strcmp(opt_key,
"uv") == 0) {
885 }
else if (strcmp(opt_key,
"up") == 0) {
912 for (
int i = 0; i < map_count; i++) {
980 LOG_W(
TAG,
"makeCredential: pinUvAuthParam provided but no valid pinToken");
985 uint8_t expected_hmac[32];
986 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
995 LOG_W(
TAG,
"makeCredential: pinUvAuthParam verification failed");
999 LOG_I(
TAG,
"makeCredential: pinUvAuthParam verified - UV=1");
1014 uint8_t appid_hash[32];
1030 uint8_t *response, uint16_t *response_len) {
1031 LOG_I(
TAG,
"Browser probe request (%s) - waiting for user selection", p->
rp_id);
1041 uint8_t dummy_pubkey[64];
1043 if (
ctap2_random(NULL, dummy_cred_id,
sizeof(dummy_cred_id)) != 0) {
1049 uint8_t attested_cred[256];
1050 uint16_t attested_len = 0;
1051 uint8_t auth_data[256];
1052 uint16_t auth_data_len = 0;
1055 EXT_RAM_BSS_ATTR
static uint8_t to_sign[512];
1056 uint8_t signature[128];
1059 mbedtls_ecp_keypair ephemeral_key;
1069 mbedtls_ecp_keypair_free(&ephemeral_key);
1076 0, auth_data, &auth_data_len)) {
1077 mbedtls_ecp_keypair_free(&ephemeral_key);
1083 if (auth_data_len + 32 >
sizeof(to_sign)) {
1084 mbedtls_ecp_keypair_free(&ephemeral_key);
1090 memcpy(to_sign, auth_data, auth_data_len);
1092 uint16_t to_sign_len = auth_data_len + 32;
1095 signature,
sizeof(signature), &sig_len)) {
1096 mbedtls_ecp_keypair_free(&ephemeral_key);
1101 mbedtls_ecp_keypair_free(&ephemeral_key);
1103 LOG_I(
TAG,
"User selected this authenticator");
1105 auth_data, auth_data_len, signature, (uint8_t)sig_len,
1106 NULL, 0, response, response_len);
1107 LOG_I(
TAG,
"Probe makeCredential status=0x%02X resp_len=%u", status, *response_len);
1117 return strcmp(
rp_id,
"make.me.blink") == 0 || strcmp(
rp_id,
".dummy") == 0;
1128 uint16_t *response_len) {
1145 uint8_t *response, uint16_t *response_len) {
1154 LOG_I(
TAG,
"Calling fido2_storage_create_credential...");
1163 uint8_t attested_cred[256];
1164 uint16_t attested_len = 0;
1165 uint8_t auth_data[256];
1166 uint16_t auth_data_len = 0;
1169 attested_cred,
sizeof(attested_cred),
1177 EXT_RAM_BSS_ATTR
static uint8_t mc_to_sign[512];
1178 if (auth_data_len + 32 >
sizeof(mc_to_sign)) {
1181 memcpy(mc_to_sign, auth_data, auth_data_len);
1183 uint16_t to_sign_len = auth_data_len + 32;
1186 uint8_t signature[128];
1187 uint8_t sig_len = 0;
1188 const uint8_t *att_cert = NULL;
1189 uint16_t att_cert_len = 0;
1191 LOG_I(
TAG,
"PIN state: pinToken_valid=%d, is_pin_verified=%d",
1196 LOG_E(
TAG,
"Attestation certificate not initialized");
1204 LOG_E(
TAG,
"Attestation signing failed");
1207 LOG_I(
TAG,
"Using basic attestation (cert=%u, sig=%u)", att_cert_len, sig_len);
1210 auth_data, auth_data_len, signature, sig_len,
1211 att_cert, att_cert_len, response, response_len);
1216 LOG_I(
TAG,
"Created credential for %s (slot %d)", p->
rp_id, slot);
1219 LOG_I(
TAG,
"makeCredential status=0x%02X resp_len=%u", status, *response_len);
1220 for (uint16_t offset = 0; offset < *response_len; offset += 16) {
1222 int dump_len = ((*response_len - offset) < 16) ? (*response_len - offset) : 16;
1223 for (
int i = 0; i < dump_len; i++) {
1224 sprintf(hex + (i * 3),
"%02X ", response[offset + i]);
1226 LOG_D(
TAG,
"%03u: %s", offset, hex);
1242 uint8_t *response, uint16_t *response_len) {
1248 response[0] = status;
1253 LOG_I(
TAG,
"makeCredential rp_id=%s rk=%d uv=%d up=%d alg=%d pinProto=%d pinAuthLen=%zu",
1260 response[0] = status;
1268 response[0] = status;
1313 LOG_I(
TAG,
"User presence OK, creating credential (curve=%d)...",
curve);
1368 size_t *cred_id_len) {
1375 bool have_id =
false;
1377 for (
int k = 0; k < cred_map; k++) {
1384 if (strcmp(cred_key,
"id") == 0) {
1405 if (list_count < 0) {
1409 p->allow_list_present =
true;
1411 for (
int j = 0; j < list_count; j++) {
1413 size_t cred_id_len = 0;
1425 bool exists =
false;
1426 for (uint8_t m = 0; m < p->allow_list_count; m++) {
1427 if (p->allow_list_slots[m] == (uint8_t)slot) {
1433 p->allow_list_slots[p->allow_list_count++] = (uint8_t)slot;
1448 if (ext_count < 0) {
1452 for (
int j = 0; j < ext_count; j++) {
1459 if (strcmp(ext_key,
"appid") == 0) {
1462 p->has_appid = (len > 0);
1483 if (opt_count < 0) {
1487 for (
int j = 0; j < opt_count; j++) {
1494 if (strcmp(opt_key,
"uv") == 0) {
1496 }
else if (strcmp(opt_key,
"up") == 0) {
1514 GetAssertionParams *p) {
1515 memset(p, 0,
sizeof(*p));
1516 p->option_up =
true;
1522 if (map_count < 0) {
1526 for (
int i = 0; i < map_count; i++) {
1547 if (!
cbor_read_bytes(&r, p->client_data_hash, 32, &len) || len != 32) {
1550 p->has_client_data =
true;
1556 if (status !=
CTAP2_OK)
return status;
1561 if (status !=
CTAP2_OK)
return status;
1566 if (status !=
CTAP2_OK)
return status;
1570 cbor_read_bytes(&r, p->pin_uv_auth_param,
sizeof(p->pin_uv_auth_param),
1571 &p->pin_uv_auth_param_len);
1578 p->pin_uv_auth_protocol = (uint8_t)proto;
1599 *uv_verified =
false;
1601 if (p->pin_uv_auth_param_len == 0) {
1606 LOG_W(
TAG,
"pinUvAuthParam provided but no valid pinToken");
1611 uint8_t expected_hmac[32];
1612 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
1614 p->client_data_hash, 32,
1618 size_t compare_len = (p->pin_uv_auth_protocol == 2) ? 32 : 16;
1619 if (p->pin_uv_auth_param_len < compare_len) {
1620 LOG_W(
TAG,
"pinUvAuthParam too short: %zu < %zu",
1621 p->pin_uv_auth_param_len, compare_len);
1626 LOG_D(
TAG,
"pinUvAuthParam received (%zu bytes):", p->pin_uv_auth_param_len);
1627 LOG_D(
TAG,
" %02X%02X%02X%02X %02X%02X%02X%02X...",
1628 p->pin_uv_auth_param[0], p->pin_uv_auth_param[1],
1629 p->pin_uv_auth_param[2], p->pin_uv_auth_param[3],
1630 p->pin_uv_auth_param[4], p->pin_uv_auth_param[5],
1631 p->pin_uv_auth_param[6], p->pin_uv_auth_param[7]);
1632 LOG_D(
TAG,
"Expected HMAC (first %zu bytes):", compare_len);
1633 LOG_D(
TAG,
" %02X%02X%02X%02X %02X%02X%02X%02X...",
1634 expected_hmac[0], expected_hmac[1], expected_hmac[2], expected_hmac[3],
1635 expected_hmac[4], expected_hmac[5], expected_hmac[6], expected_hmac[7]);
1638 if (memcmp(p->pin_uv_auth_param, expected_hmac, compare_len) != 0) {
1639 LOG_W(
TAG,
"pinUvAuthParam verification failed");
1643 LOG_I(
TAG,
"pinUvAuthParam verified - UV=1");
1644 *uv_verified =
true;
1658 creds->hash_in_use = p->rp_id_hash;
1661 uint8_t temp_count = 0;
1663 if (p->allow_list_present && p->allow_list_count > 0) {
1665 uint8_t filtered = 0;
1666 for (uint8_t i = 0; i < p->allow_list_count; i++) {
1667 fido2_credential_info_t info;
1669 memcmp(info.rp_id_hash, p->rp_id_hash, 32) == 0) {
1670 creds->slots[filtered++] = p->allow_list_slots[i];
1673 creds->count = filtered;
1674 creds->include_user =
false;
1676 LOG_I(
TAG,
"getAssertion using allowList, matches=%u",
creds->count);
1680 uint8_t filtered_appid = 0;
1681 for (uint8_t i = 0; i < p->allow_list_count; i++) {
1682 fido2_credential_info_t info;
1684 memcmp(info.rp_id_hash, p->appid_hash, 32) == 0) {
1685 temp_slots[filtered_appid++] = p->allow_list_slots[i];
1688 if (filtered_appid > 0) {
1689 memcpy(
creds->slots, temp_slots, filtered_appid);
1690 creds->count = filtered_appid;
1691 creds->appid_used =
true;
1692 creds->hash_in_use = p->appid_hash;
1699 creds->include_user =
true;
1701 LOG_I(
TAG,
"getAssertion using all RP creds, matches=%u",
creds->count);
1707 if (temp_count > 0) {
1708 memcpy(
creds->slots, temp_slots, temp_count);
1709 creds->count = temp_count;
1710 creds->appid_used =
true;
1711 creds->hash_in_use = p->appid_hash;
1728 uint16_t auth_data_len,
const uint8_t *client_data_hash,
1729 uint8_t *signature, uint8_t *sig_len) {
1731 uint8_t to_sign[96];
1732 if (auth_data_len >
sizeof(to_sign) - 32) {
1733 LOG_E(
TAG,
"auth_data too large: %u", auth_data_len);
1737 memcpy(to_sign, auth_data, auth_data_len);
1738 memcpy(to_sign + auth_data_len, client_data_hash, 32);
1739 uint16_t to_sign_len = auth_data_len + 32;
1763 uint16_t auth_data_len,
const uint8_t *signature,
1764 uint8_t sig_len,
const fido2_credential_info_t *cred,
1765 bool include_user, uint8_t total_creds,
1766 uint8_t *response, uint16_t *response_len) {
1770 int resp_fields = 3;
1771 if (include_user) resp_fields++;
1772 if (total_creds > 1) resp_fields++;
1796 int user_fields = 1;
1797 if (cred->user_name[0] !=
'\0') user_fields++;
1803 if (cred->user_name[0] !=
'\0') {
1810 if (total_creds > 1) {
1833 uint8_t *response, uint16_t *response_len) {
1835 GetAssertionParams p;
1838 response[0] = status;
1843 LOG_I(
TAG,
"getAssertion rp_id=%s allowList=%d count=%u uv=%d up=%d appid=%s",
1844 p.rp_id[0] ? p.rp_id :
"(none)", p.allow_list_present ? 1 : 0,
1845 p.allow_list_count, p.option_uv, p.option_up, p.has_appid ? p.appid :
"(none)");
1848 if (!p.has_rp || !p.has_client_data) {
1861 bool uv_verified =
false;
1864 response[0] = status;
1870 AssertionCredentials
creds;
1873 if (
creds.count == 0) {
1892 memcpy(
g_ctap2.assertion_rp_id_hash,
creds.hash_in_use, 32);
1893 memcpy(
g_ctap2.assertion_client_data_hash, p.client_data_hash, 32);
1895 g_ctap2.assertion_up_done = p.option_up;
1899 uint8_t slot =
g_ctap2.assertion_creds[0];
1900 fido2_credential_info_t cred;
1915 uint8_t
flags = p.option_up ? 0x01 : 0x00;
1920 uint8_t ext_data[32];
1921 uint16_t ext_len = 0;
1922 if (
creds.appid_used) {
1926 uint8_t auth_data[128];
1927 uint16_t auth_data_len;
1929 ext_data, ext_len, auth_data, &auth_data_len);
1932 uint8_t signature[128];
1935 signature, &sig_len);
1937 response[0] = status;
1945 LOG_E(
TAG,
"Failed to get credential ID for slot %d", slot);
1952 status =
ga_build_response(cred_id, auth_data, auth_data_len, signature, sig_len,
1953 &cred,
g_ctap2.assertion_include_user,
1954 g_ctap2.assertion_count, response, response_len);
1956 response[0] = status;
1963 LOG_I(
TAG,
"Assertion for %s (slot %d)", p.rp_id, slot);
1974 if (
g_ctap2.assertion_count == 0) {
1989 fido2_credential_info_t cred;
2003 uint8_t auth_data[128];
2004 uint16_t auth_data_len;
2005 uint8_t ext_data[32];
2006 uint16_t ext_len = 0;
2007 if (
g_ctap2.assertion_appid_used) {
2011 NULL, 0, ext_data, ext_len, auth_data, &auth_data_len);
2014 uint8_t to_sign[96];
2015 if (auth_data_len >
sizeof(to_sign) - 32) {
2016 LOG_E(
TAG,
"auth_data too large: %u", auth_data_len);
2021 memcpy(to_sign, auth_data, auth_data_len);
2022 memcpy(to_sign + auth_data_len,
g_ctap2.assertion_client_data_hash, 32);
2023 uint16_t to_sign_len = auth_data_len + 32;
2025 uint8_t signature[128];
2036 LOG_E(
TAG,
"Failed to get credential ID for slot %d", slot);
2079 int ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1,
2103 const uint8_t *platform_key_y,
2104 uint8_t pin_protocol,
2105 uint8_t *shared_secret) {
2108 mbedtls_ecp_point platform_point;
2109 mbedtls_mpi shared_x;
2111 mbedtls_ecp_point_init(&platform_point);
2112 mbedtls_mpi_init(&shared_x);
2117 ret = mbedtls_mpi_read_binary(&platform_point.MBEDTLS_PRIVATE(X), platform_key_x, 32);
2118 if (ret != 0)
goto cleanup;
2120 ret = mbedtls_mpi_read_binary(&platform_point.MBEDTLS_PRIVATE(Y), platform_key_y, 32);
2121 if (ret != 0)
goto cleanup;
2123 ret = mbedtls_mpi_lset(&platform_point.MBEDTLS_PRIVATE(Z), 1);
2124 if (ret != 0)
goto cleanup;
2127 ret = mbedtls_ecdh_compute_shared(&
g_client_pin.ecdh_key.MBEDTLS_PRIVATE(grp),
2139 ret = mbedtls_mpi_write_binary(&shared_x, ecdh_z, 32);
2140 if (ret != 0)
goto cleanup;
2144 LOG_I(
TAG_PIN,
"=== ECDH DEBUG (full 32-byte values) ===");
2146 LOG_I(
TAG_PIN,
" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2147 ecdh_z[0], ecdh_z[1], ecdh_z[2], ecdh_z[3], ecdh_z[4], ecdh_z[5], ecdh_z[6], ecdh_z[7],
2148 ecdh_z[8], ecdh_z[9], ecdh_z[10], ecdh_z[11], ecdh_z[12], ecdh_z[13], ecdh_z[14], ecdh_z[15]);
2149 LOG_I(
TAG_PIN,
" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2150 ecdh_z[16], ecdh_z[17], ecdh_z[18], ecdh_z[19], ecdh_z[20], ecdh_z[21], ecdh_z[22], ecdh_z[23],
2151 ecdh_z[24], ecdh_z[25], ecdh_z[26], ecdh_z[27], ecdh_z[28], ecdh_z[29], ecdh_z[30], ecdh_z[31]);
2154 if (pin_protocol == 1) {
2157 mbedtls_sha256(ecdh_z, 32, shared_secret, 0);
2162 const char *info =
"CTAP2 AES key";
2164 uint8_t zero_salt[32] = {0};
2167 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
2168 zero_salt, 32, ecdh_z, 32, prk);
2171 uint8_t expand_input[32];
2172 size_t info_len = strlen(info);
2173 memcpy(expand_input, info, info_len);
2174 expand_input[info_len] = 0x01;
2177 LOG_I(
TAG_PIN,
"HKDF PRK (HMAC-SHA256(salt=0, IKM=Z)):");
2178 LOG_I(
TAG_PIN,
" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2179 prk[0], prk[1], prk[2], prk[3], prk[4], prk[5], prk[6], prk[7],
2180 prk[8], prk[9], prk[10], prk[11], prk[12], prk[13], prk[14], prk[15]);
2181 LOG_I(
TAG_PIN,
" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2182 prk[16], prk[17], prk[18], prk[19], prk[20], prk[21], prk[22], prk[23],
2183 prk[24], prk[25], prk[26], prk[27], prk[28], prk[29], prk[30], prk[31]);
2184 LOG_I(
TAG_PIN,
"HKDF info: '%s' || 0x01 (len=%zu)", info, info_len + 1);
2187 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
2188 prk, 32, expand_input, info_len + 1, shared_secret);
2193 LOG_I(
TAG_PIN,
" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2194 shared_secret[0], shared_secret[1], shared_secret[2], shared_secret[3],
2195 shared_secret[4], shared_secret[5], shared_secret[6], shared_secret[7],
2196 shared_secret[8], shared_secret[9], shared_secret[10], shared_secret[11],
2197 shared_secret[12], shared_secret[13], shared_secret[14], shared_secret[15]);
2198 LOG_I(
TAG_PIN,
" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2199 shared_secret[16], shared_secret[17], shared_secret[18], shared_secret[19],
2200 shared_secret[20], shared_secret[21], shared_secret[22], shared_secret[23],
2201 shared_secret[24], shared_secret[25], shared_secret[26], shared_secret[27],
2202 shared_secret[28], shared_secret[29], shared_secret[30], shared_secret[31]);
2207 mbedtls_ecp_point_free(&platform_point);
2208 mbedtls_mpi_free(&shared_x);
2222 const uint8_t *input,
size_t len, uint8_t *output) {
2223 mbedtls_aes_context aes;
2224 mbedtls_aes_init(&aes);
2226 uint8_t iv_copy[16];
2227 memcpy(iv_copy, iv, 16);
2229 int ret = mbedtls_aes_setkey_dec(&aes, key, 256);
2231 mbedtls_aes_free(&aes);
2235 ret = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, len, iv_copy, input, output);
2236 mbedtls_aes_free(&aes);
2249 size_t len, uint8_t *output) {
2250 uint8_t iv[16] = {0};
2263 size_t len, uint8_t *output) {
2264 mbedtls_aes_context aes;
2265 mbedtls_aes_init(&aes);
2267 uint8_t iv[16] = {0};
2269 int ret = mbedtls_aes_setkey_enc(&aes, key, 256);
2271 mbedtls_aes_free(&aes);
2275 ret = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, len, iv, input, output);
2276 mbedtls_aes_free(&aes);
2289 size_t len, uint8_t *output) {
2290 mbedtls_aes_context aes;
2291 mbedtls_aes_init(&aes);
2298 memcpy(output, iv, 16);
2300 int ret = mbedtls_aes_setkey_enc(&aes, key, 256);
2302 mbedtls_aes_free(&aes);
2307 ret = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, len, iv, input, output + 16);
2308 mbedtls_aes_free(&aes);
2351 uint8_t pub_x[32], pub_y[32];
2352 mbedtls_mpi_write_binary(&
g_client_pin.ecdh_key.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), pub_x, 32);
2353 mbedtls_mpi_write_binary(&
g_client_pin.ecdh_key.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), pub_y, 32);
2357 LOG_I(
TAG_PIN,
"X: %02X%02X%02X%02X %02X%02X%02X%02X...",
2358 pub_x[0], pub_x[1], pub_x[2], pub_x[3], pub_x[4], pub_x[5], pub_x[6], pub_x[7]);
2359 LOG_I(
TAG_PIN,
"Y: %02X%02X%02X%02X %02X%02X%02X%02X...",
2360 pub_y[0], pub_y[1], pub_y[2], pub_y[3], pub_y[4], pub_y[5], pub_y[6], pub_y[7]);
2407 uint8_t *response, uint16_t *response_len) {
2417 LOG_E(
TAG_PIN,
"FIDO2 hash not available - user must reset PIN");
2427 uint8_t platform_key_x[32] = {0};
2428 uint8_t platform_key_y[32] = {0};
2429 uint8_t pin_hash_enc[64] = {0};
2430 size_t pin_hash_enc_len = 0;
2431 uint8_t pin_protocol = 2;
2432 bool has_key =
false, has_pin =
false;
2441 for (
int i = 0; i < map_size; i++) {
2451 key = (int64_t)item.value;
2453 key = -1 - (int64_t)item.value;
2466 pin_protocol = (uint8_t)proto;
2467 LOG_I(
TAG_PIN,
"Client requested protocol: %d", pin_protocol);
2474 if (cose_size < 0)
break;
2475 for (
int j = 0; j < cose_size; j++) {
2477 cbor_item_t cose_item;
2485 cose_key = (int64_t)cose_item.value;
2487 cose_key = -1 - (int64_t)cose_item.value;
2489 LOG_E(
TAG_PIN,
"Unexpected COSE key type: %d", cose_item.type);
2513 if (
cbor_read_bytes(&r, pin_hash_enc,
sizeof(pin_hash_enc), &pin_hash_enc_len)) {
2514 LOG_D(
TAG_PIN,
"pinHashEnc read OK, len=%zu", pin_hash_enc_len);
2515 if (pin_hash_enc_len == 16 || pin_hash_enc_len == 32 || pin_hash_enc_len == 64) {
2517 LOG_D(
TAG_PIN,
"Got pinHashEnc (%zu bytes)", pin_hash_enc_len);
2519 LOG_E(
TAG_PIN,
"pinHashEnc unexpected size: %zu", pin_hash_enc_len);
2532 if (!has_key || !has_pin) {
2541 LOG_I(
TAG_PIN,
"Received pinHashEnc (%zu bytes):", pin_hash_enc_len);
2542 for (
size_t i = 0; i < pin_hash_enc_len; i += 16) {
2543 size_t row_len = (pin_hash_enc_len - i < 16) ? (pin_hash_enc_len - i) : 16;
2546 for (
size_t j = 0; j < row_len; j++) {
2547 p += sprintf(p,
"%02X ", pin_hash_enc[i + j]);
2554 uint8_t shared_secret[32];
2564 LOG_I(
TAG_PIN,
" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2565 platform_key_x[0], platform_key_x[1], platform_key_x[2], platform_key_x[3],
2566 platform_key_x[4], platform_key_x[5], platform_key_x[6], platform_key_x[7],
2567 platform_key_x[8], platform_key_x[9], platform_key_x[10], platform_key_x[11],
2568 platform_key_x[12], platform_key_x[13], platform_key_x[14], platform_key_x[15]);
2569 LOG_I(
TAG_PIN,
" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2570 platform_key_x[16], platform_key_x[17], platform_key_x[18], platform_key_x[19],
2571 platform_key_x[20], platform_key_x[21], platform_key_x[22], platform_key_x[23],
2572 platform_key_x[24], platform_key_x[25], platform_key_x[26], platform_key_x[27],
2573 platform_key_x[28], platform_key_x[29], platform_key_x[30], platform_key_x[31]);
2575 LOG_I(
TAG_PIN,
" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2576 platform_key_y[0], platform_key_y[1], platform_key_y[2], platform_key_y[3],
2577 platform_key_y[4], platform_key_y[5], platform_key_y[6], platform_key_y[7],
2578 platform_key_y[8], platform_key_y[9], platform_key_y[10], platform_key_y[11],
2579 platform_key_y[12], platform_key_y[13], platform_key_y[14], platform_key_y[15]);
2580 LOG_I(
TAG_PIN,
" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2581 platform_key_y[16], platform_key_y[17], platform_key_y[18], platform_key_y[19],
2582 platform_key_y[20], platform_key_y[21], platform_key_y[22], platform_key_y[23],
2583 platform_key_y[24], platform_key_y[25], platform_key_y[26], platform_key_y[27],
2584 platform_key_y[28], platform_key_y[29], platform_key_y[30], platform_key_y[31]);
2590 uint8_t decrypted_pin_hash[16];
2592 if (pin_protocol == 2 && pin_hash_enc_len == 32) {
2594 const uint8_t *iv = pin_hash_enc;
2595 const uint8_t *ciphertext = pin_hash_enc + 16;
2597 LOG_D(
TAG_PIN,
"Protocol 2 IV: %02X%02X%02X%02X %02X%02X%02X%02X...",
2598 iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]);
2599 LOG_D(
TAG_PIN,
"Ciphertext: %02X%02X%02X%02X %02X%02X%02X%02X...",
2600 ciphertext[0], ciphertext[1], ciphertext[2], ciphertext[3],
2601 ciphertext[4], ciphertext[5], ciphertext[6], ciphertext[7]);
2611 uint8_t decrypted[64];
2618 memcpy(decrypted_pin_hash, decrypted, 16);
2622 LOG_D(
TAG_PIN,
"Decrypted PIN hash: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2623 decrypted_pin_hash[0], decrypted_pin_hash[1], decrypted_pin_hash[2], decrypted_pin_hash[3],
2624 decrypted_pin_hash[4], decrypted_pin_hash[5], decrypted_pin_hash[6], decrypted_pin_hash[7],
2625 decrypted_pin_hash[8], decrypted_pin_hash[9], decrypted_pin_hash[10], decrypted_pin_hash[11],
2626 decrypted_pin_hash[12], decrypted_pin_hash[13], decrypted_pin_hash[14], decrypted_pin_hash[15]);
2629 uint8_t stored_hash[16];
2631 LOG_D(
TAG_PIN,
"Stored FIDO2 hash: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2632 stored_hash[0], stored_hash[1], stored_hash[2], stored_hash[3],
2633 stored_hash[4], stored_hash[5], stored_hash[6], stored_hash[7],
2634 stored_hash[8], stored_hash[9], stored_hash[10], stored_hash[11],
2635 stored_hash[12], stored_hash[13], stored_hash[14], stored_hash[15]);
2638 uint8_t test_full[32];
2639 sha256((
const uint8_t*)
"0000", 4, test_full);
2640 LOG_D(
TAG_PIN,
"Expected for 0000: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
2641 test_full[0], test_full[1], test_full[2], test_full[3],
2642 test_full[4], test_full[5], test_full[6], test_full[7],
2643 test_full[8], test_full[9], test_full[10], test_full[11],
2644 test_full[12], test_full[13], test_full[14], test_full[15]);
2670 size_t encrypted_len;
2672 if (pin_protocol == 2) {
2679 LOG_D(
TAG_PIN,
"Encrypted pinToken (Protocol 2, %zu bytes with IV)", encrypted_len);
2687 LOG_D(
TAG_PIN,
"Encrypted pinToken (Protocol 1, %zu bytes)", encrypted_len);
2707 LOG_I(
TAG_PIN,
"PIN verified, token issued (legacy, all permissions)");
2720 uint8_t *response, uint16_t *response_len) {
2730 LOG_E(
TAG_PIN,
"FIDO2 hash not available - user must reset PIN");
2740 uint8_t platform_key_x[32] = {0};
2741 uint8_t platform_key_y[32] = {0};
2742 uint8_t pin_hash_enc[64] = {0};
2743 size_t pin_hash_enc_len = 0;
2744 uint8_t pin_protocol = 2;
2745 uint8_t permissions = 0;
2746 char rp_id[64] = {0};
2747 bool has_key =
false, has_pin =
false, has_permissions =
false;
2756 for (
int i = 0; i < map_size; i++) {
2762 key = (int64_t)item.value;
2764 key = -1 - (int64_t)item.value;
2774 pin_protocol = (uint8_t)proto;
2780 if (cose_size < 0)
break;
2781 for (
int j = 0; j < cose_size; j++) {
2782 cbor_item_t cose_item;
2787 cose_key = (int64_t)cose_item.value;
2789 cose_key = -1 - (int64_t)cose_item.value;
2810 if (
cbor_read_bytes(&r, pin_hash_enc,
sizeof(pin_hash_enc), &pin_hash_enc_len)) {
2811 if (pin_hash_enc_len == 16 || pin_hash_enc_len == 32 || pin_hash_enc_len == 64) {
2820 permissions = (uint8_t)perm;
2821 has_permissions =
true;
2822 LOG_I(
TAG_PIN,
"Requested permissions: 0x%02X", permissions);
2839 if (!has_key || !has_pin) {
2846 if (!has_permissions) {
2854 uint8_t shared_secret[32];
2862 uint8_t decrypted_pin_hash[16];
2863 if (pin_protocol == 2 && pin_hash_enc_len == 32) {
2864 const uint8_t *iv = pin_hash_enc;
2865 const uint8_t *ciphertext = pin_hash_enc + 16;
2873 uint8_t decrypted[64];
2880 memcpy(decrypted_pin_hash, decrypted, 16);
2908 size_t encrypted_len;
2910 if (pin_protocol == 2) {
2936 LOG_I(
TAG_PIN,
"PIN verified, token issued with permissions=0x%02X", permissions);
2949 uint8_t *response, uint16_t *response_len) {
2968 uint64_t pin_protocol = 0;
2969 uint64_t sub_command = 0;
2971 for (
int i = 0; i < map_size; i++) {
2977 }
else if (key == 0x02) {
2984 LOG_I(
TAG_PIN,
"ClientPIN: protocol=%llu, subCommand=0x%02llx", pin_protocol, sub_command);
2993 switch (sub_command) {
3014 LOG_W(
TAG_PIN,
"Unknown subCommand: 0x%02lx", sub_command);
3042 LOG_I(
TAG,
"Factory reset complete");
3055 LOG_W(
TAG,
"credMgmt: skipping slot %d (no SE key)", slot);
3073 fido2_credential_info_t info;
3079 for (uint8_t j = 0; j < count; j++) {
3080 if (memcmp(unique_hashes[j], info.rp_id_hash, 32) == 0) {
3087 memcpy(unique_hashes[count], info.rp_id_hash, 32);
3107 fido2_credential_info_t info;
3110 if (memcmp(info.rp_id_hash,
rp_id_hash, 32) != 0)
continue;
3127 fido2_credential_info_t info;
3144 if (include_total) {
3160 fido2_credential_info_t info;
3177 if (info.user_name[0]) {
3200 if (include_total) {
3221 uint8_t *response, uint16_t *response_len) {
3223 if (params_len < 1) {
3233 if (map_count < 1) {
3239 uint8_t subcommand = 0;
3241 bool has_rp_id_hash =
false;
3243 uint16_t cred_id_len = 0;
3244 bool has_cred_id =
false;
3247 for (
int i = 0; i < map_count; i++) {
3259 subcommand = (uint8_t)cmd;
3267 for (
int j = 0; j < sub_count; j++) {
3278 has_rp_id_hash =
true;
3282 for (
int k = 0; k < cred_map; k++) {
3286 if (strcmp(cred_key,
"id") == 0) {
3320 LOG_I(
TAG,
"credMgmt subCmd=0x%02X", subcommand);
3333 switch (subcommand) {
3337 uint8_t existing = 0;
3352 LOG_I(
TAG,
"credMgmt metadata: %d existing, %d remaining",
3398 if (!has_rp_id_hash) {
3465 LOG_I(
TAG,
"credMgmt deleted credential slot %d", slot);
3530 uint8_t *response, uint16_t *response_len) {
3531 if (!
g_ctap2.initialized || cmd_len < 1) {
3537 uint8_t command = cmd[0];
3538 const uint8_t *params = cmd + 1;
3539 uint16_t params_len = cmd_len - 1;
3542 const char *cmd_name =
"?";
3553 LOG_I(
TAG,
"CMD 0x%02X (%s) %d bytes", command, cmd_name, params_len);
3555 g_ctap2.operation_pending =
true;
3606 g_ctap2.operation_pending =
false;
void cbor_encode_cose_key_p256(cbor_writer_t *w, const uint8_t *x, const uint8_t *y)
Encodes COSE P-256 public key map.
void cbor_encode_uint(cbor_writer_t *w, uint64_t value)
Encodes CBOR unsigned integer.
void cbor_encode_bool(cbor_writer_t *w, bool value)
Encodes CBOR boolean.
void cbor_encode_cose_key_ed25519(cbor_writer_t *w, const uint8_t *pubkey)
Encodes COSE Ed25519 public key map.
void cbor_writer_init(cbor_writer_t *w, uint8_t *buffer, size_t size)
CBOR writer implementation.
void cbor_encode_text(cbor_writer_t *w, const char *str)
Encodes CBOR text string.
size_t cbor_writer_length(const cbor_writer_t *w)
Returns number of bytes written by CBOR writer.
bool cbor_writer_error(const cbor_writer_t *w)
Returns whether writer encountered an error.
void cbor_encode_bytes(cbor_writer_t *w, const uint8_t *data, size_t len)
Encodes CBOR byte-string.
void cbor_encode_array(cbor_writer_t *w, size_t count)
Encodes CBOR array header.
void cbor_encode_int(cbor_writer_t *w, int64_t value)
Encodes CBOR signed integer.
void cbor_encode_map(cbor_writer_t *w, size_t count)
Encodes CBOR map header.
void cbor_reader_init(cbor_reader_t *r, const uint8_t *data, size_t size)
CBOR reader implementation.
bool cbor_read_text(cbor_reader_t *r, char *out, size_t max_len, size_t *out_len)
Reads CBOR text string into output buffer.
int cbor_read_map(cbor_reader_t *r)
Reads CBOR map header and returns pair count.
int cbor_read_array(cbor_reader_t *r)
Reads CBOR array header and returns element count.
bool cbor_skip_item(cbor_reader_t *r)
Skips one complete CBOR item including nested container content.
bool cbor_read_bool(cbor_reader_t *r, bool *value)
Reads CBOR boolean simple value.
bool cbor_read_item(cbor_reader_t *r, cbor_item_t *item)
Reads next CBOR item metadata and optional inline payload pointer.
bool cbor_read_int(cbor_reader_t *r, int64_t *value)
Reads CBOR integer (positive or negative).
bool cbor_read_uint(cbor_reader_t *r, uint64_t *value)
Reads CBOR unsigned integer.
bool cbor_read_bytes(cbor_reader_t *r, uint8_t *out, size_t max_len, size_t *out_len)
Reads CBOR byte-string into optional output buffer.
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
#define LOG_D(tag, fmt,...)
#define LOG_I(tag, fmt,...)
#define LOG_E(tag, fmt,...)
#define PIN_CMD_GET_PIN_TOKEN
void sha256_str(const char *str, uint8_t out[32])
static bool cred_mgmt_encode_rp(cbor_writer_t *w, uint8_t slot, bool include_total)
Encodes a credential-management RP response entry.
uint8_t ctap2_reset(uint8_t *response, uint16_t *response_len)
Handles CTAP2 authenticatorReset (0x07).
bool assertion_appid_used
static uint16_t ctap2_build_cred_protect_extension(uint8_t level, uint8_t *out, size_t out_size)
Builds CBOR payload for the credProtect extension.
static void ga_find_credentials(GetAssertionParams *p, AssertionCredentials *creds)
Finds credentials matching RP/allowList and appid extension rules.
static constexpr uint64_t CTAP2_INFO_PIN_UV_AUTH_PROTOCOL_VALUE
Reported PIN/UV auth protocol version (Protocol Two).
#define PIN_CMD_GET_PIN_UV_TOKEN
uint8_t ctap2_client_pin(const uint8_t *params, uint16_t params_len, uint8_t *response, uint16_t *response_len)
Handles CTAP2 authenticatorClientPIN (0x06).
static bool cred_mgmt_slot_has_key(uint8_t slot)
Credential-management helper and command implementation.
static void encode_info_transports(cbor_writer_t *w)
Encodes the supported transports list.
void ctap2_cancel(void)
Marks current CTAP2 operation as cancelled.
uint8_t ctap2_process_command(const uint8_t *cmd, uint16_t cmd_len, uint8_t *response, uint16_t *response_len)
Dispatches one CTAP2 command and writes response payload.
mbedtls_ecp_keypair ecdh_key
void ctap2_clear_cancel(void)
Clears any latched cancel flag. Called at the start of a new CTAPHID channel (INIT) so a cancel from ...
#define PIN_CMD_GET_KEY_AGREEMENT
static uint8_t ga_build_response(const uint8_t *cred_id, const uint8_t *auth_data, uint16_t auth_data_len, const uint8_t *signature, uint8_t sig_len, const fido2_credential_info_t *cred, bool include_user, uint8_t total_creds, uint8_t *response, uint16_t *response_len)
Builds CBOR response payload for getAssertion/getNextAssertion.
static const uint8_t AAGUID[16]
Authenticator Attestation GUID for this authenticator model.
static bool client_pin_compute_shared_secret(const uint8_t *platform_key_x, const uint8_t *platform_key_y, uint8_t pin_protocol, uint8_t *shared_secret)
Computes ClientPIN shared secret from platform ECDH public key.
static void encode_info_versions(cbor_writer_t *w)
Encodes the supported FIDO/U2F versions into the getInfo CBOR map.
uint8_t assertion_creds[32]
static void encode_info_options(cbor_writer_t *w)
Encodes the supported authenticator options, keys sorted by length.
static void encode_info_max_msg_size(cbor_writer_t *w)
Encodes the maxMsgSize entry into the getInfo CBOR map.
static bool wait_for_user_presence(const char *rp_id, fido2_action_t action, const char *user_name)
Requests user-presence confirmation through platform callback.
static uint8_t build_authenticator_data(const uint8_t *rp_id_hash, uint8_t flags, uint32_t sign_count, const uint8_t *attested_cred_data, uint16_t attested_cred_len, const uint8_t *ext_data, uint16_t ext_len, uint8_t *out, uint16_t *out_len)
Builds raw authenticatorData structure.
static void secure_random_fill(uint8_t *out, size_t len)
Fills a buffer with cryptographically secure random bytes.
static uint8_t ga_parse_extensions(cbor_reader_t *r, GetAssertionParams *p)
Parses getAssertion extensions (map key 0x04).
static uint8_t cred_mgmt_find_creds_for_rp(const uint8_t *rp_id_hash)
Collects resident credentials for the given RP ID hash.
static uint8_t client_pin_get_retries(uint8_t *response, uint16_t *response_len)
Handles ClientPIN subcommand getPINRetries (0x01).
bool ctap2_init(void)
Initializes CTAP2 runtime state.
uint8_t token_permissions
static struct @345050366056176050043354151136135170030316236203 g_client_pin
static bool ctap2_build_auth_data_for_cred(const uint8_t *rp_id_hash, const uint8_t *attested_cred, uint16_t attested_len, uint8_t cred_protect, uint8_t *auth_data, uint16_t *auth_data_len)
Builds authenticator data for makeCredential with optional credProtect extension.
static uint8_t client_pin_get_pin_token(const uint8_t *params, uint16_t params_len, uint8_t *response, uint16_t *response_len)
Handles ClientPIN subcommand getPinToken (0x05).
static bool aes_256_cbc_encrypt(const uint8_t *key, const uint8_t *input, size_t len, uint8_t *output)
Encrypts Protocol-1 PIN payload (AES-256-CBC with zero IV).
#define CRED_MGMT_ENUMERATE_RPS_GET_NEXT
#define PIN_PROTOCOL_VERSION
ClientPIN constants and state for PIN protocol support.
static uint8_t client_pin_get_pin_uv_auth_token(const uint8_t *params, uint16_t params_len, uint8_t *response, uint16_t *response_len)
Handles ClientPIN subcommand getPinUvAuthTokenUsingPinWithPermissions (0x09).
uint8_t ctap2_selection(uint8_t *response, uint16_t *response_len)
Handles CTAP2 authenticatorSelection (0x0B).
bool ctap2_is_cancelled(void)
Returns true if the current CTAP2 operation has been cancelled.
static constexpr uint64_t CTAP2_INFO_MAX_CRED_LIST_COUNT_VALUE
Reported maxCredentialCountInList for authenticatorGetInfo.
uint8_t assertion_rp_id_hash[32]
static uint16_t ctap2_build_appid_extension(uint8_t *out, size_t out_size)
Builds CBOR payload for appid extension in assertions.
static bool client_pin_init_ecdh(void)
ClientPIN command implementation helpers.
#define CRED_MGMT_GET_CREDS_METADATA
Credential management constants and enumeration state.
uint8_t current_rp_id_hash[32]
static uint8_t cred_mgmt_count_unique_rps(void)
Counts unique RP IDs among resident credentials.
#define PIN_UV_RETRIES_MAX
void sha256(const uint8_t *data, size_t len, uint8_t out[32])
static struct @363146237155063244362205253337222300366302103074 g_ctap2
Global CTAP2 runtime state.
static uint8_t ga_parse_allow_list(cbor_reader_t *r, GetAssertionParams *p)
Parses getAssertion allowList (map key 0x03).
static struct @074350050112271276332254352137370356012162354162 g_cred_mgmt
#define CRED_MGMT_ENUMERATE_CREDS_BEGIN
static constexpr uint64_t CTAP2_INFO_MAX_MSG_SIZE_VALUE
Reported maximum message size for authenticatorGetInfo.
static int ctap2_random(void *ctx, unsigned char *out, size_t len)
mbedTLS RNG callback backed by secure random source.
#define CRED_MGMT_ENUMERATE_CREDS_GET_NEXT
static const char * TAG_PIN
uint8_t token_rp_id_hash[32]
static bool ctap2_build_attested_cred(const uint8_t *cred_id, uint16_t cred_id_len, const uint8_t *pubkey, uint8_t curve, uint8_t *out, size_t out_size, uint16_t *out_len)
Builds attested credential data (AAGUID, credential ID, COSE key).
static bool aes_256_cbc_encrypt_p2(const uint8_t *key, const uint8_t *input, size_t len, uint8_t *output)
Encrypts Protocol-2 PIN payload and prefixes random IV (IV || ciphertext).
static uint8_t ga_sign_assertion(uint8_t slot, const uint8_t *auth_data, uint16_t auth_data_len, const uint8_t *client_data_hash, uint8_t *signature, uint8_t *sig_len)
Signs assertion message (authData || clientDataHash) for one credential slot.
static uint8_t ga_parse_params(const uint8_t *params, uint16_t params_len, GetAssertionParams *p)
Parses complete getAssertion request map from CBOR payload.
static bool cred_mgmt_encode_credential(cbor_writer_t *w, uint8_t slot, bool include_total)
Encodes a credential-management credential response entry.
static void encode_info_pin_uv_auth_protocols(cbor_writer_t *w)
Encodes the supported pinUvAuthProtocols list.
static void encode_info_aaguid(cbor_writer_t *w)
Encodes the authenticator AAGUID into the getInfo CBOR map.
void ctap2_send_keepalive(uint8_t status)
Sends CTAPHID keepalive for currently active channel.
uint8_t ctap2_get_assertion(const uint8_t *params, uint16_t params_len, uint8_t *response, uint16_t *response_len)
Handles CTAP2 authenticatorGetAssertion (0x02).
#define PIN_CMD_GET_RETRIES
ClientPIN subcommand identifiers.
static bool ctap2_sign_with_keypair(mbedtls_ecp_keypair *key, const uint8_t *msg, size_t msg_len, uint8_t *sig, size_t sig_size, size_t *sig_len)
Signs message using provided keypair (ECDSA over SHA-256).
uint8_t ctap2_cred_management(const uint8_t *params, uint16_t params_len, uint8_t *response, uint16_t *response_len)
Handles CTAP2 authenticatorCredentialManagement (0x0A).
static void encode_info_algorithms(cbor_writer_t *w)
Encodes the supported algorithms array (PublicKeyCredentialParameters).
static bool aes_256_cbc_decrypt_iv(const uint8_t *key, const uint8_t *iv, const uint8_t *input, size_t len, uint8_t *output)
Decrypts data using AES-256-CBC with caller-provided IV.
#define CRED_MGMT_DELETE_CREDENTIAL
static bool ctap2_generate_ephemeral_keypair(mbedtls_ecp_keypair *key, uint8_t pubkey[64])
Generates ephemeral P-256 key pair and exports 64-byte X||Y public key.
#define CTAP2_DEBUG_COMMANDS
uint8_t assertion_client_data_hash[32]
static uint8_t ga_verify_pin_auth(const GetAssertionParams *p, bool *uv_verified)
Verifies getAssertion pinUvAuthParam via HMAC.
static uint8_t ctap2_build_make_credential_response_packed(const uint8_t *auth_data, uint16_t auth_data_len, const uint8_t *sig, uint8_t sig_len, const uint8_t *cert, uint16_t cert_len, uint8_t *response, uint16_t *response_len)
Builds packed-attestation makeCredential response CBOR payload.
#define PIN_CMD_CHANGE_PIN
#define CRED_MGMT_ENUMERATE_RPS_BEGIN
static void encode_info_extensions(cbor_writer_t *w)
Encodes the supported CTAP extensions, sorted for CBOR canonical form.
static uint8_t client_pin_get_key_agreement(uint8_t *response, uint16_t *response_len)
Handles ClientPIN subcommand getKeyAgreement (0x02).
static void encode_info_max_cred_id_length(cbor_writer_t *w)
Encodes the maxCredentialIdLength entry.
static bool aes_256_cbc_decrypt(const uint8_t *key, const uint8_t *input, size_t len, uint8_t *output)
Decrypts Protocol-1 PIN payload (AES-256-CBC with zero IV).
static void encode_info_max_cred_count(cbor_writer_t *w)
Encodes the maxCredentialCountInList entry.
uint8_t ctap2_get_next_assertion(uint8_t *response, uint16_t *response_len)
Handles CTAP2 authenticatorGetNextAssertion (0x08).
bool assertion_include_user
static bool ga_parse_allow_list_credential(cbor_reader_t *r, uint8_t *cred_id, size_t *cred_id_len)
Parses one allowList credential descriptor and extracts credential ID.
uint8_t ctap2_get_info(uint8_t *response, uint16_t *response_len)
Handles CTAP2 authenticatorGetInfo (0x04).
static const char * INFO_TRANSPORTS[]
Device info strings reported by authenticatorGetInfo.
static uint8_t ga_parse_options(cbor_reader_t *r, GetAssertionParams *p)
Parses getAssertion options (map key 0x05).
#define CTAP2_MC_RESP_AUTH_DATA
#define CTAP1_ERR_INVALID_PARAMETER
#define CTAP2_PIN_PROTOCOL
#define CTAP2_INFO_ALGORITHMS
#define CTAP2_CMD_GET_NEXT_ASSERTION
#define CTAP2_MC_PUB_KEY_CRED_PARAMS
#define CTAP2_INFO_MAX_CRED_ID_LENGTH
#define CTAP2_GA_ALLOW_LIST
#define CTAP2_GA_RESP_AUTH_DATA
#define CTAP2_CMD_GET_INFO
#define CTAP2_CM_PIN_UV_AUTH_PROTOCOL
#define CTAP2_GA_RESP_USER
#define COSE_KEY_LABEL_ALG
#define CTAP2_CMD_CLIENT_PIN
#define CTAP2_MC_CLIENT_DATA_HASH
#define CTAP2_ERR_UNSUPPORTED_OPTION
#define CTAP2_INFO_MAX_MSG_SIZE
#define CTAP2_ERR_KEY_STORE_FULL
#define CTAP2_CM_SUB_RP_ID_HASH
#define CTAP2_INFO_PIN_UV_AUTH_PROTOCOLS
#define CTAP2_CM_RESP_PUBLIC_KEY
#define CTAP2_MC_RESP_FMT
#define COSE_ALG_ECDH_ES_HKDF_256
#define COSE_KEY_LABEL_CRV
#define CTAP2_MC_EXTENSIONS
#define CTAP2_PIN_RESP_PIN_RETRIES
#define CTAP2_ERR_PIN_NOT_SET
#define CTAP2_GA_CLIENT_DATA_HASH
#define CTAP2_INFO_EXTENSIONS
#define CTAP2_CM_RESP_CREDENTIAL_ID
#define CTAP2_CM_RESP_EXISTING_CRED_COUNT
#define CTAP2_PIN_PERMISSIONS_RPID
#define CTAP2_CM_RESP_USER
#define CTAP2_PIN_RESP_UV_RETRIES
#define CTAP2_ERR_OPERATION_DENIED
#define CTAP2_ERR_PIN_BLOCKED
#define CTAP2_CMD_GET_ASSERTION
#define CTAP2_CMD_CRED_MANAGEMENT
#define CTAP2_INFO_TRANSPORTS
#define CTAP2_CM_RESP_REMAINING_CRED_COUNT
#define CTAP2_CM_RESP_TOTAL_CREDENTIALS
#define CTAP1_ERR_INVALID_COMMAND
#define COSE_KEY_TYPE_EC2
#define CTAP2_CMD_LARGE_BLOBS
#define CTAP2_ERR_PIN_AUTH_INVALID
#define CTAP2_INFO_MAX_CRED_COUNT_IN_LIST
#define CTAP2_INFO_AAGUID
#define CTAP2_ERR_INVALID_OPTION
#define CTAP2_CMD_MAKE_CREDENTIAL
#define CTAP2_ERR_NO_CREDENTIALS
#define CTAP2_GA_PIN_UV_AUTH_PARAM
#define CTAP2_CM_SUBCOMMAND
#define CTAP2_ERR_PIN_INVALID
#define CTAP2_PIN_HASH_ENC
#define CTAP2_CM_PIN_UV_AUTH_PARAM
#define CTAP2_ERR_CREDENTIAL_EXCLUDED
#define CTAP2_CM_RESP_TOTAL_RPS
#define CTAP2_CM_SUB_CREDENTIAL_ID
void ctap2_send_keepalive(uint8_t status)
Sends CTAPHID keepalive for currently active channel.
uint8_t ctap2_make_credential(const uint8_t *params, uint16_t params_len, uint8_t *response, uint16_t *response_len)
#define CTAP2_CM_SUBCOMMAND_PARAMS
#define CTAP2_ERR_MISSING_PARAMETER
#define CTAP2_CM_RESP_RP_ID_HASH
#define CTAP2_PIN_PERMISSIONS
#define CTAP2_GA_PIN_UV_AUTH_PROTOCOL
#define CTAP2_MC_RESP_ATT_STMT
#define CTAP2_GA_EXTENSIONS
#define CTAP2_ERR_INVALID_CBOR
#define CTAP2_CM_RESP_CRED_PROTECT
#define CTAP2_INFO_OPTIONS
#define CTAP2_PIN_RESP_KEY_AGREEMENT
#define CTAP2_MC_PIN_UV_AUTH_PROTOCOL
#define CTAP2_PIN_RESP_PIN_TOKEN
#define CTAP2_GA_RESP_NUMBER_OF_CREDS
#define CTAP2_ERR_NOT_ALLOWED
#define CTAP2_INFO_VERSIONS
#define CTAP2_GA_RESP_CREDENTIAL
#define CTAP2_CMD_SELECTION
#define CTAP2_PIN_KEY_AGREEMENT
#define CTAP2_GA_RESP_SIGNATURE
#define CTAP2_ERR_UNSUPPORTED_ALGORITHM
#define COSE_KEY_LABEL_KTY
#define CTAP2_MC_PIN_UV_AUTH_PARAM
uint32_t ctaphid_get_current_cid(void)
Returns the channel identifier of the currently processed request.
#define CTAPHID_STATUS_PROCESSING
#define CTAPHID_STATUS_UPNEEDED
void ctaphid_send_keepalive(uint32_t cid, uint8_t status)
Sends a CTAPHID KEEPALIVE packet immediately over USB.
#define CDC_CURVE_ED25519
#define FIDO2_MAX_CREDENTIALS
void fido2_set_pin_verified(bool verified)
Stores whether PIN verification was completed via ClientPIN.
#define FIDO2_CRED_ID_LEN
#define FIDO2_RP_ID_MAX_LEN
#define FIDO2_USER_NAME_MAX_LEN
void fido2_increment_auth_counter(void)
Increments global authentication counter.
fido2_user_presence_result_t fido2_request_user_presence(const char *rp_id, fido2_action_t action, const char *user_name)
Requests user presence from host/application callback.
fido2_user_presence_result_t
bool fido2_factory_reset(void)
Removes all credentials and resets FIDO2 data.
bool fido2_is_pin_verified(void)
Returns current PIN-verified state.
#define FIDO2_USER_ID_MAX_LEN
@ FIDO2_ACTION_AUTHENTICATE
struct @262231322003320050276064353325174062307231151161::@131310070117174352112321004206244146355206237313 creds[FIDO2_MAX_CREDENTIALS]
char rp_id[FIDO2_RP_ID_MAX_LEN]
char user_name[FIDO2_USER_NAME_MAX_LEN]
bool fido2_storage_sign_raw(uint8_t slot, const uint8_t *msg, uint16_t msg_len, uint8_t *signature, uint8_t *sig_len)
Signs message and returns raw signature (EdDSA/ECDSA).
bool fido2_storage_get_credential(uint8_t slot, fido2_credential_info_t *info)
Credential create/read/delete operations.
bool fido2_storage_delete_credential(uint8_t slot)
Deletes credential and associated slot data.
bool fido2_storage_get_cred_id(uint8_t slot, uint8_t *out_cred_id)
Builds credential-id blob for logical slot.
int8_t fido2_storage_find_slot_by_cred_id(const uint8_t *cred_id, uint16_t cred_id_len)
Resolves and verifies logical slot from credential-id blob.
bool fido2_storage_get_pubkey(uint8_t slot, uint8_t *pubkey)
Reads public key from secure-element slot.
bool fido2_storage_create_credential(const char *rp_id, const uint8_t *rp_id_hash, const uint8_t *user_id, uint8_t user_id_len, const char *user_name, bool resident_key, uint8_t cred_protect, uint8_t curve, uint8_t *out_slot, uint8_t *out_cred_id, uint8_t *out_pubkey)
Creates or replaces credential in secure-element storage.
uint8_t fido2_storage_find_by_rp(const uint8_t *rp_id_hash, uint8_t *out_slots, uint8_t max_slots)
Finds credentials matching RP hash.
uint32_t fido2_storage_increment_sign_count(uint8_t slot)
Increments per-credential sign counter and persists metadata.
bool fido2_storage_is_resident(uint8_t slot)
Returns resident-key flag for slot.
int8_t fido2_storage_find_by_rp_user(const uint8_t *rp_id_hash, const uint8_t *user_id, uint8_t user_id_len)
Finds credential by RP hash and user handle for replacement logic.
ISecureElement * getSecureElementInstance()
Returns singleton secure-element stub instance.
void sha256_str(const char *str, uint8_t out[32])
static uint8_t parse_make_credential_params(const uint8_t *data, uint16_t data_len, MakeCredentialParams *p)
Parses complete makeCredential request map from CBOR payload.
static bool parse_options_map(cbor_reader_t *r, MakeCredentialParams *p)
Parses makeCredential options map from CBOR.
static uint8_t mc_rollback_credential(uint8_t slot, uint8_t *response, uint16_t *response_len)
Deletes a just-created credential and reports CTAP2_ERR_OTHER.
uint8_t ctap2_make_credential(const uint8_t *params, uint16_t params_len, uint8_t *response, uint16_t *response_len)
Handles CTAP2 authenticatorMakeCredential (0x01).
static bool is_browser_probe(const char *rp_id)
Detects known browser probe RP IDs.
static uint8_t create_credential_and_respond(const MakeCredentialParams *p, uint8_t curve, uint8_t *response, uint16_t *response_len)
Creates credential, signs attestation statement, and builds response.
static bool parse_rp_map(cbor_reader_t *r, MakeCredentialParams *p)
Parses the RP map from a makeCredential CBOR request.
static bool parse_user_map(cbor_reader_t *r, MakeCredentialParams *p)
Parses the user map from a makeCredential CBOR request.
void sha256(const uint8_t *data, size_t len, uint8_t out[32])
static bool parse_extensions_map(cbor_reader_t *r, MakeCredentialParams *p)
Parses makeCredential extensions map from CBOR.
static bool parse_pubkey_cred_params(cbor_reader_t *r, MakeCredentialParams *p)
Parses pubKeyCredParams and selects a supported algorithm.
static uint8_t verify_pin_uv_auth(const MakeCredentialParams *p)
Verifies pinUvAuthParam for makeCredential.
static uint8_t check_appid_exclude(const MakeCredentialParams *p)
Validates the appidExclude extension against existing credentials.
static uint8_t handle_browser_probe(const MakeCredentialParams *p, uint8_t *response, uint16_t *response_len)
Handles browser probe RP IDs by returning a synthetic attested response.
bool pin_storage_verify_fido2_hash(const uint8_t *hash_in)
bool pin_storage_fido2_available(void)
bool pin_storage_get_fido2_hash(uint8_t *hash_out)
Credential-selection result used to build assertion responses.
Parsed parameters for authenticatorGetAssertion.
uint8_t client_data_hash[32]
size_t pin_uv_auth_param_len
uint8_t allow_list_slots[32]
uint8_t pin_uv_auth_protocol
uint8_t pin_uv_auth_param[32]
Parsed parameters for authenticatorMakeCredential.
uint8_t client_data_hash[32]
uint8_t pin_uv_auth_protocol
size_t pin_uv_auth_param_len
uint8_t pin_uv_auth_param[64]
bool u2f_get_attestation_cert(const uint8_t **cert, uint16_t *cert_len)
Returns attestation certificate pointer and length, initializing attestation on demand if the boot-ti...
bool u2f_attestation_sign(const uint8_t *data, size_t data_len, uint8_t *signature, uint8_t *sig_len)
Signs payload using the attestation key, initializing attestation on demand if the boot-time init did...