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"
17#include "rsa.h"
18#include "mod_gpg/openpgp/kdf.h"
20#include "cdc_core/PinManager.h"
22#include <mbedtls/platform_util.h>
23#include <mbedtls/sha256.h>
24#include <mbedtls/aes.h>
25#include <mbedtls/ecp.h>
26#include <mbedtls/ecdsa.h>
27#include <mbedtls/bignum.h>
28#include <esp_attr.h>
29#include <string.h>
30#include <time.h>
31#include "cdc_log.h"
32#include <esp_mac.h> // For esp_efuse_mac_get_default()
33#include <nvs_flash.h>
34#include <nvs.h>
35#include <esp_random.h>
36
37static const char *TAG = "OpenPGP";
38
46
55static bool se_ecc_key_read(uint8_t slot, uint8_t* pubkey, size_t max_len, uint8_t* curve_out) {
56 auto* se = get_se();
57 if (!se || !pubkey) return false;
59 auto res = se->eccGetPublicKey(slot, pubkey, &curve);
60 if (res != cdc::hal::SeResult::OK) return false;
61 if (curve_out) {
63 }
65 return max_len >= ED25519_PUBKEY_SIZE;
66 }
67 return max_len >= P256_PUBKEY_SIZE;
68}
69
76static bool se_ecc_key_generate(uint8_t slot, uint8_t curve) {
77 auto* se = get_se();
78 if (!se) {
79 LOG_E(TAG, "se_ecc_key_generate: SE not available (slot=%u)", slot);
80 return false;
81 }
84 // Per TROPIC01: lt_ecc_key_generate fails with SLOT_OCCUPIED if the slot
85 // already holds material. A previous (incomplete) generation, or a
86 // GPG_RESET that did not propagate to the SE, leaves the slot used and
87 // the next attempt returns SW=6F00 to the host. Pre-wipe defensively.
88 cdc::hal::SeResult res = se->eccGenerate(slot, c);
89 if (res != cdc::hal::SeResult::OK) {
90 LOG_W(TAG, "se_ecc_key_generate: slot %u initial fail (SeResult=%d), deleting and retrying",
91 slot, static_cast<int>(res));
92 se->eccDelete(slot);
93 res = se->eccGenerate(slot, c);
94 }
95 if (res != cdc::hal::SeResult::OK) {
96 LOG_E(TAG, "se_ecc_key_generate(slot=%u curve=%u) failed: SeResult=%d",
97 slot, curve, static_cast<int>(res));
98 return false;
99 }
100 return true;
101}
102
111static bool se_ecdsa_sign(uint8_t slot, const uint8_t* hash, size_t hash_len, uint8_t* sig) {
112 auto* se = get_se();
113 if (!se || !hash || !sig) return false;
114 size_t sig_len = 64;
115 return se->ecdsaSign(slot, hash, hash_len, sig, &sig_len) == cdc::hal::SeResult::OK;
116}
117
126static bool se_eddsa_sign(uint8_t slot, const uint8_t* msg, size_t msg_len, uint8_t* sig) {
127 auto* se = get_se();
128 if (!se || !msg || !sig) return false;
129 return se->eddsaSign(slot, msg, msg_len, sig) == cdc::hal::SeResult::OK;
130}
131
137static void se_random_fill(uint8_t* buf, size_t len) {
138 auto* se = get_se();
139 if (se && se->getRandom(buf, static_cast<uint16_t>(len))) {
140 return;
141 }
142 esp_fill_random(buf, len);
143}
144
151static uint8_t s_openpgp_aid[16] = {
152 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01, // RID + Application (OpenPGP)
153 0x03, 0x04, // Version 3.4
154 0x00, 0x00, // Manufacturer (set in init)
155 0x00, 0x00, 0x00, 0x00, // Serial number (set in init from MAC)
156 0x00, 0x00 // RFU
157};
158const uint8_t* OPENPGP_AID = s_openpgp_aid;
159const uint8_t OPENPGP_AID_LEN = sizeof(s_openpgp_aid);
160
164
168static bool app_selected = false;
169static bool pw1_verified = false;
170static bool pw3_verified = false;
171static uint32_t sig_count = 0;
172
184#define OPENPGP_RC_MIN_LEN 8
185static constexpr size_t RC_SALT_SIZE = 16;
186static constexpr size_t RC_HASH_SIZE = 32;
187static constexpr size_t RC_KDF_TOTAL_BYTES = 100000;
188static uint8_t s_rc_salt[RC_SALT_SIZE] = {0};
189static uint8_t s_rc_hash[RC_HASH_SIZE] = {0};
190static uint8_t s_rc_len = 0;
191static uint8_t s_rc_retries = 3;
192
197static bool compute_rc_hash(const uint8_t* rc, size_t rc_len,
198 const uint8_t* salt, uint8_t* hash_out) {
199 if (!rc || !salt || !hash_out || rc_len == 0 || rc_len > OPENPGP_PIN_MAX_LEN) {
200 return false;
201 }
202 uint8_t buffer[RC_SALT_SIZE + OPENPGP_PIN_MAX_LEN];
203 memcpy(buffer, salt, RC_SALT_SIZE);
204 memcpy(buffer + RC_SALT_SIZE, rc, rc_len);
205 const size_t combined = RC_SALT_SIZE + rc_len;
206
207 mbedtls_sha256_context ctx;
208 mbedtls_sha256_init(&ctx);
209 if (mbedtls_sha256_starts(&ctx, 0) != 0) {
210 mbedtls_sha256_free(&ctx);
211 mbedtls_platform_zeroize(buffer, sizeof(buffer));
212 return false;
213 }
214 size_t processed = 0;
215 while (processed < RC_KDF_TOTAL_BYTES) {
216 const size_t chunk = (RC_KDF_TOTAL_BYTES - processed < combined)
217 ? (RC_KDF_TOTAL_BYTES - processed)
218 : combined;
219 if (mbedtls_sha256_update(&ctx, buffer, chunk) != 0) {
220 mbedtls_sha256_free(&ctx);
221 mbedtls_platform_zeroize(buffer, sizeof(buffer));
222 return false;
223 }
224 processed += chunk;
225 }
226 mbedtls_sha256_finish(&ctx, hash_out);
227 mbedtls_sha256_free(&ctx);
228 mbedtls_platform_zeroize(buffer, sizeof(buffer));
229 return true;
230}
231
241
250static bool role_is_rsa[3] = {false, false, false};
251static uint16_t role_rsa_n_bits[3] = {0, 0, 0};
252static uint16_t role_rsa_e_bits[3] = {0, 0, 0};
253static uint8_t role_rsa_fmt[3] = {0, 0, 0};
254
261static bool kdf_active = false;
262static uint8_t kdf_pin_len = 0; // 32 (SHA-256) or 64 (SHA-512)
263static uint8_t kdf_do_bytes[124] = {};
264static uint8_t kdf_do_len = 0;
265
274static bool card_terminated = false;
275
286EXT_RAM_BSS_ATTR static uint8_t g_resp_buffer[4096];
287static size_t g_resp_remaining = 0;
288static size_t g_resp_pos = 0;
289
299EXT_RAM_BSS_ATTR static uint8_t g_chain_buffer[4096];
300static size_t g_chain_len = 0;
301static bool g_chain_active = false;
302static uint8_t g_chain_ins = 0;
303static uint8_t g_chain_p1 = 0;
304static uint8_t g_chain_p2 = 0;
305
306static void chain_reset(void) {
307 g_chain_len = 0;
308 g_chain_active = false;
309 g_chain_ins = 0;
310 g_chain_p1 = 0;
311 g_chain_p2 = 0;
312}
313
317static char s_session_pin[OPENPGP_PIN_MAX_LEN + 1] = {};
318
322#define NVS_NAMESPACE "openpgp"
323#define NVS_STATE_KEY "state"
324
332struct __attribute__((packed)) OpenpgpNvsState {
333 uint8_t schema_version;
334 uint8_t card_terminated;
335 uint8_t selected_curve_sig;
336 uint8_t selected_curve_aut;
337 uint8_t rc_len;
338 uint8_t rc_retries;
339 uint8_t rc_salt[RC_SALT_SIZE];
340 uint8_t rc_hash[RC_HASH_SIZE];
341 uint32_t sig_count;
348 uint8_t gen_time_sig[4];
349 uint8_t gen_time_dec[4];
350 uint8_t gen_time_aut[4];
351 uint8_t cardholder_sex;
352 char cardholder_name[40];
353 char cardholder_lang[8];
354 char cardholder_url[64];
355 char cardholder_login[32];
356 uint8_t role_is_rsa[3];
357 uint16_t role_rsa_n_bits[3];
358 uint16_t role_rsa_e_bits[3];
359 uint8_t role_rsa_fmt[3];
360 uint8_t kdf_active;
361 uint8_t kdf_pin_len;
362 uint8_t kdf_do_len;
363 uint8_t kdf_do_bytes[124];
364};
365
366static constexpr uint8_t OPENPGP_NVS_SCHEMA_V3 = 3;
367
374
378static uint8_t gen_time_sig[4] = {0};
379static uint8_t gen_time_dec[4] = {0};
380static uint8_t gen_time_aut[4] = {0};
381
385static uint8_t ca_fp_1[OPENPGP_FINGERPRINT_SIZE] = {0};
386static uint8_t ca_fp_2[OPENPGP_FINGERPRINT_SIZE] = {0};
387static uint8_t ca_fp_3[OPENPGP_FINGERPRINT_SIZE] = {0};
388
392static char cardholder_name[40] = {0}; // "Surname<<Firstname"
393static char cardholder_lang[8] = "en"; // ISO 639-1 language
394static uint8_t cardholder_sex = 0x39; // '9' = not specified
395static char cardholder_url[64] = {0}; // URL for public key retrieval
396static char cardholder_login[32] = {0}; // Login data
397
401static const uint8_t HIST_BYTES[] = {
402 0x00, // Category indicator: card has no indication of services
403 0x31, // Card capabilities (card can process T=1)
404 0xC5, // Tag: card issuer data follows
405 0x73, 0xC0, 0x01, 0x80, // Card issuer proprietary
406 0x05, // Tag: card capabilities
407 0x90, 0x00 // Card status: OK
408};
409
415static const uint8_t ALGO_ATTR_ED25519[] = {
416 ALGO_EDDSA, // Algorithm: EdDSA (0x16)
417 0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01 // OID 1.3.6.1.4.1.11591.15.1 (ed25519)
418};
419
425static const uint8_t ALGO_ATTR_P256_ECDSA[] = {
426 ALGO_ECDSA, // Algorithm: ECDSA (0x13)
427 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 // OID 1.2.840.10045.3.1.7 (secp256r1)
428};
429
435static const uint8_t ALGO_ATTR_P256_ECDH[] = {
436 ALGO_ECDH, // Algorithm: ECDH (0x12)
437 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 // OID 1.2.840.10045.3.1.7 (secp256r1)
438};
439
443static const uint8_t EXT_CAPABILITIES[] = {
444 0x7D,
445 0x00, // SM Algorithm: none
446 0x00, 0x80, // Max GET CHALLENGE length: 128 bytes
447 0x08, 0x00, // Max Cardholder Certificate length: 2048 bytes
448 0x01, 0x00, // Max special DO length: 256 bytes
449 0x00, // PIN block 2 format not supported
450 0x00, // MSE for key selection not supported
451};
452
456
463static size_t tlv_write_tag(uint8_t *buf, uint16_t tag) {
464 if (tag > 0xFF) {
465 buf[0] = (tag >> 8) & 0xFF;
466 buf[1] = tag & 0xFF;
467 return 2;
468 }
469 buf[0] = tag & 0xFF;
470 return 1;
471}
472
479static size_t tlv_write_len(uint8_t *buf, size_t len) {
480 if (len < 128) {
481 buf[0] = len;
482 return 1;
483 } else if (len < 256) {
484 buf[0] = 0x81;
485 buf[1] = len;
486 return 2;
487 } else {
488 buf[0] = 0x82;
489 buf[1] = (len >> 8) & 0xFF;
490 buf[2] = len & 0xFF;
491 return 3;
492 }
493}
494
504static size_t tlv_build(uint8_t *buf, size_t buf_max, uint16_t tag,
505 const uint8_t *value, size_t value_len) {
506 size_t pos = 0;
507 pos += tlv_write_tag(buf + pos, tag);
508 pos += tlv_write_len(buf + pos, value_len);
509 if (value && value_len > 0) {
510 memcpy(buf + pos, value, value_len);
511 pos += value_len;
512 }
513 return pos;
514}
515
519
523typedef enum {
524 KEY_TYPE_SIG = 0, // Signature (ECDSA/EdDSA)
525 KEY_TYPE_DEC = 1, // Decryption (ECDH)
526 KEY_TYPE_AUT = 2 // Authentication (ECDSA/EdDSA)
527} key_type_t;
528
535static const uint8_t* get_algo_attr(key_type_t key_type, size_t *len) {
536 const int r = static_cast<int>(key_type); // SIG=0, DEC=1, AUT=2
537 // RSA roles advertise the RSA attribute: 01 || N-bits || E-bits || import-fmt.
538 if (role_is_rsa[r]) {
539 static uint8_t s_rsa_attr[6];
540 const uint16_t eb = role_rsa_e_bits[r] ? role_rsa_e_bits[r] : 32;
541 s_rsa_attr[0] = ALGO_RSA;
542 s_rsa_attr[1] = static_cast<uint8_t>((role_rsa_n_bits[r] >> 8) & 0xFF);
543 s_rsa_attr[2] = static_cast<uint8_t>(role_rsa_n_bits[r] & 0xFF);
544 s_rsa_attr[3] = static_cast<uint8_t>((eb >> 8) & 0xFF);
545 s_rsa_attr[4] = static_cast<uint8_t>(eb & 0xFF);
546 s_rsa_attr[5] = role_rsa_fmt[r];
547 *len = sizeof(s_rsa_attr);
548 return s_rsa_attr;
549 }
550 // ECC: DEC is P-256 ECDH (software path); SIG / AUT follow the configured
551 // curve which PUT DATA C1 / C3 may override.
552 if (key_type == KEY_TYPE_DEC) {
553 *len = sizeof(ALGO_ATTR_P256_ECDH);
554 return ALGO_ATTR_P256_ECDH;
555 }
556 const uint8_t curve = (key_type == KEY_TYPE_AUT) ? selected_curve_aut
558 if (curve == CDC_CURVE_P256) {
559 *len = sizeof(ALGO_ATTR_P256_ECDSA);
561 }
562 *len = sizeof(ALGO_ATTR_ED25519);
563 return ALGO_ATTR_ED25519;
564}
565
572static int build_do_app_related(uint8_t *buf, size_t buf_max) {
573 uint8_t inner[512];
574 size_t inner_len = 0;
575
576 // 4F: AID
577 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
579
580 // 5F52: Historical bytes
581 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
583
584 // 73: Discretionary data objects (nested)
585 uint8_t discret[384];
586 size_t discret_len = 0;
587
588 // C0: Extended capabilities
589 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
591
592 // C1: Algorithm attributes - Signature (ECDSA/EdDSA)
593 size_t algo_len;
594 const uint8_t *algo = get_algo_attr(KEY_TYPE_SIG, &algo_len);
595 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
596 DO_ALGO_SIG, algo, algo_len);
597
598 // C2: Algorithm attributes - Decryption (ECDH)
599 algo = get_algo_attr(KEY_TYPE_DEC, &algo_len);
600 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
601 DO_ALGO_DEC, algo, algo_len);
602
603 // C3: Algorithm attributes - Authentication (ECDSA/EdDSA)
604 algo = get_algo_attr(KEY_TYPE_AUT, &algo_len);
605 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
606 DO_ALGO_AUT, algo, algo_len);
607
608 // C4: PW Status Bytes (retries from TROPIC01 storage)
609 // Note: Max lengths limited for practical use on hardware keypad
610 uint8_t pw_status[7] = {
611 0x01, // PW1 valid for multiple signatures
612 OPENPGP_PIN_MAX_LEN, // Max length PW1 (practical limit)
613 OPENPGP_PIN_MAX_LEN, // Max length RC
614 OPENPGP_PIN_MAX_LEN, // Max length PW3
616 s_rc_len > 0 ? s_rc_retries : static_cast<uint8_t>(0),
618 };
619 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
620 DO_PW_STATUS, pw_status, sizeof(pw_status));
621
622 // C5: Fingerprints (3 * 20 bytes: SIG + DEC + AUT)
623 uint8_t fps[3 * OPENPGP_FINGERPRINT_SIZE];
627 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
628 0xC5, fps, sizeof(fps)); // 0xC5 = combined fingerprints (no separate constant)
629
630 // C6: CA Fingerprints (3 * 20 bytes)
631 uint8_t ca_fps[3 * OPENPGP_FINGERPRINT_SIZE];
635 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
636 0xC6, ca_fps, sizeof(ca_fps));
637
638 // CD: Generation dates (12 bytes: SIG + DEC + AUT)
639 uint8_t gen_times[12];
640 memcpy(gen_times, gen_time_sig, 4);
641 memcpy(gen_times + 4, gen_time_dec, 4);
642 memcpy(gen_times + 8, gen_time_aut, 4);
643 discret_len += tlv_build(discret + discret_len, sizeof(discret) - discret_len,
644 0xCD, gen_times, 12);
645
646 // Add discretionary DOs to inner
647 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
648 0x73, discret, discret_len);
649
650 // Build final 6E response
651 size_t total = 0;
652 total += tlv_write_tag(buf + total, 0x6E);
653 total += tlv_write_len(buf + total, inner_len);
654 memcpy(buf + total, inner, inner_len);
655 total += inner_len;
656
657 return total;
658}
659
666static int build_do_cardholder(uint8_t *buf, size_t buf_max) {
667 uint8_t inner[128];
668 size_t inner_len = 0;
669
670 // 5B: Name
671 size_t name_len = strlen(cardholder_name);
672 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
673 DO_NAME, (const uint8_t *)cardholder_name, name_len);
674
675 // 5F2D: Language preference
676 size_t lang_len = strlen(cardholder_lang);
677 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
678 DO_LANG_PREF, (const uint8_t *)cardholder_lang, lang_len);
679
680 // 5F35: Sex
681 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len,
683
684 // Build final 65 response
685 size_t total = 0;
686 total += tlv_write_tag(buf + total, DO_CARDHOLDER);
687 total += tlv_write_len(buf + total, inner_len);
688 memcpy(buf + total, inner, inner_len);
689 total += inner_len;
690
691 return total;
692}
693
694static constexpr uint8_t ATTESTATION_ECC_SLOT = 0;
695static constexpr size_t OPENPGP_STATE_SIG_SIZE = 64;
696
702 const uint8_t* payload, size_t payload_len,
703 const uint8_t* sig, size_t sig_len) {
704 if (!se || sig_len != OPENPGP_STATE_SIG_SIZE) return false;
705 uint8_t pub_raw[64];
708 return false;
709 }
710 if (curve != cdc::hal::EccCurve::P256) return false;
711
712 uint8_t pub_sec1[65];
713 pub_sec1[0] = 0x04;
714 memcpy(pub_sec1 + 1, pub_raw, 64);
715
716 uint8_t hash[32];
717 mbedtls_sha256(payload, payload_len, hash, 0);
718
719 mbedtls_ecp_group grp;
720 mbedtls_ecp_point Q;
721 mbedtls_mpi r, s;
722 mbedtls_ecp_group_init(&grp);
723 mbedtls_ecp_point_init(&Q);
724 mbedtls_mpi_init(&r);
725 mbedtls_mpi_init(&s);
726
727 bool ok = false;
728 do {
729 if (mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1) != 0) break;
730 if (mbedtls_ecp_point_read_binary(&grp, &Q, pub_sec1, sizeof(pub_sec1)) != 0) break;
731 if (mbedtls_mpi_read_binary(&r, sig + 0, 32) != 0) break;
732 if (mbedtls_mpi_read_binary(&s, sig + 32, 32) != 0) break;
733 ok = (mbedtls_ecdsa_verify(&grp, hash, sizeof(hash), &Q, &r, &s) == 0);
734 } while (0);
735
736 mbedtls_mpi_free(&r);
737 mbedtls_mpi_free(&s);
738 mbedtls_ecp_point_free(&Q);
739 mbedtls_ecp_group_free(&grp);
740 return ok;
741}
742
746static void load_state_from_nvs(void) {
747 nvs_handle_t nvs;
748 if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs) != ESP_OK) {
749 return;
750 }
751
752 constexpr size_t BLOB_SIZE = sizeof(OpenpgpNvsState) + OPENPGP_STATE_SIG_SIZE;
753 uint8_t blob[BLOB_SIZE];
754 size_t len = BLOB_SIZE;
755 esp_err_t err = nvs_get_blob(nvs, NVS_STATE_KEY, blob, &len);
756 nvs_close(nvs);
757
758 if (err != ESP_OK || len != BLOB_SIZE) {
759 return;
760 }
761
762 OpenpgpNvsState state = {};
763 memcpy(&state, blob, sizeof(state));
764 if (state.schema_version != OPENPGP_NVS_SCHEMA_V3) {
765 return;
766 }
767 if (!verify_state_signature(get_se(), blob, sizeof(state),
768 blob + sizeof(state), OPENPGP_STATE_SIG_SIZE)) {
769 LOG_W(TAG, "OpenPGP state signature invalid - re-initialising");
770 return;
771 }
772
773 card_terminated = state.card_terminated != 0;
774 if (state.selected_curve_sig == CDC_CURVE_P256 ||
775 state.selected_curve_sig == CDC_CURVE_ED25519) {
776 selected_curve_sig = state.selected_curve_sig;
777 }
778 if (state.selected_curve_aut == CDC_CURVE_P256 ||
779 state.selected_curve_aut == CDC_CURVE_ED25519) {
780 selected_curve_aut = state.selected_curve_aut;
781 }
782 if (state.rc_len > 0 && state.rc_len <= OPENPGP_PIN_MAX_LEN) {
783 memcpy(s_rc_salt, state.rc_salt, RC_SALT_SIZE);
784 memcpy(s_rc_hash, state.rc_hash, RC_HASH_SIZE);
785 s_rc_len = state.rc_len;
786 }
787 s_rc_retries = state.rc_retries;
788 sig_count = state.sig_count;
789 memcpy(fingerprint_sig, state.fingerprint_sig, OPENPGP_FINGERPRINT_SIZE);
790 memcpy(fingerprint_dec, state.fingerprint_dec, OPENPGP_FINGERPRINT_SIZE);
791 memcpy(fingerprint_aut, state.fingerprint_aut, OPENPGP_FINGERPRINT_SIZE);
792 memcpy(ca_fp_1, state.ca_fp_1, OPENPGP_FINGERPRINT_SIZE);
793 memcpy(ca_fp_2, state.ca_fp_2, OPENPGP_FINGERPRINT_SIZE);
794 memcpy(ca_fp_3, state.ca_fp_3, OPENPGP_FINGERPRINT_SIZE);
795 memcpy(gen_time_sig, state.gen_time_sig, 4);
796 memcpy(gen_time_dec, state.gen_time_dec, 4);
797 memcpy(gen_time_aut, state.gen_time_aut, 4);
798 cardholder_sex = state.cardholder_sex;
799 memcpy(cardholder_name, state.cardholder_name, sizeof(cardholder_name));
800 memcpy(cardholder_lang, state.cardholder_lang, sizeof(cardholder_lang));
801 memcpy(cardholder_url, state.cardholder_url, sizeof(cardholder_url));
802 memcpy(cardholder_login, state.cardholder_login, sizeof(cardholder_login));
803 cardholder_name[sizeof(cardholder_name) - 1] = '\0';
804 cardholder_lang[sizeof(cardholder_lang) - 1] = '\0';
805 cardholder_url[sizeof(cardholder_url) - 1] = '\0';
806 cardholder_login[sizeof(cardholder_login) - 1] = '\0';
807
808 for (int r = 0; r < 3; ++r) {
809 role_is_rsa[r] = state.role_is_rsa[r] != 0;
810 role_rsa_n_bits[r] = state.role_rsa_n_bits[r];
811 role_rsa_e_bits[r] = state.role_rsa_e_bits[r];
812 role_rsa_fmt[r] = state.role_rsa_fmt[r];
813 }
814 kdf_active = state.kdf_active != 0;
815 kdf_pin_len = state.kdf_pin_len;
816 kdf_do_len = (state.kdf_do_len <= sizeof(kdf_do_bytes)) ? state.kdf_do_len : 0;
817 memcpy(kdf_do_bytes, state.kdf_do_bytes, sizeof(kdf_do_bytes));
818}
819
824static void save_state_to_nvs(void) {
825 OpenpgpNvsState state = {};
826 state.schema_version = OPENPGP_NVS_SCHEMA_V3;
827 state.card_terminated = card_terminated ? 1 : 0;
828 state.selected_curve_sig = selected_curve_sig;
829 state.selected_curve_aut = selected_curve_aut;
830 state.rc_len = s_rc_len;
831 state.rc_retries = s_rc_retries;
832 if (s_rc_len > 0) {
833 memcpy(state.rc_salt, s_rc_salt, RC_SALT_SIZE);
834 memcpy(state.rc_hash, s_rc_hash, RC_HASH_SIZE);
835 }
836 state.sig_count = sig_count;
837 memcpy(state.fingerprint_sig, fingerprint_sig, OPENPGP_FINGERPRINT_SIZE);
838 memcpy(state.fingerprint_dec, fingerprint_dec, OPENPGP_FINGERPRINT_SIZE);
839 memcpy(state.fingerprint_aut, fingerprint_aut, OPENPGP_FINGERPRINT_SIZE);
840 memcpy(state.ca_fp_1, ca_fp_1, OPENPGP_FINGERPRINT_SIZE);
841 memcpy(state.ca_fp_2, ca_fp_2, OPENPGP_FINGERPRINT_SIZE);
842 memcpy(state.ca_fp_3, ca_fp_3, OPENPGP_FINGERPRINT_SIZE);
843 memcpy(state.gen_time_sig, gen_time_sig, 4);
844 memcpy(state.gen_time_dec, gen_time_dec, 4);
845 memcpy(state.gen_time_aut, gen_time_aut, 4);
846 state.cardholder_sex = cardholder_sex;
847 memcpy(state.cardholder_name, cardholder_name, sizeof(state.cardholder_name));
848 memcpy(state.cardholder_lang, cardholder_lang, sizeof(state.cardholder_lang));
849 memcpy(state.cardholder_url, cardholder_url, sizeof(state.cardholder_url));
850 memcpy(state.cardholder_login, cardholder_login, sizeof(state.cardholder_login));
851 for (int r = 0; r < 3; ++r) {
852 state.role_is_rsa[r] = role_is_rsa[r] ? 1 : 0;
853 state.role_rsa_n_bits[r] = role_rsa_n_bits[r];
854 state.role_rsa_e_bits[r] = role_rsa_e_bits[r];
855 state.role_rsa_fmt[r] = role_rsa_fmt[r];
856 }
857 state.kdf_active = kdf_active ? 1 : 0;
858 state.kdf_pin_len = kdf_pin_len;
859 state.kdf_do_len = kdf_do_len;
860 memcpy(state.kdf_do_bytes, kdf_do_bytes, sizeof(state.kdf_do_bytes));
861
862 constexpr size_t BLOB_SIZE = sizeof(OpenpgpNvsState) + OPENPGP_STATE_SIG_SIZE;
863 uint8_t blob[BLOB_SIZE];
864 memcpy(blob, &state, sizeof(state));
865
866 auto* se = get_se();
867 if (!se) {
868 LOG_E(TAG, "save_state: no SE");
869 return;
870 }
871 size_t sig_len = OPENPGP_STATE_SIG_SIZE;
872 cdc::hal::SeResult sign_res = se->ecdsaSign(ATTESTATION_ECC_SLOT,
873 blob, sizeof(state),
874 blob + sizeof(state), &sig_len);
875 if (sign_res != cdc::hal::SeResult::OK || sig_len != OPENPGP_STATE_SIG_SIZE) {
876 LOG_E(TAG, "save_state: attestation sign failed (%d)",
877 static_cast<int>(sign_res));
878 return;
879 }
880
881 nvs_handle_t nvs;
882 esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs);
883 if (err != ESP_OK) {
884 LOG_E(TAG, "save_state: nvs_open %s", esp_err_to_name(err));
885 return;
886 }
887
888 err = nvs_set_blob(nvs, NVS_STATE_KEY, blob, BLOB_SIZE);
889 if (err == ESP_OK) {
890 err = nvs_commit(nvs);
891 }
892 nvs_close(nvs);
893 if (err != ESP_OK) {
894 LOG_E(TAG, "save_state: %s", esp_err_to_name(err));
895 }
896}
897
901#define NVS_CERT_KEY "cardcert"
902static constexpr size_t CARDHOLDER_CERT_MAX = 2048;
903
908static bool save_cardholder_cert(const uint8_t* data, size_t len) {
909 nvs_handle_t nvs;
910 if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs) != ESP_OK) return false;
911 esp_err_t err;
912 if (len == 0) {
913 err = nvs_erase_key(nvs, NVS_CERT_KEY);
914 if (err == ESP_ERR_NVS_NOT_FOUND) err = ESP_OK;
915 } else {
916 err = nvs_set_blob(nvs, NVS_CERT_KEY, data, len);
917 }
918 if (err == ESP_OK) err = nvs_commit(nvs);
919 nvs_close(nvs);
920 return err == ESP_OK;
921}
922
927static size_t load_cardholder_cert(uint8_t* out, size_t cap) {
928 nvs_handle_t nvs;
929 if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs) != ESP_OK) return 0;
930 size_t len = cap;
931 esp_err_t err = nvs_get_blob(nvs, NVS_CERT_KEY, out, &len);
932 nvs_close(nvs);
933 return (err == ESP_OK) ? len : 0;
934}
935
939static uint8_t kdf_hash_len(kdf_hash_t hash) {
940 return (hash == KDF_HASH_SHA512) ? 64 : 32;
941}
942
951static uint16_t apply_kdf_do(const uint8_t* data, size_t len) {
952 kdf_do_t parsed;
953 if (kdf_do_parse(data, len, &parsed) != KDF_OK) {
954 return SW_WRONG_DATA;
955 }
956 if (len > sizeof(kdf_do_bytes)) {
957 return SW_WRONG_LENGTH;
958 }
959
960 if (parsed.algo == KDF_ALGO_NONE) {
961 kdf_active = false;
962 kdf_pin_len = 0;
963 kdf_do_len = static_cast<uint8_t>(len);
964 memcpy(kdf_do_bytes, data, len);
968 LOG_I(TAG, "KDF disabled, PINs reset to defaults");
969 return SW_OK;
970 }
971
972 if (parsed.hash != KDF_HASH_SHA256 && parsed.hash != KDF_HASH_SHA512) {
973 return SW_WRONG_DATA;
974 }
975 kdf_active = true;
976 kdf_pin_len = kdf_hash_len(parsed.hash);
977 kdf_do_len = static_cast<uint8_t>(len);
978 memcpy(kdf_do_bytes, data, len);
979 if (parsed.has_pw1_initial) {
981 }
982 if (parsed.has_pw3_initial) {
984 }
986 LOG_I(TAG, "KDF enabled (hash len %u)", kdf_pin_len);
987 return SW_OK;
988}
989
994static int role_index_for_key_ref(uint8_t key_ref) {
995 switch (key_ref) {
996 case KEY_SIG: return 0;
997 case KEY_DEC: return 1;
998 case KEY_AUT: return 2;
999 default: return -1;
1000 }
1001}
1002
1007static void init_aid_from_mac(void) {
1008 uint8_t mac[6];
1009 if (esp_efuse_mac_get_default(mac) == ESP_OK) {
1010 // Use last 4 bytes of MAC as serial number (big-endian)
1011 // MAC format: [0][1][2][3][4][5] - use [2][3][4][5] for better uniqueness
1012 s_openpgp_aid[10] = mac[2];
1013 s_openpgp_aid[11] = mac[3];
1014 s_openpgp_aid[12] = mac[4];
1015 s_openpgp_aid[13] = mac[5];
1016
1017 // Set manufacturer: CDC Badge = 0x4344 ("CD" in ASCII)
1018 s_openpgp_aid[8] = 0x43; // 'C'
1019 s_openpgp_aid[9] = 0x44; // 'D'
1020
1021 LOG_I(TAG, "AID initialized: Manufacturer=0x%02X%02X Serial=%02X%02X%02X%02X",
1024 s_openpgp_aid[12], s_openpgp_aid[13]);
1025 } else {
1026 LOG_W(TAG, "Failed to read MAC, using default AID");
1027 // Keep defaults: FFFE / 00000001
1028 s_openpgp_aid[8] = 0xFF;
1029 s_openpgp_aid[9] = 0xFE;
1030 s_openpgp_aid[10] = 0x00;
1031 s_openpgp_aid[11] = 0x00;
1032 s_openpgp_aid[12] = 0x00;
1033 s_openpgp_aid[13] = 0x01;
1034 }
1035}
1036
1037bool openpgp_init(void) {
1038 // Initialize AID with device-unique serial number
1040
1041 // Initialize GPG component (TROPIC01 backend)
1042 if (!gpg_init()) {
1043 LOG_E(TAG, "Failed to initialize GPG/TROPIC01");
1044 return false;
1045 }
1046
1047 // Initialize OpenPGP PIN storage (loads PINs from TROPIC01)
1049
1051
1052 LOG_I(TAG, "OpenPGP application initialized, sig_count=%lu", sig_count);
1053 return true;
1054}
1055
1057 return app_selected;
1058}
1059
1061 return sig_count;
1062}
1063
1064bool openpgp_get_fingerprint(uint8_t key_type, uint8_t *fp_out) {
1065 if (!fp_out) return false;
1066 switch (key_type) {
1067 case KEY_SIG: memcpy(fp_out, fingerprint_sig, OPENPGP_FINGERPRINT_SIZE); return true;
1068 case KEY_DEC: memcpy(fp_out, fingerprint_dec, OPENPGP_FINGERPRINT_SIZE); return true;
1069 case KEY_AUT: memcpy(fp_out, fingerprint_aut, OPENPGP_FINGERPRINT_SIZE); return true;
1070 default: return false;
1071 }
1072}
1073
1074static bool fp_is_set(const uint8_t fp[OPENPGP_FINGERPRINT_SIZE]) {
1075 for (size_t i = 0; i < OPENPGP_FINGERPRINT_SIZE; i++) {
1076 if (fp[i] != 0) return true;
1077 }
1078 return false;
1079}
1080
1086
1087size_t openpgp_get_cardholder_name(char *out, size_t out_size) {
1088 if (!out || out_size == 0) return 0;
1089 size_t len = strlen(cardholder_name);
1090 if (len >= out_size) len = out_size - 1;
1091 memcpy(out, cardholder_name, len);
1092 out[len] = '\0';
1093 return len;
1094}
1095
1096uint32_t openpgp_get_gen_time(uint8_t key_type) {
1097 const uint8_t *src = nullptr;
1098 switch (key_type) {
1099 case KEY_SIG: src = gen_time_sig; break;
1100 case KEY_DEC: src = gen_time_dec; break;
1101 case KEY_AUT: src = gen_time_aut; break;
1102 default: return 0;
1103 }
1104 return (static_cast<uint32_t>(src[0]) << 24) |
1105 (static_cast<uint32_t>(src[1]) << 16) |
1106 (static_cast<uint32_t>(src[2]) << 8) |
1107 static_cast<uint32_t>(src[3]);
1108}
1109
1111 if (!name) return false;
1112 size_t len = strlen(name);
1113 if (len >= sizeof(cardholder_name)) len = sizeof(cardholder_name) - 1;
1114 memcpy(cardholder_name, name, len);
1115 cardholder_name[len] = '\0';
1116 if (len + 1 < sizeof(cardholder_name)) {
1117 memset(cardholder_name + len + 1, 0, sizeof(cardholder_name) - len - 1);
1118 }
1120 return true;
1121}
1122
1123bool openpgp_set_key_fingerprint(uint8_t key_type, const uint8_t *fingerprint,
1124 uint32_t gen_time) {
1125 if (!fingerprint) return false;
1126
1127 // Convert gen_time to big-endian bytes
1128 uint8_t ts[4] = {
1129 (uint8_t)((gen_time >> 24) & 0xFF),
1130 (uint8_t)((gen_time >> 16) & 0xFF),
1131 (uint8_t)((gen_time >> 8) & 0xFF),
1132 (uint8_t)(gen_time & 0xFF)
1133 };
1134
1135 switch (key_type) {
1136 case KEY_SIG:
1137 memcpy(fingerprint_sig, fingerprint, OPENPGP_FINGERPRINT_SIZE);
1138 memcpy(gen_time_sig, ts, 4);
1139 break;
1140 case KEY_DEC:
1141 memcpy(fingerprint_dec, fingerprint, OPENPGP_FINGERPRINT_SIZE);
1142 memcpy(gen_time_dec, ts, 4);
1143 break;
1144 case KEY_AUT:
1145 memcpy(fingerprint_aut, fingerprint, OPENPGP_FINGERPRINT_SIZE);
1146 memcpy(gen_time_aut, ts, 4);
1147 break;
1148 default:
1149 LOG_E(TAG, "Invalid key type: 0x%02X", key_type);
1150 return false;
1151 }
1152
1154 LOG_I(TAG, "Fingerprint set for key type 0x%02X", key_type);
1155 return true;
1156}
1157
1165static int cmd_select(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1166 if (apdu->lc >= 6 && memcmp(apdu->data, OPENPGP_AID, 6) == 0) {
1167 app_selected = true;
1168 pw1_verified = false;
1169 pw3_verified = false;
1170 mbedtls_platform_zeroize(s_session_pin, sizeof(s_session_pin));
1172 LOG_I(TAG, "OpenPGP application selected");
1173 return apdu_sw(resp, SW_OK);
1174 }
1175
1176 if (app_selected) {
1177 mbedtls_platform_zeroize(s_session_pin, sizeof(s_session_pin));
1179 }
1180 return apdu_sw(resp, SW_FILE_NOT_FOUND);
1181}
1182
1192static int respond_chunked(const uint8_t *payload, size_t payload_len, uint32_t le,
1193 uint8_t *resp, size_t resp_max) {
1194 size_t first = (le > 0 && le < payload_len) ? le : payload_len;
1195 if (first + 2 > resp_max) first = resp_max - 2;
1196 memcpy(resp, payload, first);
1197 const size_t remainder = payload_len - first;
1198 if (remainder == 0) {
1199 resp[first] = 0x90;
1200 resp[first + 1] = 0x00;
1201 return static_cast<int>(first + 2);
1202 }
1203 if (remainder > sizeof(g_resp_buffer)) {
1204 return apdu_sw(resp, SW_WRONG_LENGTH);
1205 }
1206 memcpy(g_resp_buffer, payload + first, remainder);
1207 g_resp_remaining = remainder;
1208 g_resp_pos = 0;
1209 resp[first] = 0x61;
1210 resp[first + 1] = (remainder > 0xFF) ? 0x00 : static_cast<uint8_t>(remainder);
1211 return static_cast<int>(first + 2);
1212}
1213
1221static int cmd_get_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1222 uint16_t tag = (apdu->p1 << 8) | apdu->p2;
1223
1224 switch (tag) {
1225 case DO_AID: // 0x4F: Full AID
1226 return apdu_build_response(resp, resp_max, OPENPGP_AID, OPENPGP_AID_LEN, SW_OK);
1227
1228 case DO_APP_RELATED: { // 0x6E: Application Related Data
1229 uint8_t data[512];
1230 int len = build_do_app_related(data, sizeof(data));
1231 if (len <= 0) {
1232 return apdu_sw(resp, SW_UNKNOWN);
1233 }
1234 return apdu_build_response(resp, resp_max, data, len, SW_OK);
1235 }
1236
1237 case DO_CARDHOLDER: { // 0x65: Cardholder Related Data
1238 uint8_t data[128];
1239 int len = build_do_cardholder(data, sizeof(data));
1240 if (len <= 0) {
1241 return apdu_sw(resp, SW_UNKNOWN);
1242 }
1243 return apdu_build_response(resp, resp_max, data, len, SW_OK);
1244 }
1245
1246 case DO_HIST_BYTES: // 0x5F52: Historical bytes
1247 return apdu_build_response(resp, resp_max, HIST_BYTES, sizeof(HIST_BYTES), SW_OK);
1248
1249 case DO_EXT_CAP: // 0xC0: Extended Capabilities
1250 return apdu_build_response(resp, resp_max, EXT_CAPABILITIES, sizeof(EXT_CAPABILITIES), SW_OK);
1251
1252 case DO_ALGO_SIG: { // 0xC1: Algorithm Attributes - Signature
1253 size_t algo_len;
1254 const uint8_t *algo = get_algo_attr(KEY_TYPE_SIG, &algo_len);
1255 return apdu_build_response(resp, resp_max, algo, algo_len, SW_OK);
1256 }
1257
1258 case DO_ALGO_DEC: { // 0xC2: Algorithm Attributes - Decryption
1259 size_t algo_len;
1260 const uint8_t *algo = get_algo_attr(KEY_TYPE_DEC, &algo_len);
1261 return apdu_build_response(resp, resp_max, algo, algo_len, SW_OK);
1262 }
1263
1264 case DO_ALGO_AUT: { // 0xC3: Algorithm Attributes - Authentication
1265 size_t algo_len;
1266 const uint8_t *algo = get_algo_attr(KEY_TYPE_AUT, &algo_len);
1267 return apdu_build_response(resp, resp_max, algo, algo_len, SW_OK);
1268 }
1269
1270 case DO_PW_STATUS: { // 0xC4: PW Status Bytes
1271 uint8_t status[7] = {
1272 0x01, // PW1 valid for multiple signatures
1273 OPENPGP_PIN_MAX_LEN, // Max length PW1
1274 OPENPGP_PIN_MAX_LEN, // Max length RC
1275 OPENPGP_PIN_MAX_LEN, // Max length PW3
1277 s_rc_len > 0 ? s_rc_retries : static_cast<uint8_t>(0),
1279 };
1280 return apdu_build_response(resp, resp_max, status, 7, SW_OK);
1281 }
1282
1283 case DO_FP_SIG: // 0xC7: Fingerprint SIG
1285
1286 case DO_FP_DEC: // 0xC8: Fingerprint DEC
1288
1289 case DO_FP_AUT: // 0xC9: Fingerprint AUT
1291
1292 case DO_CA_FP_1: // 0xCA: CA Fingerprint 1
1294
1295 case DO_CA_FP_2: // 0xCB: CA Fingerprint 2
1297
1298 case DO_CA_FP_3: // 0xCC: CA Fingerprint 3
1300
1301 case DO_GEN_TIME_SIG: // 0xCE: Generation time - Signature
1302 return apdu_build_response(resp, resp_max, gen_time_sig, 4, SW_OK);
1303
1304 case DO_GEN_TIME_DEC: // 0xCF: Generation time - Decryption
1305 return apdu_build_response(resp, resp_max, gen_time_dec, 4, SW_OK);
1306
1307 case DO_GEN_TIME_AUT: // 0xD0: Generation time - Authentication
1308 return apdu_build_response(resp, resp_max, gen_time_aut, 4, SW_OK);
1309
1310 case DO_SIG_COUNT: { // 0x93: Signature counter
1311 uint8_t count[3] = {
1312 (uint8_t)((sig_count >> 16) & 0xFF),
1313 (uint8_t)((sig_count >> 8) & 0xFF),
1314 (uint8_t)(sig_count & 0xFF)
1315 };
1316 return apdu_build_response(resp, resp_max, count, 3, SW_OK);
1317 }
1318
1319 // URL for public key retrieval
1320 case DO_URL: { // 0x5F50
1321 size_t len = strlen(cardholder_url);
1322 return apdu_build_response(resp, resp_max, (const uint8_t*)cardholder_url, len, SW_OK);
1323 }
1324
1325 // Login data
1326 case DO_LOGIN: { // 0x5E
1327 size_t len = strlen(cardholder_login);
1328 return apdu_build_response(resp, resp_max, (const uint8_t*)cardholder_login, len, SW_OK);
1329 }
1330
1331 // These are already in build_do_cardholder (0x65), but GPG may query them directly too
1332 case DO_NAME: // 0x5B: Cardholder name
1333 return apdu_build_response(resp, resp_max, (const uint8_t*)cardholder_name, strlen(cardholder_name), SW_OK);
1334
1335 case DO_LANG_PREF: // 0x5F2D: Language preference
1336 return apdu_build_response(resp, resp_max, (const uint8_t*)cardholder_lang, strlen(cardholder_lang), SW_OK);
1337
1338 case DO_SEX: // 0x5F35: Sex
1339 return apdu_build_response(resp, resp_max, &cardholder_sex, 1, SW_OK);
1340
1341 // UIF (User Interaction Flag) - 2 bytes: mode + features
1342 case DO_UIF_SIG: // 0xD6: UIF Signature
1343 case DO_UIF_DEC: // 0xD7: UIF Decryption
1344 case DO_UIF_AUT: { // 0xD8: UIF Authentication
1345 uint8_t uif[2] = { 0x00, 0x20 }; // Disabled, button available
1346 return apdu_build_response(resp, resp_max, uif, 2, SW_OK);
1347 }
1348
1349 // Key Information - 6 bytes (status of 3 keys)
1350 // Format: key_ref, status (0x00=generated, 0x01=imported, 0x02=not present)
1351 case DO_KEY_INFO: {
1352 uint8_t key_info[6];
1353 uint8_t pubkey[P256_PUBKEY_SIZE], curve;
1354
1355 // Check SIG key (RSA blob or SE ECC slot)
1356 key_info[0] = 0x01; // Key reference for SIG
1357 key_info[1] = (role_is_rsa[0] ? gpg_storage_has_rsa_key(0)
1358 : se_ecc_key_read(gpg_storage_sig_slot(), pubkey, sizeof(pubkey), &curve))
1359 ? 0x00 // present
1360 : 0x02; // Not present
1361
1362 // Check DEC key (RSA blob or software ECDH key)
1363 key_info[2] = 0x02; // Key reference for DEC
1364 key_info[3] = (role_is_rsa[1] ? gpg_storage_has_rsa_key(1)
1366 ? 0x00
1367 : 0x02;
1368
1369 // Check AUT key (RSA blob or SE ECC slot)
1370 key_info[4] = 0x03; // Key reference for AUT
1371 key_info[5] = (role_is_rsa[2] ? gpg_storage_has_rsa_key(2)
1372 : se_ecc_key_read(gpg_storage_aut_slot(), pubkey, sizeof(pubkey), &curve))
1373 ? 0x00
1374 : 0x02;
1375
1376 return apdu_build_response(resp, resp_max, key_info, 6, SW_OK);
1377 }
1378
1379 // Security Support Template - contains signature counter
1380 case DO_SEC_TPL: {
1381 // Format: 7A <len> { 93 03 <sig_count[3]> }
1382 uint8_t sec_tpl[7] = {
1383 0x93, 0x03, // Tag + length for signature counter
1384 (uint8_t)((sig_count >> 16) & 0xFF),
1385 (uint8_t)((sig_count >> 8) & 0xFF),
1386 (uint8_t)(sig_count & 0xFF)
1387 };
1388 return apdu_build_response(resp, resp_max, sec_tpl, 5, SW_OK);
1389 }
1390
1391 // KDF-DO (Key Derivation Function). Returns the stored DO bytes, or the
1392 // "disabled" body (81 01 00) when KDF has never been configured.
1393 case DO_KDF: {
1394 if (kdf_do_len > 0) {
1395 return apdu_build_response(resp, resp_max, kdf_do_bytes, kdf_do_len, SW_OK);
1396 }
1397 uint8_t disabled[3];
1398 size_t disabled_len = 0;
1399 kdf_do_build_disabled(disabled, sizeof(disabled), &disabled_len);
1400 return apdu_build_response(resp, resp_max, disabled, disabled_len, SW_OK);
1401 }
1402
1403 case DO_CARDHOLDER_CERT: {
1404 static EXT_RAM_BSS_ATTR uint8_t cert_buf[CARDHOLDER_CERT_MAX];
1405 size_t cert_len = load_cardholder_cert(cert_buf, sizeof(cert_buf));
1406 if (cert_len == 0) {
1408 }
1409 return respond_chunked(cert_buf, cert_len, apdu->le, resp, resp_max);
1410 }
1411
1412 default:
1413 LOG_W(TAG, "GET DATA: Unknown tag 0x%04X", tag);
1415 }
1416}
1417
1428
1435typedef struct {
1436 uint16_t tag;
1437 void *buffer;
1438 size_t max_size;
1440 const char *log_label;
1442
1448static const put_data_desc_t* find_put_data_desc(uint16_t tag) {
1449 static const put_data_desc_t k_put_data_table[] = {
1450 // Cardholder profile (string-bounded, written with trailing NUL)
1451 { DO_NAME, cardholder_name, sizeof(cardholder_name), PUT_KIND_STRING_BOUNDED, "Cardholder name" },
1455
1456 // Fixed-size fingerprints (SHA-1 size)
1463
1464 // Fixed-size 4-byte big-endian generation timestamps
1468 };
1469
1470 const size_t n = sizeof(k_put_data_table) / sizeof(k_put_data_table[0]);
1471 for (size_t i = 0; i < n; ++i) {
1472 if (k_put_data_table[i].tag == tag) {
1473 return &k_put_data_table[i];
1474 }
1475 }
1476 return NULL;
1477}
1478
1486static int apply_put_data_desc(const put_data_desc_t *desc, const apdu_t *apdu, uint8_t *resp) {
1487 if (desc->kind == PUT_KIND_STRING_BOUNDED) {
1488 if (apdu->lc >= desc->max_size) {
1489 return apdu_sw(resp, SW_WRONG_LENGTH);
1490 }
1491 char *str = (char *)desc->buffer;
1492 memcpy(str, apdu->data, apdu->lc);
1493 str[apdu->lc] = '\0';
1495 if (desc->log_label) {
1496 LOG_I(TAG, "%s set: %s", desc->log_label, str);
1497 }
1498 return apdu_sw(resp, SW_OK);
1499 }
1500
1501 // BLOB_FIXED: exact length match required
1502 if (apdu->lc != desc->max_size) {
1503 return apdu_sw(resp, SW_WRONG_LENGTH);
1504 }
1505 memcpy(desc->buffer, apdu->data, desc->max_size);
1507 if (desc->log_label) {
1508 LOG_I(TAG, "%s stored", desc->log_label);
1509 }
1510 return apdu_sw(resp, SW_OK);
1511}
1512
1535static void wipe_role_key(int r) {
1536 auto* se = get_se();
1537 gpg_storage_delete_rsa_key(static_cast<uint8_t>(r));
1538 switch (r) {
1539 case 0:
1540 if (se) se->eccDelete(gpg_storage_sig_slot());
1541 memset(fingerprint_sig, 0, sizeof(fingerprint_sig));
1542 memset(gen_time_sig, 0, sizeof(gen_time_sig));
1543 break;
1544 case 1:
1546 memset(fingerprint_dec, 0, sizeof(fingerprint_dec));
1547 memset(gen_time_dec, 0, sizeof(gen_time_dec));
1548 break;
1549 case 2:
1550 if (se) se->eccDelete(gpg_storage_aut_slot());
1551 memset(fingerprint_aut, 0, sizeof(fingerprint_aut));
1552 memset(gen_time_aut, 0, sizeof(gen_time_aut));
1553 break;
1554 default:
1555 break;
1556 }
1557}
1558
1559static int put_data_algo_attr(uint16_t tag, const apdu_t *apdu, uint8_t *resp) {
1560 algo_attr_t attr;
1561 if (algo_attr_parse(apdu->data, apdu->lc, &attr) != ALGO_ATTR_OK) {
1562 return apdu_sw(resp, SW_WRONG_DATA);
1563 }
1564 algo_attr_role_t role;
1565 int r;
1566 switch (tag) {
1567 case DO_ALGO_SIG: role = ALGO_ATTR_ROLE_SIG; r = 0; break;
1568 case DO_ALGO_AUT: role = ALGO_ATTR_ROLE_AUT; r = 2; break;
1569 case DO_ALGO_DEC: role = ALGO_ATTR_ROLE_DEC; r = 1; break;
1570 default: return apdu_sw(resp, SW_FILE_NOT_FOUND);
1571 }
1572 if (algo_attr_validate_role(&attr, role) != ALGO_ATTR_OK) {
1573 return apdu_sw(resp, SW_WRONG_DATA);
1574 }
1575 if (algo_attr_validate_capability(&attr, /*rsa_supported=*/true) != ALGO_ATTR_OK) {
1576 return apdu_sw(resp, SW_WRONG_DATA);
1577 }
1578
1579 if (attr.is_rsa) {
1580 if (role_is_rsa[r] && role_rsa_n_bits[r] == attr.rsa_n_bits) {
1581 return apdu_sw(resp, SW_OK);
1582 }
1583 wipe_role_key(r);
1584 role_is_rsa[r] = true;
1585 role_rsa_n_bits[r] = attr.rsa_n_bits;
1586 role_rsa_e_bits[r] = attr.rsa_e_bits ? attr.rsa_e_bits : 32;
1587 role_rsa_fmt[r] = attr.rsa_import_fmt;
1589 LOG_I(TAG, "Algorithm attributes for role %d set to RSA-%u", r, attr.rsa_n_bits);
1590 return apdu_sw(resp, SW_OK);
1591 }
1592
1593 if (role == ALGO_ATTR_ROLE_DEC) {
1594 // DEC ECC is constrained to P-256 ECDH (software path).
1595 if (attr.curve != ALGO_ATTR_CURVE_P256 || attr.algo_id != ALGO_ATTR_ID_ECDH) {
1596 return apdu_sw(resp, SW_WRONG_DATA);
1597 }
1598 if (!role_is_rsa[r]) {
1599 return apdu_sw(resp, SW_OK);
1600 }
1601 wipe_role_key(r);
1602 role_is_rsa[r] = false;
1604 LOG_I(TAG, "DEC role reverted to P-256 ECDH");
1605 return apdu_sw(resp, SW_OK);
1606 }
1607
1608 // SIG / AUT ECC: honour the selected curve.
1609 const uint8_t new_curve = (attr.curve == ALGO_ATTR_CURVE_ED25519) ? CDC_CURVE_ED25519
1611 uint8_t* target = (tag == DO_ALGO_SIG) ? &selected_curve_sig : &selected_curve_aut;
1612 if (!role_is_rsa[r] && *target == new_curve) {
1613 return apdu_sw(resp, SW_OK);
1614 }
1615 wipe_role_key(r);
1616 role_is_rsa[r] = false;
1617 *target = new_curve;
1619 LOG_I(TAG, "Algorithm attributes for %s updated to curve %u",
1620 (tag == DO_ALGO_SIG) ? "SIG" : "AUT", new_curve);
1621 return apdu_sw(resp, SW_OK);
1622}
1623
1624static int cmd_put_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1625 (void)resp_max;
1626 if (!pw3_verified) {
1627 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
1628 }
1629
1630 uint16_t tag = (apdu->p1 << 8) | apdu->p2;
1631
1632 // Special-case DOs that need custom handling stay inline.
1633 switch (tag) {
1634 case DO_SEX:
1635 if (apdu->lc == 1) {
1636 cardholder_sex = apdu->data[0];
1638 return apdu_sw(resp, SW_OK);
1639 }
1640 return apdu_sw(resp, SW_WRONG_LENGTH);
1641 case DO_ALGO_SIG:
1642 case DO_ALGO_DEC:
1643 case DO_ALGO_AUT:
1644 return put_data_algo_attr(tag, apdu, resp);
1645 case DO_RC:
1646 // Set or clear the Resetting Code. Lc==0 clears the RC entirely.
1647 if (apdu->lc == 0) {
1648 mbedtls_platform_zeroize(s_rc_salt, sizeof(s_rc_salt));
1649 mbedtls_platform_zeroize(s_rc_hash, sizeof(s_rc_hash));
1650 s_rc_len = 0;
1651 s_rc_retries = 3;
1653 LOG_I(TAG, "Resetting Code cleared");
1654 return apdu_sw(resp, SW_OK);
1655 }
1656 if (apdu->lc < OPENPGP_RC_MIN_LEN || apdu->lc > OPENPGP_PIN_MAX_LEN) {
1657 return apdu_sw(resp, SW_WRONG_LENGTH);
1658 }
1659 {
1660 uint8_t new_salt[RC_SALT_SIZE];
1661 uint8_t new_hash[RC_HASH_SIZE];
1662 se_random_fill(new_salt, RC_SALT_SIZE);
1663 if (!compute_rc_hash(apdu->data, apdu->lc, new_salt, new_hash)) {
1664 mbedtls_platform_zeroize(new_salt, sizeof(new_salt));
1665 mbedtls_platform_zeroize(new_hash, sizeof(new_hash));
1666 return apdu_sw(resp, SW_UNKNOWN);
1667 }
1668 memcpy(s_rc_salt, new_salt, RC_SALT_SIZE);
1669 memcpy(s_rc_hash, new_hash, RC_HASH_SIZE);
1670 mbedtls_platform_zeroize(new_salt, sizeof(new_salt));
1671 mbedtls_platform_zeroize(new_hash, sizeof(new_hash));
1672 }
1673 s_rc_len = static_cast<uint8_t>(apdu->lc);
1674 s_rc_retries = 3;
1676 LOG_I(TAG, "Resetting Code configured (length=%u)", s_rc_len);
1677 return apdu_sw(resp, SW_OK);
1678 case DO_AES_KEY: {
1679 if (apdu->lc != 16 && apdu->lc != 32) {
1680 return apdu_sw(resp, SW_WRONG_LENGTH);
1681 }
1682 const char* pin = s_session_pin[0] ? s_session_pin : nullptr;
1683 bool ok = gpg_storage_save_aes_key(apdu->data, apdu->lc, pin);
1684 return apdu_sw(resp, ok ? SW_OK : SW_UNKNOWN);
1685 }
1686 case DO_KDF:
1687 return apdu_sw(resp, apply_kdf_do(apdu->data, apdu->lc));
1688 case DO_CARDHOLDER_CERT:
1689 if (apdu->lc > CARDHOLDER_CERT_MAX) {
1690 return apdu_sw(resp, SW_WRONG_LENGTH);
1691 }
1692 if (!save_cardholder_cert(apdu->data, apdu->lc)) {
1693 return apdu_sw(resp, SW_UNKNOWN);
1694 }
1695 LOG_I(TAG, "Cardholder certificate stored (%u bytes)", apdu->lc);
1696 return apdu_sw(resp, SW_OK);
1697 default:
1698 break;
1699 }
1700
1701 // Table-driven simple cases (validate, memcpy, persist).
1702 const put_data_desc_t *desc = find_put_data_desc(tag);
1703 if (desc) {
1704 return apply_put_data_desc(desc, apdu, resp);
1705 }
1706
1707 LOG_W(TAG, "PUT DATA: Unknown tag 0x%04X", tag);
1708 return apdu_sw(resp, SW_FILE_NOT_FOUND);
1709}
1710
1711static void update_generation_timestamp(uint8_t key_ref);
1712
1721static bool ehl_parse_one(const uint8_t *buf, size_t buf_len, size_t *pos,
1722 uint16_t *tag_out, const uint8_t **value_out,
1723 size_t *value_len_out) {
1724 if (!buf || !pos || *pos >= buf_len) return false;
1725 size_t p = *pos;
1726
1727 uint16_t tag = buf[p++];
1728 if ((tag & 0x1F) == 0x1F) {
1729 if (p >= buf_len) return false;
1730 tag = (tag << 8) | buf[p++];
1731 }
1732 if (p >= buf_len) return false;
1733
1734 size_t len;
1735 uint8_t lb = buf[p++];
1736 if (lb < 0x80) {
1737 len = lb;
1738 } else if (lb == 0x81) {
1739 if (p >= buf_len) return false;
1740 len = buf[p++];
1741 } else if (lb == 0x82) {
1742 if (p + 1 >= buf_len) return false;
1743 len = (static_cast<size_t>(buf[p]) << 8) | buf[p + 1];
1744 p += 2;
1745 } else {
1746 return false;
1747 }
1748 if (p + len > buf_len) return false;
1749
1750 *tag_out = tag;
1751 *value_out = buf + p;
1752 *value_len_out = len;
1753 *pos = p + len;
1754 return true;
1755}
1756
1766static bool parse_rsa_import(const uint8_t* tmpl, size_t tmpl_len,
1767 const uint8_t* concat, size_t concat_len,
1768 const uint8_t** e, size_t* e_len,
1769 const uint8_t** p, size_t* p_len,
1770 const uint8_t** q, size_t* q_len) {
1771 size_t tpos = 0, cpos = 0;
1772 *e = *p = *q = nullptr;
1773 *e_len = *p_len = *q_len = 0;
1774 while (tpos < tmpl_len) {
1775 const uint8_t t = tmpl[tpos++];
1776 if (tpos >= tmpl_len) return false;
1777 size_t clen;
1778 const uint8_t lb = tmpl[tpos++];
1779 if (lb < 0x80) {
1780 clen = lb;
1781 } else if (lb == 0x81) {
1782 if (tpos >= tmpl_len) return false;
1783 clen = tmpl[tpos++];
1784 } else if (lb == 0x82) {
1785 if (tpos + 1 >= tmpl_len) return false;
1786 clen = (static_cast<size_t>(tmpl[tpos]) << 8) | tmpl[tpos + 1];
1787 tpos += 2;
1788 } else {
1789 return false;
1790 }
1791 if (cpos + clen > concat_len) return false;
1792 const uint8_t* val = concat + cpos;
1793 cpos += clen;
1794 switch (t) {
1795 case 0x91: *e = val; *e_len = clen; break;
1796 case 0x92: *p = val; *p_len = clen; break;
1797 case 0x93: *q = val; *q_len = clen; break;
1798 default: break;
1799 }
1800 }
1801 return (*e && *p && *q);
1802}
1803
1819static int cmd_put_data_odd(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1820 (void)resp_max;
1821 if (!pw3_verified) {
1822 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
1823 }
1824 if (apdu->p1 != 0x3F || apdu->p2 != 0xFF) {
1825 return apdu_sw(resp, SW_INCORRECT_P1P2);
1826 }
1827 if (apdu->lc == 0 || !apdu->data) {
1828 return apdu_sw(resp, SW_WRONG_LENGTH);
1829 }
1830
1831 size_t pos = 0;
1832 uint16_t tag = 0;
1833 const uint8_t *outer_val = nullptr;
1834 size_t outer_len = 0;
1835 if (!ehl_parse_one(apdu->data, apdu->lc, &pos, &tag, &outer_val, &outer_len) ||
1836 tag != 0x4D) {
1837 return apdu_sw(resp, SW_WRONG_DATA);
1838 }
1839
1840 // First inner element is the CRT identifying the target role.
1841 size_t inner = 0;
1842 const uint8_t *crt_val = nullptr;
1843 size_t crt_len = 0;
1844 if (!ehl_parse_one(outer_val, outer_len, &inner, &tag, &crt_val, &crt_len)) {
1845 return apdu_sw(resp, SW_WRONG_DATA);
1846 }
1847 const uint8_t key_ref = static_cast<uint8_t>(tag);
1848 const int r = role_index_for_key_ref(key_ref);
1849 if (r < 0) {
1850 return apdu_sw(resp, SW_WRONG_DATA);
1851 }
1852
1853 // Collect the private-key template (7F48) and the value concatenation (5F48).
1854 const uint8_t *tmpl = nullptr; size_t tmpl_len = 0;
1855 const uint8_t *concat = nullptr; size_t concat_len = 0;
1856 while (inner < outer_len) {
1857 const uint8_t *v = nullptr;
1858 size_t vlen = 0;
1859 if (!ehl_parse_one(outer_val, outer_len, &inner, &tag, &v, &vlen)) {
1860 return apdu_sw(resp, SW_WRONG_DATA);
1861 }
1862 if (tag == 0x7F48) { tmpl = v; tmpl_len = vlen; }
1863 else if (tag == 0x5F48) { concat = v; concat_len = vlen; }
1864 }
1865 if (!concat || concat_len == 0) {
1866 return apdu_sw(resp, SW_WRONG_DATA);
1867 }
1868
1869 if (role_is_rsa[r]) {
1870 if (!tmpl) {
1871 return apdu_sw(resp, SW_WRONG_DATA);
1872 }
1873 const uint8_t *e = nullptr, *p = nullptr, *q = nullptr;
1874 size_t e_len = 0, p_len = 0, q_len = 0;
1875 if (!parse_rsa_import(tmpl, tmpl_len, concat, concat_len,
1876 &e, &e_len, &p, &p_len, &q, &q_len)) {
1877 return apdu_sw(resp, SW_WRONG_DATA);
1878 }
1879 static EXT_RAM_BSS_ATTR uint8_t blob[GPG_RSA_BLOB_MAX];
1880 size_t blob_len = 0;
1881 bool built = gpg_rsa_blob_build(role_rsa_n_bits[r], e, e_len, p, p_len, q, q_len,
1882 blob, sizeof(blob), &blob_len);
1883 bool saved = built && gpg_storage_save_rsa_key(static_cast<uint8_t>(r), blob, blob_len, nullptr);
1884 mbedtls_platform_zeroize(blob, sizeof(blob));
1885 if (!saved) {
1886 return apdu_sw(resp, built ? SW_UNKNOWN : SW_WRONG_DATA);
1887 }
1889 LOG_I(TAG, "Imported RSA key for role %d", r);
1890 return apdu_sw(resp, SW_OK);
1891 }
1892
1893 // ECC import: the first 32 bytes of the concatenation are the private scalar.
1894 if (concat_len < P256_PRIVKEY_SIZE) {
1895 return apdu_sw(resp, SW_WRONG_DATA);
1896 }
1897 uint8_t scalar[P256_PRIVKEY_SIZE];
1898 memcpy(scalar, concat, P256_PRIVKEY_SIZE);
1899 bool ok = false;
1900 if (r == 1) {
1901 // DEC: software ECDH key.
1902 ok = gpg_storage_save_dec_privkey(scalar, nullptr);
1903 } else {
1904 // SIG / AUT: inject into the TROPIC01 ECC slot.
1905 auto* se = get_se();
1906 const uint8_t slot = (r == 0) ? gpg_storage_sig_slot() : gpg_storage_aut_slot();
1907 const uint8_t curve = (r == 0) ? selected_curve_sig : selected_curve_aut;
1910 ok = se && (se->eccImport(slot, scalar, ec) == cdc::hal::SeResult::OK);
1911 }
1912 mbedtls_platform_zeroize(scalar, sizeof(scalar));
1913 if (!ok) {
1914 return apdu_sw(resp, SW_UNKNOWN);
1915 }
1917 LOG_I(TAG, "Imported ECC key for role %d", r);
1918 return apdu_sw(resp, SW_OK);
1919}
1920
1928static int cmd_verify(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
1929 uint8_t pw_ref = apdu->p2;
1930
1931 // Check remaining retries (Lc=0 means query)
1932 if (apdu->lc == 0) {
1933 uint8_t retries;
1934 if (pw_ref == PW1_CODE_1 || pw_ref == PW1_CODE_2) {
1936 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
1937 }
1939 } else if (pw_ref == PW3_CODE) {
1941 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
1942 }
1944 } else {
1945 return apdu_sw(resp, SW_INCORRECT_P1P2);
1946 }
1947 return apdu_sw(resp, 0x63C0 | retries);
1948 }
1949
1950 // In KDF mode the host sends a PBKDF2 pre-hash (binary, 32/64 bytes); the
1951 // cleartext path caps at OPENPGP_PIN_MAX_LEN and NUL-terminates.
1952 bool verified = false;
1953 uint8_t retries;
1954
1955 if (pw_ref == PW1_CODE_1 || pw_ref == PW1_CODE_2) {
1956 // A 32/64-byte field is a KDF pre-hash (a cleartext PIN never reaches
1957 // that length): verify it raw even before PUT DATA 0xF9 flips kdf_active
1958 // on, so gpg's kdf-setup VERIFY succeeds during the enable transition.
1959 if (kdf_active || apdu->lc == 32 || apdu->lc == 64) {
1960 verified = pin_storage_openpgp_verify_pw1_raw(apdu->data, apdu->lc);
1961 if (verified) {
1962 pw1_verified = true;
1963 mbedtls_platform_zeroize(s_session_pin, sizeof(s_session_pin));
1965 }
1966 } else {
1967 if (apdu->lc > OPENPGP_PIN_MAX_LEN) {
1968 return apdu_sw(resp, SW_WRONG_LENGTH);
1969 }
1970 char pin_str[OPENPGP_PIN_MAX_LEN + 1];
1971 memcpy(pin_str, apdu->data, apdu->lc);
1972 pin_str[apdu->lc] = '\0';
1973 verified = pin_storage_openpgp_verify_pw1(pin_str);
1974 if (verified) {
1975 pw1_verified = true;
1976 strncpy(s_session_pin, pin_str, OPENPGP_PIN_MAX_LEN);
1979 }
1980 mbedtls_platform_zeroize(pin_str, sizeof(pin_str));
1981 }
1982 if (verified) {
1983 LOG_I(TAG, "PW1 verified successfully");
1984 }
1986 } else if (pw_ref == PW3_CODE) {
1987 if (kdf_active || apdu->lc == 32 || apdu->lc == 64) {
1988 verified = pin_storage_openpgp_verify_pw3_raw(apdu->data, apdu->lc);
1989 } else {
1990 if (apdu->lc > OPENPGP_PIN_MAX_LEN) {
1991 return apdu_sw(resp, SW_WRONG_LENGTH);
1992 }
1993 char pin_str[OPENPGP_PIN_MAX_LEN + 1];
1994 memcpy(pin_str, apdu->data, apdu->lc);
1995 pin_str[apdu->lc] = '\0';
1996 verified = pin_storage_openpgp_verify_pw3(pin_str);
1997 mbedtls_platform_zeroize(pin_str, sizeof(pin_str));
1998 }
1999 if (verified) {
2000 pw3_verified = true;
2001 LOG_I(TAG, "PW3 verified successfully");
2002 }
2004 } else {
2005 return apdu_sw(resp, SW_INCORRECT_P1P2);
2006 }
2007
2008 if (verified) {
2009 return apdu_sw(resp, SW_OK);
2010 }
2011
2012 // Verification failed
2013 if (retries == 0) {
2014 LOG_W(TAG, "PIN blocked after too many failures");
2015 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
2016 }
2017 LOG_W(TAG, "PIN verification failed, %d retries left", retries);
2018 return apdu_sw(resp, 0x63C0 | retries);
2019}
2020
2024typedef enum {
2027} pin_slot_t;
2028
2037static bool compute_kdf_hash(const char* pin, const uint8_t* salt, uint32_t iterations,
2038 uint8_t hash_out[32]) {
2039 if (!pin || !salt || !hash_out) return false;
2040 size_t pin_len = strlen(pin);
2041 if (pin_len > OPENPGP_PIN_MAX_LEN) return false;
2042 size_t combined = 8 + pin_len;
2043 if (combined == 0) return false;
2044
2045 uint8_t buffer[8 + OPENPGP_PIN_MAX_LEN];
2046 memcpy(buffer, salt, 8);
2047 memcpy(buffer + 8, pin, pin_len);
2048
2049 mbedtls_sha256_context ctx;
2050 mbedtls_sha256_init(&ctx);
2051 if (mbedtls_sha256_starts(&ctx, 0) != 0) {
2052 mbedtls_sha256_free(&ctx);
2053 mbedtls_platform_zeroize(buffer, sizeof(buffer));
2054 return false;
2055 }
2056 size_t processed = 0;
2057 size_t total_bytes = iterations;
2058 while (processed < total_bytes) {
2059 size_t chunk = (total_bytes - processed < combined) ? (total_bytes - processed) : combined;
2060 if (mbedtls_sha256_update(&ctx, buffer, chunk) != 0) {
2061 mbedtls_sha256_free(&ctx);
2062 mbedtls_platform_zeroize(buffer, sizeof(buffer));
2063 return false;
2064 }
2065 processed += chunk;
2066 }
2067 int rc = mbedtls_sha256_finish(&ctx, hash_out);
2068 mbedtls_sha256_free(&ctx);
2069 mbedtls_platform_zeroize(buffer, sizeof(buffer));
2070 return rc == 0;
2071}
2072
2080static bool const_time_equal(const uint8_t* a, const uint8_t* b, size_t n) {
2081 uint8_t diff = 0;
2082 for (size_t i = 0; i < n; ++i) diff |= a[i] ^ b[i];
2083 return diff == 0;
2084}
2085
2092static bool peek_verify_pin(pin_slot_t slot, const char* pin) {
2093 auto& mgr = cdc::core::PinManager::instance();
2094 uint8_t salt[8] = {};
2095 uint8_t stored[32] = {};
2096 uint8_t candidate[32] = {};
2097 bool ok = false;
2098
2099 if (slot == PIN_SLOT_PW1) {
2100 if (!mgr.getPW1Salt(salt)) goto done;
2101 if (!mgr.getPW1Hash(stored)) goto done;
2102 } else {
2103 if (!mgr.getPW3Salt(salt)) goto done;
2104 if (!mgr.getPW3Hash(stored)) goto done;
2105 }
2106
2107 if (!compute_kdf_hash(pin, salt, mgr.getIterationCount(), candidate)) goto done;
2108 ok = const_time_equal(candidate, stored, sizeof(stored));
2109
2110done:
2111 mbedtls_platform_zeroize(stored, sizeof(stored));
2112 mbedtls_platform_zeroize(candidate, sizeof(candidate));
2113 return ok;
2114}
2115
2119typedef bool (*pin_change_fn_t)(const char *pin);
2120
2136static bool try_change_pin(const uint8_t *data, size_t len, size_t min_len,
2137 pin_slot_t slot, pin_change_fn_t change_fn) {
2138 if (len < min_len * 2) {
2139 return false;
2140 }
2141 for (size_t old_len = min_len; old_len <= len - min_len; ++old_len) {
2142 size_t new_len = len - old_len;
2143 if (old_len > OPENPGP_PIN_MAX_LEN || new_len > OPENPGP_PIN_MAX_LEN) {
2144 continue;
2145 }
2146 char old_pin[OPENPGP_PIN_MAX_LEN + 1];
2147 char new_pin[OPENPGP_PIN_MAX_LEN + 1];
2148 memcpy(old_pin, data, old_len);
2149 old_pin[old_len] = '\0';
2150 memcpy(new_pin, data + old_len, new_len);
2151 new_pin[new_len] = '\0';
2152
2153 if (peek_verify_pin(slot, old_pin) && change_fn(new_pin)) {
2154 mbedtls_platform_zeroize(old_pin, sizeof(old_pin));
2155 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
2156 return true;
2157 }
2158 mbedtls_platform_zeroize(old_pin, sizeof(old_pin));
2159 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
2160 }
2161 return false;
2162}
2163
2171static int cmd_change_reference_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2172 (void)resp_max;
2173 uint8_t pw_ref = apdu->p2;
2174
2175 if (apdu->lc == 0) {
2176 return apdu_sw(resp, SW_WRONG_LENGTH);
2177 }
2178
2179 pin_change_fn_t change_fn = NULL;
2180 size_t min_len = 0;
2181 uint8_t (*retries_fn)(void) = NULL;
2182 pin_slot_t slot;
2183 const char *log_label = NULL;
2184
2185 if (pw_ref == PW1_CODE_1) {
2186 slot = PIN_SLOT_PW1;
2189 min_len = OPENPGP_PW1_MIN_LEN;
2190 log_label = "PW1";
2191 } else if (pw_ref == PW3_CODE) {
2192 slot = PIN_SLOT_PW3;
2195 min_len = OPENPGP_PW3_MIN_LEN;
2196 log_label = "PW3";
2197 } else {
2198 return apdu_sw(resp, SW_INCORRECT_P1P2);
2199 }
2200
2201 // KDF mode: payload is old-prehash || new-prehash, each kdf_pin_len bytes.
2202 if (kdf_active) {
2203 if (apdu->lc != static_cast<uint16_t>(2 * kdf_pin_len)) {
2204 return apdu_sw(resp, SW_WRONG_LENGTH);
2205 }
2206 const uint8_t* old_h = apdu->data;
2207 const uint8_t* new_h = apdu->data + kdf_pin_len;
2208 bool old_ok = (slot == PIN_SLOT_PW1)
2211 if (!old_ok) {
2212 uint8_t retries = retries_fn();
2213 if (retries == 0) {
2214 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
2215 }
2216 return apdu_sw(resp, 0x63C0 | retries);
2217 }
2218 bool set_ok = (slot == PIN_SLOT_PW1)
2221 if (!set_ok) {
2222 return apdu_sw(resp, SW_WRONG_DATA);
2223 }
2224 LOG_I(TAG, "%s changed successfully (KDF)", log_label);
2225 return apdu_sw(resp, SW_OK);
2226 }
2227
2228 // KDF-enable transition: gpg sends `cleartext-old || raw-new` here (before
2229 // PUT DATA 0xF9 turns KDF on), where the new value is a 32- or 64-byte KDF
2230 // pre-hash. Verify the cleartext old normally, then store the raw new ref.
2231 {
2232 size_t new_raw = 0;
2233 if (apdu->lc >= min_len + 64 && apdu->lc - 64 <= OPENPGP_PIN_MAX_LEN) {
2234 new_raw = 64;
2235 } else if (apdu->lc >= min_len + 32 && apdu->lc - 32 <= OPENPGP_PIN_MAX_LEN) {
2236 new_raw = 32;
2237 }
2238 if (new_raw) {
2239 const size_t old_len = apdu->lc - new_raw;
2240 char old_pin[OPENPGP_PIN_MAX_LEN + 1];
2241 memcpy(old_pin, apdu->data, old_len);
2242 old_pin[old_len] = '\0';
2243 const bool old_ok = (slot == PIN_SLOT_PW1)
2246 mbedtls_platform_zeroize(old_pin, sizeof(old_pin));
2247 if (!old_ok) {
2248 uint8_t retries = retries_fn();
2249 if (retries == 0) {
2250 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
2251 }
2252 return apdu_sw(resp, 0x63C0 | retries);
2253 }
2254 const bool set_ok = (slot == PIN_SLOT_PW1)
2255 ? pin_storage_openpgp_set_pw1_raw(apdu->data + old_len, new_raw)
2256 : pin_storage_openpgp_set_pw3_raw(apdu->data + old_len, new_raw);
2257 if (!set_ok) {
2258 return apdu_sw(resp, SW_WRONG_DATA);
2259 }
2260 LOG_I(TAG, "%s changed (KDF enable transition)", log_label);
2261 return apdu_sw(resp, SW_OK);
2262 }
2263 }
2264
2265 if (apdu->lc < min_len * 2) {
2266 return apdu_sw(resp, SW_WRONG_LENGTH);
2267 }
2268
2269 if (try_change_pin(apdu->data, apdu->lc, min_len, slot, change_fn)) {
2270 LOG_I(TAG, "%s changed successfully", log_label);
2271 return apdu_sw(resp, SW_OK);
2272 }
2273
2274 pin_slot_t slot_for_decrement = slot;
2275 char dummy_pin[OPENPGP_PIN_MAX_LEN + 1] = {};
2276 // Trigger a single retry decrement via the regular path to keep the
2277 // remote counter in sync with the failed CHANGE attempt.
2278 if (slot_for_decrement == PIN_SLOT_PW1) {
2280 } else {
2282 }
2283
2284 uint8_t retries = retries_fn();
2285 if (retries == 0) {
2286 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
2287 }
2288 return apdu_sw(resp, 0x63C0 | retries);
2289}
2290
2298static int cmd_pso_cds(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2299 if (!pw1_verified) {
2300 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2301 }
2302
2303 if (role_is_rsa[0]) {
2304 static EXT_RAM_BSS_ATTR uint8_t blob[GPG_RSA_BLOB_MAX];
2305 size_t blob_len = 0;
2306 if (!gpg_storage_load_rsa_key(0, blob, sizeof(blob), &blob_len, nullptr)) {
2308 }
2309 static EXT_RAM_BSS_ATTR uint8_t rsa_sig[GPG_RSA_MAX_MODULUS_BYTES];
2310 size_t rsa_sig_len = 0;
2311 bool ok = gpg_rsa_sign(blob, blob_len, apdu->data, apdu->lc,
2312 rsa_sig, sizeof(rsa_sig), &rsa_sig_len);
2313 mbedtls_platform_zeroize(blob, sizeof(blob));
2314 if (!ok) {
2315 return apdu_sw(resp, SW_UNKNOWN);
2316 }
2317 sig_count++;
2319 return apdu_build_response(resp, resp_max, rsa_sig, rsa_sig_len, SW_OK);
2320 }
2321
2322 // Check if signature key exists by trying to read it
2323 uint8_t pubkey[P256_PUBKEY_SIZE];
2324 uint8_t curve;
2325 if (!se_ecc_key_read(gpg_storage_sig_slot(), pubkey, sizeof(pubkey), &curve)) {
2326 LOG_E(TAG, "No signature key configured");
2328 }
2329
2330 // Sign hash directly using TROPIC01
2331 // P-256 uses ECDSA, Ed25519 uses EdDSA
2332 uint8_t signature[64]; // R (32 bytes) || S (32 bytes)
2333
2334 bool success;
2335 if (curve == CDC_CURVE_P256) {
2336 if (apdu->lc != SHA256_DIGEST_SIZE) {
2337 return apdu_sw(resp, SW_WRONG_DATA);
2338 }
2339 success = se_ecdsa_sign(gpg_storage_sig_slot(), apdu->data, apdu->lc, signature);
2340 } else {
2341 success = se_eddsa_sign(gpg_storage_sig_slot(), apdu->data, apdu->lc, signature);
2342 }
2343
2344 if (!success) {
2345 LOG_E(TAG, "Signature failed");
2346 return apdu_sw(resp, SW_UNKNOWN);
2347 }
2348
2349 // Increment signature counter
2350 sig_count++;
2352
2353 LOG_I(TAG, "Signature created, count=%lu", sig_count);
2354 return apdu_build_response(resp, resp_max, signature, 64, SW_OK);
2355}
2356
2398static int cmd_pso_decipher_aes(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2399 if (!gpg_storage_has_aes_key()) {
2401 }
2402 if (apdu->lc < 1 + 16 + 1) {
2403 return apdu_sw(resp, SW_WRONG_DATA);
2404 }
2405 const uint8_t* iv_in = apdu->data + 1;
2406 const uint8_t* ct = apdu->data + 1 + 16;
2407 size_t ct_len = apdu->lc - 1 - 16;
2408 if (ct_len > resp_max - 2) {
2409 return apdu_sw(resp, SW_WRONG_LENGTH);
2410 }
2411
2412 uint8_t aes_key[32] = {};
2413 size_t aes_key_len = 0;
2414 if (!gpg_storage_load_aes_key(aes_key, &aes_key_len, s_session_pin[0] ? s_session_pin : nullptr)) {
2415 mbedtls_platform_zeroize(aes_key, sizeof(aes_key));
2416 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2417 }
2418
2419 mbedtls_aes_context aes;
2420 mbedtls_aes_init(&aes);
2421 int rc = mbedtls_aes_setkey_enc(&aes, aes_key, static_cast<unsigned int>(aes_key_len * 8));
2422 if (rc != 0) {
2423 mbedtls_aes_free(&aes);
2424 mbedtls_platform_zeroize(aes_key, sizeof(aes_key));
2425 return apdu_sw(resp, SW_UNKNOWN);
2426 }
2427
2428 uint8_t iv[16];
2429 memcpy(iv, iv_in, sizeof(iv));
2430 size_t iv_off = 0;
2431 uint8_t plain[256];
2432 if (ct_len > sizeof(plain)) {
2433 mbedtls_aes_free(&aes);
2434 mbedtls_platform_zeroize(aes_key, sizeof(aes_key));
2435 return apdu_sw(resp, SW_WRONG_LENGTH);
2436 }
2437 rc = mbedtls_aes_crypt_cfb128(&aes, MBEDTLS_AES_DECRYPT, ct_len, &iv_off, iv, ct, plain);
2438 mbedtls_aes_free(&aes);
2439 mbedtls_platform_zeroize(aes_key, sizeof(aes_key));
2440 mbedtls_platform_zeroize(iv, sizeof(iv));
2441 if (rc != 0) {
2442 mbedtls_platform_zeroize(plain, sizeof(plain));
2443 return apdu_sw(resp, SW_UNKNOWN);
2444 }
2445 size_t n = apdu_build_response(resp, resp_max, plain, ct_len, SW_OK);
2446 mbedtls_platform_zeroize(plain, sizeof(plain));
2447 return n;
2448}
2449
2450static int cmd_pso_decipher(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2451 if (!pw1_verified) {
2452 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2453 }
2454 if (apdu->lc < 1) {
2455 return apdu_sw(resp, SW_WRONG_DATA);
2456 }
2457
2458 // OpenPGP 3.4.1 §7.2.11: padding indicator 0x02 = AES decryption.
2459 if (apdu->data[0] == 0x02) {
2460 return cmd_pso_decipher_aes(apdu, resp, resp_max);
2461 }
2462
2463 if (role_is_rsa[1]) {
2464 // RSA decipher: data[0] = 0x00 padding indicator, remainder = cryptogram.
2465 if (apdu->lc < 2) {
2466 return apdu_sw(resp, SW_WRONG_DATA);
2467 }
2468 static EXT_RAM_BSS_ATTR uint8_t blob[GPG_RSA_BLOB_MAX];
2469 size_t blob_len = 0;
2470 if (!gpg_storage_load_rsa_key(1, blob, sizeof(blob), &blob_len, nullptr)) {
2472 }
2473 static EXT_RAM_BSS_ATTR uint8_t pt[GPG_RSA_MAX_MODULUS_BYTES];
2474 size_t pt_len = 0;
2475 bool ok = gpg_rsa_decrypt(blob, blob_len, apdu->data + 1, apdu->lc - 1,
2476 pt, sizeof(pt), &pt_len);
2477 mbedtls_platform_zeroize(blob, sizeof(blob));
2478 if (!ok) {
2479 mbedtls_platform_zeroize(pt, sizeof(pt));
2480 return apdu_sw(resp, SW_UNKNOWN);
2481 }
2482 int n = apdu_build_response(resp, resp_max, pt, pt_len, SW_OK);
2483 mbedtls_platform_zeroize(pt, sizeof(pt));
2484 return n;
2485 }
2486
2489 }
2490
2491 // Parse Cipher DO (A6 -> 7F49 -> 86), per OpenPGP 3.4.1 §7.2.11.
2492 // Minimum: A6 <len1> 7F49 <len2> 86 <len3> <65 bytes pubkey>
2493 // With single-byte lengths: A6 46 7F49 43 86 41 <65 bytes> = 72 bytes
2494 if (apdu->lc < 70) {
2495 return apdu_sw(resp, SW_WRONG_DATA);
2496 }
2497
2498 const uint8_t* p = apdu->data;
2499 const uint8_t* end = apdu->data + apdu->lc;
2500
2501 if (p >= end || *p != 0xA6) {
2502 return apdu_sw(resp, SW_WRONG_DATA);
2503 }
2504 p++;
2505
2506 if (p >= end) return apdu_sw(resp, SW_WRONG_DATA);
2507 if (*p < 0x80) {
2508 p += 1;
2509 } else if (*p == 0x81 && p + 1 < end) {
2510 p += 2;
2511 } else if (*p == 0x82 && p + 2 < end) {
2512 p += 3;
2513 } else {
2514 return apdu_sw(resp, SW_WRONG_DATA);
2515 }
2516
2517 if (p + 2 > end || p[0] != 0x7F || p[1] != 0x49) {
2518 return apdu_sw(resp, SW_WRONG_DATA);
2519 }
2520 p += 2;
2521
2522 if (p >= end) return apdu_sw(resp, SW_WRONG_DATA);
2523 if (*p < 0x80) {
2524 p += 1;
2525 } else if (*p == 0x81 && p + 1 < end) {
2526 p += 2;
2527 } else if (*p == 0x82 && p + 2 < end) {
2528 p += 3;
2529 } else {
2530 return apdu_sw(resp, SW_WRONG_DATA);
2531 }
2532
2533 if (p >= end || *p != 0x86) {
2534 return apdu_sw(resp, SW_WRONG_DATA);
2535 }
2536 p++;
2537
2538 if (p >= end) return apdu_sw(resp, SW_WRONG_DATA);
2539 size_t pubkey_len;
2540 if (*p < 0x80) {
2541 pubkey_len = *p++;
2542 } else if (*p == 0x81 && p + 1 < end) {
2543 pubkey_len = p[1];
2544 p += 2;
2545 } else if (*p == 0x82 && p + 2 < end) {
2546 pubkey_len = (static_cast<size_t>(p[1]) << 8) | p[2];
2547 p += 3;
2548 } else {
2549 return apdu_sw(resp, SW_WRONG_DATA);
2550 }
2551
2552 if (pubkey_len != P256_PUBKEY_SIZE || p + pubkey_len > end) {
2553 return apdu_sw(resp, SW_WRONG_DATA);
2554 }
2555 if (p[0] != 0x04) {
2556 return apdu_sw(resp, SW_WRONG_DATA);
2557 }
2558 const uint8_t* peer_pubkey = p;
2559
2560 uint8_t dec_privkey[P256_PRIVKEY_SIZE];
2561 if (!gpg_storage_load_dec_privkey(dec_privkey, nullptr)) {
2562 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2563 }
2564
2565 uint8_t shared_secret[P256_ECDH_SECRET_SIZE];
2566 bool ok = ecdh_p256_compute_shared_secret(dec_privkey, peer_pubkey, shared_secret);
2567 mbedtls_platform_zeroize(dec_privkey, sizeof(dec_privkey));
2568 if (!ok) {
2569 mbedtls_platform_zeroize(shared_secret, sizeof(shared_secret));
2570 return apdu_sw(resp, SW_UNKNOWN);
2571 }
2572 int n = apdu_build_response(resp, resp_max, shared_secret, P256_ECDH_SECRET_SIZE, SW_OK);
2573 mbedtls_platform_zeroize(shared_secret, sizeof(shared_secret));
2574 return n;
2575}
2576
2591static int cmd_manage_security_env(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2592 (void)resp_max;
2593 if (apdu->p1 != 0x41) {
2594 return apdu_sw(resp, SW_INCORRECT_P1P2);
2595 }
2596 if (apdu->p2 != KEY_SIG && apdu->p2 != KEY_DEC && apdu->p2 != KEY_AUT) {
2597 return apdu_sw(resp, SW_INCORRECT_P1P2);
2598 }
2599 if (apdu->lc == 0 || apdu->data == nullptr) {
2600 return apdu_sw(resp, SW_WRONG_LENGTH);
2601 }
2602 // Parse the Cryptographic Reference Template: expect 83 01 <ref>.
2603 if (apdu->lc < 3 || apdu->data[0] != 0x83 || apdu->data[1] != 0x01) {
2604 return apdu_sw(resp, SW_WRONG_DATA);
2605 }
2606 const uint8_t ref = apdu->data[2];
2607 if (ref != 0x01 && ref != 0x02 && ref != 0x03) {
2608 return apdu_sw(resp, SW_WRONG_DATA);
2609 }
2610 // Cross-check: tag-83 reference must agree with P2 role.
2611 if ((apdu->p2 == KEY_SIG && ref != 0x01) ||
2612 (apdu->p2 == KEY_DEC && ref != 0x02) ||
2613 (apdu->p2 == KEY_AUT && ref != 0x03)) {
2615 }
2616 return apdu_sw(resp, SW_OK);
2617}
2618
2631static int cmd_internal_authenticate(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2632 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2633 return apdu_sw(resp, SW_INCORRECT_P1P2);
2634 }
2635 if (!pw1_verified) {
2636 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2637 }
2638 if (apdu->lc == 0 || apdu->data == nullptr) {
2639 return apdu_sw(resp, SW_WRONG_LENGTH);
2640 }
2641
2642 if (role_is_rsa[2]) {
2643 static EXT_RAM_BSS_ATTR uint8_t blob[GPG_RSA_BLOB_MAX];
2644 size_t blob_len = 0;
2645 if (!gpg_storage_load_rsa_key(2, blob, sizeof(blob), &blob_len, nullptr)) {
2647 }
2648 static EXT_RAM_BSS_ATTR uint8_t rsa_sig[GPG_RSA_MAX_MODULUS_BYTES];
2649 size_t rsa_sig_len = 0;
2650 bool ok = gpg_rsa_sign(blob, blob_len, apdu->data, apdu->lc,
2651 rsa_sig, sizeof(rsa_sig), &rsa_sig_len);
2652 mbedtls_platform_zeroize(blob, sizeof(blob));
2653 if (!ok) {
2654 return apdu_sw(resp, SW_UNKNOWN);
2655 }
2656 return apdu_build_response(resp, resp_max, rsa_sig, rsa_sig_len, SW_OK);
2657 }
2658
2659 uint8_t pubkey[P256_PUBKEY_SIZE];
2660 uint8_t curve = 0;
2661 if (!se_ecc_key_read(gpg_storage_aut_slot(), pubkey, sizeof(pubkey), &curve)) {
2662 LOG_E(TAG, "No AUT key configured");
2664 }
2665
2666 uint8_t signature[64];
2667 bool ok = false;
2668 if (curve == CDC_CURVE_P256) {
2669 ok = se_ecdsa_sign(gpg_storage_aut_slot(), apdu->data, apdu->lc, signature);
2670 } else {
2671 ok = se_eddsa_sign(gpg_storage_aut_slot(), apdu->data, apdu->lc, signature);
2672 }
2673 if (!ok) {
2674 LOG_E(TAG, "INTERNAL AUTHENTICATE: signing failed");
2675 return apdu_sw(resp, SW_UNKNOWN);
2676 }
2677 return apdu_build_response(resp, resp_max, signature, sizeof(signature), SW_OK);
2678}
2679
2690static int cmd_terminate_df(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2691 (void)resp_max;
2692 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2693 return apdu_sw(resp, SW_INCORRECT_P1P2);
2694 }
2695 const bool both_blocked = pin_storage_openpgp_pw1_blocked() &&
2697 if (!pw3_verified && !both_blocked) {
2698 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2699 }
2700 card_terminated = true;
2701 pw1_verified = false;
2702 pw3_verified = false;
2704 LOG_W(TAG, "Card moved to TERMINATED state");
2705 return apdu_sw(resp, SW_OK);
2706}
2707
2716static int cmd_activate_file(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2717 (void)resp_max;
2718 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
2719 return apdu_sw(resp, SW_INCORRECT_P1P2);
2720 }
2721 if (!card_terminated) {
2722 return apdu_sw(resp, SW_OK);
2723 }
2725 LOG_W(TAG, "ACTIVATE FILE: card reset to factory defaults");
2726 return apdu_sw(resp, SW_OK);
2727}
2728
2730 auto* se = get_se();
2731 if (se) {
2732 se->eccDelete(gpg_storage_sig_slot());
2733 se->eccDelete(gpg_storage_aut_slot());
2734 }
2741 save_cardholder_cert(nullptr, 0);
2742
2743 memset(fingerprint_sig, 0, sizeof(fingerprint_sig));
2744 memset(fingerprint_dec, 0, sizeof(fingerprint_dec));
2745 memset(fingerprint_aut, 0, sizeof(fingerprint_aut));
2746 memset(gen_time_sig, 0, sizeof(gen_time_sig));
2747 memset(gen_time_dec, 0, sizeof(gen_time_dec));
2748 memset(gen_time_aut, 0, sizeof(gen_time_aut));
2749 memset(ca_fp_1, 0, sizeof(ca_fp_1));
2750 memset(ca_fp_2, 0, sizeof(ca_fp_2));
2751 memset(ca_fp_3, 0, sizeof(ca_fp_3));
2752 memset(cardholder_name, 0, sizeof(cardholder_name));
2753 memset(cardholder_url, 0, sizeof(cardholder_url));
2754 memset(cardholder_login, 0, sizeof(cardholder_login));
2755 snprintf(cardholder_lang, sizeof(cardholder_lang), "en");
2756 cardholder_sex = 0x39;
2757 sig_count = 0;
2760 for (int r = 0; r < 3; ++r) {
2761 role_is_rsa[r] = false;
2762 role_rsa_n_bits[r] = 0;
2763 role_rsa_e_bits[r] = 0;
2764 role_rsa_fmt[r] = 0;
2765 }
2766 kdf_active = false;
2767 kdf_pin_len = 0;
2768 kdf_do_len = 0;
2769 mbedtls_platform_zeroize(kdf_do_bytes, sizeof(kdf_do_bytes));
2770 mbedtls_platform_zeroize(s_rc_salt, sizeof(s_rc_salt));
2771 mbedtls_platform_zeroize(s_rc_hash, sizeof(s_rc_hash));
2772 s_rc_len = 0;
2773 s_rc_retries = 3;
2774 pw1_verified = false;
2775 pw3_verified = false;
2776 card_terminated = false;
2778}
2779
2790static int cmd_reset_retry_counter(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
2791 (void)resp_max;
2792 if (apdu->p2 != PW1_CODE_1) {
2793 return apdu_sw(resp, SW_INCORRECT_P1P2);
2794 }
2795 if (apdu->p1 != 0x00 && apdu->p1 != 0x02) {
2796 return apdu_sw(resp, SW_INCORRECT_P1P2);
2797 }
2798 if (apdu->p1 == 0x00) {
2799 // RC path. Lc = RC || new PW1 (concatenated, no length prefix).
2800 if (s_rc_len == 0) {
2802 }
2803 if (s_rc_retries == 0) {
2804 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
2805 }
2806 if (apdu->lc < s_rc_len + OPENPGP_PW1_MIN_LEN ||
2807 apdu->lc - s_rc_len > OPENPGP_PIN_MAX_LEN) {
2808 return apdu_sw(resp, SW_WRONG_LENGTH);
2809 }
2810 uint8_t input_hash[RC_HASH_SIZE];
2811 if (!compute_rc_hash(apdu->data, s_rc_len, s_rc_salt, input_hash)) {
2812 return apdu_sw(resp, SW_UNKNOWN);
2813 }
2814 uint8_t diff = 0;
2815 for (size_t i = 0; i < RC_HASH_SIZE; ++i) {
2816 diff |= static_cast<uint8_t>(s_rc_hash[i] ^ input_hash[i]);
2817 }
2818 mbedtls_platform_zeroize(input_hash, sizeof(input_hash));
2819 if (diff != 0) {
2820 if (s_rc_retries > 0) s_rc_retries -= 1;
2822 const uint8_t retries = s_rc_retries;
2823 if (retries == 0) {
2824 return apdu_sw(resp, SW_AUTH_METHOD_BLOCKED);
2825 }
2826 return apdu_sw(resp, static_cast<uint16_t>(0x63C0 | retries));
2827 }
2828
2829 const size_t new_pw1_len = apdu->lc - s_rc_len;
2830 char new_pin[OPENPGP_PIN_MAX_LEN + 1] = {};
2831 memcpy(new_pin, apdu->data + s_rc_len, new_pw1_len);
2832 new_pin[new_pw1_len] = '\0';
2833 if (!pin_storage_openpgp_change_pw1(new_pin)) {
2834 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
2835 return apdu_sw(resp, SW_UNKNOWN);
2836 }
2837 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
2839 s_rc_retries = 3;
2840 pw1_verified = false;
2842 LOG_I(TAG, "RESET RETRY COUNTER: PW1 reset via RC");
2843 return apdu_sw(resp, SW_OK);
2844 }
2845 // P1 == 0x02 — admin-driven reset.
2846 if (!pw3_verified) {
2847 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
2848 }
2849 // A 32/64-byte new PW1 is a KDF pre-hash, even before kdf_active flips on
2850 // (gpg's kdf-setup resets PW1 this way prior to PUT DATA 0xF9).
2851 if (kdf_active || apdu->lc == 32 || apdu->lc == 64) {
2852 // New PW1 arrives as a PBKDF2 pre-hash.
2853 if (!pin_storage_openpgp_set_pw1_raw(apdu->data, apdu->lc)) {
2854 return apdu_sw(resp, SW_WRONG_LENGTH);
2855 }
2857 pw1_verified = false;
2858 LOG_I(TAG, "RESET RETRY COUNTER: PW1 reset by admin (KDF)");
2859 return apdu_sw(resp, SW_OK);
2860 }
2861 if (apdu->lc < OPENPGP_PW1_MIN_LEN || apdu->lc > OPENPGP_PIN_MAX_LEN) {
2862 return apdu_sw(resp, SW_WRONG_LENGTH);
2863 }
2864 char new_pin[OPENPGP_PIN_MAX_LEN + 1] = {};
2865 memcpy(new_pin, apdu->data, apdu->lc);
2866 new_pin[apdu->lc] = '\0';
2867 if (!pin_storage_openpgp_change_pw1(new_pin)) {
2868 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
2869 return apdu_sw(resp, SW_UNKNOWN);
2870 }
2871 mbedtls_platform_zeroize(new_pin, sizeof(new_pin));
2873 pw1_verified = false; // force re-verification with new PW1
2874 LOG_I(TAG, "RESET RETRY COUNTER: PW1 reset by admin");
2875 return apdu_sw(resp, SW_OK);
2876}
2877
2883static uint8_t get_ecc_slot_for_key_ref(uint8_t key_ref) {
2884 switch (key_ref) {
2885 case KEY_SIG: // 0xB6 - Signature
2886 return gpg_storage_sig_slot();
2887 case KEY_DEC: // 0xB8 - Decryption
2888 return gpg_storage_dec_slot();
2889 case KEY_AUT: // 0xA4 - Authentication
2890 return gpg_storage_aut_slot();
2891 default:
2892 return gpg_storage_sig_slot(); // Default to SIG
2893 }
2894}
2895
2901static key_type_t get_key_type_for_ref(uint8_t key_ref) {
2902 switch (key_ref) {
2903 case KEY_SIG: return KEY_TYPE_SIG;
2904 case KEY_DEC: return KEY_TYPE_DEC;
2905 case KEY_AUT: return KEY_TYPE_AUT;
2906 default: return KEY_TYPE_SIG;
2907 }
2908}
2909
2919static uint16_t generate_dec_key(uint8_t *pubkey_out) {
2920 uint8_t privkey[P256_PRIVKEY_SIZE];
2921 if (!ecdh_p256_generate_keypair(privkey, pubkey_out)) {
2922 return SW_UNKNOWN;
2923 }
2924 if (!gpg_storage_save_dec_privkey(privkey, nullptr)) {
2925 mbedtls_platform_zeroize(privkey, sizeof(privkey));
2926 return SW_UNKNOWN;
2927 }
2928 mbedtls_platform_zeroize(privkey, sizeof(privkey));
2929 return SW_OK;
2930}
2931
2938static uint16_t generate_hardware_key(uint8_t ecc_slot, uint8_t curve) {
2939 if (!se_ecc_key_generate(ecc_slot, curve)) {
2940 LOG_E(TAG, "Key generation failed for slot %d", ecc_slot);
2941 return SW_UNKNOWN;
2942 }
2943 LOG_I(TAG, "Key pair generated in slot %d (hardware)", ecc_slot);
2944 return SW_OK;
2945}
2946
2951static void update_generation_timestamp(uint8_t key_ref) {
2952 uint32_t now = (uint32_t)time(NULL);
2953 uint8_t ts[4] = {
2954 (uint8_t)((now >> 24) & 0xFF),
2955 (uint8_t)((now >> 16) & 0xFF),
2956 (uint8_t)((now >> 8) & 0xFF),
2957 (uint8_t)(now & 0xFF)
2958 };
2959
2960 switch (key_ref) {
2961 case KEY_SIG: memcpy(gen_time_sig, ts, 4); break;
2962 case KEY_DEC: memcpy(gen_time_dec, ts, 4); break;
2963 case KEY_AUT: memcpy(gen_time_aut, ts, 4); break;
2964 default: break;
2965 }
2967}
2968
2982static bool read_public_key(key_type_t key_type, uint8_t ecc_slot,
2983 uint8_t *pubkey, uint8_t *curve_out) {
2984 if (key_type == KEY_TYPE_DEC) {
2985 LOG_I(TAG, "read_public_key DEC: checking has_dec_privkey");
2987 LOG_W(TAG, "read_public_key DEC: no privkey");
2988 return false;
2989 }
2990 LOG_I(TAG, "read_public_key DEC: loading privkey");
2991 uint8_t privkey[P256_PRIVKEY_SIZE];
2992 if (!gpg_storage_load_dec_privkey(privkey, nullptr)) {
2993 LOG_W(TAG, "read_public_key DEC: load_dec_privkey failed");
2994 return false;
2995 }
2996 LOG_I(TAG, "read_public_key DEC: deriving pubkey");
2997 bool ok = ecdh_p256_derive_pubkey(privkey, pubkey);
2998 mbedtls_platform_zeroize(privkey, sizeof(privkey));
2999 if (curve_out) {
3000 *curve_out = CDC_CURVE_P256;
3001 }
3002 LOG_I(TAG, "read_public_key DEC: derive ok=%d", ok);
3003 return ok;
3004 }
3005
3006 return se_ecc_key_read(ecc_slot, pubkey, P256_PUBKEY_SIZE, curve_out);
3007}
3008
3020static void encode_pubkey_with_prefix(const uint8_t *pubkey, uint8_t curve,
3021 uint8_t *out, size_t *out_len) {
3022 if (curve == CDC_CURVE_P256) {
3023 if (pubkey[0] == 0x04) {
3024 memcpy(out, pubkey, P256_PUBKEY_SIZE);
3025 } else {
3026 out[0] = 0x04;
3027 memcpy(out + 1, pubkey, P256_PUBKEY_SIZE - 1);
3028 }
3029 *out_len = P256_PUBKEY_SIZE;
3030 } else {
3031 // Ed25519: raw 32-byte encoding, no SEC1 prefix.
3032 memcpy(out, pubkey, ED25519_PUBKEY_SIZE);
3033 *out_len = ED25519_PUBKEY_SIZE;
3034 }
3035}
3036
3043static int build_rsa_pubkey_from_storage(int r, uint8_t *resp, size_t resp_max) {
3044 static EXT_RAM_BSS_ATTR uint8_t blob[GPG_RSA_BLOB_MAX];
3045 size_t blob_len = 0;
3046 if (!gpg_storage_load_rsa_key(static_cast<uint8_t>(r), blob, sizeof(blob), &blob_len, nullptr)) {
3048 }
3049 static EXT_RAM_BSS_ATTR uint8_t n_buf[GPG_RSA_MAX_MODULUS_BYTES];
3050 uint8_t e_buf[8];
3051 size_t n_len = 0, e_len = 0;
3052 bool ok = gpg_rsa_blob_public(blob, blob_len, n_buf, sizeof(n_buf), &n_len,
3053 e_buf, sizeof(e_buf), &e_len);
3054 mbedtls_platform_zeroize(blob, sizeof(blob));
3055 if (!ok) {
3056 return apdu_sw(resp, SW_UNKNOWN);
3057 }
3058 // Inner public-key DO: 81 <modulus> 82 <exponent>.
3059 static EXT_RAM_BSS_ATTR uint8_t inner[GPG_RSA_MAX_MODULUS_BYTES + 16];
3060 size_t inner_len = 0;
3061 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len, 0x81, n_buf, n_len);
3062 inner_len += tlv_build(inner + inner_len, sizeof(inner) - inner_len, 0x82, e_buf, e_len);
3063 // Wrap in 7F49 (Public Key DO).
3064 static EXT_RAM_BSS_ATTR uint8_t outbuf[GPG_RSA_MAX_MODULUS_BYTES + 32];
3065 size_t pos = 0;
3066 pos += tlv_write_tag(outbuf + pos, 0x7F49);
3067 pos += tlv_write_len(outbuf + pos, inner_len);
3068 memcpy(outbuf + pos, inner, inner_len);
3069 pos += inner_len;
3070 return apdu_build_response(resp, resp_max, outbuf, pos, SW_OK);
3071}
3072
3080static int cmd_generate_keypair(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
3081 // Parse control reference template (CRT) from data.
3082 // Format: B6 00 (SIG) / B8 00 (DEC) / A4 00 (AUT)
3083 uint8_t key_ref = KEY_SIG; // Default to Signature key
3084
3085 if (apdu->lc >= 2) {
3086 key_ref = apdu->data[0];
3087 LOG_I(TAG, "Key ref from CRT: 0x%02X", key_ref);
3088 }
3089
3090 const uint8_t ecc_slot = get_ecc_slot_for_key_ref(key_ref);
3091 const key_type_t key_type = get_key_type_for_ref(key_ref);
3092
3093 LOG_I(TAG, "GENERATE_KEYPAIR: P1=0x%02X, key_ref=0x%02X, slot=%d, type=%d",
3094 apdu->p1, key_ref, ecc_slot, key_type);
3095
3096 // RSA roles are software keys (slower, less secure than the SE-backed ECC
3097 // path); generation and public-key read go through the mbedTLS backend.
3098 const int role_idx = role_index_for_key_ref(key_ref);
3099 if (role_idx >= 0 && role_is_rsa[role_idx]) {
3100 if (apdu->p1 == 0x80) {
3101 if (!pw3_verified) {
3102 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
3103 }
3104 static EXT_RAM_BSS_ATTR uint8_t blob[GPG_RSA_BLOB_MAX];
3105 size_t blob_len = 0;
3106 LOG_W(TAG, "Generating RSA-%u key (software fallback, slow) for role %d",
3107 role_rsa_n_bits[role_idx], role_idx);
3108 if (!gpg_rsa_generate(role_rsa_n_bits[role_idx], blob, sizeof(blob), &blob_len)) {
3109 return apdu_sw(resp, SW_UNKNOWN);
3110 }
3111 bool saved = gpg_storage_save_rsa_key(static_cast<uint8_t>(role_idx), blob, blob_len, nullptr);
3112 mbedtls_platform_zeroize(blob, sizeof(blob));
3113 if (!saved) {
3114 return apdu_sw(resp, SW_UNKNOWN);
3115 }
3117 return build_rsa_pubkey_from_storage(role_idx, resp, resp_max);
3118 }
3119 // P1 == 0x81: read existing public key.
3120 return build_rsa_pubkey_from_storage(role_idx, resp, resp_max);
3121 }
3122
3123 if (apdu->p1 == 0x80) {
3124 // P1=0x80: Generate new key (admin PIN required).
3125 if (!pw3_verified) {
3126 return apdu_sw(resp, SW_SECURITY_NOT_SATISFIED);
3127 }
3128
3129 // Curve choice follows the configured algorithm attributes:
3130 // SIG / AUT honour PUT DATA C1 / C3; DEC is fixed to P-256 (the only
3131 // curve the software-ECDH path supports).
3132 uint8_t curve = CDC_CURVE_P256;
3133 if (key_type == KEY_TYPE_SIG) curve = selected_curve_sig;
3134 else if (key_type == KEY_TYPE_AUT) curve = selected_curve_aut;
3135
3136 LOG_I(TAG, "Generating key in slot %d (curve=%d, type=%s)",
3137 ecc_slot, curve,
3138 key_type == KEY_TYPE_SIG ? "SIG" :
3139 key_type == KEY_TYPE_DEC ? "DEC" : "AUT");
3140
3141 uint8_t fresh_pubkey[P256_PUBKEY_SIZE] = {};
3142 uint16_t gen_sw;
3143 if (key_type == KEY_TYPE_DEC) {
3144 gen_sw = generate_dec_key(fresh_pubkey);
3145 } else {
3146 gen_sw = generate_hardware_key(ecc_slot, curve);
3147 }
3148 if (gen_sw != SW_OK) {
3149 return apdu_sw(resp, gen_sw);
3150 }
3151
3153
3154 if (key_type == KEY_TYPE_DEC) {
3155 uint8_t pubkey_with_prefix[P256_PUBKEY_SIZE];
3156 size_t pubkey_len = 0;
3158 pubkey_with_prefix, &pubkey_len);
3159
3160 uint8_t tlv_data[128];
3161 size_t pos = tlv_build(tlv_data, sizeof(tlv_data), 0x86,
3162 pubkey_with_prefix, pubkey_len);
3163
3164 uint8_t final_resp[140];
3165 size_t final_len = 0;
3166 final_resp[final_len++] = 0x7F;
3167 final_resp[final_len++] = 0x49;
3168 final_len += tlv_write_len(final_resp + final_len, pos);
3169 memcpy(final_resp + final_len, tlv_data, pos);
3170 final_len += pos;
3171 return apdu_build_response(resp, resp_max, final_resp, final_len, SW_OK);
3172 }
3173 }
3174
3175 // Read public key (P1=0x81 read, or after key generation above).
3176 uint8_t pubkey[P256_PUBKEY_SIZE];
3177 uint8_t read_curve = CDC_CURVE_P256;
3178 if (!read_public_key(key_type, ecc_slot, pubkey, &read_curve)) {
3179 LOG_W(TAG, "Public key read failed: empty slot=%d type=%d", ecc_slot, key_type);
3180 // Per OpenPGP 3.4.1 §7.2.14 GET DATA / GENERATE ASYMMETRIC KEY PAIR
3181 // with P1=0x81 on an unpopulated slot must signal "referenced data
3182 // not found" (SW=6A88). gpg / scdaemon treats 6A88 as "no key yet",
3183 // which is the natural state for a freshly-activated card and the
3184 // only way `gpg --card-status` completes without aborting.
3186 }
3187
3188 // Build TLV response according to OpenPGP 3.4.1: 7F49 <len> { 86 <len> <pubkey> }.
3189 // P-256: 0x04 || X || Y (65 bytes); Ed25519: 32 raw bytes.
3190 uint8_t pubkey_with_prefix[P256_PUBKEY_SIZE];
3191 size_t pubkey_len = 0;
3192 encode_pubkey_with_prefix(pubkey, read_curve, pubkey_with_prefix, &pubkey_len);
3193
3194 uint8_t tlv_data[128];
3195 size_t pos = 0;
3196 pos += tlv_build(tlv_data + pos, sizeof(tlv_data) - pos, 0x86, pubkey_with_prefix, pubkey_len);
3197
3198 // Wrap in 7F49 (Public Key DO).
3199 uint8_t final_resp[140];
3200 size_t final_len = 0;
3201 final_resp[final_len++] = 0x7F;
3202 final_resp[final_len++] = 0x49;
3203 final_len += tlv_write_len(final_resp + final_len, pos);
3204 memcpy(final_resp + final_len, tlv_data, pos);
3205 final_len += pos;
3206
3207 LOG_I(TAG, "Public key exported (%zu bytes, curve=%d, slot=%d)",
3208 pubkey_len, read_curve, ecc_slot);
3209 return apdu_build_response(resp, resp_max, final_resp, final_len, SW_OK);
3210}
3211
3221static int apply_response_chaining(uint32_t le, uint8_t *resp, size_t resp_max,
3222 int result_len) {
3223 if (result_len < 2) return result_len;
3224 const size_t payload_len = static_cast<size_t>(result_len - 2);
3225 const uint16_t sw = static_cast<uint16_t>((resp[result_len - 2] << 8) |
3226 resp[result_len - 1]);
3227 // Only chain on successful payloads; error SWs must surface verbatim.
3228 if (sw != SW_OK) return result_len;
3229 if (le == 0 || payload_len <= le) {
3230 return result_len;
3231 }
3232 const size_t remainder = payload_len - le;
3233 if (remainder > sizeof(g_resp_buffer)) {
3234 LOG_W(TAG, "Response remainder %zu > %zu, truncating", remainder, sizeof(g_resp_buffer));
3235 return result_len; // Best-effort: caller gets the truncated head.
3236 }
3237 memcpy(g_resp_buffer, resp + le, remainder);
3238 g_resp_remaining = remainder;
3239 g_resp_pos = 0;
3240
3241 if (resp_max < le + 2) return result_len; // Defensive: caller's buf too small.
3242 const uint8_t sw2 = (remainder > 0xFF) ? 0x00 : static_cast<uint8_t>(remainder);
3243 resp[le] = 0x61;
3244 resp[le + 1] = sw2;
3245 return static_cast<int>(le + 2);
3246}
3247
3251static int cmd_get_response(const apdu_t *apdu, uint8_t *resp, size_t resp_max) {
3252 if (apdu->p1 != 0x00 || apdu->p2 != 0x00) {
3253 return apdu_sw(resp, SW_INCORRECT_P1P2);
3254 }
3255 if (g_resp_remaining == 0) {
3257 }
3258 size_t want = (apdu->le > 0) ? apdu->le : 256;
3259 if (want > g_resp_remaining) want = g_resp_remaining;
3260 if (want + 2 > resp_max) want = resp_max - 2;
3261
3262 memcpy(resp, g_resp_buffer + g_resp_pos, want);
3263 // Wipe the bytes we just handed out so the PSRAM-backed chain buffer
3264 // doesn't retain a copy after delivery (PSO:DECIPHER shared secret can
3265 // end up here when Le forces a chain).
3266 mbedtls_platform_zeroize(g_resp_buffer + g_resp_pos, want);
3267 g_resp_pos += want;
3268 g_resp_remaining -= want;
3269
3270 uint16_t sw = SW_OK;
3271 if (g_resp_remaining > 0) {
3272 const uint8_t sw2 = (g_resp_remaining > 0xFF) ? 0x00
3273 : static_cast<uint8_t>(g_resp_remaining);
3274 sw = static_cast<uint16_t>((0x61 << 8) | sw2);
3275 } else {
3276 g_resp_pos = 0;
3277 }
3278 resp[want] = static_cast<uint8_t>((sw >> 8) & 0xFF);
3279 resp[want + 1] = static_cast<uint8_t>(sw & 0xFF);
3280 return static_cast<int>(want + 2);
3281}
3282
3283int openpgp_process_apdu(const uint8_t *cmd, size_t cmd_len,
3284 uint8_t *resp, size_t resp_max) {
3285 apdu_t apdu;
3286
3287 if (!apdu_parse(cmd, cmd_len, &apdu)) {
3288 LOG_E(TAG, "Invalid APDU");
3289 return apdu_sw(resp, SW_WRONG_LENGTH);
3290 }
3291
3292 LOG_D(TAG, "APDU: CLA=%02X INS=%02X P1=%02X P2=%02X Lc=%d",
3293 apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc);
3294
3295 // Any command other than GET RESPONSE invalidates a pending chained payload.
3296 if (apdu.ins != INS_GET_RESPONSE) {
3297 g_resp_remaining = 0;
3298 g_resp_pos = 0;
3299 }
3300
3301 // ISO 7816 CLA: bit 4 = chaining. Reject secure messaging and channels >0.
3302 if ((apdu.cla & ~0x10) != 0x00) {
3303 chain_reset();
3304 return apdu_sw(resp, SW_CLA_NOT_SUPPORTED);
3305 }
3306
3307 // Command chaining (ISO 7816-4 §5.1.1): accumulate data while the CLA
3308 // chaining bit is set; dispatch once it clears. Any deviation in
3309 // INS/P1/P2 mid-chain is a protocol error and the chain is dropped.
3310 const bool is_chain_block = (apdu.cla & 0x10) != 0;
3311 if (is_chain_block || g_chain_active) {
3312 if (!g_chain_active) {
3313 g_chain_active = true;
3314 g_chain_ins = apdu.ins;
3315 g_chain_p1 = apdu.p1;
3316 g_chain_p2 = apdu.p2;
3317 g_chain_len = 0;
3318 } else if (apdu.ins != g_chain_ins ||
3319 apdu.p1 != g_chain_p1 ||
3320 apdu.p2 != g_chain_p2) {
3321 chain_reset();
3322 return apdu_sw(resp, SW_WRONG_DATA);
3323 }
3324 if (g_chain_len + apdu.lc > sizeof(g_chain_buffer)) {
3325 chain_reset();
3326 return apdu_sw(resp, SW_WRONG_LENGTH);
3327 }
3328 if (apdu.lc > 0 && apdu.data != nullptr) {
3329 memcpy(g_chain_buffer + g_chain_len, apdu.data, apdu.lc);
3330 g_chain_len += apdu.lc;
3331 }
3332 if (is_chain_block) {
3333 // Intermediate block: ACK and wait for more.
3334 return apdu_sw(resp, SW_OK);
3335 }
3336 // Final block — replace the parsed apdu's payload with the
3337 // accumulator so the per-command handlers see the full data.
3338 apdu.data = g_chain_buffer;
3339 apdu.lc = static_cast<uint16_t>(g_chain_len);
3340 chain_reset();
3341 }
3342
3343 // SELECT is always allowed, even in TERMINATED state — otherwise the host
3344 // could not target the application to issue ACTIVATE FILE.
3345 if (apdu.ins == INS_SELECT) {
3346 return cmd_select(&apdu, resp, resp_max);
3347 }
3348
3349 // All other commands require application to be selected
3350 if (!app_selected) {
3352 }
3353
3354 // While terminated, only ACTIVATE FILE is honoured. Per OpenPGP 3.4.1
3355 // §7.2.18 every other INS must return SW_FILE_TERMINATED (0x6285).
3356 if (card_terminated && apdu.ins != INS_ACTIVATE) {
3357 return apdu_sw(resp, SW_FILE_TERMINATED);
3358 }
3359
3360 int result_len = 0;
3361 switch (apdu.ins) {
3362 case INS_GET_DATA:
3363 result_len = cmd_get_data(&apdu, resp, resp_max);
3364 break;
3365
3366 case INS_PUT_DATA:
3367 result_len = cmd_put_data(&apdu, resp, resp_max);
3368 break;
3369
3370 case INS_PUT_DATA_ODD:
3371 result_len = cmd_put_data_odd(&apdu, resp, resp_max);
3372 break;
3373
3374 case INS_VERIFY:
3375 result_len = cmd_verify(&apdu, resp, resp_max);
3376 break;
3377
3378 case INS_CHANGE_PIN:
3379 result_len = cmd_change_reference_data(&apdu, resp, resp_max);
3380 break;
3381
3382 case INS_RESET_RETRY:
3383 result_len = cmd_reset_retry_counter(&apdu, resp, resp_max);
3384 break;
3385
3386 case INS_PSO:
3387 if (apdu.p1 == 0x9E && apdu.p2 == 0x9A) {
3388 result_len = cmd_pso_cds(&apdu, resp, resp_max);
3389 } else if (apdu.p1 == 0x80 && apdu.p2 == 0x86) {
3390 result_len = cmd_pso_decipher(&apdu, resp, resp_max);
3391 } else {
3392 result_len = apdu_sw(resp, SW_INCORRECT_P1P2);
3393 }
3394 break;
3395
3396 case INS_INTERNAL_AUTH:
3397 result_len = cmd_internal_authenticate(&apdu, resp, resp_max);
3398 break;
3399
3400 case INS_MSE:
3401 result_len = cmd_manage_security_env(&apdu, resp, resp_max);
3402 break;
3403
3405 result_len = cmd_generate_keypair(&apdu, resp, resp_max);
3406 break;
3407
3408 case INS_GET_CHALLENGE: {
3409 uint8_t challenge[255];
3410 size_t len = apdu.le > 0 ? apdu.le : 8;
3411 if (len > sizeof(challenge)) len = sizeof(challenge);
3412 se_random_fill(challenge, len);
3413 result_len = apdu_build_response(resp, resp_max, challenge, len, SW_OK);
3414 break;
3415 }
3416
3417 case INS_GET_VERSION: {
3418 // Vendor command (pico-openpgp heritage): report the firmware version.
3419#ifndef APP_VERSION
3420#define APP_VERSION "0.0.0"
3421#endif
3422 const char* ver = APP_VERSION;
3423 result_len = apdu_build_response(resp, resp_max,
3424 reinterpret_cast<const uint8_t*>(ver),
3425 strlen(ver), SW_OK);
3426 break;
3427 }
3428
3429 case INS_GET_RESPONSE:
3430 // GET RESPONSE is handled before chaining is applied — it owns
3431 // the chained buffer directly.
3432 return cmd_get_response(&apdu, resp, resp_max);
3433
3434 case INS_TERMINATE:
3435 result_len = cmd_terminate_df(&apdu, resp, resp_max);
3436 break;
3437
3438 case INS_ACTIVATE:
3439 result_len = cmd_activate_file(&apdu, resp, resp_max);
3440 break;
3441
3442 default:
3443 LOG_W(TAG, "Unknown instruction: 0x%02X", apdu.ins);
3444 return apdu_sw(resp, SW_INS_NOT_SUPPORTED);
3445 }
3446
3447 return apply_response_chaining(apdu.le, resp, resp_max, result_len);
3448}
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_rsa_key(uint8_t role)
Deletes the RSA private-key blob for the role (both slots).
#define GPG_RSA_BLOB_MAX
Maximum serialized RSA private-key blob (RSA-4096 n_bits||e||p||q).
Definition GpgStorage.h:64
bool gpg_storage_delete_dec_privkey(void)
Deletes DEC private key record.
uint8_t gpg_storage_dec_slot(void)
bool gpg_storage_save_dec_privkey(const uint8_t *privkey, const char *pin)
Saves a DEC private key into R-Memory using PIN-bound AES-GCM.
bool gpg_storage_save_rsa_key(uint8_t role, const uint8_t *blob, size_t blob_len, const char *pin)
Saves an encrypted RSA private-key blob for a key role.
void gpg_storage_set_session_pin(const char *pin)
Stores session PIN-derived key after successful PIN verification.
bool gpg_storage_has_rsa_key(uint8_t role)
Returns true if an RSA private-key blob exists for the role.
bool gpg_storage_load_aes_key(uint8_t *key_out, size_t *key_len_out, const char *pin)
Loads the symmetric AES key from R-Memory.
bool gpg_storage_load_dec_privkey(uint8_t *privkey_out, const char *pin)
Loads and decrypts the DEC private key from R-Memory.
uint8_t gpg_storage_aut_slot(void)
void gpg_storage_clear_session(void)
Clears the cached session key.
uint8_t gpg_storage_sig_slot(void)
bool gpg_storage_load_rsa_key(uint8_t role, uint8_t *blob_out, size_t blob_cap, size_t *blob_len_out, const char *pin)
Loads and decrypts the RSA private-key blob for a key role.
bool gpg_storage_has_dec_privkey(void)
Returns true if encrypted DEC private key record exists.
bool gpg_storage_has_aes_key(void)
Returns true if a symmetric AES key record exists.
char name[cdc::hal::ISecureElement::RMEM_NAME_LEN]
algo_attr_status_t algo_attr_validate_role(const algo_attr_t *attr, algo_attr_role_t role)
Check whether the parsed attribute is compatible with the key role it will be installed into.
algo_attr_status_t algo_attr_parse(const uint8_t *bytes, size_t len, algo_attr_t *out)
Parse a raw algorithm-attribute byte sequence into structured form.
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_GET_VERSION
Definition apdu.h:36
#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 constexpr const char * DEFAULT_PW1
Definition PinManager.h:75
static constexpr const char * DEFAULT_PW3
Definition PinManager.h:76
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:74
#define P256_PUBKEY_SIZE
P-256 uncompressed public key size: 0x04 || X(32) || Y(32).
Definition constants.h:53
#define PW3_CODE
PW3 reference (Admin PIN).
Definition constants.h:96
#define PW1_CODE_1
PW1 reference for signature operations (User PIN).
Definition constants.h:90
#define PW1_CODE_2
PW1 reference for non-signature operations (User PIN, alt).
Definition constants.h:93
#define SHA256_DIGEST_SIZE
SHA-256 digest output size in bytes (FIPS 180-4).
Definition constants.h:48
#define OPENPGP_FINGERPRINT_SIZE
OpenPGP v4 fingerprint size (SHA-1 digest length, in bytes).
Definition constants.h:45
#define P256_ECDH_SECRET_SIZE
P-256 ECDH shared secret size in bytes.
Definition constants.h:59
#define P256_PRIVKEY_SIZE
P-256 private key (scalar) size in bytes.
Definition constants.h:56
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
kdf_hash_t
Hash algorithms accepted inside KDF-DO (inner tag 0x82).
Definition kdf.h:44
@ KDF_HASH_SHA256
Definition kdf.h:46
@ KDF_HASH_SHA512
Definition kdf.h:47
kdf_status_t kdf_do_parse(const uint8_t *bytes, size_t len, kdf_do_t *out)
Parse a KDF-DO byte sequence into structured form.
Definition kdf.cpp:28
kdf_status_t kdf_do_build_disabled(uint8_t *out, size_t out_cap, size_t *out_len)
Convenience helper: produce the "KDF disabled" KDF-DO body — three bytes (81 01 00) — that hosts expe...
Definition kdf.cpp:126
@ KDF_ALGO_NONE
Definition kdf.h:39
@ KDF_OK
Definition kdf.h:73
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:1064
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:3020
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:504
static uint8_t fingerprint_dec[OPENPGP_FINGERPRINT_SIZE]
Definition openpgp.cpp:372
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:1221
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:2591
static size_t g_resp_remaining
Definition openpgp.cpp:287
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:701
static const uint8_t ALGO_ATTR_P256_ECDH[]
Algorithm attributes for P-256 ECDH (decryption role).
Definition openpgp.cpp:435
static uint8_t g_chain_p2
Definition openpgp.cpp:304
static void load_state_from_nvs(void)
Loads persistent OpenPGP runtime state from NVS.
Definition openpgp.cpp:746
static uint16_t apply_kdf_do(const uint8_t *data, size_t len)
Applies a KDF-DO payload written via PUT DATA 0xF9.
Definition openpgp.cpp:951
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:2037
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:2919
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:1096
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:2080
static char cardholder_lang[8]
Definition openpgp.cpp:393
static size_t load_cardholder_cert(uint8_t *out, size_t cap)
Loads the cardholder certificate into out.
Definition openpgp.cpp:927
static const uint8_t ALGO_ATTR_P256_ECDSA[]
Algorithm attributes for P-256 ECDSA (signature/authentication roles).
Definition openpgp.cpp:425
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:3080
static constexpr uint8_t OPENPGP_NVS_SCHEMA_V3
Definition openpgp.cpp:366
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:126
static uint8_t kdf_hash_len(kdf_hash_t hash)
Returns the on-device byte length of an SHA-256/512 KDF pre-hash.
Definition openpgp.cpp:939
static uint8_t fingerprint_aut[OPENPGP_FINGERPRINT_SIZE]
Definition openpgp.cpp:373
static int build_do_app_related(uint8_t *buf, size_t buf_max)
Builds OpenPGP DO 0x6E (Application Related Data).
Definition openpgp.cpp:572
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:111
static int cmd_select(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Handles APDU SELECT command processing.
Definition openpgp.cpp:1165
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:2171
static int role_index_for_key_ref(uint8_t key_ref)
Maps an OpenPGP key reference (B6/B8/A4) to a 0-based role index.
Definition openpgp.cpp:994
static constexpr size_t RC_SALT_SIZE
Definition openpgp.cpp:185
static bool save_cardholder_cert(const uint8_t *data, size_t len)
Persists the cardholder certificate as a standalone NVS blob. len == 0 erases it. Stored unsigned: a ...
Definition openpgp.cpp:908
static uint8_t kdf_pin_len
Definition openpgp.cpp:262
static int cmd_pso_decipher(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Definition openpgp.cpp:2450
int openpgp_process_apdu(const uint8_t *cmd, size_t cmd_len, uint8_t *resp, size_t resp_max)
Definition openpgp.cpp:3283
put_data_kind_t
Storage kind for PUT DATA descriptor entries.
Definition openpgp.cpp:1424
@ PUT_KIND_BLOB_FIXED
Definition openpgp.cpp:1425
@ PUT_KIND_STRING_BOUNDED
Definition openpgp.cpp:1426
static size_t g_resp_pos
Definition openpgp.cpp:288
static cdc::hal::ISecureElement * get_se()
Returns secure-element instance used by OpenPGP backend.
Definition openpgp.cpp:43
static uint8_t kdf_do_len
Definition openpgp.cpp:264
static uint8_t g_chain_buffer[4096]
Command-chaining accumulator (ISO 7816-4 §5.1.1).
Definition openpgp.cpp:299
pin_slot_t
PIN slot identifier used by PIN helper routines.
Definition openpgp.cpp:2024
@ PIN_SLOT_PW1
Definition openpgp.cpp:2025
@ PIN_SLOT_PW3
Definition openpgp.cpp:2026
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:286
#define NVS_CERT_KEY
NVS key holding the (public) cardholder certificate (DO 0x7F21).
Definition openpgp.cpp:901
static uint8_t s_rc_hash[RC_HASH_SIZE]
Definition openpgp.cpp:189
static constexpr uint8_t ATTESTATION_ECC_SLOT
Definition openpgp.cpp:694
static void init_aid_from_mac(void)
Initializes the OpenPGP AID serial section from the ESP32 MAC address.
Definition openpgp.cpp:1007
bool openpgp_set_key_fingerprint(uint8_t key_type, const uint8_t *fingerprint, uint32_t gen_time)
Definition openpgp.cpp:1123
static constexpr size_t CARDHOLDER_CERT_MAX
Definition openpgp.cpp:902
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:2136
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:1928
static uint8_t s_rc_len
Definition openpgp.cpp:190
key_type_t
Builders for OpenPGP application-related data objects.
Definition openpgp.cpp:523
@ KEY_TYPE_SIG
Definition openpgp.cpp:524
@ KEY_TYPE_DEC
Definition openpgp.cpp:525
@ KEY_TYPE_AUT
Definition openpgp.cpp:526
static void se_random_fill(uint8_t *buf, size_t len)
Fills buffer with secure random bytes, with ESP fallback.
Definition openpgp.cpp:137
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:1087
#define NVS_STATE_KEY
Definition openpgp.cpp:323
static uint8_t gen_time_aut[4]
Definition openpgp.cpp:380
static uint16_t role_rsa_n_bits[3]
Definition openpgp.cpp:251
static constexpr size_t OPENPGP_STATE_SIG_SIZE
Definition openpgp.cpp:695
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:2938
static void update_generation_timestamp(uint8_t key_ref)
Updates and persists the generation timestamp for a key role.
Definition openpgp.cpp:2951
static bool se_ecc_key_generate(uint8_t slot, uint8_t curve)
Generates ECC key material in secure element slot.
Definition openpgp.cpp:76
bool openpgp_set_cardholder_name(const char *name)
Sets the cardholder name (OpenPGP DO 0x5B) and persists state.
Definition openpgp.cpp:1110
static uint8_t ca_fp_3[OPENPGP_FINGERPRINT_SIZE]
Definition openpgp.cpp:387
static uint8_t s_rc_retries
Definition openpgp.cpp:191
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:3221
static int respond_chunked(const uint8_t *payload, size_t payload_len, uint32_t le, uint8_t *resp, size_t resp_max)
Returns a payload larger than the response window using response chaining, priming the GET RESPONSE b...
Definition openpgp.cpp:1192
static bool pw3_verified
Definition openpgp.cpp:170
static bool pw1_verified
Definition openpgp.cpp:169
static uint8_t kdf_do_bytes[124]
Definition openpgp.cpp:263
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:2790
static char cardholder_url[64]
Definition openpgp.cpp:395
static uint8_t selected_curve_aut
Definition openpgp.cpp:240
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:535
static uint8_t ca_fp_1[OPENPGP_FINGERPRINT_SIZE]
Optional CA fingerprints for trust-chain metadata.
Definition openpgp.cpp:385
static void chain_reset(void)
Definition openpgp.cpp:306
static size_t tlv_write_len(uint8_t *buf, size_t len)
Writes a TLV length field using DER length encoding.
Definition openpgp.cpp:479
static uint8_t gen_time_sig[4]
Key-generation timestamps (4-byte big-endian Unix time each).
Definition openpgp.cpp:378
static constexpr size_t RC_HASH_SIZE
Definition openpgp.cpp:186
bool openpgp_init(void)
Definition openpgp.cpp:1037
static bool role_is_rsa[3]
Per-role algorithm selection beyond the ECC curve. When role_is_rsa[r] is set the role is an RSA soft...
Definition openpgp.cpp:250
#define APP_VERSION
#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:184
uint32_t openpgp_get_sig_count(void)
Definition openpgp.cpp:1060
static bool g_chain_active
Definition openpgp.cpp:301
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:2883
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:2901
static size_t tlv_write_tag(uint8_t *buf, uint16_t tag)
TLV builder helper functions.
Definition openpgp.cpp:463
static uint8_t s_rc_salt[RC_SALT_SIZE]
Definition openpgp.cpp:188
bool openpgp_has_any_key(void)
Reports whether any of the SIG / DEC / AUT roles has a non-zero fingerprint configured....
Definition openpgp.cpp:1081
bool openpgp_is_selected(void)
Definition openpgp.cpp:1056
static int put_data_algo_attr(uint16_t tag, const apdu_t *apdu, uint8_t *resp)
Definition openpgp.cpp:1559
static bool app_selected
ATR is defined in ccid.cpp and accessed via ccid_get_atr().
Definition openpgp.cpp:168
static bool kdf_active
KDF-DO (tag 0xF9) state. When kdf_active the host pre-hashes the PINs (PBKDF2) before VERIFY / CHANGE...
Definition openpgp.cpp:261
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:2298
static void save_state_to_nvs(void)
Persists OpenPGP runtime state to NVS.
Definition openpgp.cpp:824
static constexpr size_t RC_KDF_TOTAL_BYTES
Definition openpgp.cpp:187
static size_t g_chain_len
Definition openpgp.cpp:300
static int build_do_cardholder(uint8_t *buf, size_t buf_max)
Builds OpenPGP DO 0x65 (Cardholder Related Data).
Definition openpgp.cpp:666
void openpgp_factory_reset(void)
Definition openpgp.cpp:2729
static const put_data_desc_t * find_put_data_desc(uint16_t tag)
Returns descriptor for an OpenPGP PUT DATA tag.
Definition openpgp.cpp:1448
static int build_rsa_pubkey_from_storage(int r, uint8_t *resp, size_t resp_max)
Builds the 7F49 { 81 <modulus> 82 <exponent> } public-key response for an RSA role from its stored pr...
Definition openpgp.cpp:3043
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:2398
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:2631
static uint16_t role_rsa_e_bits[3]
Definition openpgp.cpp:252
static char cardholder_name[40]
Cardholder profile data stored in NVS.
Definition openpgp.cpp:392
static void wipe_role_key(int r)
Handles APDU PUT DATA command processing.
Definition openpgp.cpp:1535
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:1819
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:2716
static const uint8_t EXT_CAPABILITIES[]
Extended capabilities object per OpenPGP 3.4.1 section 4.2.1.
Definition openpgp.cpp:443
static char cardholder_login[32]
Definition openpgp.cpp:396
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:317
static uint8_t ca_fp_2[OPENPGP_FINGERPRINT_SIZE]
Definition openpgp.cpp:386
static uint8_t cardholder_sex
Definition openpgp.cpp:394
static int cmd_put_data(const apdu_t *apdu, uint8_t *resp, size_t resp_max)
Definition openpgp.cpp:1624
static bool parse_rsa_import(const uint8_t *tmpl, size_t tmpl_len, const uint8_t *concat, size_t concat_len, const uint8_t **e, size_t *e_len, const uint8_t **p, size_t *p_len, const uint8_t **q, size_t *q_len)
Splits the RSA key material (5F48) into e / p / q using the lengths declared in the Cardholder Privat...
Definition openpgp.cpp:1766
static uint8_t gen_time_dec[4]
Definition openpgp.cpp:379
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:55
static uint8_t s_openpgp_aid[16]
OpenPGP Application ID (RID + PIX), initialized dynamically.
Definition openpgp.cpp:151
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:1486
bool(* pin_change_fn_t)(const char *pin)
Type alias for PIN change callbacks.
Definition openpgp.cpp:2119
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:2690
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:2982
static uint8_t g_chain_ins
Definition openpgp.cpp:302
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:2092
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:239
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:197
static uint8_t role_rsa_fmt[3]
Definition openpgp.cpp:253
static const uint8_t ALGO_ATTR_ED25519[]
Algorithm attributes for Ed25519 (EdDSA with curve25519).
Definition openpgp.cpp:415
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:1721
static uint8_t g_chain_p1
Definition openpgp.cpp:303
static uint8_t fingerprint_sig[OPENPGP_FINGERPRINT_SIZE]
Data object storage buffers (fingerprints and related metadata).
Definition openpgp.cpp:371
static bool card_terminated
Card lifecycle state per OpenPGP 3.4.1 §7.2.18.
Definition openpgp.cpp:274
static bool fp_is_set(const uint8_t fp[OPENPGP_FINGERPRINT_SIZE])
Definition openpgp.cpp:1074
static const uint8_t HIST_BYTES[]
Historical bytes used in OpenPGP ATR-related data objects.
Definition openpgp.cpp:401
static uint32_t sig_count
Definition openpgp.cpp:171
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:3251
const uint8_t * OPENPGP_AID
Definition openpgp.cpp:158
#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:159
#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 ALGO_RSA
Definition openpgp.h:29
#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_set_pw1_raw(const uint8_t *data, size_t len)
bool pin_storage_openpgp_verify_pw3_raw(const uint8_t *data, size_t len)
bool pin_storage_openpgp_change_pw3(const char *new_pin)
uint8_t pin_storage_openpgp_pw3_retries(void)
bool pin_storage_openpgp_verify_pw1_raw(const uint8_t *data, size_t len)
bool pin_storage_openpgp_verify_pw1(const char *pin)
void pin_storage_openpgp_init(void)
bool pin_storage_openpgp_set_pw3_raw(const uint8_t *data, size_t len)
bool pin_storage_openpgp_verify_pw3(const char *pin)
bool pin_storage_openpgp_change_pw1(const char *new_pin)
bool pin_storage_openpgp_pw3_blocked(void)
bool gpg_rsa_decrypt(const uint8_t *blob, size_t blob_len, const uint8_t *ct, size_t ct_len, uint8_t *pt_out, size_t pt_cap, size_t *pt_len_out)
RSAES-PKCS1-v1.5 decryption of a cryptogram.
Definition rsa.cpp:267
bool gpg_rsa_sign(const uint8_t *blob, size_t blob_len, const uint8_t *digestinfo, size_t di_len, uint8_t *sig_out, size_t sig_cap, size_t *sig_len_out)
RSASSA-PKCS1-v1.5 signature over a host-supplied DigestInfo.
Definition rsa.cpp:185
bool gpg_rsa_blob_public(const uint8_t *blob, size_t blob_len, uint8_t *n_out, size_t n_cap, size_t *n_len_out, uint8_t *e_out, size_t e_cap, size_t *e_len_out)
Extracts the public modulus and exponent from a private-key blob.
Definition rsa.cpp:156
bool gpg_rsa_generate(uint16_t n_bits, uint8_t *blob_out, size_t blob_cap, size_t *blob_len_out)
Generates a fresh RSA key pair and serialises its private blob. The public exponent is fixed to 65537...
Definition rsa.cpp:138
bool gpg_rsa_blob_build(uint16_t n_bits, const uint8_t *e, size_t e_len, const uint8_t *p, size_t p_len, const uint8_t *q, size_t q_len, uint8_t *blob_out, size_t blob_cap, size_t *blob_len_out)
Serialises raw RSA components into a private-key blob.
Definition rsa.cpp:110
#define GPG_RSA_MAX_MODULUS_BYTES
Software RSA backend for the OpenPGP card (mbedTLS).
Definition rsa.h:24
Parsed algorithm-attribute payload.
Definition algo_attr.h:63
uint16_t rsa_n_bits
Definition algo_attr.h:71
algo_attr_curve_t curve
Definition algo_attr.h:67
uint16_t rsa_e_bits
Definition algo_attr.h:72
uint8_t rsa_import_fmt
Definition algo_attr.h:73
bool is_rsa
Definition algo_attr.h:65
uint8_t algo_id
Definition algo_attr.h:64
Parsed KDF-DO contents.
Definition kdf.h:51
kdf_algo_t algo
Definition kdf.h:52
uint8_t pw3_initial_len
Definition kdf.h:68
uint8_t pw3_initial[64]
Definition kdf.h:67
uint8_t pw1_initial_len
Definition kdf.h:65
bool has_pw3_initial
Definition kdf.h:66
uint8_t pw1_initial[64]
Definition kdf.h:64
kdf_hash_t hash
Definition kdf.h:53
bool has_pw1_initial
Definition kdf.h:63
Descriptor entry for table-driven PUT DATA processing.
Definition openpgp.cpp:1435
const char * log_label
Definition openpgp.cpp:1440
put_data_kind_t kind
Definition openpgp.cpp:1439