20#include <mbedtls/platform_util.h>
21#include <mbedtls/sha256.h>
22#include <mbedtls/aes.h>
23#include <mbedtls/ecp.h>
24#include <mbedtls/ecdsa.h>
25#include <mbedtls/bignum.h>
33#include <esp_random.h>
35static const char *
TAG =
"OpenPGP";
53static bool se_ecc_key_read(uint8_t slot, uint8_t* pubkey,
size_t max_len, uint8_t* curve_out) {
55 if (!se || !pubkey)
return false;
57 auto res = se->eccGetPublicKey(slot, pubkey, &
curve);
77 LOG_E(
TAG,
"se_ecc_key_generate: SE not available (slot=%u)", slot);
88 LOG_W(
TAG,
"se_ecc_key_generate: slot %u initial fail (SeResult=%d), deleting and retrying",
89 slot,
static_cast<int>(res));
91 res = se->eccGenerate(slot, c);
94 LOG_E(
TAG,
"se_ecc_key_generate(slot=%u curve=%u) failed: SeResult=%d",
95 slot,
curve,
static_cast<int>(res));
109static bool se_ecdsa_sign(uint8_t slot,
const uint8_t* hash,
size_t hash_len, uint8_t* sig) {
111 if (!se || !hash || !sig)
return false;
124static bool se_eddsa_sign(uint8_t slot,
const uint8_t* msg,
size_t msg_len, uint8_t* sig) {
126 if (!se || !msg || !sig)
return false;
137 if (se && se->getRandom(buf,
static_cast<uint16_t
>(len))) {
140 esp_fill_random(buf, len);
150 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01,
153 0x00, 0x00, 0x00, 0x00,
182#define OPENPGP_RC_MIN_LEN 8
196 const uint8_t* salt, uint8_t* hash_out) {
205 mbedtls_sha256_context ctx;
206 mbedtls_sha256_init(&ctx);
207 if (mbedtls_sha256_starts(&ctx, 0) != 0) {
208 mbedtls_sha256_free(&ctx);
209 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
212 size_t processed = 0;
217 if (mbedtls_sha256_update(&ctx, buffer, chunk) != 0) {
218 mbedtls_sha256_free(&ctx);
219 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
224 mbedtls_sha256_finish(&ctx, hash_out);
225 mbedtls_sha256_free(&ctx);
226 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
296#define NVS_NAMESPACE "openpgp"
297#define NVS_STATE_KEY "state"
307 uint8_t schema_version;
371 0x73, 0xC0, 0x01, 0x80,
383 0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01
393 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07
403 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07
431 buf[0] = (tag >> 8) & 0xFF;
449 }
else if (len < 256) {
455 buf[1] = (len >> 8) & 0xFF;
470static size_t tlv_build(uint8_t *buf,
size_t buf_max, uint16_t tag,
471 const uint8_t *value,
size_t value_len) {
475 if (value && value_len > 0) {
476 memcpy(buf + pos, value, value_len);
526 size_t inner_len = 0;
529 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
533 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
537 uint8_t discret[384];
538 size_t discret_len = 0;
541 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
547 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
552 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
557 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
562 uint8_t pw_status[7] = {
571 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
579 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
580 0xC5, fps,
sizeof(fps));
587 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
588 0xC6, ca_fps,
sizeof(ca_fps));
591 uint8_t gen_times[12];
595 discret_len +=
tlv_build(discret + discret_len,
sizeof(discret) - discret_len,
596 0xCD, gen_times, 12);
599 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
600 0x73, discret, discret_len);
606 memcpy(buf + total, inner, inner_len);
620 size_t inner_len = 0;
624 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
629 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
633 inner_len +=
tlv_build(inner + inner_len,
sizeof(inner) - inner_len,
640 memcpy(buf + total, inner, inner_len);
654 const uint8_t* payload,
size_t payload_len,
655 const uint8_t* sig,
size_t sig_len) {
664 uint8_t pub_sec1[65];
666 memcpy(pub_sec1 + 1, pub_raw, 64);
669 mbedtls_sha256(payload, payload_len, hash, 0);
671 mbedtls_ecp_group grp;
674 mbedtls_ecp_group_init(&grp);
675 mbedtls_ecp_point_init(&Q);
676 mbedtls_mpi_init(&r);
677 mbedtls_mpi_init(&s);
681 if (mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1) != 0)
break;
682 if (mbedtls_ecp_point_read_binary(&grp, &Q, pub_sec1,
sizeof(pub_sec1)) != 0)
break;
683 if (mbedtls_mpi_read_binary(&r, sig + 0, 32) != 0)
break;
684 if (mbedtls_mpi_read_binary(&s, sig + 32, 32) != 0)
break;
685 ok = (mbedtls_ecdsa_verify(&grp, hash,
sizeof(hash), &Q, &r, &s) == 0);
688 mbedtls_mpi_free(&r);
689 mbedtls_mpi_free(&s);
690 mbedtls_ecp_point_free(&Q);
691 mbedtls_ecp_group_free(&grp);
705 uint8_t blob[BLOB_SIZE];
706 size_t len = BLOB_SIZE;
707 esp_err_t err = nvs_get_blob(nvs,
NVS_STATE_KEY, blob, &len);
710 if (err != ESP_OK || len != BLOB_SIZE) {
714 OpenpgpNvsState state = {};
715 memcpy(&state, blob,
sizeof(state));
721 LOG_W(
TAG,
"OpenPGP state signature invalid - re-initialising");
766 OpenpgpNvsState state = {};
788 memcpy(state.cardholder_name,
cardholder_name,
sizeof(state.cardholder_name));
789 memcpy(state.cardholder_lang,
cardholder_lang,
sizeof(state.cardholder_lang));
790 memcpy(state.cardholder_url,
cardholder_url,
sizeof(state.cardholder_url));
791 memcpy(state.cardholder_login,
cardholder_login,
sizeof(state.cardholder_login));
794 uint8_t blob[BLOB_SIZE];
795 memcpy(blob, &state,
sizeof(state));
805 blob +
sizeof(state), &sig_len);
807 LOG_E(
TAG,
"save_state: attestation sign failed (%d)",
808 static_cast<int>(sign_res));
813 esp_err_t err = nvs_open(
NVS_NAMESPACE, NVS_READWRITE, &nvs);
815 LOG_E(
TAG,
"save_state: nvs_open %s", esp_err_to_name(err));
821 err = nvs_commit(nvs);
825 LOG_E(
TAG,
"save_state: %s", esp_err_to_name(err));
835 if (esp_efuse_mac_get_default(mac) == ESP_OK) {
847 LOG_I(
TAG,
"AID initialized: Manufacturer=0x%02X%02X Serial=%02X%02X%02X%02X",
852 LOG_W(
TAG,
"Failed to read MAC, using default AID");
869 LOG_E(
TAG,
"Failed to initialize GPG/TROPIC01");
891 if (!fp_out)
return false;
896 default:
return false;
902 if (fp[i] != 0)
return true;
914 if (!out || out_size == 0)
return 0;
916 if (len >= out_size) len = out_size - 1;
923 const uint8_t *src =
nullptr;
930 return (
static_cast<uint32_t
>(src[0]) << 24) |
931 (
static_cast<uint32_t
>(src[1]) << 16) |
932 (
static_cast<uint32_t
>(src[2]) << 8) |
933 static_cast<uint32_t
>(src[3]);
937 if (!
name)
return false;
938 size_t len = strlen(
name);
951 if (!fingerprint)
return false;
955 (uint8_t)((gen_time >> 24) & 0xFF),
956 (uint8_t)((gen_time >> 16) & 0xFF),
957 (uint8_t)((gen_time >> 8) & 0xFF),
958 (uint8_t)(gen_time & 0xFF)
975 LOG_E(
TAG,
"Invalid key type: 0x%02X", key_type);
980 LOG_I(
TAG,
"Fingerprint set for key type 0x%02X", key_type);
991static int cmd_select(
const apdu_t *apdu, uint8_t *resp,
size_t resp_max) {
992 if (apdu->lc >= 6 && memcmp(apdu->data,
OPENPGP_AID, 6) == 0) {
998 LOG_I(
TAG,
"OpenPGP application selected");
1016static int cmd_get_data(
const apdu_t *apdu, uint8_t *resp,
size_t resp_max) {
1017 uint16_t tag = (apdu->p1 << 8) | apdu->p2;
1066 uint8_t status[7] = {
1106 uint8_t count[3] = {
1140 uint8_t uif[2] = { 0x00, 0x20 };
1147 uint8_t key_info[6];
1174 uint8_t sec_tpl[7] = {
1191 LOG_W(
TAG,
"GET DATA: Unknown tag 0x%04X", tag);
1248 const size_t n =
sizeof(k_put_data_table) /
sizeof(k_put_data_table[0]);
1249 for (
size_t i = 0; i < n; ++i) {
1250 if (k_put_data_table[i].tag == tag) {
1251 return &k_put_data_table[i];
1269 char *str = (
char *)desc->
buffer;
1270 memcpy(str, apdu->data, apdu->lc);
1271 str[apdu->lc] =
'\0';
1313 uint8_t *target =
nullptr;
1336 if (*target == new_curve) {
1343 se->eccDelete(slot);
1352 *target = new_curve;
1354 LOG_I(
TAG,
"Algorithm attributes for %s updated to curve %u",
1359static int cmd_put_data(
const apdu_t *apdu, uint8_t *resp,
size_t resp_max) {
1365 uint16_t tag = (apdu->p1 << 8) | apdu->p2;
1370 if (apdu->lc == 1) {
1382 if (apdu->lc == 0) {
1388 LOG_I(
TAG,
"Resetting Code cleared");
1399 mbedtls_platform_zeroize(new_salt,
sizeof(new_salt));
1400 mbedtls_platform_zeroize(new_hash,
sizeof(new_hash));
1405 mbedtls_platform_zeroize(new_salt,
sizeof(new_salt));
1406 mbedtls_platform_zeroize(new_hash,
sizeof(new_hash));
1408 s_rc_len =
static_cast<uint8_t
>(apdu->lc);
1414 if (apdu->lc != 16 && apdu->lc != 32) {
1431 LOG_W(
TAG,
"PUT DATA: Unknown tag 0x%04X", tag);
1446 uint16_t *tag_out,
const uint8_t **value_out,
1447 size_t *value_len_out) {
1448 if (!buf || !pos || *pos >= buf_len)
return false;
1451 uint16_t tag = buf[p++];
1452 if ((tag & 0x1F) == 0x1F) {
1453 if (p >= buf_len)
return false;
1454 tag = (tag << 8) | buf[p++];
1456 if (p >= buf_len)
return false;
1459 uint8_t lb = buf[p++];
1462 }
else if (lb == 0x81) {
1463 if (p >= buf_len)
return false;
1465 }
else if (lb == 0x82) {
1466 if (p + 1 >= buf_len)
return false;
1467 len = (
static_cast<size_t>(buf[p]) << 8) | buf[p + 1];
1472 if (p + len > buf_len)
return false;
1475 *value_out = buf + p;
1476 *value_len_out = len;
1503 if (apdu->p1 != 0x3F || apdu->p2 != 0xFF) {
1506 if (apdu->lc == 0 || !apdu->data) {
1512 const uint8_t *outer_val =
nullptr;
1513 size_t outer_len = 0;
1514 if (!
ehl_parse_one(apdu->data, apdu->lc, &pos, &tag, &outer_val, &outer_len) ||
1522 const uint8_t *crt_val =
nullptr;
1524 if (!
ehl_parse_one(outer_val, outer_len, &inner, &tag, &crt_val, &crt_len) ||
1529 const uint8_t *key_concat =
nullptr;
1530 size_t key_concat_len = 0;
1531 while (inner < outer_len) {
1532 const uint8_t *v =
nullptr;
1534 if (!
ehl_parse_one(outer_val, outer_len, &inner, &tag, &v, &vlen)) {
1537 if (tag == 0x5F48) {
1539 key_concat_len = vlen;
1551 mbedtls_platform_zeroize(privkey,
sizeof(privkey));
1566static int cmd_verify(
const apdu_t *apdu, uint8_t *resp,
size_t resp_max) {
1567 uint8_t pw_ref = apdu->p2;
1570 if (apdu->lc == 0) {
1585 return apdu_sw(resp, 0x63C0 | retries);
1593 memcpy(pin_str, apdu->data, apdu->lc);
1594 pin_str[apdu->lc] =
'\0';
1597 bool verified =
false;
1607 LOG_I(
TAG,
"PW1 verified successfully");
1614 LOG_I(
TAG,
"PW3 verified successfully");
1627 LOG_W(
TAG,
"PIN blocked after too many failures");
1630 LOG_W(
TAG,
"PIN verification failed, %d retries left", retries);
1631 return apdu_sw(resp, 0x63C0 | retries);
1651 uint8_t hash_out[32]) {
1652 if (!pin || !salt || !hash_out)
return false;
1653 size_t pin_len = strlen(pin);
1655 size_t combined = 8 + pin_len;
1656 if (combined == 0)
return false;
1659 memcpy(buffer, salt, 8);
1660 memcpy(buffer + 8, pin, pin_len);
1662 mbedtls_sha256_context ctx;
1663 mbedtls_sha256_init(&ctx);
1664 if (mbedtls_sha256_starts(&ctx, 0) != 0) {
1665 mbedtls_sha256_free(&ctx);
1666 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
1669 size_t processed = 0;
1670 size_t total_bytes = iterations;
1671 while (processed < total_bytes) {
1672 size_t chunk = (total_bytes - processed < combined) ? (total_bytes - processed) : combined;
1673 if (mbedtls_sha256_update(&ctx, buffer, chunk) != 0) {
1674 mbedtls_sha256_free(&ctx);
1675 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
1680 int rc = mbedtls_sha256_finish(&ctx, hash_out);
1681 mbedtls_sha256_free(&ctx);
1682 mbedtls_platform_zeroize(buffer,
sizeof(buffer));
1695 for (
size_t i = 0; i < n; ++i) diff |= a[i] ^ b[i];
1707 uint8_t salt[8] = {};
1708 uint8_t stored[32] = {};
1709 uint8_t candidate[32] = {};
1713 if (!mgr.getPW1Salt(salt))
goto done;
1714 if (!mgr.getPW1Hash(stored))
goto done;
1716 if (!mgr.getPW3Salt(salt))
goto done;
1717 if (!mgr.getPW3Hash(stored))
goto done;
1720 if (!
compute_kdf_hash(pin, salt, mgr.getIterationCount(), candidate))
goto done;
1724 mbedtls_platform_zeroize(stored,
sizeof(stored));
1725 mbedtls_platform_zeroize(candidate,
sizeof(candidate));
1751 if (len < min_len * 2) {
1754 for (
size_t old_len = min_len; old_len <= len - min_len; ++old_len) {
1755 size_t new_len = len - old_len;
1761 memcpy(old_pin, data, old_len);
1762 old_pin[old_len] =
'\0';
1763 memcpy(new_pin, data + old_len, new_len);
1764 new_pin[new_len] =
'\0';
1767 mbedtls_platform_zeroize(old_pin,
sizeof(old_pin));
1768 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
1771 mbedtls_platform_zeroize(old_pin,
sizeof(old_pin));
1772 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
1786 uint8_t pw_ref = apdu->p2;
1788 if (apdu->lc == 0) {
1794 uint8_t (*retries_fn)(void) = NULL;
1796 const char *log_label = NULL;
1814 if (apdu->lc < min_len * 2) {
1818 if (
try_change_pin(apdu->data, apdu->lc, min_len, slot, change_fn)) {
1819 LOG_I(
TAG,
"%s changed successfully", log_label);
1833 uint8_t retries = retries_fn();
1837 return apdu_sw(resp, 0x63C0 | retries);
1847static int cmd_pso_cds(
const apdu_t *apdu, uint8_t *resp,
size_t resp_max) {
1856 LOG_E(
TAG,
"No signature key configured");
1862 uint8_t signature[64];
1932 if (apdu->lc < 1 + 16 + 1) {
1935 const uint8_t* iv_in = apdu->data + 1;
1936 const uint8_t* ct = apdu->data + 1 + 16;
1937 size_t ct_len = apdu->lc - 1 - 16;
1938 if (ct_len > resp_max - 2) {
1942 uint8_t aes_key[32] = {};
1943 size_t aes_key_len = 0;
1945 mbedtls_platform_zeroize(aes_key,
sizeof(aes_key));
1949 mbedtls_aes_context aes;
1950 mbedtls_aes_init(&aes);
1951 int rc = mbedtls_aes_setkey_enc(&aes, aes_key,
static_cast<unsigned int>(aes_key_len * 8));
1953 mbedtls_aes_free(&aes);
1954 mbedtls_platform_zeroize(aes_key,
sizeof(aes_key));
1959 memcpy(iv, iv_in,
sizeof(iv));
1962 if (ct_len >
sizeof(plain)) {
1963 mbedtls_aes_free(&aes);
1964 mbedtls_platform_zeroize(aes_key,
sizeof(aes_key));
1967 rc = mbedtls_aes_crypt_cfb128(&aes, MBEDTLS_AES_DECRYPT, ct_len, &iv_off, iv, ct, plain);
1968 mbedtls_aes_free(&aes);
1969 mbedtls_platform_zeroize(aes_key,
sizeof(aes_key));
1970 mbedtls_platform_zeroize(iv,
sizeof(iv));
1972 mbedtls_platform_zeroize(plain,
sizeof(plain));
1976 mbedtls_platform_zeroize(plain,
sizeof(plain));
1989 if (apdu->data[0] == 0x02) {
2000 if (apdu->lc < 70) {
2004 const uint8_t* p = apdu->data;
2005 const uint8_t* end = apdu->data + apdu->lc;
2007 if (p >= end || *p != 0xA6) {
2015 }
else if (*p == 0x81 && p + 1 < end) {
2017 }
else if (*p == 0x82 && p + 2 < end) {
2023 if (p + 2 > end || p[0] != 0x7F || p[1] != 0x49) {
2031 }
else if (*p == 0x81 && p + 1 < end) {
2033 }
else if (*p == 0x82 && p + 2 < end) {
2039 if (p >= end || *p != 0x86) {
2048 }
else if (*p == 0x81 && p + 1 < end) {
2051 }
else if (*p == 0x82 && p + 2 < end) {
2052 pubkey_len = (
static_cast<size_t>(p[1]) << 8) | p[2];
2064 const uint8_t* peer_pubkey = p;
2073 mbedtls_platform_zeroize(dec_privkey,
sizeof(dec_privkey));
2075 mbedtls_platform_zeroize(shared_secret,
sizeof(shared_secret));
2079 mbedtls_platform_zeroize(shared_secret,
sizeof(shared_secret));
2099 if (apdu->p1 != 0x41) {
2105 if (apdu->lc == 0 || apdu->data ==
nullptr) {
2109 if (apdu->lc < 3 || apdu->data[0] != 0x83 || apdu->data[1] != 0x01) {
2112 const uint8_t ref = apdu->data[2];
2113 if (ref != 0x01 && ref != 0x02 && ref != 0x03) {
2117 if ((apdu->p2 ==
KEY_SIG && ref != 0x01) ||
2118 (apdu->p2 ==
KEY_DEC && ref != 0x02) ||
2119 (apdu->p2 ==
KEY_AUT && ref != 0x03)) {
2138 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2144 if (apdu->lc == 0 || apdu->data ==
nullptr) {
2151 LOG_E(
TAG,
"No AUT key configured");
2155 uint8_t signature[64];
2163 LOG_E(
TAG,
"INTERNAL AUTHENTICATE: signing failed");
2181 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2193 LOG_W(
TAG,
"Card moved to TERMINATED state");
2207 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2214 LOG_W(
TAG,
"ACTIVATE FILE: card reset to factory defaults");
2270 if (apdu->p1 != 0x00 && apdu->p1 != 0x02) {
2273 if (apdu->p1 == 0x00) {
2291 diff |=
static_cast<uint8_t
>(
s_rc_hash[i] ^ input_hash[i]);
2293 mbedtls_platform_zeroize(input_hash,
sizeof(input_hash));
2301 return apdu_sw(resp,
static_cast<uint16_t
>(0x63C0 | retries));
2304 const size_t new_pw1_len = apdu->lc -
s_rc_len;
2306 memcpy(new_pin, apdu->data +
s_rc_len, new_pw1_len);
2307 new_pin[new_pw1_len] =
'\0';
2309 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
2312 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
2317 LOG_I(
TAG,
"RESET RETRY COUNTER: PW1 reset via RC");
2328 memcpy(new_pin, apdu->data, apdu->lc);
2329 new_pin[apdu->lc] =
'\0';
2331 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
2334 mbedtls_platform_zeroize(new_pin,
sizeof(new_pin));
2337 LOG_I(
TAG,
"RESET RETRY COUNTER: PW1 reset by admin");
2388 mbedtls_platform_zeroize(privkey,
sizeof(privkey));
2391 mbedtls_platform_zeroize(privkey,
sizeof(privkey));
2403 LOG_E(
TAG,
"Key generation failed for slot %d", ecc_slot);
2406 LOG_I(
TAG,
"Key pair generated in slot %d (hardware)", ecc_slot);
2415 uint32_t now = (uint32_t)time(NULL);
2417 (uint8_t)((now >> 24) & 0xFF),
2418 (uint8_t)((now >> 16) & 0xFF),
2419 (uint8_t)((now >> 8) & 0xFF),
2420 (uint8_t)(now & 0xFF)
2446 uint8_t *pubkey, uint8_t *curve_out) {
2448 LOG_I(
TAG,
"read_public_key DEC: checking has_dec_privkey");
2450 LOG_W(
TAG,
"read_public_key DEC: no privkey");
2453 LOG_I(
TAG,
"read_public_key DEC: loading privkey");
2456 LOG_W(
TAG,
"read_public_key DEC: load_dec_privkey failed");
2459 LOG_I(
TAG,
"read_public_key DEC: deriving pubkey");
2461 mbedtls_platform_zeroize(privkey,
sizeof(privkey));
2465 LOG_I(
TAG,
"read_public_key DEC: derive ok=%d", ok);
2484 uint8_t *out,
size_t *out_len) {
2486 if (pubkey[0] == 0x04) {
2512 if (apdu->lc >= 2) {
2513 key_ref = apdu->data[0];
2514 LOG_I(
TAG,
"Key ref from CRT: 0x%02X", key_ref);
2520 LOG_I(
TAG,
"GENERATE_KEYPAIR: P1=0x%02X, key_ref=0x%02X, slot=%d, type=%d",
2521 apdu->p1, key_ref, ecc_slot, key_type);
2523 if (apdu->p1 == 0x80) {
2536 LOG_I(
TAG,
"Generating key in slot %d (curve=%d, type=%s)",
2548 if (gen_sw !=
SW_OK) {
2556 size_t pubkey_len = 0;
2558 pubkey_with_prefix, &pubkey_len);
2560 uint8_t tlv_data[128];
2561 size_t pos =
tlv_build(tlv_data,
sizeof(tlv_data), 0x86,
2562 pubkey_with_prefix, pubkey_len);
2564 uint8_t final_resp[140];
2565 size_t final_len = 0;
2566 final_resp[final_len++] = 0x7F;
2567 final_resp[final_len++] = 0x49;
2569 memcpy(final_resp + final_len, tlv_data, pos);
2579 LOG_W(
TAG,
"Public key read failed: empty slot=%d type=%d", ecc_slot, key_type);
2591 size_t pubkey_len = 0;
2594 uint8_t tlv_data[128];
2596 pos +=
tlv_build(tlv_data + pos,
sizeof(tlv_data) - pos, 0x86, pubkey_with_prefix, pubkey_len);
2599 uint8_t final_resp[140];
2600 size_t final_len = 0;
2601 final_resp[final_len++] = 0x7F;
2602 final_resp[final_len++] = 0x49;
2604 memcpy(final_resp + final_len, tlv_data, pos);
2607 LOG_I(
TAG,
"Public key exported (%zu bytes, curve=%d, slot=%d)",
2608 pubkey_len, read_curve, ecc_slot);
2623 if (result_len < 2)
return result_len;
2624 const size_t payload_len =
static_cast<size_t>(result_len - 2);
2625 const uint16_t sw =
static_cast<uint16_t
>((resp[result_len - 2] << 8) |
2626 resp[result_len - 1]);
2628 if (sw !=
SW_OK)
return result_len;
2629 if (le == 0 || payload_len <= le) {
2632 const size_t remainder = payload_len - le;
2641 if (resp_max < le + 2)
return result_len;
2642 const uint8_t sw2 = (remainder > 0xFF) ? 0x00 :
static_cast<uint8_t
>(remainder);
2645 return static_cast<int>(le + 2);
2652 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2658 size_t want = (apdu->le > 0) ? apdu->le : 256;
2660 if (want + 2 > resp_max) want = resp_max - 2;
2670 uint16_t sw =
SW_OK;
2674 sw =
static_cast<uint16_t
>((0x61 << 8) | sw2);
2678 resp[want] =
static_cast<uint8_t
>((sw >> 8) & 0xFF);
2679 resp[want + 1] =
static_cast<uint8_t
>(sw & 0xFF);
2680 return static_cast<int>(want + 2);
2684 uint8_t *resp,
size_t resp_max) {
2692 LOG_D(
TAG,
"APDU: CLA=%02X INS=%02X P1=%02X P2=%02X Lc=%d",
2693 apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc);
2702 if ((apdu.cla & ~0x10) != 0x00) {
2710 const bool is_chain_block = (apdu.cla & 0x10) != 0;
2728 if (apdu.lc > 0 && apdu.data !=
nullptr) {
2732 if (is_chain_block) {
2775 result_len =
cmd_verify(&apdu, resp, resp_max);
2787 if (apdu.p1 == 0x9E && apdu.p2 == 0x9A) {
2789 }
else if (apdu.p1 == 0x80 && apdu.p2 == 0x86) {
2809 uint8_t challenge[255];
2810 size_t len = apdu.le > 0 ? apdu.le : 8;
2811 if (len >
sizeof(challenge)) len =
sizeof(challenge);
2831 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_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.
void gpg_storage_set_session_pin(const char *pin)
Stores session PIN-derived key after successful PIN verification.
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_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 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.
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 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 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 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 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 constexpr size_t RC_SALT_SIZE
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 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....
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 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 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 constexpr uint8_t OPENPGP_NVS_SCHEMA_V2
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
#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)
Handles APDU PUT DATA command processing.
static bool app_selected
ATR is defined in ccid.cpp and accessed via ccid_get_atr().
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 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 char cardholder_name[40]
Cardholder profile data stored in NVS.
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 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 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_change_pw3(const char *new_pin)
uint8_t pin_storage_openpgp_pw3_retries(void)
bool pin_storage_openpgp_verify_pw1(const char *pin)
void pin_storage_openpgp_init(void)
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)
Parsed algorithm-attribute payload.
Descriptor entry for table-driven PUT DATA processing.