CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
openpgp.cpp
Go to the documentation of this file.
1
8
13#include "cdc_log.h"
14#include "mod_gpg/gpg.h"
15#include "mod_gpg/GpgStorage.h"
16#include "ecdh.h"
18#include "cdc_core/PinManager.h"
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>
26#include <esp_attr.h>
27#include <string.h>
28#include <time.h>
29#include "cdc_log.h"
30#include <esp_mac.h> // For esp_efuse_mac_get_default()
31#include <nvs_flash.h>
32#include <nvs.h>
33#include <esp_random.h>
34
35static const char *TAG = "OpenPGP";
36
44
53static bool se_ecc_key_read(uint8_t slot, uint8_t* pubkey, size_t max_len, uint8_t* curve_out) {
54 auto* se = get_se();
55 if (!se || !pubkey) return false;
57 auto res = se->eccGetPublicKey(slot, pubkey, &curve);
58 if (res != cdc::hal::SeResult::OK) return false;
59 if (curve_out) {
61 }
63 return max_len >= ED25519_PUBKEY_SIZE;
64 }
65 return max_len >= P256_PUBKEY_SIZE;
66}
67
74static bool se_ecc_key_generate(uint8_t slot, uint8_t curve) {
75 auto* se = get_se();
76 if (!se) {
77 LOG_E(TAG, "se_ecc_key_generate: SE not available (slot=%u)", slot);
78 return false;
79 }
82 // Per TROPIC01: lt_ecc_key_generate fails with SLOT_OCCUPIED if the slot
83 // already holds material. A previous (incomplete) generation, or a
84 // GPG_RESET that did not propagate to the SE, leaves the slot used and
85 // the next attempt returns SW=6F00 to the host. Pre-wipe defensively.
86 cdc::hal::SeResult res = se->eccGenerate(slot, c);
87 if (res != cdc::hal::SeResult::OK) {
88 LOG_W(TAG, "se_ecc_key_generate: slot %u initial fail (SeResult=%d), deleting and retrying",
89 slot, static_cast<int>(res));
90 se->eccDelete(slot);
91 res = se->eccGenerate(slot, c);
92 }
93 if (res != cdc::hal::SeResult::OK) {
94 LOG_E(TAG, "se_ecc_key_generate(slot=%u curve=%u) failed: SeResult=%d",
95 slot, curve, static_cast<int>(res));
96 return false;
97 }
98 return true;
99}
100
109static bool se_ecdsa_sign(uint8_t slot, const uint8_t* hash, size_t hash_len, uint8_t* sig) {
110 auto* se = get_se();
111 if (!se || !hash || !sig) return false;
112 size_t sig_len = 64;
113 return se->ecdsaSign(slot, hash, hash_len, sig, &sig_len) == cdc::hal::SeResult::OK;
114}
115
124static bool se_eddsa_sign(uint8_t slot, const uint8_t* msg, size_t msg_len, uint8_t* sig) {
125 auto* se = get_se();
126 if (!se || !msg || !sig) return false;
127 return se->eddsaSign(slot, msg, msg_len, sig) == cdc::hal::SeResult::OK;
128}
129
135static void se_random_fill(uint8_t* buf, size_t len) {
136 auto* se = get_se();
137 if (se && se->getRandom(buf, static_cast<uint16_t>(len))) {
138 return;
139 }
140 esp_fill_random(buf, len);
141}
142
149static uint8_t s_openpgp_aid[16] = {
150 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01, // RID + Application (OpenPGP)
151 0x03, 0x04, // Version 3.4
152 0x00, 0x00, // Manufacturer (set in init)
153 0x00, 0x00, 0x00, 0x00, // Serial number (set in init from MAC)
154 0x00, 0x00 // RFU
155};
156const uint8_t* OPENPGP_AID = s_openpgp_aid;
157const uint8_t OPENPGP_AID_LEN = sizeof(s_openpgp_aid);
158
162
166static bool app_selected = false;
167static bool pw1_verified = false;
168static bool pw3_verified = false;
169static uint32_t sig_count = 0;
170
182#define OPENPGP_RC_MIN_LEN 8
183static constexpr size_t RC_SALT_SIZE = 16;
184static constexpr size_t RC_HASH_SIZE = 32;
185static constexpr size_t RC_KDF_TOTAL_BYTES = 100000;
186static uint8_t s_rc_salt[RC_SALT_SIZE] = {0};
187static uint8_t s_rc_hash[RC_HASH_SIZE] = {0};
188static uint8_t s_rc_len = 0;
189static uint8_t s_rc_retries = 3;
190
195static bool compute_rc_hash(const uint8_t* rc, size_t rc_len,
196 const uint8_t* salt, uint8_t* hash_out) {
197 if (!rc || !salt || !hash_out || rc_len == 0 || rc_len > OPENPGP_PIN_MAX_LEN) {
198 return false;
199 }
200 uint8_t buffer[RC_SALT_SIZE + OPENPGP_PIN_MAX_LEN];
201 memcpy(buffer, salt, RC_SALT_SIZE);
202 memcpy(buffer + RC_SALT_SIZE, rc, rc_len);
203 const size_t combined = RC_SALT_SIZE + rc_len;
204
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));
210 return false;
211 }
212 size_t processed = 0;
213 while (processed < RC_KDF_TOTAL_BYTES) {
214 const size_t chunk = (RC_KDF_TOTAL_BYTES - processed < combined)
215 ? (RC_KDF_TOTAL_BYTES - processed)
216 : combined;
217 if (mbedtls_sha256_update(&ctx, buffer, chunk) != 0) {
218 mbedtls_sha256_free(&ctx);
219 mbedtls_platform_zeroize(buffer, sizeof(buffer));
220 return false;
221 }
222 processed += chunk;
223 }
224 mbedtls_sha256_finish(&ctx, hash_out);
225 mbedtls_sha256_free(&ctx);
226 mbedtls_platform_zeroize(buffer, sizeof(buffer));
227 return true;
228}
229
239
248static bool card_terminated = false;
249
260EXT_RAM_BSS_ATTR static uint8_t g_resp_buffer[4096];
261static size_t g_resp_remaining = 0;
262static size_t g_resp_pos = 0;
263
273EXT_RAM_BSS_ATTR static uint8_t g_chain_buffer[4096];
274static size_t g_chain_len = 0;
275static bool g_chain_active = false;
276static uint8_t g_chain_ins = 0;
277static uint8_t g_chain_p1 = 0;
278static uint8_t g_chain_p2 = 0;
279
280static void chain_reset(void) {
281 g_chain_len = 0;
282 g_chain_active = false;
283 g_chain_ins = 0;
284 g_chain_p1 = 0;
285 g_chain_p2 = 0;
286}
287
291static char s_session_pin[OPENPGP_PIN_MAX_LEN + 1] = {};
292
296#define NVS_NAMESPACE "openpgp"
297#define NVS_STATE_KEY "state"
298
306struct __attribute__((packed)) OpenpgpNvsState {
307 uint8_t schema_version;
308 uint8_t card_terminated;
309 uint8_t selected_curve_sig;
310 uint8_t selected_curve_aut;
311 uint8_t rc_len;
312 uint8_t rc_retries;
313 uint8_t rc_salt[RC_SALT_SIZE];
314 uint8_t rc_hash[RC_HASH_SIZE];
315 uint32_t sig_count;
322 uint8_t gen_time_sig[4];
323 uint8_t gen_time_dec[4];
324 uint8_t gen_time_aut[4];
325 uint8_t cardholder_sex;
326 char cardholder_name[40];
327 char cardholder_lang[8];
328 char cardholder_url[64];
329 char cardholder_login[32];
330};
331
332static constexpr uint8_t OPENPGP_NVS_SCHEMA_V2 = 2;
333
340
344static uint8_t gen_time_sig[4] = {0};
345static uint8_t gen_time_dec[4] = {0};
346static uint8_t gen_time_aut[4] = {0};
347
351static uint8_t ca_fp_1[OPENPGP_FINGERPRINT_SIZE] = {0};
352static uint8_t ca_fp_2[OPENPGP_FINGERPRINT_SIZE] = {0};
353static uint8_t ca_fp_3[OPENPGP_FINGERPRINT_SIZE] = {0};
354
358static char cardholder_name[40] = {0}; // "Surname<<Firstname"
359static char cardholder_lang[8] = "en"; // ISO 639-1 language
360static uint8_t cardholder_sex = 0x39; // '9' = not specified
361static char cardholder_url[64] = {0}; // URL for public key retrieval
362static char cardholder_login[32] = {0}; // Login data
363
367static const uint8_t HIST_BYTES[] = {
368 0x00, // Category indicator: card has no indication of services
369 0x31, // Card capabilities (card can process T=1)
370 0xC5, // Tag: card issuer data follows
371 0x73, 0xC0, 0x01, 0x80, // Card issuer proprietary
372 0x05, // Tag: card capabilities
373 0x90, 0x00 // Card status: OK
374};
375
381static const uint8_t ALGO_ATTR_ED25519[] = {
382 ALGO_EDDSA, // Algorithm: EdDSA (0x16)
383 0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01 // OID 1.3.6.1.4.1.11591.15.1 (ed25519)
384};
385
391static const uint8_t ALGO_ATTR_P256_ECDSA[] = {
392 ALGO_ECDSA, // Algorithm: ECDSA (0x13)
393 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 // OID 1.2.840.10045.3.1.7 (secp256r1)
394};
395
401static const uint8_t ALGO_ATTR_P256_ECDH[] = {
402 ALGO_ECDH, // Algorithm: ECDH (0x12)
403 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 // OID 1.2.840.10045.3.1.7 (secp256r1)
404};
405
409static const uint8_t EXT_CAPABILITIES[] = {
410 0x7D,
411 0x00, // SM Algorithm: none
412 0x00, 0x80, // Max GET CHALLENGE length: 128 bytes
413 0x08, 0x00, // Max Cardholder Certificate length: 2048 bytes
414 0x01, 0x00, // Max special DO length: 256 bytes
415 0x00, // PIN block 2 format not supported
416 0x00, // MSE for key selection not supported
417};
418
422
429static size_t tlv_write_tag(uint8_t *buf, uint16_t tag) {
430 if (tag > 0xFF) {
431 buf[0] = (tag >> 8) & 0xFF;
432 buf[1] = tag & 0xFF;
433 return 2;
434 }
435 buf[0] = tag & 0xFF;
436 return 1;
437}
438
445static size_t tlv_write_len(uint8_t *buf, size_t len) {
446 if (len < 128) {
447 buf[0] = len;
448 return 1;
449 } else if (len < 256) {
450 buf[0] = 0x81;
451 buf[1] = len;
452 return 2;
453 } else {
454 buf[0] = 0x82;
455 buf[1] = (len >> 8) & 0xFF;
456 buf[2] = len & 0xFF;
457 return 3;
458 }
459}
460
470static size_t tlv_build(uint8_t *buf, size_t buf_max, uint16_t tag,
471 const uint8_t *value, size_t value_len) {
472 size_t pos = 0;
473 pos += tlv_write_tag(buf + pos, tag);
474 pos += tlv_write_len(buf + pos, value_len);
475 if (value && value_len > 0) {
476 memcpy(buf + pos, value, value_len);
477 pos += value_len;
478 }
479 return pos;
480}
481
485
489typedef enum {
490 KEY_TYPE_SIG = 0, // Signature (ECDSA/EdDSA)
491 KEY_TYPE_DEC = 1, // Decryption (ECDH)
492 KEY_TYPE_AUT = 2 // Authentication (ECDSA/EdDSA)
493} key_type_t;
494
501static const uint8_t* get_algo_attr(key_type_t key_type, size_t *len) {
502 // DEC is fixed to P-256 ECDH (software path); SIG / AUT follow the
503 // currently configured curve which PUT DATA C1 / C3 may override.
504 if (key_type == KEY_TYPE_DEC) {
505 *len = sizeof(ALGO_ATTR_P256_ECDH);
506 return ALGO_ATTR_P256_ECDH;
507 }
508 const uint8_t curve = (key_type == KEY_TYPE_AUT) ? selected_curve_aut
510 if (curve == CDC_CURVE_P256) {
511 *len = sizeof(ALGO_ATTR_P256_ECDSA);
513 }
514 *len = sizeof(ALGO_ATTR_ED25519);
515 return ALGO_ATTR_ED25519;
516}
517
524static int build_do_app_related(uint8_t *buf, size_t buf_max) {
525 uint8_t inner[512];
526 size_t inner_len = 0;
527
528 // 4F: AID
529 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
531
532 // 5F52: Historical bytes
533 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
535
536 // 73: Discretionary data objects (nested)
537 uint8_t discret[384];
538 size_t discret_len = 0;
539
540 // C0: Extended capabilities
541 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
543
544 // C1: Algorithm attributes - Signature (ECDSA/EdDSA)
545 size_t algo_len;
546 const uint8_t *algo = get_algo_attr(KEY_TYPE_SIG, &algo_len);
547 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
548 DO_ALGO_SIG, algo, algo_len);
549
550 // C2: Algorithm attributes - Decryption (ECDH)
551 algo = get_algo_attr(KEY_TYPE_DEC, &algo_len);
552 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
553 DO_ALGO_DEC, algo, algo_len);
554
555 // C3: Algorithm attributes - Authentication (ECDSA/EdDSA)
556 algo = get_algo_attr(KEY_TYPE_AUT, &algo_len);
557 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
558 DO_ALGO_AUT, algo, algo_len);
559
560 // C4: PW Status Bytes (retries from TROPIC01 storage)
561 // Note: Max lengths limited for practical use on hardware keypad
562 uint8_t pw_status[7] = {
563 0x01, // PW1 valid for multiple signatures
564 OPENPGP_PIN_MAX_LEN, // Max length PW1 (practical limit)
565 OPENPGP_PIN_MAX_LEN, // Max length RC
566 OPENPGP_PIN_MAX_LEN, // Max length PW3
568 s_rc_len > 0 ? s_rc_retries : static_cast<uint8_t>(0),
570 };
571 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
572 DO_PW_STATUS, pw_status, sizeof(pw_status));
573
574 // C5: Fingerprints (3 * 20 bytes: SIG + DEC + AUT)
575 uint8_t fps[3 * OPENPGP_FINGERPRINT_SIZE];
579 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
580 0xC5, fps, sizeof(fps)); // 0xC5 = combined fingerprints (no separate constant)
581
582 // C6: CA Fingerprints (3 * 20 bytes)
583 uint8_t ca_fps[3 * OPENPGP_FINGERPRINT_SIZE];
587 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
588 0xC6, ca_fps, sizeof(ca_fps));
589
590 // CD: Generation dates (12 bytes: SIG + DEC + AUT)
591 uint8_t gen_times[12];
592 memcpy(gen_times, gen_time_sig, 4);
593 memcpy(gen_times + 4, gen_time_dec, 4);
594 memcpy(gen_times + 8, gen_time_aut, 4);
595 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
596 0xCD, gen_times, 12);
597
598 // Add discretionary DOs to inner
599 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
600 0x73, discret, discret_len);
601
602 // Build final 6E response
603 size_t total = 0;
604 total += tlv_write_tag(buf + total, 0x6E);
605 total += tlv_write_len(buf + total, inner_len);
606 memcpy(buf + total, inner, inner_len);
607 total += inner_len;
608
609 return total;
610}
611
618static int build_do_cardholder(uint8_t *buf, size_t buf_max) {
619 uint8_t inner[128];
620 size_t inner_len = 0;
621
622 // 5B: Name
623 size_t name_len = strlen(cardholder_name);
624 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
625 DO_NAME, (const uint8_t *)cardholder_name, name_len);
626
627 // 5F2D: Language preference
628 size_t lang_len = strlen(cardholder_lang);
629 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
630 DO_LANG_PREF, (const uint8_t *)cardholder_lang, lang_len);
631
632 // 5F35: Sex
633 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
635
636 // Build final 65 response
637 size_t total = 0;
638 total += tlv_write_tag(buf + total, DO_CARDHOLDER);
639 total += tlv_write_len(buf + total, inner_len);
640 memcpy(buf + total, inner, inner_len);
641 total += inner_len;
642
643 return total;
644}
645
646static constexpr uint8_t ATTESTATION_ECC_SLOT = 0;
647static constexpr size_t OPENPGP_STATE_SIG_SIZE = 64;
648
654 const uint8_t* payload, size_t payload_len,
655 const uint8_t* sig, size_t sig_len) {
656 if (!se || sig_len != OPENPGP_STATE_SIG_SIZE) return false;
657 uint8_t pub_raw[64];
660 return false;
661 }
662 if (curve != cdc::hal::EccCurve::P256) return false;
663
664 uint8_t pub_sec1[65];
665 pub_sec1[0] = 0x04;
666 memcpy(pub_sec1 + 1, pub_raw, 64);
667
668 uint8_t hash[32];
669 mbedtls_sha256(payload, payload_len, hash, 0);
670
671 mbedtls_ecp_group grp;
672 mbedtls_ecp_point Q;
673 mbedtls_mpi r, s;
674 mbedtls_ecp_group_init(&grp);
675 mbedtls_ecp_point_init(&Q);
676 mbedtls_mpi_init(&r);
677 mbedtls_mpi_init(&s);
678
679 bool ok = false;
680 do {
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);
686 } while (0);
687
688 mbedtls_mpi_free(&r);
689 mbedtls_mpi_free(&s);
690 mbedtls_ecp_point_free(&Q);
691 mbedtls_ecp_group_free(&grp);
692 return ok;
693}
694
698static void load_state_from_nvs(void) {
699 nvs_handle_t nvs;
700 if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs) != ESP_OK) {
701 return;
702 }
703
704 constexpr size_t BLOB_SIZE = sizeof(OpenpgpNvsState) + OPENPGP_STATE_SIG_SIZE;
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);
708 nvs_close(nvs);
709
710 if (err != ESP_OK || len != BLOB_SIZE) {
711 return;
712 }
713
714 OpenpgpNvsState state = {};
715 memcpy(&state, blob, sizeof(state));
716 if (state.schema_version != OPENPGP_NVS_SCHEMA_V2) {
717 return;
718 }
719 if (!verify_state_signature(get_se(), blob, sizeof(state),
720 blob + sizeof(state), OPENPGP_STATE_SIG_SIZE)) {
721 LOG_W(TAG, "OpenPGP state signature invalid - re-initialising");
722 return;
723 }
724
725 card_terminated = state.card_terminated != 0;
726 if (state.selected_curve_sig == CDC_CURVE_P256 ||
727 state.selected_curve_sig == CDC_CURVE_ED25519) {
728 selected_curve_sig = state.selected_curve_sig;
729 }
730 if (state.selected_curve_aut == CDC_CURVE_P256 ||
731 state.selected_curve_aut == CDC_CURVE_ED25519) {
732 selected_curve_aut = state.selected_curve_aut;
733 }
734 if (state.rc_len > 0 && state.rc_len <= OPENPGP_PIN_MAX_LEN) {
735 memcpy(s_rc_salt, state.rc_salt, RC_SALT_SIZE);
736 memcpy(s_rc_hash, state.rc_hash, RC_HASH_SIZE);
737 s_rc_len = state.rc_len;
738 }
739 s_rc_retries = state.rc_retries;
740 sig_count = state.sig_count;
741 memcpy(fingerprint_sig, state.fingerprint_sig, OPENPGP_FINGERPRINT_SIZE);
742 memcpy(fingerprint_dec, state.fingerprint_dec, OPENPGP_FINGERPRINT_SIZE);
743 memcpy(fingerprint_aut, state.fingerprint_aut, OPENPGP_FINGERPRINT_SIZE);
744 memcpy(ca_fp_1, state.ca_fp_1, OPENPGP_FINGERPRINT_SIZE);
745 memcpy(ca_fp_2, state.ca_fp_2, OPENPGP_FINGERPRINT_SIZE);
746 memcpy(ca_fp_3, state.ca_fp_3, OPENPGP_FINGERPRINT_SIZE);
747 memcpy(gen_time_sig, state.gen_time_sig, 4);
748 memcpy(gen_time_dec, state.gen_time_dec, 4);
749 memcpy(gen_time_aut, state.gen_time_aut, 4);
750 cardholder_sex = state.cardholder_sex;
751 memcpy(cardholder_name, state.cardholder_name, sizeof(cardholder_name));
752 memcpy(cardholder_lang, state.cardholder_lang, sizeof(cardholder_lang));
753 memcpy(cardholder_url, state.cardholder_url, sizeof(cardholder_url));
754 memcpy(cardholder_login, state.cardholder_login, sizeof(cardholder_login));
755 cardholder_name[sizeof(cardholder_name) - 1] = '\0';
756 cardholder_lang[sizeof(cardholder_lang) - 1] = '\0';
757 cardholder_url[sizeof(cardholder_url) - 1] = '\0';
758 cardholder_login[sizeof(cardholder_login) - 1] = '\0';
759}
760
765static void save_state_to_nvs(void) {
766 OpenpgpNvsState state = {};
767 state.schema_version = OPENPGP_NVS_SCHEMA_V2;
768 state.card_terminated = card_terminated ? 1 : 0;
769 state.selected_curve_sig = selected_curve_sig;
770 state.selected_curve_aut = selected_curve_aut;
771 state.rc_len = s_rc_len;
772 state.rc_retries = s_rc_retries;
773 if (s_rc_len > 0) {
774 memcpy(state.rc_salt, s_rc_salt, RC_SALT_SIZE);
775 memcpy(state.rc_hash, s_rc_hash, RC_HASH_SIZE);
776 }
777 state.sig_count = sig_count;
778 memcpy(state.fingerprint_sig, fingerprint_sig, OPENPGP_FINGERPRINT_SIZE);
779 memcpy(state.fingerprint_dec, fingerprint_dec, OPENPGP_FINGERPRINT_SIZE);
780 memcpy(state.fingerprint_aut, fingerprint_aut, OPENPGP_FINGERPRINT_SIZE);
781 memcpy(state.ca_fp_1, ca_fp_1, OPENPGP_FINGERPRINT_SIZE);
782 memcpy(state.ca_fp_2, ca_fp_2, OPENPGP_FINGERPRINT_SIZE);
783 memcpy(state.ca_fp_3, ca_fp_3, OPENPGP_FINGERPRINT_SIZE);
784 memcpy(state.gen_time_sig, gen_time_sig, 4);
785 memcpy(state.gen_time_dec, gen_time_dec, 4);
786 memcpy(state.gen_time_aut, gen_time_aut, 4);
787 state.cardholder_sex = cardholder_sex;
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));
792
793 constexpr size_t BLOB_SIZE = sizeof(OpenpgpNvsState) + OPENPGP_STATE_SIG_SIZE;
794 uint8_t blob[BLOB_SIZE];
795 memcpy(blob, &state, sizeof(state));
796
797 auto* se = get_se();
798 if (!se) {
799 LOG_E(TAG, "save_state: no SE");
800 return;
801 }
802 size_t sig_len = OPENPGP_STATE_SIG_SIZE;
803 cdc::hal::SeResult sign_res = se->ecdsaSign(ATTESTATION_ECC_SLOT,
804 blob, sizeof(state),
805 blob + sizeof(state), &sig_len);
806 if (sign_res != cdc::hal::SeResult::OK || sig_len != OPENPGP_STATE_SIG_SIZE) {
807 LOG_E(TAG, "save_state: attestation sign failed (%d)",
808 static_cast<int>(sign_res));
809 return;
810 }
811
812 nvs_handle_t nvs;
813 esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs);
814 if (err != ESP_OK) {
815 LOG_E(TAG, "save_state: nvs_open %s", esp_err_to_name(err));
816 return;
817 }
818
819 err = nvs_set_blob(nvs, NVS_STATE_KEY, blob, BLOB_SIZE);
820 if (err == ESP_OK) {
821 err = nvs_commit(nvs);
822 }
823 nvs_close(nvs);
824 if (err != ESP_OK) {
825 LOG_E(TAG, "save_state: %s", esp_err_to_name(err));
826 }
827}
828
833static void init_aid_from_mac(void) {
834 uint8_t mac[6];
835 if (esp_efuse_mac_get_default(mac) == ESP_OK) {
836 // Use last 4 bytes of MAC as serial number (big-endian)
837 // MAC format: [0][1][2][3][4][5] - use [2][3][4][5] for better uniqueness
838 s_openpgp_aid[10] = mac[2];
839 s_openpgp_aid[11] = mac[3];
840 s_openpgp_aid[12] = mac[4];
841 s_openpgp_aid[13] = mac[5];
842
843 // Set manufacturer: CDC Badge = 0x4344 ("CD" in ASCII)
844 s_openpgp_aid[8] = 0x43; // 'C'
845 s_openpgp_aid[9] = 0x44; // 'D'
846
847 LOG_I(TAG, "AID initialized: Manufacturer=0x%02X%02X Serial=%02X%02X%02X%02X",
851 } else {
852 LOG_W(TAG, "Failed to read MAC, using default AID");
853 // Keep defaults: FFFE / 00000001
854 s_openpgp_aid[8] = 0xFF;
855 s_openpgp_aid[9] = 0xFE;
856 s_openpgp_aid[10] = 0x00;
857 s_openpgp_aid[11] = 0x00;
858 s_openpgp_aid[12] = 0x00;
859 s_openpgp_aid[13] = 0x01;
860 }
861}
862
863bool openpgp_init(void) {
864 // Initialize AID with device-unique serial number
866
867 // Initialize GPG component (TROPIC01 backend)
868 if (!gpg_init()) {
869 LOG_E(TAG, "Failed to initialize GPG/TROPIC01");
870 return false;
871 }
872
873 // Initialize OpenPGP PIN storage (loads PINs from TROPIC01)
875
877
878 LOG_I(TAG, "OpenPGP application initialized, sig_count=%lu", sig_count);
879 return true;
880}
881
883 return app_selected;
884}
885
886uint32_t openpgp_get_sig_count(void) {
887 return sig_count;
888}
889
890bool openpgp_get_fingerprint(uint8_t key_type, uint8_t *fp_out) {
891 if (!fp_out) return false;
892 switch (key_type) {
893 case KEY_SIG: memcpy(fp_out, fingerprint_sig, OPENPGP_FINGERPRINT_SIZE); return true;
894 case KEY_DEC: memcpy(fp_out, fingerprint_dec, OPENPGP_FINGERPRINT_SIZE); return true;
895 case KEY_AUT: memcpy(fp_out, fingerprint_aut, OPENPGP_FINGERPRINT_SIZE); return true;
896 default: return false;
897 }
898}
899
900static bool fp_is_set(const uint8_t fp[OPENPGP_FINGERPRINT_SIZE]) {
901 for (size_t i = 0; i < OPENPGP_FINGERPRINT_SIZE; i++) {
902 if (fp[i] != 0) return true;
903 }
904 return false;
905}
906
912
913size_t openpgp_get_cardholder_name(char *out, size_t out_size) {
914 if (!out || out_size == 0) return 0;
915 size_t len = strlen(cardholder_name);
916 if (len >= out_size) len = out_size - 1;
917 memcpy(out, cardholder_name, len);
918 out[len] = '\0';
919 return len;
920}
921
922uint32_t openpgp_get_gen_time(uint8_t key_type) {
923 const uint8_t *src = nullptr;
924 switch (key_type) {
925 case KEY_SIG: src = gen_time_sig; break;
926 case KEY_DEC: src = gen_time_dec; break;
927 case KEY_AUT: src = gen_time_aut; break;
928 default: return 0;
929 }
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]);
934}
935
937 if (!name) return false;
938 size_t len = strlen(name);
939 if (len >= sizeof(cardholder_name)) len = sizeof(cardholder_name) - 1;
940 memcpy(cardholder_name, name, len);
941 cardholder_name[len] = '\0';
942 if (len + 1 < sizeof(cardholder_name)) {
943 memset(cardholder_name + len + 1, 0, sizeof(cardholder_name) - len - 1);
944 }
946 return true;
947}
948
949bool openpgp_set_key_fingerprint(uint8_t key_type, const uint8_t *fingerprint,
950 uint32_t gen_time) {
951 if (!fingerprint) return false;
952
953 // Convert gen_time to big-endian bytes
954 uint8_t ts[4] = {
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)
959 };
960
961 switch (key_type) {
962 case KEY_SIG:
963 memcpy(fingerprint_sig, fingerprint, OPENPGP_FINGERPRINT_SIZE);
964 memcpy(gen_time_sig, ts, 4);
965 break;
966 case KEY_DEC:
967 memcpy(fingerprint_dec, fingerprint, OPENPGP_FINGERPRINT_SIZE);
968 memcpy(gen_time_dec, ts, 4);
969 break;
970 case KEY_AUT:
971 memcpy(fingerprint_aut, fingerprint, OPENPGP_FINGERPRINT_SIZE);
972 memcpy(gen_time_aut, ts, 4);
973 break;
974 default:
975 LOG_E(TAG, "Invalid key type: 0x%02X", key_type);
976 return false;
977 }
978
980 LOG_I(TAG, "Fingerprint set for key type 0x%02X", key_type);
981 return true;
982}
983
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) {
993 app_selected = true;
994 pw1_verified = false;
995 pw3_verified = false;
996 mbedtls_platform_zeroize(s_session_pin, sizeof(s_session_pin));
998 LOG_I(TAG, "OpenPGP application selected");
999 return apdu_sw(resp, SW_OK);
1000 }
1001
1002 if (app_selected) {
1003 mbedtls_platform_zeroize(s_session_pin, sizeof(s_session_pin));
1005 }
1006 return apdu_sw(resp, SW_FILE_NOT_FOUND);
1007}
1008
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;
1018
1019 switch (tag) {
1020 case DO_AID: // 0x4F: Full AID
1021 return apdu_build_response(resp, resp_max, OPENPGP_AID, OPENPGP_AID_LEN, SW_OK);
1022
1023 case DO_APP_RELATED: { // 0x6E: Application Related Data
1024 uint8_t data[512];
1025 int len = build_do_app_related(data, sizeof(data));
1026 if (len <= 0) {
1027 return apdu_sw(resp, SW_UNKNOWN);
1028 }
1029 return apdu_build_response(resp, resp_max, data, len, SW_OK);
1030 }
1031
1032 case DO_CARDHOLDER: { // 0x65: Cardholder Related Data
1033 uint8_t data[128];
1034 int len = build_do_cardholder(data, sizeof(data));
1035 if (len <= 0) {
1036 return apdu_sw(resp, SW_UNKNOWN);
1037 }
1038 return apdu_build_response(resp, resp_max, data, len, SW_OK);
1039 }
1040
1041 case DO_HIST_BYTES: // 0x5F52: Historical bytes
1042 return apdu_build_response(resp, resp_max, HIST_BYTES, sizeof(HIST_BYTES), SW_OK);
1043
1044 case DO_EXT_CAP: // 0xC0: Extended Capabilities
1045 return apdu_build_response(resp, resp_max, EXT_CAPABILITIES, sizeof(EXT_CAPABILITIES), SW_OK);
1046
1047 case DO_ALGO_SIG: { // 0xC1: Algorithm Attributes - Signature
1048 size_t algo_len;
1049 const uint8_t *algo = get_algo_attr(KEY_TYPE_SIG, &algo_len);
1050 return apdu_build_response(resp, resp_max, algo, algo_len, SW_OK);
1051 }
1052
1053 case DO_ALGO_DEC: { // 0xC2: Algorithm Attributes - Decryption
1054 size_t algo_len;
1055 const uint8_t *algo = get_algo_attr(KEY_TYPE_DEC, &algo_len);
1056 return apdu_build_response(resp, resp_max, algo, algo_len, SW_OK);
1057 }
1058
1059 case DO_ALGO_AUT: { // 0xC3: Algorithm Attributes - Authentication
1060 size_t algo_len;
1061 const uint8_t *algo = get_algo_attr(KEY_TYPE_AUT, &algo_len);
1062 return apdu_build_response(resp, resp_max, algo, algo_len, SW_OK);
1063 }
1064
1065 case DO_PW_STATUS: { // 0xC4: PW Status Bytes
1066 uint8_t status[7] = {
1067 0x01, // PW1 valid for multiple signatures
1068 OPENPGP_PIN_MAX_LEN, // Max length PW1
1069 OPENPGP_PIN_MAX_LEN, // Max length RC
1070 OPENPGP_PIN_MAX_LEN, // Max length PW3
1072 s_rc_len > 0 ? s_rc_retries : static_cast<uint8_t>(0),
1074 };
1075 return apdu_build_response(resp, resp_max, status, 7, SW_OK);
1076 }
1077
1078 case DO_FP_SIG: // 0xC7: Fingerprint SIG
1080
1081 case DO_FP_DEC: // 0xC8: Fingerprint DEC
1083
1084 case DO_FP_AUT: // 0xC9: Fingerprint AUT
1086
1087 case DO_CA_FP_1: // 0xCA: CA Fingerprint 1
1089
1090 case DO_CA_FP_2: // 0xCB: CA Fingerprint 2
1092
1093 case DO_CA_FP_3: // 0xCC: CA Fingerprint 3
1095
1096 case DO_GEN_TIME_SIG: // 0xCE: Generation time - Signature
1097 return apdu_build_response(resp, resp_max, gen_time_sig, 4, SW_OK);
1098
1099 case DO_GEN_TIME_DEC: // 0xCF: Generation time - Decryption
1100 return apdu_build_response(resp, resp_max, gen_time_dec, 4, SW_OK);
1101
1102 case DO_GEN_TIME_AUT: // 0xD0: Generation time - Authentication
1103 return apdu_build_response(resp, resp_max, gen_time_aut, 4, SW_OK);
1104
1105 case DO_SIG_COUNT: { // 0x93: Signature counter
1106 uint8_t count[3] = {
1107 (uint8_t)((sig_count >> 16) & 0xFF),
1108 (uint8_t)((sig_count >> 8) & 0xFF),
1109 (uint8_t)(sig_count & 0xFF)
1110 };
1111 return apdu_build_response(resp, resp_max, count, 3, SW_OK);
1112 }
1113
1114 // URL for public key retrieval
1115 case DO_URL: { // 0x5F50
1116 size_t len = strlen(cardholder_url);
1117 return apdu_build_response(resp, resp_max, (const uint8_t*)cardholder_url, len, SW_OK);
1118 }
1119
1120 // Login data
1121 case DO_LOGIN: { // 0x5E
1122 size_t len = strlen(cardholder_login);
1123 return apdu_build_response(resp, resp_max, (const uint8_t*)cardholder_login, len, SW_OK);
1124 }
1125
1126 // These are already in build_do_cardholder (0x65), but GPG may query them directly too
1127 case DO_NAME: // 0x5B: Cardholder name
1128 return apdu_build_response(resp, resp_max, (const uint8_t*)cardholder_name, strlen(cardholder_name), SW_OK);
1129
1130 case DO_LANG_PREF: // 0x5F2D: Language preference
1131 return apdu_build_response(resp, resp_max, (const uint8_t*)cardholder_lang, strlen(cardholder_lang), SW_OK);
1132
1133 case DO_SEX: // 0x5F35: Sex
1134 return apdu_build_response(resp, resp_max, &cardholder_sex, 1, SW_OK);
1135
1136 // UIF (User Interaction Flag) - 2 bytes: mode + features
1137 case DO_UIF_SIG: // 0xD6: UIF Signature
1138 case DO_UIF_DEC: // 0xD7: UIF Decryption
1139 case DO_UIF_AUT: { // 0xD8: UIF Authentication
1140 uint8_t uif[2] = { 0x00, 0x20 }; // Disabled, button available
1141 return apdu_build_response(resp, resp_max, uif, 2, SW_OK);
1142 }
1143
1144 // Key Information - 6 bytes (status of 3 keys)
1145 // Format: key_ref, status (0x00=generated, 0x01=imported, 0x02=not present)
1146 case DO_KEY_INFO: {
1147 uint8_t key_info[6];
1148 uint8_t pubkey[P256_PUBKEY_SIZE], curve;
1149
1150 // Check SIG key
1151 key_info[0] = 0x01; // Key reference for SIG
1152 key_info[1] = se_ecc_key_read(gpg_storage_sig_slot(), pubkey, sizeof(pubkey), &curve)
1153 ? 0x00 // generated
1154 : 0x02; // Not present
1155
1156 // Check DEC key
1157 key_info[2] = 0x02; // Key reference for DEC
1158 key_info[3] = se_ecc_key_read(gpg_storage_dec_slot(), pubkey, sizeof(pubkey), &curve)
1159 ? 0x00
1160 : 0x02;
1161
1162 // Check AUT key
1163 key_info[4] = 0x03; // Key reference for AUT
1164 key_info[5] = se_ecc_key_read(gpg_storage_aut_slot(), pubkey, sizeof(pubkey), &curve)
1165 ? 0x00
1166 : 0x02;
1167
1168 return apdu_build_response(resp, resp_max, key_info, 6, SW_OK);
1169 }
1170
1171 // Security Support Template - contains signature counter
1172 case DO_SEC_TPL: {
1173 // Format: 7A <len> { 93 03 <sig_count[3]> }
1174 uint8_t sec_tpl[7] = {
1175 0x93, 0x03, // Tag + length for signature counter
1176 (uint8_t)((sig_count >> 16) & 0xFF),
1177 (uint8_t)((sig_count >> 8) & 0xFF),
1178 (uint8_t)(sig_count & 0xFF)
1179 };
1180 return apdu_build_response(resp, resp_max, sec_tpl, 5, SW_OK);
1181 }
1182
1183 // KDF-DO (Key Derivation Function) - empty means no KDF
1184 case DO_KDF:
1185 return apdu_sw(resp, SW_OK); // Empty = no KDF configured
1186
1187 case DO_CARDHOLDER_CERT:
1189
1190 default:
1191 LOG_W(TAG, "GET DATA: Unknown tag 0x%04X", tag);
1193 }
1194}
1195
1206
1213typedef struct {
1214 uint16_t tag;
1215 void *buffer;
1216 size_t max_size;
1218 const char *log_label;
1220
1226static const put_data_desc_t* find_put_data_desc(uint16_t tag) {
1227 static const put_data_desc_t k_put_data_table[] = {
1228 // Cardholder profile (string-bounded, written with trailing NUL)
1229 { DO_NAME, cardholder_name, sizeof(cardholder_name), PUT_KIND_STRING_BOUNDED, "Cardholder name" },
1233
1234 // Fixed-size fingerprints (SHA-1 size)
1241
1242 // Fixed-size 4-byte big-endian generation timestamps
1246 };
1247
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];
1252 }
1253 }
1254 return NULL;
1255}
1256
1264static int apply_put_data_desc(const put_data_desc_t *desc, const apdu_t *apdu, uint8_t *resp) {
1265 if (desc->kind == PUT_KIND_STRING_BOUNDED) {
1266 if (apdu->lc >= desc->max_size) {
1267 return apdu_sw(resp, SW_WRONG_LENGTH);
1268 }
1269 char *str = (char *)desc->buffer;
1270 memcpy(str, apdu->data, apdu->lc);
1271 str[apdu->lc] = '\0';
1273 if (desc->log_label) {
1274 LOG_I(TAG, "%s set: %s", desc->log_label, str);
1275 }
1276 return apdu_sw(resp, SW_OK);
1277 }
1278
1279 // BLOB_FIXED: exact length match required
1280 if (apdu->lc != desc->max_size) {
1281 return apdu_sw(resp, SW_WRONG_LENGTH);
1282 }
1283 memcpy(desc->buffer, apdu->data, desc->max_size);
1285 if (desc->log_label) {
1286 LOG_I(TAG, "%s stored", desc->log_label);
1287 }
1288 return apdu_sw(resp, SW_OK);
1289}
1290
1307static int put_data_algo_attr(uint16_t tag, const apdu_t *apdu, uint8_t *resp) {
1308 algo_attr_t attr;
1309 if (algo_attr_parse(apdu->data, apdu->lc, &attr) != ALGO_ATTR_OK) {
1310 return apdu_sw(resp, SW_WRONG_DATA);
1311 }
1312 algo_attr_role_t role;
1313 uint8_t *target = nullptr;
1314 uint8_t slot = 0;
1315 switch (tag) {
1316 case DO_ALGO_SIG: role = ALGO_ATTR_ROLE_SIG; target = &selected_curve_sig; slot = gpg_storage_sig_slot(); break;
1317 case DO_ALGO_AUT: role = ALGO_ATTR_ROLE_AUT; target = &selected_curve_aut; slot = gpg_storage_aut_slot(); break;
1318 case DO_ALGO_DEC: role = ALGO_ATTR_ROLE_DEC; break;
1319 default: return apdu_sw(resp, SW_FILE_NOT_FOUND);
1320 }
1321 if (algo_attr_validate_role(&attr, role) != ALGO_ATTR_OK) {
1322 return apdu_sw(resp, SW_WRONG_DATA);
1323 }
1324 if (algo_attr_validate_capability(&attr, /*rsa_supported=*/false) != ALGO_ATTR_OK) {
1325 return apdu_sw(resp, SW_WRONG_DATA);
1326 }
1327 if (role == ALGO_ATTR_ROLE_DEC) {
1328 // DEC role: we only support the existing P-256 ECDH configuration.
1329 if (attr.curve != ALGO_ATTR_CURVE_P256 || attr.algo_id != ALGO_ATTR_ID_ECDH) {
1330 return apdu_sw(resp, SW_WRONG_DATA);
1331 }
1332 return apdu_sw(resp, SW_OK);
1333 }
1334 const uint8_t new_curve = (attr.curve == ALGO_ATTR_CURVE_ED25519) ? CDC_CURVE_ED25519
1336 if (*target == new_curve) {
1337 return apdu_sw(resp, SW_OK);
1338 }
1339 // Curve actually changed — invalidate the existing key in the SE so the
1340 // next GENERATE KEY PAIR yields material consistent with the new attrs.
1341 auto* se = get_se();
1342 if (se) {
1343 se->eccDelete(slot);
1344 }
1345 if (tag == DO_ALGO_SIG) {
1346 memset(fingerprint_sig, 0, sizeof(fingerprint_sig));
1347 memset(gen_time_sig, 0, sizeof(gen_time_sig));
1348 } else {
1349 memset(fingerprint_aut, 0, sizeof(fingerprint_aut));
1350 memset(gen_time_aut, 0, sizeof(gen_time_aut));
1351 }
1352 *target = new_curve;
1354 LOG_I(TAG, "Algorithm attributes for %s updated to curve %u",
1355 (tag == DO_ALGO_SIG) ? "SIG" : "AUT", new_curve);
1356 return apdu_sw(resp, SW_OK);
1357}
1358
1359static int cmd_put_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1360 (void)resp_max;
1361 if (!pw3_verified) {
1362 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
1363 }
1364
1365 uint16_t tag = (apdu->p1 << 8) | apdu->p2;
1366
1367 // Special-case DOs that need custom handling stay inline.
1368 switch (tag) {
1369 case DO_SEX:
1370 if (apdu->lc == 1) {
1371 cardholder_sex = apdu->data[0];
1373 return apdu_sw(resp, SW_OK);
1374 }
1375 return apdu_sw(resp, SW_WRONG_LENGTH);
1376 case DO_ALGO_SIG:
1377 case DO_ALGO_DEC:
1378 case DO_ALGO_AUT:
1379 return put_data_algo_attr(tag, apdu, resp);
1380 case DO_RC:
1381 // Set or clear the Resetting Code. Lc==0 clears the RC entirely.
1382 if (apdu->lc == 0) {
1383 mbedtls_platform_zeroize(s_rc_salt, sizeof(s_rc_salt));
1384 mbedtls_platform_zeroize(s_rc_hash, sizeof(s_rc_hash));
1385 s_rc_len = 0;
1386 s_rc_retries = 3;
1388 LOG_I(TAG, "Resetting Code cleared");
1389 return apdu_sw(resp, SW_OK);
1390 }
1391 if (apdu->lc < OPENPGP_RC_MIN_LEN || apdu->lc > OPENPGP_PIN_MAX_LEN) {
1392 return apdu_sw(resp, SW_WRONG_LENGTH);
1393 }
1394 {
1395 uint8_t new_salt[RC_SALT_SIZE];
1396 uint8_t new_hash[RC_HASH_SIZE];
1397 se_random_fill(new_salt, RC_SALT_SIZE);
1398 if (!compute_rc_hash(apdu->data, apdu->lc, new_salt, new_hash)) {
1399 mbedtls_platform_zeroize(new_salt, sizeof(new_salt));
1400 mbedtls_platform_zeroize(new_hash, sizeof(new_hash));
1401 return apdu_sw(resp, SW_UNKNOWN);
1402 }
1403 memcpy(s_rc_salt, new_salt, RC_SALT_SIZE);
1404 memcpy(s_rc_hash, new_hash, RC_HASH_SIZE);
1405 mbedtls_platform_zeroize(new_salt, sizeof(new_salt));
1406 mbedtls_platform_zeroize(new_hash, sizeof(new_hash));
1407 }
1408 s_rc_len = static_cast<uint8_t>(apdu->lc);
1409 s_rc_retries = 3;
1411 LOG_I(TAG, "Resetting Code configured (length=%u)", s_rc_len);
1412 return apdu_sw(resp, SW_OK);
1413 case DO_AES_KEY: {
1414 if (apdu->lc != 16 && apdu->lc != 32) {
1415 return apdu_sw(resp, SW_WRONG_LENGTH);
1416 }
1417 const char* pin = s_session_pin[0] ? s_session_pin : nullptr;
1418 bool ok = gpg_storage_save_aes_key(apdu->data, apdu->lc, pin);
1419 return apdu_sw(resp, ok ? SW_OK : SW_UNKNOWN);
1420 }
1421 default:
1422 break;
1423 }
1424
1425 // Table-driven simple cases (validate, memcpy, persist).
1426 const put_data_desc_t *desc = find_put_data_desc(tag);
1427 if (desc) {
1428 return apply_put_data_desc(desc, apdu, resp);
1429 }
1430
1431 LOG_W(TAG, "PUT DATA: Unknown tag 0x%04X", tag);
1432 return apdu_sw(resp, SW_FILE_NOT_FOUND);
1433}
1434
1435static void update_generation_timestamp(uint8_t key_ref);
1436
1445static bool ehl_parse_one(const uint8_t *buf, size_t buf_len, size_t *pos,
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;
1449 size_t p = *pos;
1450
1451 uint16_t tag = buf[p++];
1452 if ((tag & 0x1F) == 0x1F) {
1453 if (p >= buf_len) return false;
1454 tag = (tag << 8) | buf[p++];
1455 }
1456 if (p >= buf_len) return false;
1457
1458 size_t len;
1459 uint8_t lb = buf[p++];
1460 if (lb < 0x80) {
1461 len = lb;
1462 } else if (lb == 0x81) {
1463 if (p >= buf_len) return false;
1464 len = buf[p++];
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];
1468 p += 2;
1469 } else {
1470 return false;
1471 }
1472 if (p + len > buf_len) return false;
1473
1474 *tag_out = tag;
1475 *value_out = buf + p;
1476 *value_len_out = len;
1477 *pos = p + len;
1478 return true;
1479}
1480
1498static int cmd_put_data_odd(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1499 (void)resp_max;
1500 if (!pw3_verified) {
1501 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
1502 }
1503 if (apdu->p1 != 0x3F || apdu->p2 != 0xFF) {
1504 return apdu_sw(resp, SW_INCORRECT_P1P2);
1505 }
1506 if (apdu->lc == 0 || !apdu->data) {
1507 return apdu_sw(resp, SW_WRONG_LENGTH);
1508 }
1509
1510 size_t pos = 0;
1511 uint16_t tag = 0;
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) ||
1515 tag != 0x4D) {
1516 return apdu_sw(resp, SW_WRONG_DATA);
1517 }
1518
1519 // Walk the inner template: first the CRT (B6/B8/A4), then 7F48 (skipped),
1520 // then 5F48 with the concatenated key material.
1521 size_t inner = 0;
1522 const uint8_t *crt_val = nullptr;
1523 size_t crt_len = 0;
1524 if (!ehl_parse_one(outer_val, outer_len, &inner, &tag, &crt_val, &crt_len) ||
1525 tag != KEY_DEC) {
1526 return apdu_sw(resp, SW_WRONG_DATA);
1527 }
1528
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;
1533 size_t vlen = 0;
1534 if (!ehl_parse_one(outer_val, outer_len, &inner, &tag, &v, &vlen)) {
1535 return apdu_sw(resp, SW_WRONG_DATA);
1536 }
1537 if (tag == 0x5F48) {
1538 key_concat = v;
1539 key_concat_len = vlen;
1540 break;
1541 }
1542 // 7F48 (template) is informational; we know the format for our curve.
1543 }
1544 if (!key_concat || key_concat_len < P256_PRIVKEY_SIZE) {
1545 return apdu_sw(resp, SW_WRONG_DATA);
1546 }
1547
1548 uint8_t privkey[P256_PRIVKEY_SIZE];
1549 memcpy(privkey, key_concat, P256_PRIVKEY_SIZE);
1550 bool saved = gpg_storage_save_dec_privkey(privkey, nullptr);
1551 mbedtls_platform_zeroize(privkey, sizeof(privkey));
1552 if (!saved) {
1553 return apdu_sw(resp, SW_UNKNOWN);
1554 }
1556 return apdu_sw(resp, SW_OK);
1557}
1558
1566static int cmd_verify(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1567 uint8_t pw_ref = apdu->p2;
1568
1569 // Check remaining retries (Lc=0 means query)
1570 if (apdu->lc == 0) {
1571 uint8_t retries;
1572 if (pw_ref == PW1_CODE_1 || pw_ref == PW1_CODE_2) {
1574 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
1575 }
1577 } else if (pw_ref == PW3_CODE) {
1579 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
1580 }
1582 } else {
1583 return apdu_sw(resp, SW_INCORRECT_P1P2);
1584 }
1585 return apdu_sw(resp, 0x63C0 | retries);
1586 }
1587
1588 // Convert PIN data to null-terminated string
1589 char pin_str[OPENPGP_PIN_MAX_LEN + 1];
1590 if (apdu->lc > OPENPGP_PIN_MAX_LEN) {
1591 return apdu_sw(resp, SW_WRONG_LENGTH);
1592 }
1593 memcpy(pin_str, apdu->data, apdu->lc);
1594 pin_str[apdu->lc] = '\0';
1595
1596 // Verify PIN via TROPIC01 storage
1597 bool verified = false;
1598 uint8_t retries;
1599
1600 if (pw_ref == PW1_CODE_1 || pw_ref == PW1_CODE_2) {
1601 verified = pin_storage_openpgp_verify_pw1(pin_str);
1602 if (verified) {
1603 pw1_verified = true;
1604 strncpy(s_session_pin, pin_str, OPENPGP_PIN_MAX_LEN);
1607 LOG_I(TAG, "PW1 verified successfully");
1608 }
1610 } else if (pw_ref == PW3_CODE) {
1611 verified = pin_storage_openpgp_verify_pw3(pin_str);
1612 if (verified) {
1613 pw3_verified = true;
1614 LOG_I(TAG, "PW3 verified successfully");
1615 }
1617 } else {
1618 return apdu_sw(resp, SW_INCORRECT_P1P2);
1619 }
1620
1621 if (verified) {
1622 return apdu_sw(resp, SW_OK);
1623 }
1624
1625 // Verification failed
1626 if (retries == 0) {
1627 LOG_W(TAG, "PIN blocked after too many failures");
1628 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
1629 }
1630 LOG_W(TAG, "PIN verification failed, %d retries left", retries);
1631 return apdu_sw(resp, 0x63C0 | retries);
1632}
1633
1637typedef enum {
1640} pin_slot_t;
1641
1650static bool compute_kdf_hash(const char* pin, const uint8_t* salt, uint32_t iterations,
1651 uint8_t hash_out[32]) {
1652 if (!pin || !salt || !hash_out) return false;
1653 size_t pin_len = strlen(pin);
1654 if (pin_len > OPENPGP_PIN_MAX_LEN) return false;
1655 size_t combined = 8 + pin_len;
1656 if (combined == 0) return false;
1657
1658 uint8_t buffer[8 + OPENPGP_PIN_MAX_LEN];
1659 memcpy(buffer, salt, 8);
1660 memcpy(buffer + 8, pin, pin_len);
1661
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));
1667 return false;
1668 }
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));
1676 return false;
1677 }
1678 processed += chunk;
1679 }
1680 int rc = mbedtls_sha256_finish(&ctx, hash_out);
1681 mbedtls_sha256_free(&ctx);
1682 mbedtls_platform_zeroize(buffer, sizeof(buffer));
1683 return rc == 0;
1684}
1685
1693static bool const_time_equal(const uint8_t* a, const uint8_t* b, size_t n) {
1694 uint8_t diff = 0;
1695 for (size_t i = 0; i < n; ++i) diff |= a[i] ^ b[i];
1696 return diff == 0;
1697}
1698
1705static bool peek_verify_pin(pin_slot_t slot, const char* pin) {
1706 auto& mgr = cdc::core::PinManager::instance();
1707 uint8_t salt[8] = {};
1708 uint8_t stored[32] = {};
1709 uint8_t candidate[32] = {};
1710 bool ok = false;
1711
1712 if (slot == PIN_SLOT_PW1) {
1713 if (!mgr.getPW1Salt(salt)) goto done;
1714 if (!mgr.getPW1Hash(stored)) goto done;
1715 } else {
1716 if (!mgr.getPW3Salt(salt)) goto done;
1717 if (!mgr.getPW3Hash(stored)) goto done;
1718 }
1719
1720 if (!compute_kdf_hash(pin, salt, mgr.getIterationCount(), candidate)) goto done;
1721 ok = const_time_equal(candidate, stored, sizeof(stored));
1722
1723done:
1724 mbedtls_platform_zeroize(stored, sizeof(stored));
1725 mbedtls_platform_zeroize(candidate, sizeof(candidate));
1726 return ok;
1727}
1728
1732typedef bool (*pin_change_fn_t)(const char *pin);
1733
1749static bool try_change_pin(const uint8_t *data, size_t len, size_t min_len,
1750 pin_slot_t slot, pin_change_fn_t change_fn) {
1751 if (len < min_len * 2) {
1752 return false;
1753 }
1754 for (size_t old_len = min_len; old_len <= len - min_len; ++old_len) {
1755 size_t new_len = len - old_len;
1756 if (old_len > OPENPGP_PIN_MAX_LEN || new_len > OPENPGP_PIN_MAX_LEN) {
1757 continue;
1758 }
1759 char old_pin[OPENPGP_PIN_MAX_LEN + 1];
1760 char new_pin[OPENPGP_PIN_MAX_LEN + 1];
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';
1765
1766 if (peek_verify_pin(slot, old_pin) && change_fn(new_pin)) {
1767 mbedtls_platform_zeroize(old_pin, sizeof(old_pin));
1768 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
1769 return true;
1770 }
1771 mbedtls_platform_zeroize(old_pin, sizeof(old_pin));
1772 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
1773 }
1774 return false;
1775}
1776
1784static int cmd_change_reference_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1785 (void)resp_max;
1786 uint8_t pw_ref = apdu->p2;
1787
1788 if (apdu->lc == 0) {
1789 return apdu_sw(resp, SW_WRONG_LENGTH);
1790 }
1791
1792 pin_change_fn_t change_fn = NULL;
1793 size_t min_len = 0;
1794 uint8_t (*retries_fn)(void) = NULL;
1795 pin_slot_t slot;
1796 const char *log_label = NULL;
1797
1798 if (pw_ref == PW1_CODE_1) {
1799 slot = PIN_SLOT_PW1;
1802 min_len = OPENPGP_PW1_MIN_LEN;
1803 log_label = "PW1";
1804 } else if (pw_ref == PW3_CODE) {
1805 slot = PIN_SLOT_PW3;
1808 min_len = OPENPGP_PW3_MIN_LEN;
1809 log_label = "PW3";
1810 } else {
1811 return apdu_sw(resp, SW_INCORRECT_P1P2);
1812 }
1813
1814 if (apdu->lc < min_len * 2) {
1815 return apdu_sw(resp, SW_WRONG_LENGTH);
1816 }
1817
1818 if (try_change_pin(apdu->data, apdu->lc, min_len, slot, change_fn)) {
1819 LOG_I(TAG, "%s changed successfully", log_label);
1820 return apdu_sw(resp, SW_OK);
1821 }
1822
1823 pin_slot_t slot_for_decrement = slot;
1824 char dummy_pin[OPENPGP_PIN_MAX_LEN + 1] = {};
1825 // Trigger a single retry decrement via the regular path to keep the
1826 // remote counter in sync with the failed CHANGE attempt.
1827 if (slot_for_decrement == PIN_SLOT_PW1) {
1829 } else {
1831 }
1832
1833 uint8_t retries = retries_fn();
1834 if (retries == 0) {
1835 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
1836 }
1837 return apdu_sw(resp, 0x63C0 | retries);
1838}
1839
1847static int cmd_pso_cds(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1848 if (!pw1_verified) {
1849 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
1850 }
1851
1852 // Check if signature key exists by trying to read it
1853 uint8_t pubkey[P256_PUBKEY_SIZE];
1854 uint8_t curve;
1855 if (!se_ecc_key_read(gpg_storage_sig_slot(), pubkey, sizeof(pubkey), &curve)) {
1856 LOG_E(TAG, "No signature key configured");
1858 }
1859
1860 // Sign hash directly using TROPIC01
1861 // P-256 uses ECDSA, Ed25519 uses EdDSA
1862 uint8_t signature[64]; // R (32 bytes) || S (32 bytes)
1863
1864 bool success;
1865 if (curve == CDC_CURVE_P256) {
1866 if (apdu->lc != SHA256_DIGEST_SIZE) {
1867 return apdu_sw(resp, SW_WRONG_DATA);
1868 }
1869 success = se_ecdsa_sign(gpg_storage_sig_slot(), apdu->data, apdu->lc, signature);
1870 } else {
1871 success = se_eddsa_sign(gpg_storage_sig_slot(), apdu->data, apdu->lc, signature);
1872 }
1873
1874 if (!success) {
1875 LOG_E(TAG, "Signature failed");
1876 return apdu_sw(resp, SW_UNKNOWN);
1877 }
1878
1879 // Increment signature counter
1880 sig_count++;
1882
1883 LOG_I(TAG, "Signature created, count=%lu", sig_count);
1884 return apdu_build_response(resp, resp_max, signature, 64, SW_OK);
1885}
1886
1928static int cmd_pso_decipher_aes(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1929 if (!gpg_storage_has_aes_key()) {
1931 }
1932 if (apdu->lc < 1 + 16 + 1) {
1933 return apdu_sw(resp, SW_WRONG_DATA);
1934 }
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) {
1939 return apdu_sw(resp, SW_WRONG_LENGTH);
1940 }
1941
1942 uint8_t aes_key[32] = {};
1943 size_t aes_key_len = 0;
1944 if (!gpg_storage_load_aes_key(aes_key, &aes_key_len, s_session_pin[0] ? s_session_pin : nullptr)) {
1945 mbedtls_platform_zeroize(aes_key, sizeof(aes_key));
1946 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
1947 }
1948
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));
1952 if (rc != 0) {
1953 mbedtls_aes_free(&aes);
1954 mbedtls_platform_zeroize(aes_key, sizeof(aes_key));
1955 return apdu_sw(resp, SW_UNKNOWN);
1956 }
1957
1958 uint8_t iv[16];
1959 memcpy(iv, iv_in, sizeof(iv));
1960 size_t iv_off = 0;
1961 uint8_t plain[256];
1962 if (ct_len > sizeof(plain)) {
1963 mbedtls_aes_free(&aes);
1964 mbedtls_platform_zeroize(aes_key, sizeof(aes_key));
1965 return apdu_sw(resp, SW_WRONG_LENGTH);
1966 }
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));
1971 if (rc != 0) {
1972 mbedtls_platform_zeroize(plain, sizeof(plain));
1973 return apdu_sw(resp, SW_UNKNOWN);
1974 }
1975 size_t n = apdu_build_response(resp, resp_max, plain, ct_len, SW_OK);
1976 mbedtls_platform_zeroize(plain, sizeof(plain));
1977 return n;
1978}
1979
1980static int cmd_pso_decipher(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1981 if (!pw1_verified) {
1982 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
1983 }
1984 if (apdu->lc < 1) {
1985 return apdu_sw(resp, SW_WRONG_DATA);
1986 }
1987
1988 // OpenPGP 3.4.1 §7.2.11: padding indicator 0x02 = AES decryption.
1989 if (apdu->data[0] == 0x02) {
1990 return cmd_pso_decipher_aes(apdu, resp, resp_max);
1991 }
1992
1995 }
1996
1997 // Parse Cipher DO (A6 -> 7F49 -> 86), per OpenPGP 3.4.1 §7.2.11.
1998 // Minimum: A6 <len1> 7F49 <len2> 86 <len3> <65 bytes pubkey>
1999 // With single-byte lengths: A6 46 7F49 43 86 41 <65 bytes> = 72 bytes
2000 if (apdu->lc < 70) {
2001 return apdu_sw(resp, SW_WRONG_DATA);
2002 }
2003
2004 const uint8_t* p = apdu->data;
2005 const uint8_t* end = apdu->data + apdu->lc;
2006
2007 if (p >= end || *p != 0xA6) {
2008 return apdu_sw(resp, SW_WRONG_DATA);
2009 }
2010 p++;
2011
2012 if (p >= end) return apdu_sw(resp, SW_WRONG_DATA);
2013 if (*p < 0x80) {
2014 p += 1;
2015 } else if (*p == 0x81 && p + 1 < end) {
2016 p += 2;
2017 } else if (*p == 0x82 && p + 2 < end) {
2018 p += 3;
2019 } else {
2020 return apdu_sw(resp, SW_WRONG_DATA);
2021 }
2022
2023 if (p + 2 > end || p[0] != 0x7F || p[1] != 0x49) {
2024 return apdu_sw(resp, SW_WRONG_DATA);
2025 }
2026 p += 2;
2027
2028 if (p >= end) return apdu_sw(resp, SW_WRONG_DATA);
2029 if (*p < 0x80) {
2030 p += 1;
2031 } else if (*p == 0x81 && p + 1 < end) {
2032 p += 2;
2033 } else if (*p == 0x82 && p + 2 < end) {
2034 p += 3;
2035 } else {
2036 return apdu_sw(resp, SW_WRONG_DATA);
2037 }
2038
2039 if (p >= end || *p != 0x86) {
2040 return apdu_sw(resp, SW_WRONG_DATA);
2041 }
2042 p++;
2043
2044 if (p >= end) return apdu_sw(resp, SW_WRONG_DATA);
2045 size_t pubkey_len;
2046 if (*p < 0x80) {
2047 pubkey_len = *p++;
2048 } else if (*p == 0x81 && p + 1 < end) {
2049 pubkey_len = p[1];
2050 p += 2;
2051 } else if (*p == 0x82 && p + 2 < end) {
2052 pubkey_len = (static_cast<size_t>(p[1]) << 8) | p[2];
2053 p += 3;
2054 } else {
2055 return apdu_sw(resp, SW_WRONG_DATA);
2056 }
2057
2058 if (pubkey_len != P256_PUBKEY_SIZE || p + pubkey_len > end) {
2059 return apdu_sw(resp, SW_WRONG_DATA);
2060 }
2061 if (p[0] != 0x04) {
2062 return apdu_sw(resp, SW_WRONG_DATA);
2063 }
2064 const uint8_t* peer_pubkey = p;
2065
2066 uint8_t dec_privkey[P256_PRIVKEY_SIZE];
2067 if (!gpg_storage_load_dec_privkey(dec_privkey, nullptr)) {
2068 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2069 }
2070
2071 uint8_t shared_secret[P256_ECDH_SECRET_SIZE];
2072 bool ok = ecdh_p256_compute_shared_secret(dec_privkey, peer_pubkey, shared_secret);
2073 mbedtls_platform_zeroize(dec_privkey, sizeof(dec_privkey));
2074 if (!ok) {
2075 mbedtls_platform_zeroize(shared_secret, sizeof(shared_secret));
2076 return apdu_sw(resp, SW_UNKNOWN);
2077 }
2078 int n = apdu_build_response(resp, resp_max, shared_secret, P256_ECDH_SECRET_SIZE, SW_OK);
2079 mbedtls_platform_zeroize(shared_secret, sizeof(shared_secret));
2080 return n;
2081}
2082
2097static int cmd_manage_security_env(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2098 (void)resp_max;
2099 if (apdu->p1 != 0x41) {
2100 return apdu_sw(resp, SW_INCORRECT_P1P2);
2101 }
2102 if (apdu->p2 != KEY_SIG && apdu->p2 != KEY_DEC && apdu->p2 != KEY_AUT) {
2103 return apdu_sw(resp, SW_INCORRECT_P1P2);
2104 }
2105 if (apdu->lc == 0 || apdu->data == nullptr) {
2106 return apdu_sw(resp, SW_WRONG_LENGTH);
2107 }
2108 // Parse the Cryptographic Reference Template: expect 83 01 <ref>.
2109 if (apdu->lc < 3 || apdu->data[0] != 0x83 || apdu->data[1] != 0x01) {
2110 return apdu_sw(resp, SW_WRONG_DATA);
2111 }
2112 const uint8_t ref = apdu->data[2];
2113 if (ref != 0x01 && ref != 0x02 && ref != 0x03) {
2114 return apdu_sw(resp, SW_WRONG_DATA);
2115 }
2116 // Cross-check: tag-83 reference must agree with P2 role.
2117 if ((apdu->p2 == KEY_SIG && ref != 0x01) ||
2118 (apdu->p2 == KEY_DEC && ref != 0x02) ||
2119 (apdu->p2 == KEY_AUT && ref != 0x03)) {
2121 }
2122 return apdu_sw(resp, SW_OK);
2123}
2124
2137static int cmd_internal_authenticate(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2138 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2139 return apdu_sw(resp, SW_INCORRECT_P1P2);
2140 }
2141 if (!pw1_verified) {
2142 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2143 }
2144 if (apdu->lc == 0 || apdu->data == nullptr) {
2145 return apdu_sw(resp, SW_WRONG_LENGTH);
2146 }
2147
2148 uint8_t pubkey[P256_PUBKEY_SIZE];
2149 uint8_t curve = 0;
2150 if (!se_ecc_key_read(gpg_storage_aut_slot(), pubkey, sizeof(pubkey), &curve)) {
2151 LOG_E(TAG, "No AUT key configured");
2153 }
2154
2155 uint8_t signature[64];
2156 bool ok = false;
2157 if (curve == CDC_CURVE_P256) {
2158 ok = se_ecdsa_sign(gpg_storage_aut_slot(), apdu->data, apdu->lc, signature);
2159 } else {
2160 ok = se_eddsa_sign(gpg_storage_aut_slot(), apdu->data, apdu->lc, signature);
2161 }
2162 if (!ok) {
2163 LOG_E(TAG, "INTERNAL AUTHENTICATE: signing failed");
2164 return apdu_sw(resp, SW_UNKNOWN);
2165 }
2166 return apdu_build_response(resp, resp_max, signature, sizeof(signature), SW_OK);
2167}
2168
2179static int cmd_terminate_df(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2180 (void)resp_max;
2181 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2182 return apdu_sw(resp, SW_INCORRECT_P1P2);
2183 }
2184 const bool both_blocked = pin_storage_openpgp_pw1_blocked() &&
2186 if (!pw3_verified && !both_blocked) {
2187 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2188 }
2189 card_terminated = true;
2190 pw1_verified = false;
2191 pw3_verified = false;
2193 LOG_W(TAG, "Card moved to TERMINATED state");
2194 return apdu_sw(resp, SW_OK);
2195}
2196
2205static int cmd_activate_file(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2206 (void)resp_max;
2207 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2208 return apdu_sw(resp, SW_INCORRECT_P1P2);
2209 }
2210 if (!card_terminated) {
2211 return apdu_sw(resp, SW_OK);
2212 }
2214 LOG_W(TAG, "ACTIVATE FILE: card reset to factory defaults");
2215 return apdu_sw(resp, SW_OK);
2216}
2217
2219 auto* se = get_se();
2220 if (se) {
2221 se->eccDelete(gpg_storage_sig_slot());
2222 se->eccDelete(gpg_storage_aut_slot());
2223 }
2227
2228 memset(fingerprint_sig, 0, sizeof(fingerprint_sig));
2229 memset(fingerprint_dec, 0, sizeof(fingerprint_dec));
2230 memset(fingerprint_aut, 0, sizeof(fingerprint_aut));
2231 memset(gen_time_sig, 0, sizeof(gen_time_sig));
2232 memset(gen_time_dec, 0, sizeof(gen_time_dec));
2233 memset(gen_time_aut, 0, sizeof(gen_time_aut));
2234 memset(ca_fp_1, 0, sizeof(ca_fp_1));
2235 memset(ca_fp_2, 0, sizeof(ca_fp_2));
2236 memset(ca_fp_3, 0, sizeof(ca_fp_3));
2237 memset(cardholder_name, 0, sizeof(cardholder_name));
2238 memset(cardholder_url, 0, sizeof(cardholder_url));
2239 memset(cardholder_login, 0, sizeof(cardholder_login));
2240 snprintf(cardholder_lang, sizeof(cardholder_lang), "en");
2241 cardholder_sex = 0x39;
2242 sig_count = 0;
2245 mbedtls_platform_zeroize(s_rc_salt, sizeof(s_rc_salt));
2246 mbedtls_platform_zeroize(s_rc_hash, sizeof(s_rc_hash));
2247 s_rc_len = 0;
2248 s_rc_retries = 3;
2249 pw1_verified = false;
2250 pw3_verified = false;
2251 card_terminated = false;
2253}
2254
2265static int cmd_reset_retry_counter(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2266 (void)resp_max;
2267 if (apdu->p2 != PW1_CODE_1) {
2268 return apdu_sw(resp, SW_INCORRECT_P1P2);
2269 }
2270 if (apdu->p1 != 0x00 && apdu->p1 != 0x02) {
2271 return apdu_sw(resp, SW_INCORRECT_P1P2);
2272 }
2273 if (apdu->p1 == 0x00) {
2274 // RC path. Lc = RC || new PW1 (concatenated, no length prefix).
2275 if (s_rc_len == 0) {
2277 }
2278 if (s_rc_retries == 0) {
2279 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
2280 }
2281 if (apdu->lc < s_rc_len + OPENPGP_PW1_MIN_LEN ||
2282 apdu->lc - s_rc_len > OPENPGP_PIN_MAX_LEN) {
2283 return apdu_sw(resp, SW_WRONG_LENGTH);
2284 }
2285 uint8_t input_hash[RC_HASH_SIZE];
2286 if (!compute_rc_hash(apdu->data, s_rc_len, s_rc_salt, input_hash)) {
2287 return apdu_sw(resp, SW_UNKNOWN);
2288 }
2289 uint8_t diff = 0;
2290 for (size_t i = 0; i < RC_HASH_SIZE; ++i) {
2291 diff |= static_cast<uint8_t>(s_rc_hash[i] ^ input_hash[i]);
2292 }
2293 mbedtls_platform_zeroize(input_hash, sizeof(input_hash));
2294 if (diff != 0) {
2295 if (s_rc_retries > 0) s_rc_retries -= 1;
2297 const uint8_t retries = s_rc_retries;
2298 if (retries == 0) {
2299 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
2300 }
2301 return apdu_sw(resp, static_cast<uint16_t>(0x63C0 | retries));
2302 }
2303
2304 const size_t new_pw1_len = apdu->lc - s_rc_len;
2305 char new_pin[OPENPGP_PIN_MAX_LEN + 1] = {};
2306 memcpy(new_pin, apdu->data + s_rc_len, new_pw1_len);
2307 new_pin[new_pw1_len] = '\0';
2308 if (!pin_storage_openpgp_change_pw1(new_pin)) {
2309 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
2310 return apdu_sw(resp, SW_UNKNOWN);
2311 }
2312 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
2314 s_rc_retries = 3;
2315 pw1_verified = false;
2317 LOG_I(TAG, "RESET RETRY COUNTER: PW1 reset via RC");
2318 return apdu_sw(resp, SW_OK);
2319 }
2320 // P1 == 0x02 — admin-driven reset.
2321 if (!pw3_verified) {
2322 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2323 }
2324 if (apdu->lc < OPENPGP_PW1_MIN_LEN || apdu->lc > OPENPGP_PIN_MAX_LEN) {
2325 return apdu_sw(resp, SW_WRONG_LENGTH);
2326 }
2327 char new_pin[OPENPGP_PIN_MAX_LEN + 1] = {};
2328 memcpy(new_pin, apdu->data, apdu->lc);
2329 new_pin[apdu->lc] = '\0';
2330 if (!pin_storage_openpgp_change_pw1(new_pin)) {
2331 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
2332 return apdu_sw(resp, SW_UNKNOWN);
2333 }
2334 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
2336 pw1_verified = false; // force re-verification with new PW1
2337 LOG_I(TAG, "RESET RETRY COUNTER: PW1 reset by admin");
2338 return apdu_sw(resp, SW_OK);
2339}
2340
2346static uint8_t get_ecc_slot_for_key_ref(uint8_t key_ref) {
2347 switch (key_ref) {
2348 case KEY_SIG: // 0xB6 - Signature
2349 return gpg_storage_sig_slot();
2350 case KEY_DEC: // 0xB8 - Decryption
2351 return gpg_storage_dec_slot();
2352 case KEY_AUT: // 0xA4 - Authentication
2353 return gpg_storage_aut_slot();
2354 default:
2355 return gpg_storage_sig_slot(); // Default to SIG
2356 }
2357}
2358
2364static key_type_t get_key_type_for_ref(uint8_t key_ref) {
2365 switch (key_ref) {
2366 case KEY_SIG: return KEY_TYPE_SIG;
2367 case KEY_DEC: return KEY_TYPE_DEC;
2368 case KEY_AUT: return KEY_TYPE_AUT;
2369 default: return KEY_TYPE_SIG;
2370 }
2371}
2372
2382static uint16_t generate_dec_key(uint8_t *pubkey_out) {
2383 uint8_t privkey[P256_PRIVKEY_SIZE];
2384 if (!ecdh_p256_generate_keypair(privkey, pubkey_out)) {
2385 return SW_UNKNOWN;
2386 }
2387 if (!gpg_storage_save_dec_privkey(privkey, nullptr)) {
2388 mbedtls_platform_zeroize(privkey, sizeof(privkey));
2389 return SW_UNKNOWN;
2390 }
2391 mbedtls_platform_zeroize(privkey, sizeof(privkey));
2392 return SW_OK;
2393}
2394
2401static uint16_t generate_hardware_key(uint8_t ecc_slot, uint8_t curve) {
2402 if (!se_ecc_key_generate(ecc_slot, curve)) {
2403 LOG_E(TAG, "Key generation failed for slot %d", ecc_slot);
2404 return SW_UNKNOWN;
2405 }
2406 LOG_I(TAG, "Key pair generated in slot %d (hardware)", ecc_slot);
2407 return SW_OK;
2408}
2409
2414static void update_generation_timestamp(uint8_t key_ref) {
2415 uint32_t now = (uint32_t)time(NULL);
2416 uint8_t ts[4] = {
2417 (uint8_t)((now >> 24) & 0xFF),
2418 (uint8_t)((now >> 16) & 0xFF),
2419 (uint8_t)((now >> 8) & 0xFF),
2420 (uint8_t)(now & 0xFF)
2421 };
2422
2423 switch (key_ref) {
2424 case KEY_SIG: memcpy(gen_time_sig, ts, 4); break;
2425 case KEY_DEC: memcpy(gen_time_dec, ts, 4); break;
2426 case KEY_AUT: memcpy(gen_time_aut, ts, 4); break;
2427 default: break;
2428 }
2430}
2431
2445static bool read_public_key(key_type_t key_type, uint8_t ecc_slot,
2446 uint8_t *pubkey, uint8_t *curve_out) {
2447 if (key_type == KEY_TYPE_DEC) {
2448 LOG_I(TAG, "read_public_key DEC: checking has_dec_privkey");
2450 LOG_W(TAG, "read_public_key DEC: no privkey");
2451 return false;
2452 }
2453 LOG_I(TAG, "read_public_key DEC: loading privkey");
2454 uint8_t privkey[P256_PRIVKEY_SIZE];
2455 if (!gpg_storage_load_dec_privkey(privkey, nullptr)) {
2456 LOG_W(TAG, "read_public_key DEC: load_dec_privkey failed");
2457 return false;
2458 }
2459 LOG_I(TAG, "read_public_key DEC: deriving pubkey");
2460 bool ok = ecdh_p256_derive_pubkey(privkey, pubkey);
2461 mbedtls_platform_zeroize(privkey, sizeof(privkey));
2462 if (curve_out) {
2463 *curve_out = CDC_CURVE_P256;
2464 }
2465 LOG_I(TAG, "read_public_key DEC: derive ok=%d", ok);
2466 return ok;
2467 }
2468
2469 return se_ecc_key_read(ecc_slot, pubkey, P256_PUBKEY_SIZE, curve_out);
2470}
2471
2483static void encode_pubkey_with_prefix(const uint8_t *pubkey, uint8_t curve,
2484 uint8_t *out, size_t *out_len) {
2485 if (curve == CDC_CURVE_P256) {
2486 if (pubkey[0] == 0x04) {
2487 memcpy(out, pubkey, P256_PUBKEY_SIZE);
2488 } else {
2489 out[0] = 0x04;
2490 memcpy(out + 1, pubkey, P256_PUBKEY_SIZE - 1);
2491 }
2492 *out_len = P256_PUBKEY_SIZE;
2493 } else {
2494 // Ed25519: raw 32-byte encoding, no SEC1 prefix.
2495 memcpy(out, pubkey, ED25519_PUBKEY_SIZE);
2496 *out_len = ED25519_PUBKEY_SIZE;
2497 }
2498}
2499
2507static int cmd_generate_keypair(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2508 // Parse control reference template (CRT) from data.
2509 // Format: B6 00 (SIG) / B8 00 (DEC) / A4 00 (AUT)
2510 uint8_t key_ref = KEY_SIG; // Default to Signature key
2511
2512 if (apdu->lc >= 2) {
2513 key_ref = apdu->data[0];
2514 LOG_I(TAG, "Key ref from CRT: 0x%02X", key_ref);
2515 }
2516
2517 const uint8_t ecc_slot = get_ecc_slot_for_key_ref(key_ref);
2518 const key_type_t key_type = get_key_type_for_ref(key_ref);
2519
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);
2522
2523 if (apdu->p1 == 0x80) {
2524 // P1=0x80: Generate new key (admin PIN required).
2525 if (!pw3_verified) {
2526 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2527 }
2528
2529 // Curve choice follows the configured algorithm attributes:
2530 // SIG / AUT honour PUT DATA C1 / C3; DEC is fixed to P-256 (the only
2531 // curve the software-ECDH path supports).
2532 uint8_t curve = CDC_CURVE_P256;
2533 if (key_type == KEY_TYPE_SIG) curve = selected_curve_sig;
2534 else if (key_type == KEY_TYPE_AUT) curve = selected_curve_aut;
2535
2536 LOG_I(TAG, "Generating key in slot %d (curve=%d, type=%s)",
2537 ecc_slot, curve,
2538 key_type == KEY_TYPE_SIG ? "SIG" :
2539 key_type == KEY_TYPE_DEC ? "DEC" : "AUT");
2540
2541 uint8_t fresh_pubkey[P256_PUBKEY_SIZE] = {};
2542 uint16_t gen_sw;
2543 if (key_type == KEY_TYPE_DEC) {
2544 gen_sw = generate_dec_key(fresh_pubkey);
2545 } else {
2546 gen_sw = generate_hardware_key(ecc_slot, curve);
2547 }
2548 if (gen_sw != SW_OK) {
2549 return apdu_sw(resp, gen_sw);
2550 }
2551
2553
2554 if (key_type == KEY_TYPE_DEC) {
2555 uint8_t pubkey_with_prefix[P256_PUBKEY_SIZE];
2556 size_t pubkey_len = 0;
2558 pubkey_with_prefix, &pubkey_len);
2559
2560 uint8_t tlv_data[128];
2561 size_t pos = tlv_build(tlv_data, sizeof(tlv_data), 0x86,
2562 pubkey_with_prefix, pubkey_len);
2563
2564 uint8_t final_resp[140];
2565 size_t final_len = 0;
2566 final_resp[final_len++] = 0x7F;
2567 final_resp[final_len++] = 0x49;
2568 final_len += tlv_write_len(final_resp + final_len, pos);
2569 memcpy(final_resp + final_len, tlv_data, pos);
2570 final_len += pos;
2571 return apdu_build_response(resp, resp_max, final_resp, final_len, SW_OK);
2572 }
2573 }
2574
2575 // Read public key (P1=0x81 read, or after key generation above).
2576 uint8_t pubkey[P256_PUBKEY_SIZE];
2577 uint8_t read_curve = CDC_CURVE_P256;
2578 if (!read_public_key(key_type, ecc_slot, pubkey, &read_curve)) {
2579 LOG_W(TAG, "Public key read failed: empty slot=%d type=%d", ecc_slot, key_type);
2580 // Per OpenPGP 3.4.1 §7.2.14 GET DATA / GENERATE ASYMMETRIC KEY PAIR
2581 // with P1=0x81 on an unpopulated slot must signal "referenced data
2582 // not found" (SW=6A88). gpg / scdaemon treats 6A88 as "no key yet",
2583 // which is the natural state for a freshly-activated card and the
2584 // only way `gpg --card-status` completes without aborting.
2586 }
2587
2588 // Build TLV response according to OpenPGP 3.4.1: 7F49 <len> { 86 <len> <pubkey> }.
2589 // P-256: 0x04 || X || Y (65 bytes); Ed25519: 32 raw bytes.
2590 uint8_t pubkey_with_prefix[P256_PUBKEY_SIZE];
2591 size_t pubkey_len = 0;
2592 encode_pubkey_with_prefix(pubkey, read_curve, pubkey_with_prefix, &pubkey_len);
2593
2594 uint8_t tlv_data[128];
2595 size_t pos = 0;
2596 pos += tlv_build(tlv_data + pos, sizeof(tlv_data) - pos, 0x86, pubkey_with_prefix, pubkey_len);
2597
2598 // Wrap in 7F49 (Public Key DO).
2599 uint8_t final_resp[140];
2600 size_t final_len = 0;
2601 final_resp[final_len++] = 0x7F;
2602 final_resp[final_len++] = 0x49;
2603 final_len += tlv_write_len(final_resp + final_len, pos);
2604 memcpy(final_resp + final_len, tlv_data, pos);
2605 final_len += pos;
2606
2607 LOG_I(TAG, "Public key exported (%zu bytes, curve=%d, slot=%d)",
2608 pubkey_len, read_curve, ecc_slot);
2609 return apdu_build_response(resp, resp_max, final_resp, final_len, SW_OK);
2610}
2611
2621static int apply_response_chaining(uint32_t le, uint8_t *resp, size_t resp_max,
2622 int result_len) {
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]);
2627 // Only chain on successful payloads; error SWs must surface verbatim.
2628 if (sw != SW_OK) return result_len;
2629 if (le == 0 || payload_len <= le) {
2630 return result_len;
2631 }
2632 const size_t remainder = payload_len - le;
2633 if (remainder > sizeof(g_resp_buffer)) {
2634 LOG_W(TAG, "Response remainder %zu > %zu, truncating", remainder, sizeof(g_resp_buffer));
2635 return result_len; // Best-effort: caller gets the truncated head.
2636 }
2637 memcpy(g_resp_buffer, resp + le, remainder);
2638 g_resp_remaining = remainder;
2639 g_resp_pos = 0;
2640
2641 if (resp_max < le + 2) return result_len; // Defensive: caller's buf too small.
2642 const uint8_t sw2 = (remainder > 0xFF) ? 0x00 : static_cast<uint8_t>(remainder);
2643 resp[le] = 0x61;
2644 resp[le + 1] = sw2;
2645 return static_cast<int>(le + 2);
2646}
2647
2651static int cmd_get_response(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2652 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2653 return apdu_sw(resp, SW_INCORRECT_P1P2);
2654 }
2655 if (g_resp_remaining == 0) {
2657 }
2658 size_t want = (apdu->le > 0) ? apdu->le : 256;
2659 if (want > g_resp_remaining) want = g_resp_remaining;
2660 if (want + 2 > resp_max) want = resp_max - 2;
2661
2662 memcpy(resp, g_resp_buffer + g_resp_pos, want);
2663 // Wipe the bytes we just handed out so the PSRAM-backed chain buffer
2664 // doesn't retain a copy after delivery (PSO:DECIPHER shared secret can
2665 // end up here when Le forces a chain).
2666 mbedtls_platform_zeroize(g_resp_buffer + g_resp_pos, want);
2667 g_resp_pos += want;
2668 g_resp_remaining -= want;
2669
2670 uint16_t sw = SW_OK;
2671 if (g_resp_remaining > 0) {
2672 const uint8_t sw2 = (g_resp_remaining > 0xFF) ? 0x00
2673 : static_cast<uint8_t>(g_resp_remaining);
2674 sw = static_cast<uint16_t>((0x61 << 8) | sw2);
2675 } else {
2676 g_resp_pos = 0;
2677 }
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);
2681}
2682
2683int openpgp_process_apdu(const uint8_t *cmd, size_t cmd_len,
2684 uint8_t *resp, size_t resp_max) {
2685 apdu_t apdu;
2686
2687 if (!apdu_parse(cmd, cmd_len, &apdu)) {
2688 LOG_E(TAG, "Invalid APDU");
2689 return apdu_sw(resp, SW_WRONG_LENGTH);
2690 }
2691
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);
2694
2695 // Any command other than GET RESPONSE invalidates a pending chained payload.
2696 if (apdu.ins != INS_GET_RESPONSE) {
2697 g_resp_remaining = 0;
2698 g_resp_pos = 0;
2699 }
2700
2701 // ISO 7816 CLA: bit 4 = chaining. Reject secure messaging and channels >0.
2702 if ((apdu.cla & ~0x10) != 0x00) {
2703 chain_reset();
2704 return apdu_sw(resp, SW_CLA_NOT_SUPPORTED);
2705 }
2706
2707 // Command chaining (ISO 7816-4 §5.1.1): accumulate data while the CLA
2708 // chaining bit is set; dispatch once it clears. Any deviation in
2709 // INS/P1/P2 mid-chain is a protocol error and the chain is dropped.
2710 const bool is_chain_block = (apdu.cla & 0x10) != 0;
2711 if (is_chain_block || g_chain_active) {
2712 if (!g_chain_active) {
2713 g_chain_active = true;
2714 g_chain_ins = apdu.ins;
2715 g_chain_p1 = apdu.p1;
2716 g_chain_p2 = apdu.p2;
2717 g_chain_len = 0;
2718 } else if (apdu.ins != g_chain_ins ||
2719 apdu.p1 != g_chain_p1 ||
2720 apdu.p2 != g_chain_p2) {
2721 chain_reset();
2722 return apdu_sw(resp, SW_WRONG_DATA);
2723 }
2724 if (g_chain_len + apdu.lc > sizeof(g_chain_buffer)) {
2725 chain_reset();
2726 return apdu_sw(resp, SW_WRONG_LENGTH);
2727 }
2728 if (apdu.lc > 0 && apdu.data != nullptr) {
2729 memcpy(g_chain_buffer + g_chain_len, apdu.data, apdu.lc);
2730 g_chain_len += apdu.lc;
2731 }
2732 if (is_chain_block) {
2733 // Intermediate block: ACK and wait for more.
2734 return apdu_sw(resp, SW_OK);
2735 }
2736 // Final block — replace the parsed apdu's payload with the
2737 // accumulator so the per-command handlers see the full data.
2738 apdu.data = g_chain_buffer;
2739 apdu.lc = static_cast<uint16_t>(g_chain_len);
2740 chain_reset();
2741 }
2742
2743 // SELECT is always allowed, even in TERMINATED state — otherwise the host
2744 // could not target the application to issue ACTIVATE FILE.
2745 if (apdu.ins == INS_SELECT) {
2746 return cmd_select(&apdu, resp, resp_max);
2747 }
2748
2749 // All other commands require application to be selected
2750 if (!app_selected) {
2752 }
2753
2754 // While terminated, only ACTIVATE FILE is honoured. Per OpenPGP 3.4.1
2755 // §7.2.18 every other INS must return SW_FILE_TERMINATED (0x6285).
2756 if (card_terminated && apdu.ins != INS_ACTIVATE) {
2757 return apdu_sw(resp, SW_FILE_TERMINATED);
2758 }
2759
2760 int result_len = 0;
2761 switch (apdu.ins) {
2762 case INS_GET_DATA:
2763 result_len = cmd_get_data(&apdu, resp, resp_max);
2764 break;
2765
2766 case INS_PUT_DATA:
2767 result_len = cmd_put_data(&apdu, resp, resp_max);
2768 break;
2769
2770 case INS_PUT_DATA_ODD:
2771 result_len = cmd_put_data_odd(&apdu, resp, resp_max);
2772 break;
2773
2774 case INS_VERIFY:
2775 result_len = cmd_verify(&apdu, resp, resp_max);
2776 break;
2777
2778 case INS_CHANGE_PIN:
2779 result_len = cmd_change_reference_data(&apdu, resp, resp_max);
2780 break;
2781
2782 case INS_RESET_RETRY:
2783 result_len = cmd_reset_retry_counter(&apdu, resp, resp_max);
2784 break;
2785
2786 case INS_PSO:
2787 if (apdu.p1 == 0x9E && apdu.p2 == 0x9A) {
2788 result_len = cmd_pso_cds(&apdu, resp, resp_max);
2789 } else if (apdu.p1 == 0x80 && apdu.p2 == 0x86) {
2790 result_len = cmd_pso_decipher(&apdu, resp, resp_max);
2791 } else {
2792 result_len = apdu_sw(resp, SW_INCORRECT_P1P2);
2793 }
2794 break;
2795
2796 case INS_INTERNAL_AUTH:
2797 result_len = cmd_internal_authenticate(&apdu, resp, resp_max);
2798 break;
2799
2800 case INS_MSE:
2801 result_len = cmd_manage_security_env(&apdu, resp, resp_max);
2802 break;
2803
2805 result_len = cmd_generate_keypair(&apdu, resp, resp_max);
2806 break;
2807
2808 case INS_GET_CHALLENGE: {
2809 uint8_t challenge[255];
2810 size_t len = apdu.le > 0 ? apdu.le : 8;
2811 if (len > sizeof(challenge)) len = sizeof(challenge);
2812 se_random_fill(challenge, len);
2813 result_len = apdu_build_response(resp, resp_max, challenge, len, SW_OK);
2814 break;
2815 }
2816
2817 case INS_GET_RESPONSE:
2818 // GET RESPONSE is handled before chaining is applied — it owns
2819 // the chained buffer directly.
2820 return cmd_get_response(&apdu, resp, resp_max);
2821
2822 case INS_TERMINATE:
2823 result_len = cmd_terminate_df(&apdu, resp, resp_max);
2824 break;
2825
2826 case INS_ACTIVATE:
2827 result_len = cmd_activate_file(&apdu, resp, resp_max);
2828 break;
2829
2830 default:
2831 LOG_W(TAG, "Unknown instruction: 0x%02X", apdu.ins);
2832 return apdu_sw(resp, SW_INS_NOT_SUPPORTED);
2833 }
2834
2835 return apply_response_chaining(apdu.le, resp, resp_max, result_len);
2836}
static const char * TAG
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.
Definition algo_attr.cpp:53
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).
Definition algo_attr.h:56
@ ALGO_ATTR_ROLE_AUT
Definition algo_attr.h:59
@ ALGO_ATTR_ROLE_DEC
Definition algo_attr.h:58
@ ALGO_ATTR_ROLE_SIG
Definition algo_attr.h:57
@ ALGO_ATTR_CURVE_P256
Definition algo_attr.h:50
@ ALGO_ATTR_CURVE_ED25519
Definition algo_attr.h:51
@ ALGO_ATTR_OK
Definition algo_attr.h:78
@ ALGO_ATTR_ID_ECDH
Definition algo_attr.h:42
#define INS_TERMINATE
Definition apdu.h:34
#define INS_GET_RESPONSE
Definition apdu.h:33
#define INS_PUT_DATA
Definition apdu.h:24
#define INS_PSO
Definition apdu.h:29
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.
Definition apdu.cpp:17
#define INS_VERIFY
Definition apdu.h:26
#define INS_GET_CHALLENGE
Definition apdu.h:32
#define INS_RESET_RETRY
Definition apdu.h:28
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.
Definition apdu.cpp:99
#define INS_CHANGE_PIN
Definition apdu.h:27
#define INS_GENERATE_KEYPAIR
Definition apdu.h:31
static size_t apdu_sw(uint8_t *buf, uint16_t sw)
Definition apdu.h:75
#define INS_INTERNAL_AUTH
Definition apdu.h:30
#define INS_GET_DATA
Definition apdu.h:23
#define INS_PUT_DATA_ODD
Definition apdu.h:25
#define INS_MSE
Definition apdu.h:37
#define INS_ACTIVATE
Definition apdu.h:35
#define INS_SELECT
Definition apdu.h:22
struct __attribute__((packed))
Definition ccid.h:65
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
#define LOG_D(tag, fmt,...)
Definition cdc_log.h:148
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145
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.
Definition constants.h:56
#define P256_PUBKEY_SIZE
P-256 uncompressed public key size: 0x04 || X(32) || Y(32).
Definition constants.h:42
#define PW3_CODE
PW3 reference (Admin PIN).
Definition constants.h:78
#define PW1_CODE_1
PW1 reference for signature operations (User PIN).
Definition constants.h:72
#define PW1_CODE_2
PW1 reference for non-signature operations (User PIN, alt).
Definition constants.h:75
#define SHA256_DIGEST_SIZE
SHA-256 digest output size in bytes (FIPS 180-4).
Definition constants.h:37
#define OPENPGP_FINGERPRINT_SIZE
OpenPGP v4 fingerprint size (SHA-1 digest length, in bytes).
Definition constants.h:34
#define P256_ECDH_SECRET_SIZE
P-256 ECDH shared secret size in bytes.
Definition constants.h:48
#define P256_PRIVKEY_SIZE
P-256 private key (scalar) size in bytes.
Definition constants.h:45
bool ecdh_p256_generate_keypair(uint8_t *privkey_out, uint8_t *pubkey_out)
Definition ecdh.cpp:153
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.
Definition ecdh.cpp:63
bool ecdh_p256_derive_pubkey(const uint8_t *privkey, uint8_t *pubkey_out)
Definition ecdh.cpp:214
#define CDC_CURVE_ED25519
Definition fido2.h:23
#define CDC_CURVE_P256
Definition fido2.h:24
uint8_t curve
#define NVS_NAMESPACE
bool gpg_init(void)
Initializes the GPG module bookkeeping.
Definition gpg.cpp:86
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.
Definition openpgp.cpp:890
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.
Definition openpgp.cpp:2483
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.
Definition openpgp.cpp:470
static uint8_t fingerprint_dec[OPENPGP_FINGERPRINT_SIZE]
Definition openpgp.cpp:338
static int cmd_get_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU GET DATA command processing.
Definition openpgp.cpp:1016
static int cmd_manage_security_env(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU MANAGE SECURITY ENVIRONMENT (INS 0x22).
Definition openpgp.cpp:2097
static size_t g_resp_remaining
Definition openpgp.cpp:261
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...
Definition openpgp.cpp:653
static const uint8_t ALGO_ATTR_P256_ECDH[]
Algorithm attributes for P-256 ECDH (decryption role).
Definition openpgp.cpp:401
static uint8_t g_chain_p2
Definition openpgp.cpp:278
static void load_state_from_nvs(void)
Loads persistent OpenPGP runtime state from NVS.
Definition openpgp.cpp:698
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.
Definition openpgp.cpp:1650
static uint16_t generate_dec_key(uint8_t *pubkey_out)
Generates a software ECDH P-256 key pair for the DEC slot.
Definition openpgp.cpp:2382
uint32_t openpgp_get_gen_time(uint8_t key_type)
Returns the stored Unix timestamp of key generation, or 0 when unset.
Definition openpgp.cpp:922
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.
Definition openpgp.cpp:1693
static char cardholder_lang[8]
Definition openpgp.cpp:359
static const uint8_t ALGO_ATTR_P256_ECDSA[]
Algorithm attributes for P-256 ECDSA (signature/authentication roles).
Definition openpgp.cpp:391
static int cmd_generate_keypair(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU GENERATE ASYMMETRIC KEY PAIR.
Definition openpgp.cpp:2507
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.
Definition openpgp.cpp:124
static uint8_t fingerprint_aut[OPENPGP_FINGERPRINT_SIZE]
Definition openpgp.cpp:339
static int build_do_app_related(uint8_t *buf, size_t buf_max)
Builds OpenPGP DO 0x6E (Application Related Data).
Definition openpgp.cpp:524
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.
Definition openpgp.cpp:109
static int cmd_select(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU SELECT command processing.
Definition openpgp.cpp:991
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.
Definition openpgp.cpp:1784
static constexpr size_t RC_SALT_SIZE
Definition openpgp.cpp:183
static int cmd_pso_decipher(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Definition openpgp.cpp:1980
int openpgp_process_apdu(const uint8_t *cmd, size_t cmd_len, uint8_t *resp, size_t resp_max)
Definition openpgp.cpp:2683
put_data_kind_t
Storage kind for PUT DATA descriptor entries.
Definition openpgp.cpp:1202
@ PUT_KIND_BLOB_FIXED
Definition openpgp.cpp:1203
@ PUT_KIND_STRING_BOUNDED
Definition openpgp.cpp:1204
static size_t g_resp_pos
Definition openpgp.cpp:262
static cdc::hal::ISecureElement * get_se()
Returns secure-element instance used by OpenPGP backend.
Definition openpgp.cpp:41
static uint8_t g_chain_buffer[4096]
Command-chaining accumulator (ISO 7816-4 §5.1.1).
Definition openpgp.cpp:273
pin_slot_t
PIN slot identifier used by PIN helper routines.
Definition openpgp.cpp:1637
@ PIN_SLOT_PW1
Definition openpgp.cpp:1638
@ PIN_SLOT_PW3
Definition openpgp.cpp:1639
static uint8_t g_resp_buffer[4096]
Buffered remainder of an APDU response that did not fit into the caller-supplied Le window....
Definition openpgp.cpp:260
static uint8_t s_rc_hash[RC_HASH_SIZE]
Definition openpgp.cpp:187
static constexpr uint8_t ATTESTATION_ECC_SLOT
Definition openpgp.cpp:646
static void init_aid_from_mac(void)
Initializes the OpenPGP AID serial section from the ESP32 MAC address.
Definition openpgp.cpp:833
bool openpgp_set_key_fingerprint(uint8_t key_type, const uint8_t *fingerprint, uint32_t gen_time)
Definition openpgp.cpp:949
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.
Definition openpgp.cpp:1749
static int cmd_verify(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU VERIFY command for PIN verification.
Definition openpgp.cpp:1566
static uint8_t s_rc_len
Definition openpgp.cpp:188
key_type_t
Builders for OpenPGP application-related data objects.
Definition openpgp.cpp:489
@ KEY_TYPE_SIG
Definition openpgp.cpp:490
@ KEY_TYPE_DEC
Definition openpgp.cpp:491
@ KEY_TYPE_AUT
Definition openpgp.cpp:492
static void se_random_fill(uint8_t *buf, size_t len)
Fills buffer with secure random bytes, with ESP fallback.
Definition openpgp.cpp:135
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...
Definition openpgp.cpp:913
#define NVS_STATE_KEY
Definition openpgp.cpp:297
static uint8_t gen_time_aut[4]
Definition openpgp.cpp:346
static constexpr size_t OPENPGP_STATE_SIG_SIZE
Definition openpgp.cpp:647
static uint16_t generate_hardware_key(uint8_t ecc_slot, uint8_t curve)
Generates a hardware ECC key pair in the TROPIC01 secure element.
Definition openpgp.cpp:2401
static void update_generation_timestamp(uint8_t key_ref)
Updates and persists the generation timestamp for a key role.
Definition openpgp.cpp:2414
static bool se_ecc_key_generate(uint8_t slot, uint8_t curve)
Generates ECC key material in secure element slot.
Definition openpgp.cpp:74
bool openpgp_set_cardholder_name(const char *name)
Sets the cardholder name (OpenPGP DO 0x5B) and persists state.
Definition openpgp.cpp:936
static uint8_t ca_fp_3[OPENPGP_FINGERPRINT_SIZE]
Definition openpgp.cpp:353
static uint8_t s_rc_retries
Definition openpgp.cpp:189
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.
Definition openpgp.cpp:2621
static constexpr uint8_t OPENPGP_NVS_SCHEMA_V2
Definition openpgp.cpp:332
static bool pw3_verified
Definition openpgp.cpp:168
static bool pw1_verified
Definition openpgp.cpp:167
static int cmd_reset_retry_counter(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU RESET RETRY COUNTER (INS 0x2C).
Definition openpgp.cpp:2265
static char cardholder_url[64]
Definition openpgp.cpp:361
static uint8_t selected_curve_aut
Definition openpgp.cpp:238
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.
Definition openpgp.cpp:501
static uint8_t ca_fp_1[OPENPGP_FINGERPRINT_SIZE]
Optional CA fingerprints for trust-chain metadata.
Definition openpgp.cpp:351
static void chain_reset(void)
Definition openpgp.cpp:280
static size_t tlv_write_len(uint8_t *buf, size_t len)
Writes a TLV length field using DER length encoding.
Definition openpgp.cpp:445
static uint8_t gen_time_sig[4]
Key-generation timestamps (4-byte big-endian Unix time each).
Definition openpgp.cpp:344
static constexpr size_t RC_HASH_SIZE
Definition openpgp.cpp:184
bool openpgp_init(void)
Definition openpgp.cpp:863
#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 ...
Definition openpgp.cpp:182
uint32_t openpgp_get_sig_count(void)
Definition openpgp.cpp:886
static bool g_chain_active
Definition openpgp.cpp:275
static uint8_t get_ecc_slot_for_key_ref(uint8_t key_ref)
Returns ECC slot mapping for an OpenPGP key reference.
Definition openpgp.cpp:2346
static key_type_t get_key_type_for_ref(uint8_t key_ref)
Maps an OpenPGP key reference to an internal key type.
Definition openpgp.cpp:2364
static size_t tlv_write_tag(uint8_t *buf, uint16_t tag)
TLV builder helper functions.
Definition openpgp.cpp:429
static uint8_t s_rc_salt[RC_SALT_SIZE]
Definition openpgp.cpp:186
bool openpgp_has_any_key(void)
Reports whether any of the SIG / DEC / AUT roles has a non-zero fingerprint configured....
Definition openpgp.cpp:907
bool openpgp_is_selected(void)
Definition openpgp.cpp:882
static int put_data_algo_attr(uint16_t tag, const apdu_t *apdu, uint8_t *resp)
Handles APDU PUT DATA command processing.
Definition openpgp.cpp:1307
static bool app_selected
ATR is defined in ccid.cpp and accessed via ccid_get_atr().
Definition openpgp.cpp:166
static int cmd_pso_cds(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU PSO: COMPUTE DIGITAL SIGNATURE.
Definition openpgp.cpp:1847
static void save_state_to_nvs(void)
Persists OpenPGP runtime state to NVS.
Definition openpgp.cpp:765
static constexpr size_t RC_KDF_TOTAL_BYTES
Definition openpgp.cpp:185
static size_t g_chain_len
Definition openpgp.cpp:274
static int build_do_cardholder(uint8_t *buf, size_t buf_max)
Builds OpenPGP DO 0x65 (Cardholder Related Data).
Definition openpgp.cpp:618
void openpgp_factory_reset(void)
Definition openpgp.cpp:2218
static const put_data_desc_t * find_put_data_desc(uint16_t tag)
Returns descriptor for an OpenPGP PUT DATA tag.
Definition openpgp.cpp:1226
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.
Definition openpgp.cpp:1928
static int cmd_internal_authenticate(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU INTERNAL AUTHENTICATE (INS 0x88).
Definition openpgp.cpp:2137
static char cardholder_name[40]
Cardholder profile data stored in NVS.
Definition openpgp.cpp:358
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.
Definition openpgp.cpp:1498
static int cmd_activate_file(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU ACTIVATE FILE (INS 0x44).
Definition openpgp.cpp:2205
static const uint8_t EXT_CAPABILITIES[]
Extended capabilities object per OpenPGP 3.4.1 section 4.2.1.
Definition openpgp.cpp:409
static char cardholder_login[32]
Definition openpgp.cpp:362
static char s_session_pin[OPENPGP_PIN_MAX_LEN+1]
Session PIN cache for DEC key decryption (temporary after VERIFY for PSO:DECIPHER).
Definition openpgp.cpp:291
static uint8_t ca_fp_2[OPENPGP_FINGERPRINT_SIZE]
Definition openpgp.cpp:352
static uint8_t cardholder_sex
Definition openpgp.cpp:360
static int cmd_put_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Definition openpgp.cpp:1359
static uint8_t gen_time_dec[4]
Definition openpgp.cpp:345
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.
Definition openpgp.cpp:53
static uint8_t s_openpgp_aid[16]
OpenPGP Application ID (RID + PIX), initialized dynamically.
Definition openpgp.cpp:149
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.
Definition openpgp.cpp:1264
bool(* pin_change_fn_t)(const char *pin)
Type alias for PIN change callbacks.
Definition openpgp.cpp:1732
static int cmd_terminate_df(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU TERMINATE DF (INS 0xE6).
Definition openpgp.cpp:2179
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.
Definition openpgp.cpp:2445
static uint8_t g_chain_ins
Definition openpgp.cpp:276
static bool peek_verify_pin(pin_slot_t slot, const char *pin)
Compares a candidate PIN against the stored hash without touching retry counters.
Definition openpgp.cpp:1705
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 ...
Definition openpgp.cpp:237
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...
Definition openpgp.cpp:195
static const uint8_t ALGO_ATTR_ED25519[]
Algorithm attributes for Ed25519 (EdDSA with curve25519).
Definition openpgp.cpp:381
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.
Definition openpgp.cpp:1445
static uint8_t g_chain_p1
Definition openpgp.cpp:277
static uint8_t fingerprint_sig[OPENPGP_FINGERPRINT_SIZE]
Data object storage buffers (fingerprints and related metadata).
Definition openpgp.cpp:337
static bool card_terminated
Card lifecycle state per OpenPGP 3.4.1 §7.2.18.
Definition openpgp.cpp:248
static bool fp_is_set(const uint8_t fp[OPENPGP_FINGERPRINT_SIZE])
Definition openpgp.cpp:900
static const uint8_t HIST_BYTES[]
Historical bytes used in OpenPGP ATR-related data objects.
Definition openpgp.cpp:367
static uint32_t sig_count
Definition openpgp.cpp:169
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.
Definition openpgp.cpp:2651
const uint8_t * OPENPGP_AID
Definition openpgp.cpp:156
#define SW_CLA_NOT_SUPPORTED
Definition openpgp.h:102
#define SW_AUTH_METHOD_BLOCKED
Definition openpgp.h:94
#define DO_GEN_TIME_SIG
Definition openpgp.h:71
#define ALGO_EDDSA
Definition openpgp.h:32
#define DO_UIF_SIG
Definition openpgp.h:80
#define DO_CA_FP_1
Definition openpgp.h:59
#define SW_INCORRECT_P1P2
Definition openpgp.h:98
#define SW_REFERENCED_DATA_NOT_FOUND
Definition openpgp.h:99
#define DO_ALGO_DEC
Definition openpgp.h:52
#define SW_INS_NOT_SUPPORTED
Definition openpgp.h:101
#define DO_RC
Definition openpgp.h:55
#define DO_UIF_AUT
Definition openpgp.h:82
#define KEY_AUT
Definition openpgp.h:37
#define DO_FP_SIG
Definition openpgp.h:56
#define DO_FP_AUT
Definition openpgp.h:58
#define DO_GEN_TIME_AUT
Definition openpgp.h:73
#define DO_URL
Definition openpgp.h:75
#define DO_SEC_TPL
Definition openpgp.h:84
#define DO_LANG_PREF
Definition openpgp.h:78
#define DO_APP_RELATED
Definition openpgp.h:48
#define SW_CONDITIONS_NOT_SATISFIED
Definition openpgp.h:95
#define DO_KDF
Definition openpgp.h:85
#define DO_KEY_INFO
Definition openpgp.h:83
#define KEY_SIG
Definition openpgp.h:35
#define DO_AID
Definition openpgp.h:45
#define OPENPGP_PW1_MIN_LEN
Definition openpgp.h:40
#define SW_FILE_NOT_FOUND
Definition openpgp.h:97
#define DO_HIST_BYTES
Definition openpgp.h:46
#define OPENPGP_PIN_MAX_LEN
Definition openpgp.h:42
#define SW_OK
Definition openpgp.h:90
#define DO_SIG_COUNT
Definition openpgp.h:74
#define DO_EXT_CAP
Definition openpgp.h:50
#define ALGO_ECDH
Definition openpgp.h:30
#define DO_CARDHOLDER
Definition openpgp.h:47
#define SW_WRONG_DATA
Definition openpgp.h:96
#define DO_FP_DEC
Definition openpgp.h:57
#define DO_CA_FP_2
Definition openpgp.h:60
#define ALGO_ECDSA
Definition openpgp.h:31
const uint8_t OPENPGP_AID_LEN
Definition openpgp.cpp:157
#define OPENPGP_PW3_MIN_LEN
Definition openpgp.h:41
#define DO_SEX
Definition openpgp.h:79
#define SW_FILE_TERMINATED
Definition openpgp.h:91
#define DO_AES_KEY
Definition openpgp.h:86
#define SW_SECURITY_NOT_SATISFIED
Definition openpgp.h:93
#define DO_CA_FP_3
Definition openpgp.h:61
#define SW_WRONG_LENGTH
Definition openpgp.h:92
#define DO_LOGIN
Definition openpgp.h:76
#define DO_ALGO_AUT
Definition openpgp.h:53
#define DO_ALGO_SIG
Definition openpgp.h:51
#define SW_UNKNOWN
Definition openpgp.h:103
#define DO_UIF_DEC
Definition openpgp.h:81
#define KEY_DEC
Definition openpgp.h:36
#define DO_PW_STATUS
Definition openpgp.h:54
#define DO_NAME
Definition openpgp.h:77
#define DO_GEN_TIME_DEC
Definition openpgp.h:72
#define DO_CARDHOLDER_CERT
Definition openpgp.h:87
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.
Definition algo_attr.h:63
algo_attr_curve_t curve
Definition algo_attr.h:67
uint8_t algo_id
Definition algo_attr.h:64
Descriptor entry for table-driven PUT DATA processing.
Definition openpgp.cpp:1213
const char * log_label
Definition openpgp.cpp:1218
put_data_kind_t kind
Definition openpgp.cpp:1217