22#include <mbedtls/platform_util.h>
23#include <mbedtls/sha256.h>
24#include <mbedtls/aes.h>
25#include <mbedtls/ecp.h>
26#include <mbedtls/ecdsa.h>
27#include <mbedtls/bignum.h>
35#include <esp_random.h>
37static const char *
TAG =
"OpenPGP";
55static bool se_ecc_key_read(uint8_t slot, uint8_t* pubkey,
size_t max_len, uint8_t* curve_out) {
57 if (!se || !pubkey)
return false;
59 auto res = se->eccGetPublicKey(slot, pubkey, &
curve);
79 LOG_E(
TAG,
"se_ecc_key_generate: SE not available (slot=%u)", slot);
90 LOG_W(
TAG,
"se_ecc_key_generate: slot %u initial fail (SeResult=%d), deleting and retrying",
91 slot,
static_cast<int>(res));
93 res = se->eccGenerate(slot, c);
96 LOG_E(
TAG,
"se_ecc_key_generate(slot=%u curve=%u) failed: SeResult=%d",
97 slot,
curve,
static_cast<int>(res));
111static bool se_ecdsa_sign(uint8_t slot,
const uint8_t* hash,
size_t hash_len, uint8_t* sig) {
113 if (!se || !hash || !sig)
return false;
126static bool se_eddsa_sign(uint8_t slot,
const uint8_t* msg,
size_t msg_len, uint8_t* sig) {
128 if (!se || !msg || !sig)
return false;
139 if (se && se->getRandom(buf,
static_cast<uint16_t
>(len))) {
142 esp_fill_random(buf, len);
152 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01,
155 0x00, 0x00, 0x00, 0x00,
184#define OPENPGP_RC_MIN_LEN 8
198 const uint8_t* salt, uint8_t* hash_out) {
207 mbedtls_sha256_context ctx;
208 mbedtls_sha256_init(&ctx);
209 if (mbedtls_sha256_starts(&ctx, 0) != 0) {
210 mbedtls_sha256_free(&ctx);
211 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
214 size_t processed = 0;
219 if (mbedtls_sha256_update(&ctx, buffer, chunk) != 0) {
220 mbedtls_sha256_free(&ctx);
221 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
226 mbedtls_sha256_finish(&ctx, hash_out);
227 mbedtls_sha256_free(&ctx);
228 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
322#define NVS_NAMESPACE "openpgp"
323#define NVS_STATE_KEY "state"
333 uint8_t schema_version;
405 0x73, 0xC0, 0x01, 0x80,
417 0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01
427 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07
437 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07
465 buf[0] = (tag >> 8) & 0xFF;
483 }
else if (len < 256) {
489 buf[1] = (len >> 8) & 0xFF;
504static size_t tlv_build(uint8_t *buf,
size_t buf_max, uint16_t tag,
505 const uint8_t *value,
size_t value_len) {
509 if (value && value_len > 0) {
510 memcpy(buf + pos, value, value_len);
536 const int r =
static_cast<int>(key_type);
539 static uint8_t s_rsa_attr[6];
542 s_rsa_attr[1] =
static_cast<uint8_t
>((
role_rsa_n_bits[r] >> 8) & 0xFF);
544 s_rsa_attr[3] =
static_cast<uint8_t
>((eb >> 8) & 0xFF);
545 s_rsa_attr[4] =
static_cast<uint8_t
>(eb & 0xFF);
547 *len =
sizeof(s_rsa_attr);
574 size_t inner_len = 0;
577 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
581 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
585 uint8_t discret[384];
586 size_t discret_len = 0;
589 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
595 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
600 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
605 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
610 uint8_t pw_status[7] = {
619 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
627 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
628 0xC5, fps,
sizeof(fps));
635 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
636 0xC6, ca_fps,
sizeof(ca_fps));
639 uint8_t gen_times[12];
643 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
644 0xCD, gen_times, 12);
647 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
648 0x73, discret, discret_len);
654 memcpy(buf + total, inner, inner_len);
668 size_t inner_len = 0;
672 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
677 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
681 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
688 memcpy(buf + total, inner, inner_len);
702 const uint8_t* payload,
size_t payload_len,
703 const uint8_t* sig,
size_t sig_len) {
712 uint8_t pub_sec1[65];
714 memcpy(pub_sec1 + 1, pub_raw, 64);
717 mbedtls_sha256(payload, payload_len, hash, 0);
719 mbedtls_ecp_group grp;
722 mbedtls_ecp_group_init(&grp);
723 mbedtls_ecp_point_init(&Q);
724 mbedtls_mpi_init(&r);
725 mbedtls_mpi_init(&s);
729 if (mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1) != 0)
break;
730 if (mbedtls_ecp_point_read_binary(&grp, &Q, pub_sec1,
sizeof(pub_sec1)) != 0)
break;
731 if (mbedtls_mpi_read_binary(&r, sig + 0, 32) != 0)
break;
732 if (mbedtls_mpi_read_binary(&s, sig + 32, 32) != 0)
break;
733 ok = (mbedtls_ecdsa_verify(&grp, hash,
sizeof(hash), &Q, &r, &s) == 0);
736 mbedtls_mpi_free(&r);
737 mbedtls_mpi_free(&s);
738 mbedtls_ecp_point_free(&Q);
739 mbedtls_ecp_group_free(&grp);
753 uint8_t blob[BLOB_SIZE];
754 size_t len = BLOB_SIZE;
755 esp_err_t err = nvs_get_blob(nvs,
NVS_STATE_KEY, blob, &len);
758 if (err != ESP_OK || len != BLOB_SIZE) {
762 OpenpgpNvsState state = {};
763 memcpy(&state, blob,
sizeof(state));
769 LOG_W(
TAG,
"OpenPGP state signature invalid - re-initialising");
808 for (
int r = 0; r < 3; ++r) {
825 OpenpgpNvsState state = {};
847 memcpy(state.cardholder_name,
cardholder_name,
sizeof(state.cardholder_name));
848 memcpy(state.cardholder_lang,
cardholder_lang,
sizeof(state.cardholder_lang));
849 memcpy(state.cardholder_url,
cardholder_url,
sizeof(state.cardholder_url));
850 memcpy(state.cardholder_login,
cardholder_login,
sizeof(state.cardholder_login));
851 for (
int r = 0; r < 3; ++r) {
860 memcpy(state.kdf_do_bytes,
kdf_do_bytes,
sizeof(state.kdf_do_bytes));
863 uint8_t blob[BLOB_SIZE];
864 memcpy(blob, &state,
sizeof(state));
874 blob +
sizeof(state), &sig_len);
876 LOG_E(
TAG,
"save_state: attestation sign failed (%d)",
877 static_cast<int>(sign_res));
882 esp_err_t err = nvs_open(
NVS_NAMESPACE, NVS_READWRITE, &nvs);
884 LOG_E(
TAG,
"save_state: nvs_open %s", esp_err_to_name(err));
890 err = nvs_commit(nvs);
894 LOG_E(
TAG,
"save_state: %s", esp_err_to_name(err));
901#define NVS_CERT_KEY "cardcert"
910 if (nvs_open(
NVS_NAMESPACE, NVS_READWRITE, &nvs) != ESP_OK)
return false;
914 if (err == ESP_ERR_NVS_NOT_FOUND) err = ESP_OK;
918 if (err == ESP_OK) err = nvs_commit(nvs);
920 return err == ESP_OK;
929 if (nvs_open(
NVS_NAMESPACE, NVS_READONLY, &nvs) != ESP_OK)
return 0;
931 esp_err_t err = nvs_get_blob(nvs,
NVS_CERT_KEY, out, &len);
933 return (err == ESP_OK) ? len : 0;
968 LOG_I(
TAG,
"KDF disabled, PINs reset to defaults");
1009 if (esp_efuse_mac_get_default(mac) == ESP_OK) {
1021 LOG_I(
TAG,
"AID initialized: Manufacturer=0x%02X%02X Serial=%02X%02X%02X%02X",
1026 LOG_W(
TAG,
"Failed to read MAC, using default AID");
1043 LOG_E(
TAG,
"Failed to initialize GPG/TROPIC01");
1065 if (!fp_out)
return false;
1070 default:
return false;
1076 if (fp[i] != 0)
return true;
1088 if (!out || out_size == 0)
return 0;
1090 if (len >= out_size) len = out_size - 1;
1097 const uint8_t *src =
nullptr;
1104 return (
static_cast<uint32_t
>(src[0]) << 24) |
1105 (
static_cast<uint32_t
>(src[1]) << 16) |
1106 (
static_cast<uint32_t
>(src[2]) << 8) |
1107 static_cast<uint32_t
>(src[3]);
1111 if (!
name)
return false;
1112 size_t len = strlen(
name);
1124 uint32_t gen_time) {
1125 if (!fingerprint)
return false;
1129 (uint8_t)((gen_time >> 24) & 0xFF),
1130 (uint8_t)((gen_time >> 16) & 0xFF),
1131 (uint8_t)((gen_time >> 8) & 0xFF),
1132 (uint8_t)(gen_time & 0xFF)
1149 LOG_E(
TAG,
"Invalid key type: 0x%02X", key_type);
1154 LOG_I(
TAG,
"Fingerprint set for key type 0x%02X", key_type);
1165static int cmd_select(
const apdu_t *apdu, uint8_t *resp,
size_t resp_max) {
1166 if (apdu->lc >= 6 && memcmp(apdu->data,
OPENPGP_AID, 6) == 0) {
1172 LOG_I(
TAG,
"OpenPGP application selected");
1193 uint8_t *resp,
size_t resp_max) {
1194 size_t first = (le > 0 && le < payload_len) ? le : payload_len;
1195 if (first + 2 > resp_max) first = resp_max - 2;
1196 memcpy(resp, payload, first);
1197 const size_t remainder = payload_len - first;
1198 if (remainder == 0) {
1200 resp[first + 1] = 0x00;
1201 return static_cast<int>(first + 2);
1210 resp[first + 1] = (remainder > 0xFF) ? 0x00 :
static_cast<uint8_t
>(remainder);
1211 return static_cast<int>(first + 2);
1221static int cmd_get_data(
const apdu_t *apdu, uint8_t *resp,
size_t resp_max) {
1222 uint16_t tag = (apdu->p1 << 8) | apdu->p2;
1271 uint8_t status[7] = {
1311 uint8_t count[3] = {
1345 uint8_t uif[2] = { 0x00, 0x20 };
1352 uint8_t key_info[6];
1382 uint8_t sec_tpl[7] = {
1397 uint8_t disabled[3];
1398 size_t disabled_len = 0;
1406 if (cert_len == 0) {
1413 LOG_W(
TAG,
"GET DATA: Unknown tag 0x%04X", tag);
1470 const size_t n =
sizeof(k_put_data_table) /
sizeof(k_put_data_table[0]);
1471 for (
size_t i = 0; i < n; ++i) {
1472 if (k_put_data_table[i].tag == tag) {
1473 return &k_put_data_table[i];
1491 char *str = (
char *)desc->
buffer;
1492 memcpy(str, apdu->data, apdu->lc);
1493 str[apdu->lc] =
'\0';
1604 LOG_I(
TAG,
"DEC role reverted to P-256 ECDH");
1617 *target = new_curve;
1619 LOG_I(
TAG,
"Algorithm attributes for %s updated to curve %u",
1624static int cmd_put_data(
const apdu_t *apdu, uint8_t *resp,
size_t resp_max) {
1630 uint16_t tag = (apdu->p1 << 8) | apdu->p2;
1635 if (apdu->lc == 1) {
1647 if (apdu->lc == 0) {
1653 LOG_I(
TAG,
"Resetting Code cleared");
1664 mbedtls_platform_zeroize(new_salt,
sizeof(new_salt));
1665 mbedtls_platform_zeroize(new_hash,
sizeof(new_hash));
1670 mbedtls_platform_zeroize(new_salt,
sizeof(new_salt));
1671 mbedtls_platform_zeroize(new_hash,
sizeof(new_hash));
1673 s_rc_len =
static_cast<uint8_t
>(apdu->lc);
1679 if (apdu->lc != 16 && apdu->lc != 32) {
1695 LOG_I(
TAG,
"Cardholder certificate stored (%u bytes)", apdu->lc);
1707 LOG_W(
TAG,
"PUT DATA: Unknown tag 0x%04X", tag);
1722 uint16_t *tag_out,
const uint8_t **value_out,
1723 size_t *value_len_out) {
1724 if (!buf || !pos || *pos >= buf_len)
return false;
1727 uint16_t tag = buf[p++];
1728 if ((tag & 0x1F) == 0x1F) {
1729 if (p >= buf_len)
return false;
1730 tag = (tag << 8) | buf[p++];
1732 if (p >= buf_len)
return false;
1735 uint8_t lb = buf[p++];
1738 }
else if (lb == 0x81) {
1739 if (p >= buf_len)
return false;
1741 }
else if (lb == 0x82) {
1742 if (p + 1 >= buf_len)
return false;
1743 len = (
static_cast<size_t>(buf[p]) << 8) | buf[p + 1];
1748 if (p + len > buf_len)
return false;
1751 *value_out = buf + p;
1752 *value_len_out = len;
1767 const uint8_t* concat,
size_t concat_len,
1768 const uint8_t** e,
size_t* e_len,
1769 const uint8_t** p,
size_t* p_len,
1770 const uint8_t** q,
size_t* q_len) {
1771 size_t tpos = 0, cpos = 0;
1772 *e = *p = *q =
nullptr;
1773 *e_len = *p_len = *q_len = 0;
1774 while (tpos < tmpl_len) {
1775 const uint8_t t = tmpl[tpos++];
1776 if (tpos >= tmpl_len)
return false;
1778 const uint8_t lb = tmpl[tpos++];
1781 }
else if (lb == 0x81) {
1782 if (tpos >= tmpl_len)
return false;
1783 clen = tmpl[tpos++];
1784 }
else if (lb == 0x82) {
1785 if (tpos + 1 >= tmpl_len)
return false;
1786 clen = (
static_cast<size_t>(tmpl[tpos]) << 8) | tmpl[tpos + 1];
1791 if (cpos + clen > concat_len)
return false;
1792 const uint8_t* val = concat + cpos;
1795 case 0x91: *e = val; *e_len = clen;
break;
1796 case 0x92: *p = val; *p_len = clen;
break;
1797 case 0x93: *q = val; *q_len = clen;
break;
1801 return (*e && *p && *q);
1824 if (apdu->p1 != 0x3F || apdu->p2 != 0xFF) {
1827 if (apdu->lc == 0 || !apdu->data) {
1833 const uint8_t *outer_val =
nullptr;
1834 size_t outer_len = 0;
1835 if (!
ehl_parse_one(apdu->data, apdu->lc, &pos, &tag, &outer_val, &outer_len) ||
1842 const uint8_t *crt_val =
nullptr;
1844 if (!
ehl_parse_one(outer_val, outer_len, &inner, &tag, &crt_val, &crt_len)) {
1847 const uint8_t key_ref =
static_cast<uint8_t
>(tag);
1854 const uint8_t *tmpl =
nullptr;
size_t tmpl_len = 0;
1855 const uint8_t *concat =
nullptr;
size_t concat_len = 0;
1856 while (inner < outer_len) {
1857 const uint8_t *v =
nullptr;
1859 if (!
ehl_parse_one(outer_val, outer_len, &inner, &tag, &v, &vlen)) {
1862 if (tag == 0x7F48) { tmpl = v; tmpl_len = vlen; }
1863 else if (tag == 0x5F48) { concat = v; concat_len = vlen; }
1865 if (!concat || concat_len == 0) {
1873 const uint8_t *e =
nullptr, *p =
nullptr, *q =
nullptr;
1874 size_t e_len = 0, p_len = 0, q_len = 0;
1876 &e, &e_len, &p, &p_len, &q, &q_len)) {
1880 size_t blob_len = 0;
1882 blob,
sizeof(blob), &blob_len);
1884 mbedtls_platform_zeroize(blob,
sizeof(blob));
1889 LOG_I(
TAG,
"Imported RSA key for role %d", r);
1912 mbedtls_platform_zeroize(scalar,
sizeof(scalar));
1917 LOG_I(
TAG,
"Imported ECC key for role %d", r);
1928static int cmd_verify(
const apdu_t *apdu, uint8_t *resp,
size_t resp_max) {
1929 uint8_t pw_ref = apdu->p2;
1932 if (apdu->lc == 0) {
1947 return apdu_sw(resp, 0x63C0 | retries);
1952 bool verified =
false;
1959 if (
kdf_active || apdu->lc == 32 || apdu->lc == 64) {
1971 memcpy(pin_str, apdu->data, apdu->lc);
1972 pin_str[apdu->lc] =
'\0';
1980 mbedtls_platform_zeroize(pin_str,
sizeof(pin_str));
1983 LOG_I(
TAG,
"PW1 verified successfully");
1987 if (
kdf_active || apdu->lc == 32 || apdu->lc == 64) {
1994 memcpy(pin_str, apdu->data, apdu->lc);
1995 pin_str[apdu->lc] =
'\0';
1997 mbedtls_platform_zeroize(pin_str,
sizeof(pin_str));
2001 LOG_I(
TAG,
"PW3 verified successfully");
2014 LOG_W(
TAG,
"PIN blocked after too many failures");
2017 LOG_W(
TAG,
"PIN verification failed, %d retries left", retries);
2018 return apdu_sw(resp, 0x63C0 | retries);
2038 uint8_t hash_out[32]) {
2039 if (!pin || !salt || !hash_out)
return false;
2040 size_t pin_len = strlen(pin);
2042 size_t combined = 8 + pin_len;
2043 if (combined == 0)
return false;
2046 memcpy(buffer, salt, 8);
2047 memcpy(buffer + 8, pin, pin_len);
2049 mbedtls_sha256_context ctx;
2050 mbedtls_sha256_init(&ctx);
2051 if (mbedtls_sha256_starts(&ctx, 0) != 0) {
2052 mbedtls_sha256_free(&ctx);
2053 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
2056 size_t processed = 0;
2057 size_t total_bytes = iterations;
2058 while (processed < total_bytes) {
2059 size_t chunk = (total_bytes - processed < combined) ? (total_bytes - processed) : combined;
2060 if (mbedtls_sha256_update(&ctx, buffer, chunk) != 0) {
2061 mbedtls_sha256_free(&ctx);
2062 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
2067 int rc = mbedtls_sha256_finish(&ctx, hash_out);
2068 mbedtls_sha256_free(&ctx);
2069 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
2082 for (
size_t i = 0; i < n; ++i) diff |= a[i] ^ b[i];
2094 uint8_t salt[8] = {};
2095 uint8_t stored[32] = {};
2096 uint8_t candidate[32] = {};
2100 if (!mgr.getPW1Salt(salt))
goto done;
2101 if (!mgr.getPW1Hash(stored))
goto done;
2103 if (!mgr.getPW3Salt(salt))
goto done;
2104 if (!mgr.getPW3Hash(stored))
goto done;
2107 if (!
compute_kdf_hash(pin, salt, mgr.getIterationCount(), candidate))
goto done;
2111 mbedtls_platform_zeroize(stored,
sizeof(stored));
2112 mbedtls_platform_zeroize(candidate,
sizeof(candidate));
2138 if (len < min_len * 2) {
2141 for (
size_t old_len = min_len; old_len <= len - min_len; ++old_len) {
2142 size_t new_len = len - old_len;
2148 memcpy(old_pin, data, old_len);
2149 old_pin[old_len] =
'\0';
2150 memcpy(new_pin, data + old_len, new_len);
2151 new_pin[new_len] =
'\0';
2154 mbedtls_platform_zeroize(old_pin,
sizeof(old_pin));
2155 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
2158 mbedtls_platform_zeroize(old_pin,
sizeof(old_pin));
2159 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
2173 uint8_t pw_ref = apdu->p2;
2175 if (apdu->lc == 0) {
2181 uint8_t (*retries_fn)(void) = NULL;
2183 const char *log_label = NULL;
2203 if (apdu->lc !=
static_cast<uint16_t
>(2 *
kdf_pin_len)) {
2206 const uint8_t* old_h = apdu->data;
2212 uint8_t retries = retries_fn();
2216 return apdu_sw(resp, 0x63C0 | retries);
2224 LOG_I(
TAG,
"%s changed successfully (KDF)", log_label);
2239 const size_t old_len = apdu->lc - new_raw;
2241 memcpy(old_pin, apdu->data, old_len);
2242 old_pin[old_len] =
'\0';
2246 mbedtls_platform_zeroize(old_pin,
sizeof(old_pin));
2248 uint8_t retries = retries_fn();
2252 return apdu_sw(resp, 0x63C0 | retries);
2260 LOG_I(
TAG,
"%s changed (KDF enable transition)", log_label);
2265 if (apdu->lc < min_len * 2) {
2269 if (
try_change_pin(apdu->data, apdu->lc, min_len, slot, change_fn)) {
2270 LOG_I(
TAG,
"%s changed successfully", log_label);
2284 uint8_t retries = retries_fn();
2288 return apdu_sw(resp, 0x63C0 | retries);
2298static int cmd_pso_cds(
const apdu_t *apdu, uint8_t *resp,
size_t resp_max) {
2305 size_t blob_len = 0;
2310 size_t rsa_sig_len = 0;
2311 bool ok =
gpg_rsa_sign(blob, blob_len, apdu->data, apdu->lc,
2312 rsa_sig,
sizeof(rsa_sig), &rsa_sig_len);
2313 mbedtls_platform_zeroize(blob,
sizeof(blob));
2326 LOG_E(
TAG,
"No signature key configured");
2332 uint8_t signature[64];
2402 if (apdu->lc < 1 + 16 + 1) {
2405 const uint8_t* iv_in = apdu->data + 1;
2406 const uint8_t* ct = apdu->data + 1 + 16;
2407 size_t ct_len = apdu->lc - 1 - 16;
2408 if (ct_len > resp_max - 2) {
2412 uint8_t aes_key[32] = {};
2413 size_t aes_key_len = 0;
2415 mbedtls_platform_zeroize(aes_key,
sizeof(aes_key));
2419 mbedtls_aes_context aes;
2420 mbedtls_aes_init(&aes);
2421 int rc = mbedtls_aes_setkey_enc(&aes, aes_key,
static_cast<unsigned int>(aes_key_len * 8));
2423 mbedtls_aes_free(&aes);
2424 mbedtls_platform_zeroize(aes_key,
sizeof(aes_key));
2429 memcpy(iv, iv_in,
sizeof(iv));
2432 if (ct_len >
sizeof(plain)) {
2433 mbedtls_aes_free(&aes);
2434 mbedtls_platform_zeroize(aes_key,
sizeof(aes_key));
2437 rc = mbedtls_aes_crypt_cfb128(&aes, MBEDTLS_AES_DECRYPT, ct_len, &iv_off, iv, ct, plain);
2438 mbedtls_aes_free(&aes);
2439 mbedtls_platform_zeroize(aes_key,
sizeof(aes_key));
2440 mbedtls_platform_zeroize(iv,
sizeof(iv));
2442 mbedtls_platform_zeroize(plain,
sizeof(plain));
2446 mbedtls_platform_zeroize(plain,
sizeof(plain));
2459 if (apdu->data[0] == 0x02) {
2469 size_t blob_len = 0;
2475 bool ok =
gpg_rsa_decrypt(blob, blob_len, apdu->data + 1, apdu->lc - 1,
2476 pt,
sizeof(pt), &pt_len);
2477 mbedtls_platform_zeroize(blob,
sizeof(blob));
2479 mbedtls_platform_zeroize(pt,
sizeof(pt));
2483 mbedtls_platform_zeroize(pt,
sizeof(pt));
2494 if (apdu->lc < 70) {
2498 const uint8_t* p = apdu->data;
2499 const uint8_t* end = apdu->data + apdu->lc;
2501 if (p >= end || *p != 0xA6) {
2509 }
else if (*p == 0x81 && p + 1 < end) {
2511 }
else if (*p == 0x82 && p + 2 < end) {
2517 if (p + 2 > end || p[0] != 0x7F || p[1] != 0x49) {
2525 }
else if (*p == 0x81 && p + 1 < end) {
2527 }
else if (*p == 0x82 && p + 2 < end) {
2533 if (p >= end || *p != 0x86) {
2542 }
else if (*p == 0x81 && p + 1 < end) {
2545 }
else if (*p == 0x82 && p + 2 < end) {
2546 pubkey_len = (
static_cast<size_t>(p[1]) << 8) | p[2];
2558 const uint8_t* peer_pubkey = p;
2567 mbedtls_platform_zeroize(dec_privkey,
sizeof(dec_privkey));
2569 mbedtls_platform_zeroize(shared_secret,
sizeof(shared_secret));
2573 mbedtls_platform_zeroize(shared_secret,
sizeof(shared_secret));
2593 if (apdu->p1 != 0x41) {
2599 if (apdu->lc == 0 || apdu->data ==
nullptr) {
2603 if (apdu->lc < 3 || apdu->data[0] != 0x83 || apdu->data[1] != 0x01) {
2606 const uint8_t ref = apdu->data[2];
2607 if (ref != 0x01 && ref != 0x02 && ref != 0x03) {
2611 if ((apdu->p2 ==
KEY_SIG && ref != 0x01) ||
2612 (apdu->p2 ==
KEY_DEC && ref != 0x02) ||
2613 (apdu->p2 ==
KEY_AUT && ref != 0x03)) {
2632 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2638 if (apdu->lc == 0 || apdu->data ==
nullptr) {
2644 size_t blob_len = 0;
2649 size_t rsa_sig_len = 0;
2650 bool ok =
gpg_rsa_sign(blob, blob_len, apdu->data, apdu->lc,
2651 rsa_sig,
sizeof(rsa_sig), &rsa_sig_len);
2652 mbedtls_platform_zeroize(blob,
sizeof(blob));
2662 LOG_E(
TAG,
"No AUT key configured");
2666 uint8_t signature[64];
2674 LOG_E(
TAG,
"INTERNAL AUTHENTICATE: signing failed");
2692 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2704 LOG_W(
TAG,
"Card moved to TERMINATED state");
2718 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2725 LOG_W(
TAG,
"ACTIVATE FILE: card reset to factory defaults");
2760 for (
int r = 0; r < 3; ++r) {
2795 if (apdu->p1 != 0x00 && apdu->p1 != 0x02) {
2798 if (apdu->p1 == 0x00) {
2816 diff |=
static_cast<uint8_t
>(
s_rc_hash[i] ^ input_hash[i]);
2818 mbedtls_platform_zeroize(input_hash,
sizeof(input_hash));
2826 return apdu_sw(resp,
static_cast<uint16_t
>(0x63C0 | retries));
2829 const size_t new_pw1_len = apdu->lc -
s_rc_len;
2831 memcpy(new_pin, apdu->data +
s_rc_len, new_pw1_len);
2832 new_pin[new_pw1_len] =
'\0';
2834 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
2837 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
2842 LOG_I(
TAG,
"RESET RETRY COUNTER: PW1 reset via RC");
2851 if (
kdf_active || apdu->lc == 32 || apdu->lc == 64) {
2858 LOG_I(
TAG,
"RESET RETRY COUNTER: PW1 reset by admin (KDF)");
2865 memcpy(new_pin, apdu->data, apdu->lc);
2866 new_pin[apdu->lc] =
'\0';
2868 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
2871 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
2874 LOG_I(
TAG,
"RESET RETRY COUNTER: PW1 reset by admin");
2925 mbedtls_platform_zeroize(privkey,
sizeof(privkey));
2928 mbedtls_platform_zeroize(privkey,
sizeof(privkey));
2940 LOG_E(
TAG,
"Key generation failed for slot %d", ecc_slot);
2943 LOG_I(
TAG,
"Key pair generated in slot %d (hardware)", ecc_slot);
2952 uint32_t now = (uint32_t)time(NULL);
2954 (uint8_t)((now >> 24) & 0xFF),
2955 (uint8_t)((now >> 16) & 0xFF),
2956 (uint8_t)((now >> 8) & 0xFF),
2957 (uint8_t)(now & 0xFF)
2983 uint8_t *pubkey, uint8_t *curve_out) {
2985 LOG_I(
TAG,
"read_public_key DEC: checking has_dec_privkey");
2987 LOG_W(
TAG,
"read_public_key DEC: no privkey");
2990 LOG_I(
TAG,
"read_public_key DEC: loading privkey");
2993 LOG_W(
TAG,
"read_public_key DEC: load_dec_privkey failed");
2996 LOG_I(
TAG,
"read_public_key DEC: deriving pubkey");
2998 mbedtls_platform_zeroize(privkey,
sizeof(privkey));
3002 LOG_I(
TAG,
"read_public_key DEC: derive ok=%d", ok);
3021 uint8_t *out,
size_t *out_len) {
3023 if (pubkey[0] == 0x04) {
3045 size_t blob_len = 0;
3051 size_t n_len = 0, e_len = 0;
3053 e_buf,
sizeof(e_buf), &e_len);
3054 mbedtls_platform_zeroize(blob,
sizeof(blob));
3060 size_t inner_len = 0;
3061 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len, 0x81, n_buf, n_len);
3062 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len, 0x82, e_buf, e_len);
3068 memcpy(outbuf + pos, inner, inner_len);
3085 if (apdu->lc >= 2) {
3086 key_ref = apdu->data[0];
3087 LOG_I(
TAG,
"Key ref from CRT: 0x%02X", key_ref);
3093 LOG_I(
TAG,
"GENERATE_KEYPAIR: P1=0x%02X, key_ref=0x%02X, slot=%d, type=%d",
3094 apdu->p1, key_ref, ecc_slot, key_type);
3100 if (apdu->p1 == 0x80) {
3105 size_t blob_len = 0;
3106 LOG_W(
TAG,
"Generating RSA-%u key (software fallback, slow) for role %d",
3112 mbedtls_platform_zeroize(blob,
sizeof(blob));
3123 if (apdu->p1 == 0x80) {
3136 LOG_I(
TAG,
"Generating key in slot %d (curve=%d, type=%s)",
3148 if (gen_sw !=
SW_OK) {
3156 size_t pubkey_len = 0;
3158 pubkey_with_prefix, &pubkey_len);
3160 uint8_t tlv_data[128];
3161 size_t pos =
tlv_build(tlv_data,
sizeof(tlv_data), 0x86,
3162 pubkey_with_prefix, pubkey_len);
3164 uint8_t final_resp[140];
3165 size_t final_len = 0;
3166 final_resp[final_len++] = 0x7F;
3167 final_resp[final_len++] = 0x49;
3169 memcpy(final_resp + final_len, tlv_data, pos);
3179 LOG_W(
TAG,
"Public key read failed: empty slot=%d type=%d", ecc_slot, key_type);
3191 size_t pubkey_len = 0;
3194 uint8_t tlv_data[128];
3196 pos +=
tlv_build(tlv_data + pos,
sizeof(tlv_data) - pos, 0x86, pubkey_with_prefix, pubkey_len);
3199 uint8_t final_resp[140];
3200 size_t final_len = 0;
3201 final_resp[final_len++] = 0x7F;
3202 final_resp[final_len++] = 0x49;
3204 memcpy(final_resp + final_len, tlv_data, pos);
3207 LOG_I(
TAG,
"Public key exported (%zu bytes, curve=%d, slot=%d)",
3208 pubkey_len, read_curve, ecc_slot);
3223 if (result_len < 2)
return result_len;
3224 const size_t payload_len =
static_cast<size_t>(result_len - 2);
3225 const uint16_t sw =
static_cast<uint16_t
>((resp[result_len - 2] << 8) |
3226 resp[result_len - 1]);
3228 if (sw !=
SW_OK)
return result_len;
3229 if (le == 0 || payload_len <= le) {
3232 const size_t remainder = payload_len - le;
3241 if (resp_max < le + 2)
return result_len;
3242 const uint8_t sw2 = (remainder > 0xFF) ? 0x00 :
static_cast<uint8_t
>(remainder);
3245 return static_cast<int>(le + 2);
3252 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
3258 size_t want = (apdu->le > 0) ? apdu->le : 256;
3260 if (want + 2 > resp_max) want = resp_max - 2;
3270 uint16_t sw =
SW_OK;
3274 sw =
static_cast<uint16_t
>((0x61 << 8) | sw2);
3278 resp[want] =
static_cast<uint8_t
>((sw >> 8) & 0xFF);
3279 resp[want + 1] =
static_cast<uint8_t
>(sw & 0xFF);
3280 return static_cast<int>(want + 2);
3284 uint8_t *resp,
size_t resp_max) {
3292 LOG_D(
TAG,
"APDU: CLA=%02X INS=%02X P1=%02X P2=%02X Lc=%d",
3293 apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc);
3302 if ((apdu.cla & ~0x10) != 0x00) {
3310 const bool is_chain_block = (apdu.cla & 0x10) != 0;
3328 if (apdu.lc > 0 && apdu.data !=
nullptr) {
3332 if (is_chain_block) {
3375 result_len =
cmd_verify(&apdu, resp, resp_max);
3387 if (apdu.p1 == 0x9E && apdu.p2 == 0x9A) {
3389 }
else if (apdu.p1 == 0x80 && apdu.p2 == 0x86) {
3409 uint8_t challenge[255];
3410 size_t len = apdu.le > 0 ? apdu.le : 8;
3411 if (len >
sizeof(challenge)) len =
sizeof(challenge);
3420#define APP_VERSION "0.0.0"
3424 reinterpret_cast<const uint8_t*
>(ver),
3425 strlen(ver),
SW_OK);
3443 LOG_W(
TAG,
"Unknown instruction: 0x%02X", apdu.ins);
bool gpg_storage_save_aes_key(const uint8_t *key, size_t key_len, const char *pin)
Saves the symmetric AES key for PSO:DECIPHER (DO 0xD5).
bool gpg_storage_delete_rsa_key(uint8_t role)
Deletes the RSA private-key blob for the role (both slots).
#define GPG_RSA_BLOB_MAX
Maximum serialized RSA private-key blob (RSA-4096 n_bits||e||p||q).
bool gpg_storage_delete_dec_privkey(void)
Deletes DEC private key record.
uint8_t gpg_storage_dec_slot(void)
bool gpg_storage_save_dec_privkey(const uint8_t *privkey, const char *pin)
Saves a DEC private key into R-Memory using PIN-bound AES-GCM.
bool gpg_storage_save_rsa_key(uint8_t role, const uint8_t *blob, size_t blob_len, const char *pin)
Saves an encrypted RSA private-key blob for a key role.
void gpg_storage_set_session_pin(const char *pin)
Stores session PIN-derived key after successful PIN verification.
bool gpg_storage_has_rsa_key(uint8_t role)
Returns true if an RSA private-key blob exists for the role.
bool gpg_storage_load_aes_key(uint8_t *key_out, size_t *key_len_out, const char *pin)
Loads the symmetric AES key from R-Memory.
bool gpg_storage_load_dec_privkey(uint8_t *privkey_out, const char *pin)
Loads and decrypts the DEC private key from R-Memory.
uint8_t gpg_storage_aut_slot(void)
void gpg_storage_clear_session(void)
Clears the cached session key.
uint8_t gpg_storage_sig_slot(void)
bool gpg_storage_load_rsa_key(uint8_t role, uint8_t *blob_out, size_t blob_cap, size_t *blob_len_out, const char *pin)
Loads and decrypts the RSA private-key blob for a key role.
bool gpg_storage_has_dec_privkey(void)
Returns true if encrypted DEC private key record exists.
bool gpg_storage_has_aes_key(void)
Returns true if a symmetric AES key record exists.
char name[cdc::hal::ISecureElement::RMEM_NAME_LEN]
algo_attr_status_t algo_attr_validate_role(const algo_attr_t *attr, algo_attr_role_t role)
Check whether the parsed attribute is compatible with the key role it will be installed into.
algo_attr_status_t algo_attr_parse(const uint8_t *bytes, size_t len, algo_attr_t *out)
Parse a raw algorithm-attribute byte sequence into structured form.
algo_attr_status_t algo_attr_validate_capability(const algo_attr_t *attr, bool rsa_supported)
Check whether the badge's secure element / mbedTLS combination can actually execute this algorithm.
algo_attr_role_t
Key role (selects which DO tag is being parsed / built).
@ ALGO_ATTR_CURVE_ED25519
bool apdu_parse(const uint8_t *raw, size_t raw_len, apdu_t *apdu)
ISO 7816 APDU parsing/building helpers for CDC Badge OpenPGP stack.
#define INS_GET_CHALLENGE
size_t apdu_build_response(uint8_t *buf, size_t buf_max, const uint8_t *data, size_t data_len, uint16_t sw)
Builds APDU response payload with status word trailer.
#define INS_GENERATE_KEYPAIR
static size_t apdu_sw(uint8_t *buf, uint16_t sw)
#define INS_INTERNAL_AUTH
struct __attribute__((packed))
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,...)
static constexpr const char * DEFAULT_PW1
static constexpr const char * DEFAULT_PW3
static PinManager & instance()
Returns singleton PIN manager instance.
virtual SeResult eccGetPublicKey(uint8_t slot, uint8_t *pubKey, EccCurve *curve=nullptr)=0
#define ED25519_PUBKEY_SIZE
Ed25519 raw public key size in bytes.
#define P256_PUBKEY_SIZE
P-256 uncompressed public key size: 0x04 || X(32) || Y(32).
#define PW3_CODE
PW3 reference (Admin PIN).
#define PW1_CODE_1
PW1 reference for signature operations (User PIN).
#define PW1_CODE_2
PW1 reference for non-signature operations (User PIN, alt).
#define SHA256_DIGEST_SIZE
SHA-256 digest output size in bytes (FIPS 180-4).
#define OPENPGP_FINGERPRINT_SIZE
OpenPGP v4 fingerprint size (SHA-1 digest length, in bytes).
#define P256_ECDH_SECRET_SIZE
P-256 ECDH shared secret size in bytes.
#define P256_PRIVKEY_SIZE
P-256 private key (scalar) size in bytes.
bool ecdh_p256_generate_keypair(uint8_t *privkey_out, uint8_t *pubkey_out)
bool ecdh_p256_compute_shared_secret(uint8_t *privkey, const uint8_t *peer_pubkey, uint8_t *shared_out)
Computes ECDH shared secret on P-256 using local private key and peer public key.
bool ecdh_p256_derive_pubkey(const uint8_t *privkey, uint8_t *pubkey_out)
#define CDC_CURVE_ED25519
bool gpg_init(void)
Initializes the GPG module bookkeeping.
kdf_hash_t
Hash algorithms accepted inside KDF-DO (inner tag 0x82).
kdf_status_t kdf_do_parse(const uint8_t *bytes, size_t len, kdf_do_t *out)
Parse a KDF-DO byte sequence into structured form.
kdf_status_t kdf_do_build_disabled(uint8_t *out, size_t out_cap, size_t *out_len)
Convenience helper: produce the "KDF disabled" KDF-DO body — three bytes (81 01 00) — that hosts expe...
ISecureElement * getSecureElementInstance()
Returns singleton secure-element stub instance.
bool openpgp_get_fingerprint(uint8_t key_type, uint8_t *fp_out)
Reads the stored OpenPGP v4 fingerprint for a key role.
static void encode_pubkey_with_prefix(const uint8_t *pubkey, uint8_t curve, uint8_t *out, size_t *out_len)
Encodes the public key with the OpenPGP/SEC1 uncompressed prefix.
static size_t tlv_build(uint8_t *buf, size_t buf_max, uint16_t tag, const uint8_t *value, size_t value_len)
Builds complete TLV object and returns total encoded length.
static uint8_t fingerprint_dec[OPENPGP_FINGERPRINT_SIZE]
static int cmd_get_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU GET DATA command processing.
static int cmd_manage_security_env(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU MANAGE SECURITY ENVIRONMENT (INS 0x22).
static size_t g_resp_remaining
static bool verify_state_signature(cdc::hal::ISecureElement *se, const uint8_t *payload, size_t payload_len, const uint8_t *sig, size_t sig_len)
Verifies the P-256 ECDSA attestation signature over an OpenPGP state payload. Same construction as Pi...
static const uint8_t ALGO_ATTR_P256_ECDH[]
Algorithm attributes for P-256 ECDH (decryption role).
static uint8_t g_chain_p2
static void load_state_from_nvs(void)
Loads persistent OpenPGP runtime state from NVS.
static uint16_t apply_kdf_do(const uint8_t *data, size_t len)
Applies a KDF-DO payload written via PUT DATA 0xF9.
static bool compute_kdf_hash(const char *pin, const uint8_t *salt, uint32_t iterations, uint8_t hash_out[32])
Computes the iterated-salted S2K hash (OpenPGP KDF) for a PIN candidate.
static uint16_t generate_dec_key(uint8_t *pubkey_out)
Generates a software ECDH P-256 key pair for the DEC slot.
uint32_t openpgp_get_gen_time(uint8_t key_type)
Returns the stored Unix timestamp of key generation, or 0 when unset.
static bool const_time_equal(const uint8_t *a, const uint8_t *b, size_t n)
Constant-time comparison of two equal-length byte buffers.
static char cardholder_lang[8]
static size_t load_cardholder_cert(uint8_t *out, size_t cap)
Loads the cardholder certificate into out.
static const uint8_t ALGO_ATTR_P256_ECDSA[]
Algorithm attributes for P-256 ECDSA (signature/authentication roles).
static int cmd_generate_keypair(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU GENERATE ASYMMETRIC KEY PAIR.
static constexpr uint8_t OPENPGP_NVS_SCHEMA_V3
static bool se_eddsa_sign(uint8_t slot, const uint8_t *msg, size_t msg_len, uint8_t *sig)
Signs a message using secure-element EdDSA key.
static uint8_t kdf_hash_len(kdf_hash_t hash)
Returns the on-device byte length of an SHA-256/512 KDF pre-hash.
static uint8_t fingerprint_aut[OPENPGP_FINGERPRINT_SIZE]
static int build_do_app_related(uint8_t *buf, size_t buf_max)
Builds OpenPGP DO 0x6E (Application Related Data).
static bool se_ecdsa_sign(uint8_t slot, const uint8_t *hash, size_t hash_len, uint8_t *sig)
Signs a hash using secure-element ECDSA key.
static int cmd_select(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU SELECT command processing.
static int cmd_change_reference_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU CHANGE REFERENCE DATA command for PIN updates.
static int role_index_for_key_ref(uint8_t key_ref)
Maps an OpenPGP key reference (B6/B8/A4) to a 0-based role index.
static constexpr size_t RC_SALT_SIZE
static bool save_cardholder_cert(const uint8_t *data, size_t len)
Persists the cardholder certificate as a standalone NVS blob. len == 0 erases it. Stored unsigned: a ...
static uint8_t kdf_pin_len
static int cmd_pso_decipher(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
int openpgp_process_apdu(const uint8_t *cmd, size_t cmd_len, uint8_t *resp, size_t resp_max)
put_data_kind_t
Storage kind for PUT DATA descriptor entries.
@ PUT_KIND_STRING_BOUNDED
static cdc::hal::ISecureElement * get_se()
Returns secure-element instance used by OpenPGP backend.
static uint8_t kdf_do_len
static uint8_t g_chain_buffer[4096]
Command-chaining accumulator (ISO 7816-4 §5.1.1).
pin_slot_t
PIN slot identifier used by PIN helper routines.
static uint8_t g_resp_buffer[4096]
Buffered remainder of an APDU response that did not fit into the caller-supplied Le window....
#define NVS_CERT_KEY
NVS key holding the (public) cardholder certificate (DO 0x7F21).
static uint8_t s_rc_hash[RC_HASH_SIZE]
static constexpr uint8_t ATTESTATION_ECC_SLOT
static void init_aid_from_mac(void)
Initializes the OpenPGP AID serial section from the ESP32 MAC address.
bool openpgp_set_key_fingerprint(uint8_t key_type, const uint8_t *fingerprint, uint32_t gen_time)
static constexpr size_t CARDHOLDER_CERT_MAX
static bool try_change_pin(const uint8_t *data, size_t len, size_t min_len, pin_slot_t slot, pin_change_fn_t change_fn)
Searches the split point for CHANGE REFERENCE DATA without consuming retries.
static int cmd_verify(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU VERIFY command for PIN verification.
key_type_t
Builders for OpenPGP application-related data objects.
static void se_random_fill(uint8_t *buf, size_t len)
Fills buffer with secure random bytes, with ESP fallback.
size_t openpgp_get_cardholder_name(char *out, size_t out_size)
Copies the cardholder name (OpenPGP DO 0x5B) into the caller buffer. Format is gpg's "Surname<<Firstn...
static uint8_t gen_time_aut[4]
static uint16_t role_rsa_n_bits[3]
static constexpr size_t OPENPGP_STATE_SIG_SIZE
static uint16_t generate_hardware_key(uint8_t ecc_slot, uint8_t curve)
Generates a hardware ECC key pair in the TROPIC01 secure element.
static void update_generation_timestamp(uint8_t key_ref)
Updates and persists the generation timestamp for a key role.
static bool se_ecc_key_generate(uint8_t slot, uint8_t curve)
Generates ECC key material in secure element slot.
bool openpgp_set_cardholder_name(const char *name)
Sets the cardholder name (OpenPGP DO 0x5B) and persists state.
static uint8_t ca_fp_3[OPENPGP_FINGERPRINT_SIZE]
static uint8_t s_rc_retries
static int apply_response_chaining(uint32_t le, uint8_t *resp, size_t resp_max, int result_len)
Trim an APDU response to the host-requested Le window.
static int respond_chunked(const uint8_t *payload, size_t payload_len, uint32_t le, uint8_t *resp, size_t resp_max)
Returns a payload larger than the response window using response chaining, priming the GET RESPONSE b...
static uint8_t kdf_do_bytes[124]
static int cmd_reset_retry_counter(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU RESET RETRY COUNTER (INS 0x2C).
static char cardholder_url[64]
static uint8_t selected_curve_aut
static const uint8_t * get_algo_attr(key_type_t key_type, size_t *len)
Returns algorithm attributes for a key role based on stored key type.
static uint8_t ca_fp_1[OPENPGP_FINGERPRINT_SIZE]
Optional CA fingerprints for trust-chain metadata.
static void chain_reset(void)
static size_t tlv_write_len(uint8_t *buf, size_t len)
Writes a TLV length field using DER length encoding.
static uint8_t gen_time_sig[4]
Key-generation timestamps (4-byte big-endian Unix time each).
static constexpr size_t RC_HASH_SIZE
static bool role_is_rsa[3]
Per-role algorithm selection beyond the ECC curve. When role_is_rsa[r] is set the role is an RSA soft...
#define OPENPGP_RC_MIN_LEN
Resetting Code (RC) — optional per OpenPGP 3.4.1 §4.3.2. When set, the host can unblock PW1 with the ...
uint32_t openpgp_get_sig_count(void)
static bool g_chain_active
static uint8_t get_ecc_slot_for_key_ref(uint8_t key_ref)
Returns ECC slot mapping for an OpenPGP key reference.
static key_type_t get_key_type_for_ref(uint8_t key_ref)
Maps an OpenPGP key reference to an internal key type.
static size_t tlv_write_tag(uint8_t *buf, uint16_t tag)
TLV builder helper functions.
static uint8_t s_rc_salt[RC_SALT_SIZE]
bool openpgp_has_any_key(void)
Reports whether any of the SIG / DEC / AUT roles has a non-zero fingerprint configured....
bool openpgp_is_selected(void)
static int put_data_algo_attr(uint16_t tag, const apdu_t *apdu, uint8_t *resp)
static bool app_selected
ATR is defined in ccid.cpp and accessed via ccid_get_atr().
static bool kdf_active
KDF-DO (tag 0xF9) state. When kdf_active the host pre-hashes the PINs (PBKDF2) before VERIFY / CHANGE...
static int cmd_pso_cds(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU PSO: COMPUTE DIGITAL SIGNATURE.
static void save_state_to_nvs(void)
Persists OpenPGP runtime state to NVS.
static constexpr size_t RC_KDF_TOTAL_BYTES
static size_t g_chain_len
static int build_do_cardholder(uint8_t *buf, size_t buf_max)
Builds OpenPGP DO 0x65 (Cardholder Related Data).
void openpgp_factory_reset(void)
static const put_data_desc_t * find_put_data_desc(uint16_t tag)
Returns descriptor for an OpenPGP PUT DATA tag.
static int build_rsa_pubkey_from_storage(int r, uint8_t *resp, size_t resp_max)
Builds the 7F49 { 81 <modulus> 82 <exponent> } public-key response for an RSA role from its stored pr...
static int cmd_pso_decipher_aes(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU PSO: DECIPHER for ECDH key agreement.
static int cmd_internal_authenticate(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU INTERNAL AUTHENTICATE (INS 0x88).
static uint16_t role_rsa_e_bits[3]
static char cardholder_name[40]
Cardholder profile data stored in NVS.
static void wipe_role_key(int r)
Handles APDU PUT DATA command processing.
static int cmd_put_data_odd(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU PUT DATA (odd INS, 0xDB) for keypair import.
static int cmd_activate_file(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU ACTIVATE FILE (INS 0x44).
static const uint8_t EXT_CAPABILITIES[]
Extended capabilities object per OpenPGP 3.4.1 section 4.2.1.
static char cardholder_login[32]
static char s_session_pin[OPENPGP_PIN_MAX_LEN+1]
Session PIN cache for DEC key decryption (temporary after VERIFY for PSO:DECIPHER).
static uint8_t ca_fp_2[OPENPGP_FINGERPRINT_SIZE]
static uint8_t cardholder_sex
static int cmd_put_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
static bool parse_rsa_import(const uint8_t *tmpl, size_t tmpl_len, const uint8_t *concat, size_t concat_len, const uint8_t **e, size_t *e_len, const uint8_t **p, size_t *p_len, const uint8_t **q, size_t *q_len)
Splits the RSA key material (5F48) into e / p / q using the lengths declared in the Cardholder Privat...
static uint8_t gen_time_dec[4]
static bool se_ecc_key_read(uint8_t slot, uint8_t *pubkey, size_t max_len, uint8_t *curve_out)
Reads ECC public key from secure element and exposes curve metadata.
static uint8_t s_openpgp_aid[16]
OpenPGP Application ID (RID + PIX), initialized dynamically.
static int apply_put_data_desc(const put_data_desc_t *desc, const apdu_t *apdu, uint8_t *resp)
Applies a PUT DATA descriptor to the request payload.
bool(* pin_change_fn_t)(const char *pin)
Type alias for PIN change callbacks.
static int cmd_terminate_df(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU TERMINATE DF (INS 0xE6).
static bool read_public_key(key_type_t key_type, uint8_t ecc_slot, uint8_t *pubkey, uint8_t *curve_out)
Reads the public key for a given key role.
static uint8_t g_chain_ins
static bool peek_verify_pin(pin_slot_t slot, const char *pin)
Compares a candidate PIN against the stored hash without touching retry counters.
static uint8_t selected_curve_sig
Host-selected ECC curve per key role. DEC is fixed to P-256 because the TROPIC01 cannot perform ECDH ...
static bool compute_rc_hash(const uint8_t *rc, size_t rc_len, const uint8_t *salt, uint8_t *hash_out)
Iterated-salted SHA-256 over salt||rc for resetting-code storage. Same construction as PinManager::co...
static uint8_t role_rsa_fmt[3]
static const uint8_t ALGO_ATTR_ED25519[]
Algorithm attributes for Ed25519 (EdDSA with curve25519).
static bool ehl_parse_one(const uint8_t *buf, size_t buf_len, size_t *pos, uint16_t *tag_out, const uint8_t **value_out, size_t *value_len_out)
Parse one BER-TLV field at pos.
static uint8_t g_chain_p1
static uint8_t fingerprint_sig[OPENPGP_FINGERPRINT_SIZE]
Data object storage buffers (fingerprints and related metadata).
static bool card_terminated
Card lifecycle state per OpenPGP 3.4.1 §7.2.18.
static bool fp_is_set(const uint8_t fp[OPENPGP_FINGERPRINT_SIZE])
static const uint8_t HIST_BYTES[]
Historical bytes used in OpenPGP ATR-related data objects.
static uint32_t sig_count
static int cmd_get_response(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles INS GET RESPONSE (0xC0) — drains the chained response buffer.
const uint8_t * OPENPGP_AID
#define SW_CLA_NOT_SUPPORTED
#define SW_AUTH_METHOD_BLOCKED
#define SW_INCORRECT_P1P2
#define SW_REFERENCED_DATA_NOT_FOUND
#define SW_INS_NOT_SUPPORTED
#define SW_CONDITIONS_NOT_SATISFIED
#define OPENPGP_PW1_MIN_LEN
#define SW_FILE_NOT_FOUND
#define OPENPGP_PIN_MAX_LEN
const uint8_t OPENPGP_AID_LEN
#define OPENPGP_PW3_MIN_LEN
#define SW_FILE_TERMINATED
#define SW_SECURITY_NOT_SATISFIED
#define DO_CARDHOLDER_CERT
uint8_t pin_storage_openpgp_pw1_retries(void)
bool pin_storage_openpgp_reset(void)
void pin_storage_openpgp_reset_pw1_retries(void)
bool pin_storage_openpgp_pw1_blocked(void)
bool pin_storage_openpgp_set_pw1_raw(const uint8_t *data, size_t len)
bool pin_storage_openpgp_verify_pw3_raw(const uint8_t *data, size_t len)
bool pin_storage_openpgp_change_pw3(const char *new_pin)
uint8_t pin_storage_openpgp_pw3_retries(void)
bool pin_storage_openpgp_verify_pw1_raw(const uint8_t *data, size_t len)
bool pin_storage_openpgp_verify_pw1(const char *pin)
void pin_storage_openpgp_init(void)
bool pin_storage_openpgp_set_pw3_raw(const uint8_t *data, size_t len)
bool pin_storage_openpgp_verify_pw3(const char *pin)
bool pin_storage_openpgp_change_pw1(const char *new_pin)
bool pin_storage_openpgp_pw3_blocked(void)
bool gpg_rsa_decrypt(const uint8_t *blob, size_t blob_len, const uint8_t *ct, size_t ct_len, uint8_t *pt_out, size_t pt_cap, size_t *pt_len_out)
RSAES-PKCS1-v1.5 decryption of a cryptogram.
bool gpg_rsa_sign(const uint8_t *blob, size_t blob_len, const uint8_t *digestinfo, size_t di_len, uint8_t *sig_out, size_t sig_cap, size_t *sig_len_out)
RSASSA-PKCS1-v1.5 signature over a host-supplied DigestInfo.
bool gpg_rsa_blob_public(const uint8_t *blob, size_t blob_len, uint8_t *n_out, size_t n_cap, size_t *n_len_out, uint8_t *e_out, size_t e_cap, size_t *e_len_out)
Extracts the public modulus and exponent from a private-key blob.
bool gpg_rsa_generate(uint16_t n_bits, uint8_t *blob_out, size_t blob_cap, size_t *blob_len_out)
Generates a fresh RSA key pair and serialises its private blob. The public exponent is fixed to 65537...
bool gpg_rsa_blob_build(uint16_t n_bits, const uint8_t *e, size_t e_len, const uint8_t *p, size_t p_len, const uint8_t *q, size_t q_len, uint8_t *blob_out, size_t blob_cap, size_t *blob_len_out)
Serialises raw RSA components into a private-key blob.
#define GPG_RSA_MAX_MODULUS_BYTES
Software RSA backend for the OpenPGP card (mbedTLS).
Parsed algorithm-attribute payload.
Descriptor entry for table-driven PUT DATA processing.