CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
openpgp.cpp File Reference
#include "mod_gpg/openpgp/openpgp.h"
#include "mod_gpg/openpgp/apdu.h"
#include "mod_gpg/openpgp/algo_attr.h"
#include "mod_gpg/openpgp/constants.h"
#include "cdc_log.h"
#include "mod_gpg/gpg.h"
#include "mod_gpg/GpgStorage.h"
#include "ecdh.h"
#include "cdc_core/pin_storage_c.h"
#include "cdc_core/PinManager.h"
#include "cdc_hal/ISecureElement.h"
#include <mbedtls/platform_util.h>
#include <mbedtls/sha256.h>
#include <mbedtls/aes.h>
#include <mbedtls/ecp.h>
#include <mbedtls/ecdsa.h>
#include <mbedtls/bignum.h>
#include <esp_attr.h>
#include <string.h>
#include <time.h>
#include <esp_mac.h>
#include <nvs_flash.h>
#include <nvs.h>
#include <esp_random.h>

Go to the source code of this file.

Classes

struct  put_data_desc_t
 Descriptor entry for table-driven PUT DATA processing. More...

Macros

#define OPENPGP_RC_MIN_LEN   8
 Resetting Code (RC) — optional per OpenPGP 3.4.1 §4.3.2. When set, the host can unblock PW1 with the RC instead of PW3 (RESET RETRY COUNTER with P1=0x00). We persist the configured RC bytes plus a separate retry counter in NVS.
#define NVS_NAMESPACE   "openpgp"
 NVS namespace used for OpenPGP persistent data.
#define NVS_STATE_KEY   "state"

Typedefs

typedef bool(* pin_change_fn_t) (const char *pin)
 Type alias for PIN change callbacks.

Enumerations

enum  key_type_t { KEY_TYPE_SIG = 0 , KEY_TYPE_DEC = 1 , KEY_TYPE_AUT = 2 }
 Builders for OpenPGP application-related data objects. More...
enum  put_data_kind_t { PUT_KIND_BLOB_FIXED = 0 , PUT_KIND_STRING_BOUNDED = 1 }
 Storage kind for PUT DATA descriptor entries. More...
enum  pin_slot_t { PIN_SLOT_PW1 = 0 , PIN_SLOT_PW3 = 1 }
 PIN slot identifier used by PIN helper routines. More...

Functions

static cdc::hal::ISecureElementget_se ()
 Returns secure-element instance used by OpenPGP backend.
static bool se_ecc_key_read (uint8_t slot, uint8_t *pubkey, size_t max_len, uint8_t *curve_out)
 Reads ECC public key from secure element and exposes curve metadata.
static bool se_ecc_key_generate (uint8_t slot, uint8_t curve)
 Generates ECC key material in secure element slot.
static bool se_ecdsa_sign (uint8_t slot, const uint8_t *hash, size_t hash_len, uint8_t *sig)
 Signs a hash using secure-element ECDSA key.
static bool se_eddsa_sign (uint8_t slot, const uint8_t *msg, size_t msg_len, uint8_t *sig)
 Signs a message using secure-element EdDSA key.
static void se_random_fill (uint8_t *buf, size_t len)
 Fills buffer with secure random bytes, with ESP fallback.
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::computeKdfHash (OpenPGP S2K).
static void chain_reset (void)
struct __attribute__ ((packed)) OpenpgpNvsState
 Single-blob persistent OpenPGP runtime state.
static size_t tlv_write_tag (uint8_t *buf, uint16_t tag)
 TLV builder helper functions.
static size_t tlv_write_len (uint8_t *buf, size_t len)
 Writes a TLV length field using DER length encoding.
static size_t tlv_build (uint8_t *buf, size_t buf_max, uint16_t tag, const uint8_t *value, size_t value_len)
 Builds complete TLV object and returns total encoded length.
static const uint8_t * get_algo_attr (key_type_t key_type, size_t *len)
 Returns algorithm attributes for a key role based on stored key type.
static int build_do_app_related (uint8_t *buf, size_t buf_max)
 Builds OpenPGP DO 0x6E (Application Related Data).
static int build_do_cardholder (uint8_t *buf, size_t buf_max)
 Builds OpenPGP DO 0x65 (Cardholder Related Data).
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 PinManager.
static void load_state_from_nvs (void)
 Loads persistent OpenPGP runtime state from NVS.
static void save_state_to_nvs (void)
 Persists OpenPGP runtime state to NVS.
static void init_aid_from_mac (void)
 Initializes the OpenPGP AID serial section from the ESP32 MAC address.
bool openpgp_init (void)
bool openpgp_is_selected (void)
uint32_t openpgp_get_sig_count (void)
bool openpgp_get_fingerprint (uint8_t key_type, uint8_t *fp_out)
 Reads the stored OpenPGP v4 fingerprint for a key role.
static bool fp_is_set (const uint8_t fp[OPENPGP_FINGERPRINT_SIZE])
bool openpgp_has_any_key (void)
 Reports whether any of the SIG / DEC / AUT roles has a non-zero fingerprint configured. Acts as the canonical "card has keys" check.
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<<Firstname" or empty when unset.
uint32_t openpgp_get_gen_time (uint8_t key_type)
 Returns the stored Unix timestamp of key generation, or 0 when unset.
bool openpgp_set_cardholder_name (const char *name)
 Sets the cardholder name (OpenPGP DO 0x5B) and persists state.
bool openpgp_set_key_fingerprint (uint8_t key_type, const uint8_t *fingerprint, uint32_t gen_time)
static int cmd_select (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU SELECT command processing.
static int cmd_get_data (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU GET DATA command processing.
static const put_data_desc_tfind_put_data_desc (uint16_t tag)
 Returns descriptor for an OpenPGP PUT DATA tag.
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.
static int put_data_algo_attr (uint16_t tag, const apdu_t *apdu, uint8_t *resp)
 Handles APDU PUT DATA command processing.
static int cmd_put_data (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
static void update_generation_timestamp (uint8_t key_ref)
 Updates and persists the generation timestamp for a key role.
static bool ehl_parse_one (const uint8_t *buf, size_t buf_len, size_t *pos, uint16_t *tag_out, const uint8_t **value_out, size_t *value_len_out)
 Parse one BER-TLV field at pos.
static int cmd_put_data_odd (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU PUT DATA (odd INS, 0xDB) for keypair import.
static int cmd_verify (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU VERIFY command for PIN verification.
static bool compute_kdf_hash (const char *pin, const uint8_t *salt, uint32_t iterations, uint8_t hash_out[32])
 Computes the iterated-salted S2K hash (OpenPGP KDF) for a PIN candidate.
static bool const_time_equal (const uint8_t *a, const uint8_t *b, size_t n)
 Constant-time comparison of two equal-length byte buffers.
static bool peek_verify_pin (pin_slot_t slot, const char *pin)
 Compares a candidate PIN against the stored hash without touching retry counters.
static bool try_change_pin (const uint8_t *data, size_t len, size_t min_len, pin_slot_t slot, pin_change_fn_t change_fn)
 Searches the split point for CHANGE REFERENCE DATA without consuming retries.
static int cmd_change_reference_data (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU CHANGE REFERENCE DATA command for PIN updates.
static int cmd_pso_cds (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU PSO: COMPUTE DIGITAL SIGNATURE.
static int cmd_pso_decipher_aes (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU PSO: DECIPHER for ECDH key agreement.
static int cmd_pso_decipher (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
static int cmd_manage_security_env (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU MANAGE SECURITY ENVIRONMENT (INS 0x22).
static int cmd_internal_authenticate (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU INTERNAL AUTHENTICATE (INS 0x88).
static int cmd_terminate_df (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU TERMINATE DF (INS 0xE6).
static int cmd_activate_file (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU ACTIVATE FILE (INS 0x44).
void openpgp_factory_reset (void)
static int cmd_reset_retry_counter (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU RESET RETRY COUNTER (INS 0x2C).
static uint8_t get_ecc_slot_for_key_ref (uint8_t key_ref)
 Returns ECC slot mapping for an OpenPGP key reference.
static key_type_t get_key_type_for_ref (uint8_t key_ref)
 Maps an OpenPGP key reference to an internal key type.
static uint16_t generate_dec_key (uint8_t *pubkey_out)
 Generates a software ECDH P-256 key pair for the DEC slot.
static uint16_t generate_hardware_key (uint8_t ecc_slot, uint8_t curve)
 Generates a hardware ECC key pair in the TROPIC01 secure element.
static bool read_public_key (key_type_t key_type, uint8_t ecc_slot, uint8_t *pubkey, uint8_t *curve_out)
 Reads the public key for a given key role.
static void encode_pubkey_with_prefix (const uint8_t *pubkey, uint8_t curve, uint8_t *out, size_t *out_len)
 Encodes the public key with the OpenPGP/SEC1 uncompressed prefix.
static int cmd_generate_keypair (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles APDU GENERATE ASYMMETRIC KEY PAIR.
static int apply_response_chaining (uint32_t le, uint8_t *resp, size_t resp_max, int result_len)
 Trim an APDU response to the host-requested Le window.
static int cmd_get_response (const apdu_t *apdu, uint8_t *resp, size_t resp_max)
 Handles INS GET RESPONSE (0xC0) — drains the chained response buffer.
int openpgp_process_apdu (const uint8_t *cmd, size_t cmd_len, uint8_t *resp, size_t resp_max)

Variables

static const char * TAG = "OpenPGP"
 OpenPGP smart-card application implementation for CDC Badge.
static uint8_t s_openpgp_aid [16]
 OpenPGP Application ID (RID + PIX), initialized dynamically.
const uint8_t * OPENPGP_AID = s_openpgp_aid
const uint8_t OPENPGP_AID_LEN = sizeof(s_openpgp_aid)
static bool app_selected = false
 ATR is defined in ccid.cpp and accessed via ccid_get_atr().
static bool pw1_verified = false
static bool pw3_verified = false
static uint32_t sig_count = 0
static constexpr size_t RC_SALT_SIZE = 16
static constexpr size_t RC_HASH_SIZE = 32
static constexpr size_t RC_KDF_TOTAL_BYTES = 100000
static uint8_t s_rc_salt [RC_SALT_SIZE] = {0}
static uint8_t s_rc_hash [RC_HASH_SIZE] = {0}
static uint8_t s_rc_len = 0
static uint8_t s_rc_retries = 3
static uint8_t selected_curve_sig = CDC_CURVE_ED25519
 Host-selected ECC curve per key role. DEC is fixed to P-256 because the TROPIC01 cannot perform ECDH natively and the firmware only carries a software P-256 ECDH path in ecdh.cpp. SIG and AUT default to Ed25519 (the project's preferred curve for signing) and can be flipped to P-256 via PUT DATA C1 / C3 per OpenPGP 3.4.1 §4.4.3.7-9.
static uint8_t selected_curve_aut = CDC_CURVE_ED25519
static bool card_terminated = false
 Card lifecycle state per OpenPGP 3.4.1 §7.2.18.
static uint8_t g_resp_buffer [4096]
 Buffered remainder of an APDU response that did not fit into the caller-supplied Le window. Drained one chunk at a time via GET RESPONSE (INS 0xC0) per ISO 7816-4 §5.3.4. Lifetime is bound to the next APDU on the same logical channel: the buffer is invalidated when any non-GET RESPONSE command arrives.
static size_t g_resp_remaining = 0
static size_t g_resp_pos = 0
static uint8_t g_chain_buffer [4096]
 Command-chaining accumulator (ISO 7816-4 §5.1.1).
static size_t g_chain_len = 0
static bool g_chain_active = false
static uint8_t g_chain_ins = 0
static uint8_t g_chain_p1 = 0
static uint8_t g_chain_p2 = 0
static char s_session_pin [OPENPGP_PIN_MAX_LEN+1] = {}
 Session PIN cache for DEC key decryption (temporary after VERIFY for PSO:DECIPHER).
static constexpr uint8_t OPENPGP_NVS_SCHEMA_V2 = 2
static uint8_t fingerprint_sig [OPENPGP_FINGERPRINT_SIZE] = {0}
 Data object storage buffers (fingerprints and related metadata).
static uint8_t fingerprint_dec [OPENPGP_FINGERPRINT_SIZE] = {0}
static uint8_t fingerprint_aut [OPENPGP_FINGERPRINT_SIZE] = {0}
static uint8_t gen_time_sig [4] = {0}
 Key-generation timestamps (4-byte big-endian Unix time each).
static uint8_t gen_time_dec [4] = {0}
static uint8_t gen_time_aut [4] = {0}
static uint8_t ca_fp_1 [OPENPGP_FINGERPRINT_SIZE] = {0}
 Optional CA fingerprints for trust-chain metadata.
static uint8_t ca_fp_2 [OPENPGP_FINGERPRINT_SIZE] = {0}
static uint8_t ca_fp_3 [OPENPGP_FINGERPRINT_SIZE] = {0}
static char cardholder_name [40] = {0}
 Cardholder profile data stored in NVS.
static char cardholder_lang [8] = "en"
static uint8_t cardholder_sex = 0x39
static char cardholder_url [64] = {0}
static char cardholder_login [32] = {0}
static const uint8_t HIST_BYTES []
 Historical bytes used in OpenPGP ATR-related data objects.
static const uint8_t ALGO_ATTR_ED25519 []
 Algorithm attributes for Ed25519 (EdDSA with curve25519).
static const uint8_t ALGO_ATTR_P256_ECDSA []
 Algorithm attributes for P-256 ECDSA (signature/authentication roles).
static const uint8_t ALGO_ATTR_P256_ECDH []
 Algorithm attributes for P-256 ECDH (decryption role).
static const uint8_t EXT_CAPABILITIES []
 Extended capabilities object per OpenPGP 3.4.1 section 4.2.1.
static constexpr uint8_t ATTESTATION_ECC_SLOT = 0
static constexpr size_t OPENPGP_STATE_SIG_SIZE = 64

Macro Definition Documentation

◆ NVS_NAMESPACE

#define NVS_NAMESPACE   "openpgp"

NVS namespace used for OpenPGP persistent data.

Definition at line 296 of file openpgp.cpp.

◆ NVS_STATE_KEY

#define NVS_STATE_KEY   "state"

Definition at line 297 of file openpgp.cpp.

Referenced by load_state_from_nvs(), and save_state_to_nvs().

◆ OPENPGP_RC_MIN_LEN

#define OPENPGP_RC_MIN_LEN   8

Resetting Code (RC) — optional per OpenPGP 3.4.1 §4.3.2. When set, the host can unblock PW1 with the RC instead of PW3 (RESET RETRY COUNTER with P1=0x00). We persist the configured RC bytes plus a separate retry counter in NVS.

Storage rationale: the existing PW1/PW3 path stores hashes inside the TROPIC01 R-Memory; allocating extra slots there for RC is deferred until the next slot-map revision. Plain NVS bytes are equivalent to plaintext Yubikey behaviour and unlock the same workflow.

Definition at line 182 of file openpgp.cpp.

Referenced by cmd_put_data().

Typedef Documentation

◆ pin_change_fn_t

typedef bool(* pin_change_fn_t) (const char *pin)

Type alias for PIN change callbacks.

Definition at line 1732 of file openpgp.cpp.

Enumeration Type Documentation

◆ key_type_t

enum key_type_t

Builders for OpenPGP application-related data objects.

Key role discriminator used for algorithm-attribute selection.

Enumerator
KEY_TYPE_SIG 
KEY_TYPE_DEC 
KEY_TYPE_AUT 

Definition at line 489 of file openpgp.cpp.

◆ pin_slot_t

enum pin_slot_t

PIN slot identifier used by PIN helper routines.

Enumerator
PIN_SLOT_PW1 
PIN_SLOT_PW3 

Definition at line 1637 of file openpgp.cpp.

◆ put_data_kind_t

Storage kind for PUT DATA descriptor entries.

BLOB_FIXED requires apdu->lc == max_size; STRING_BOUNDED requires apdu->lc < max_size and writes a trailing NUL terminator.

Enumerator
PUT_KIND_BLOB_FIXED 
PUT_KIND_STRING_BOUNDED 

Definition at line 1202 of file openpgp.cpp.

Function Documentation

◆ __attribute__()

struct __attribute__ ( (packed) )

Single-blob persistent OpenPGP runtime state.

One nvs_set_blob per save keeps NVS page fragmentation bounded: the default 20 KB NVS partition is shared with every other module and the old per-field layout (~21 entries) silently filled up under load.

Definition at line 291 of file openpgp.cpp.

References OPENPGP_PIN_MAX_LEN, and s_session_pin.

◆ apply_put_data_desc()

int apply_put_data_desc ( const put_data_desc_t * desc,
const apdu_t * apdu,
uint8_t * resp )
static

Applies a PUT DATA descriptor to the request payload.

Parameters
descDescriptor from find_put_data_desc.
apduParsed APDU containing the new value.
respResponse buffer.
Returns
APDU status/response length result.

Definition at line 1264 of file openpgp.cpp.

References apdu_sw(), put_data_desc_t::buffer, put_data_desc_t::kind, LOG_I, put_data_desc_t::log_label, put_data_desc_t::max_size, PUT_KIND_STRING_BOUNDED, save_state_to_nvs(), SW_OK, SW_WRONG_LENGTH, and TAG.

Referenced by cmd_put_data().

◆ apply_response_chaining()

int apply_response_chaining ( uint32_t le,
uint8_t * resp,
size_t resp_max,
int result_len )
static

Trim an APDU response to the host-requested Le window.

If the dispatcher produced more payload than the host asked for, we keep the head (Le bytes) in resp and stash the rest in g_resp_buffer so the host can retrieve it via GET RESPONSE (INS 0xC0). The status word becomes 61xx where xx = remaining payload size (capped at 0xFF, 0x00 means "more than 255 still pending"). Responses that already fit pass through.

Definition at line 2621 of file openpgp.cpp.

References g_resp_buffer, g_resp_pos, g_resp_remaining, LOG_W, SW_OK, and TAG.

Referenced by openpgp_process_apdu().

◆ build_do_app_related()

int build_do_app_related ( uint8_t * buf,
size_t buf_max )
static

Builds OpenPGP DO 0x6E (Application Related Data).

Parameters
bufOutput buffer for the encoded TLV object.
buf_maxMaximum size of buf.
Returns
Encoded length on success, or a negative error code.

Definition at line 524 of file openpgp.cpp.

References ca_fp_1, ca_fp_2, ca_fp_3, DO_AID, DO_ALGO_AUT, DO_ALGO_DEC, DO_ALGO_SIG, DO_EXT_CAP, DO_HIST_BYTES, DO_PW_STATUS, EXT_CAPABILITIES, fingerprint_aut, fingerprint_dec, fingerprint_sig, gen_time_aut, gen_time_dec, gen_time_sig, get_algo_attr(), HIST_BYTES, KEY_TYPE_AUT, KEY_TYPE_DEC, KEY_TYPE_SIG, OPENPGP_AID, OPENPGP_AID_LEN, OPENPGP_FINGERPRINT_SIZE, OPENPGP_PIN_MAX_LEN, pin_storage_openpgp_pw1_retries(), pin_storage_openpgp_pw3_retries(), s_rc_len, s_rc_retries, tlv_build(), tlv_write_len(), and tlv_write_tag().

Referenced by cmd_get_data().

◆ build_do_cardholder()

int build_do_cardholder ( uint8_t * buf,
size_t buf_max )
static

Builds OpenPGP DO 0x65 (Cardholder Related Data).

Parameters
bufOutput buffer for the encoded TLV object.
buf_maxMaximum size of buf.
Returns
Encoded length on success, or a negative error code.

Definition at line 618 of file openpgp.cpp.

References cardholder_lang, cardholder_name, cardholder_sex, DO_CARDHOLDER, DO_LANG_PREF, DO_NAME, DO_SEX, tlv_build(), tlv_write_len(), and tlv_write_tag().

Referenced by cmd_get_data().

◆ chain_reset()

void chain_reset ( void )
static

Definition at line 280 of file openpgp.cpp.

References g_chain_active, g_chain_ins, g_chain_len, g_chain_p1, and g_chain_p2.

Referenced by openpgp_process_apdu().

◆ cmd_activate_file()

int cmd_activate_file ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU ACTIVATE FILE (INS 0x44).

Per OpenPGP 3.4.1 §7.2.18: in operational state ACTIVATE FILE is a no-op (SW_OK). In terminated state it wipes every persistent OpenPGP artefact (keys, fingerprints, generation times, cardholder data, PINs) and returns the card to operational state.

Definition at line 2205 of file openpgp.cpp.

References apdu_sw(), card_terminated, LOG_W, openpgp_factory_reset(), SW_INCORRECT_P1P2, SW_OK, and TAG.

Referenced by openpgp_process_apdu().

◆ cmd_change_reference_data()

int cmd_change_reference_data ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU CHANGE REFERENCE DATA command for PIN updates.

Parameters
apduParsed APDU request.
respOutput response buffer.
resp_maxMaximum size of resp.
Returns
APDU status/response length result.

Definition at line 1784 of file openpgp.cpp.

References apdu_sw(), LOG_I, OPENPGP_PIN_MAX_LEN, OPENPGP_PW1_MIN_LEN, OPENPGP_PW3_MIN_LEN, PIN_SLOT_PW1, PIN_SLOT_PW3, pin_storage_openpgp_change_pw1(), pin_storage_openpgp_change_pw3(), pin_storage_openpgp_pw1_retries(), pin_storage_openpgp_pw3_retries(), pin_storage_openpgp_verify_pw1(), pin_storage_openpgp_verify_pw3(), PW1_CODE_1, PW3_CODE, SW_AUTH_METHOD_BLOCKED, SW_INCORRECT_P1P2, SW_OK, SW_WRONG_LENGTH, TAG, and try_change_pin().

Referenced by openpgp_process_apdu().

◆ cmd_generate_keypair()

int cmd_generate_keypair ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

◆ cmd_get_data()

◆ cmd_get_response()

int cmd_get_response ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles INS GET RESPONSE (0xC0) — drains the chained response buffer.

Definition at line 2651 of file openpgp.cpp.

References apdu_sw(), g_resp_buffer, g_resp_pos, g_resp_remaining, SW_CONDITIONS_NOT_SATISFIED, SW_INCORRECT_P1P2, and SW_OK.

Referenced by openpgp_process_apdu().

◆ cmd_internal_authenticate()

int cmd_internal_authenticate ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU INTERNAL AUTHENTICATE (INS 0x88).

Signs the supplied authentication challenge with the AUT key (ECC slot 3). Used by gpg-agent / ssh for client authentication.

Per OpenPGP 3.4.1 §7.2.13: P1=0x00 P2=0x00, Lc = challenge length, Le = signature length. PW1 (reference 0x82) must be verified; we reuse the PW1 session flag.

Signature format matches PSO:CDS: raw R||S for ECDSA, 64-byte EdDSA sig.

Definition at line 2137 of file openpgp.cpp.

References apdu_build_response(), apdu_sw(), CDC_CURVE_P256, curve, gpg_storage_aut_slot(), LOG_E, P256_PUBKEY_SIZE, pw1_verified, se_ecc_key_read(), se_ecdsa_sign(), se_eddsa_sign(), SW_CONDITIONS_NOT_SATISFIED, SW_INCORRECT_P1P2, SW_OK, SW_SECURITY_NOT_SATISFIED, SW_UNKNOWN, SW_WRONG_LENGTH, and TAG.

Referenced by openpgp_process_apdu().

◆ cmd_manage_security_env()

int cmd_manage_security_env ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU MANAGE SECURITY ENVIRONMENT (INS 0x22).

Per OpenPGP 3.4.1 §7.2.10: gpg / scd issue MSE before PSO to point the key reference into a different slot. Two combinations matter in practice: P1=0x41 P2=0xB6 + tag 83 01 01 → use SIG key for DSI (signature) P1=0x41 P2=0xA4 + tag 83 01 03 → use AUT key for INTERNAL AUTHENTICATE

Our key-slot mapping is fixed by role (SIG=B6, DEC=B8, AUT=A4) so MSE is effectively a no-op as long as the requested reference matches the role encoded in P2. We accept the documented combinations and return SW_OK, which is enough for gpg-card / ssh workflows that issue MSE defensively.

Definition at line 2097 of file openpgp.cpp.

References apdu_sw(), KEY_AUT, KEY_DEC, KEY_SIG, SW_CONDITIONS_NOT_SATISFIED, SW_INCORRECT_P1P2, SW_OK, SW_WRONG_DATA, and SW_WRONG_LENGTH.

Referenced by openpgp_process_apdu().

◆ cmd_pso_cds()

int cmd_pso_cds ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU PSO: COMPUTE DIGITAL SIGNATURE.

Parameters
apduParsed APDU request.
respOutput response buffer.
resp_maxMaximum size of resp.
Returns
APDU status/response length result.

Definition at line 1847 of file openpgp.cpp.

References apdu_build_response(), apdu_sw(), CDC_CURVE_P256, curve, gpg_storage_sig_slot(), LOG_E, LOG_I, P256_PUBKEY_SIZE, pw1_verified, save_state_to_nvs(), se_ecc_key_read(), se_ecdsa_sign(), se_eddsa_sign(), SHA256_DIGEST_SIZE, sig_count, SW_CONDITIONS_NOT_SATISFIED, SW_OK, SW_SECURITY_NOT_SATISFIED, SW_UNKNOWN, SW_WRONG_DATA, and TAG.

Referenced by openpgp_process_apdu().

◆ cmd_pso_decipher()

◆ cmd_pso_decipher_aes()

int cmd_pso_decipher_aes ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU PSO: DECIPHER for ECDH key agreement.

Parameters
apduParsed APDU request.
respOutput response buffer.
resp_maxMaximum size of resp.
Returns
APDU status/response length result.

PSO:DECIPHER - ECDH Decryption.

SECURITY NOTE: The TROPIC01 secure element does NOT support native ECDH operations. Therefore, the DEC private key is stored encrypted in R-Memory and temporarily decrypted in RAM for the ECDH computation.

This is a necessary trade-off for GPG compatibility. See docs/GPG_ECDH_SECURITY.md for details.

Mitigations:

  • Key is cleared from RAM immediately after use
  • Encrypted with PIN-derived key (brute-force protected)
  • MbedTLS uses constant-time ECDH implementation

OpenPGP 3.4.1, Section 7.2.11: Command: 00 2A 80 86 <Lc> <data> <Le> Data format for ECDH: 7F49 <len> – Cipher DO A6 <len> – External Public Key template 86 <len> – External Public Key point (04||X||Y for P-256) <65 bytes ephemeral pubkey> Response: Shared Secret (32 bytes for P-256)

Decrypts an AES-CFB128 payload using the stored symmetric key (DO 0xD5).

Parameters
apduParsed APDU request, payload[0] = 0x02 padding indicator.
respOutput response buffer.
resp_maxMaximum size of resp.
Returns
APDU status/response length result.

Payload format: 0x02 || IV(16) || ciphertext.

Definition at line 1928 of file openpgp.cpp.

References apdu_build_response(), apdu_sw(), gpg_storage_has_aes_key(), gpg_storage_load_aes_key(), s_session_pin, SW_CONDITIONS_NOT_SATISFIED, SW_OK, SW_SECURITY_NOT_SATISFIED, SW_UNKNOWN, SW_WRONG_DATA, and SW_WRONG_LENGTH.

Referenced by cmd_pso_decipher().

◆ cmd_put_data()

◆ cmd_put_data_odd()

int cmd_put_data_odd ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU PUT DATA (odd INS, 0xDB) for keypair import.

Used by gpg --card-editgenerate when the user answers "Y" to "Make off-card backup of encryption key?": GnuPG generates the key on the host and ships it as an OpenPGP 3.4.1 §7.2.8 Extended Header List

4D LL
   <CRT>          // B6 00 (SIG) / B8 00 (DEC) / A4 00 (AUT)
   7F48 LL        // Cardholder Private Key Template (tag list)
   5F48 LL        // Concatenation of values (priv [|| pub])

Currently only DEC (CRT B8) is supported, mirroring the on-card path that also reserves software-ECDH for the decipher key (the only role TROPIC01 does not handle natively). SIG/AUT keys live in the secure element with no key-import path exposed by our HAL.

Definition at line 1498 of file openpgp.cpp.

References apdu_sw(), ehl_parse_one(), gpg_storage_save_dec_privkey(), KEY_DEC, P256_PRIVKEY_SIZE, pw3_verified, SW_INCORRECT_P1P2, SW_OK, SW_SECURITY_NOT_SATISFIED, SW_UNKNOWN, SW_WRONG_DATA, SW_WRONG_LENGTH, and update_generation_timestamp().

Referenced by openpgp_process_apdu().

◆ cmd_reset_retry_counter()

int cmd_reset_retry_counter ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU RESET RETRY COUNTER (INS 0x2C).

Per OpenPGP 3.4.1 §7.2.7: P1=0x02, P2=0x81: reset PW1 with admin authorisation. Lc = new PW1 length. Requires PW3 verified. P1=0x00, P2=0x81: reset PW1 using the Resetting Code. Lc = RC || new PW1. Not yet implemented — returns SW_INS_NOT_SUPPORTED until the RC store lands (planned in Phase D.3, full path).

Definition at line 2265 of file openpgp.cpp.

References apdu_sw(), compute_rc_hash(), LOG_I, OPENPGP_PIN_MAX_LEN, OPENPGP_PW1_MIN_LEN, pin_storage_openpgp_change_pw1(), pin_storage_openpgp_reset_pw1_retries(), PW1_CODE_1, pw1_verified, pw3_verified, RC_HASH_SIZE, s_rc_hash, s_rc_len, s_rc_retries, s_rc_salt, save_state_to_nvs(), SW_AUTH_METHOD_BLOCKED, SW_CONDITIONS_NOT_SATISFIED, SW_INCORRECT_P1P2, SW_OK, SW_SECURITY_NOT_SATISFIED, SW_UNKNOWN, SW_WRONG_LENGTH, and TAG.

Referenced by openpgp_process_apdu().

◆ cmd_select()

int cmd_select ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU SELECT command processing.

Parameters
apduParsed APDU request.
respOutput response buffer.
resp_maxMaximum size of resp.
Returns
APDU status/response length result.

Definition at line 991 of file openpgp.cpp.

References apdu_sw(), app_selected, gpg_storage_clear_session(), LOG_I, OPENPGP_AID, pw1_verified, pw3_verified, s_session_pin, SW_FILE_NOT_FOUND, SW_OK, and TAG.

Referenced by openpgp_process_apdu().

◆ cmd_terminate_df()

int cmd_terminate_df ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU TERMINATE DF (INS 0xE6).

Per OpenPGP 3.4.1 §7.2.18: takes the card into the terminated lifecycle state. Allowed under PW3 verification, or unconditionally when both PW1 and PW3 retry counters have reached zero (last-resort recovery).

After TERMINATE only SELECT and ACTIVATE FILE are accepted; every other command returns SW_FILE_TERMINATED (0x6285).

Definition at line 2179 of file openpgp.cpp.

References apdu_sw(), card_terminated, LOG_W, pin_storage_openpgp_pw1_blocked(), pin_storage_openpgp_pw3_blocked(), pw1_verified, pw3_verified, save_state_to_nvs(), SW_INCORRECT_P1P2, SW_OK, SW_SECURITY_NOT_SATISFIED, and TAG.

Referenced by openpgp_process_apdu().

◆ cmd_verify()

int cmd_verify ( const apdu_t * apdu,
uint8_t * resp,
size_t resp_max )
static

Handles APDU VERIFY command for PIN verification.

Parameters
apduParsed APDU request.
respOutput response buffer.
resp_maxMaximum size of resp.
Returns
APDU status/response length result.

Definition at line 1566 of file openpgp.cpp.

References apdu_sw(), gpg_storage_set_session_pin(), LOG_I, LOG_W, OPENPGP_PIN_MAX_LEN, pin_storage_openpgp_pw1_blocked(), pin_storage_openpgp_pw1_retries(), pin_storage_openpgp_pw3_blocked(), pin_storage_openpgp_pw3_retries(), pin_storage_openpgp_verify_pw1(), pin_storage_openpgp_verify_pw3(), PW1_CODE_1, PW1_CODE_2, pw1_verified, PW3_CODE, pw3_verified, s_session_pin, SW_AUTH_METHOD_BLOCKED, SW_INCORRECT_P1P2, SW_OK, SW_WRONG_LENGTH, and TAG.

Referenced by openpgp_process_apdu().

◆ compute_kdf_hash()

bool compute_kdf_hash ( const char * pin,
const uint8_t * salt,
uint32_t iterations,
uint8_t hash_out[32] )
static

Computes the iterated-salted S2K hash (OpenPGP KDF) for a PIN candidate.

Parameters
pinPIN string.
salt8-byte salt.
iterationsIteration count from PinManager.
hash_out32-byte output buffer.
Returns
true on success.

Definition at line 1650 of file openpgp.cpp.

References OPENPGP_PIN_MAX_LEN.

Referenced by peek_verify_pin().

◆ compute_rc_hash()

bool compute_rc_hash ( const uint8_t * rc,
size_t rc_len,
const uint8_t * salt,
uint8_t * hash_out )
static

Iterated-salted SHA-256 over salt||rc for resetting-code storage. Same construction as PinManager::computeKdfHash (OpenPGP S2K).

Definition at line 195 of file openpgp.cpp.

References OPENPGP_PIN_MAX_LEN, RC_KDF_TOTAL_BYTES, and RC_SALT_SIZE.

Referenced by cmd_put_data(), and cmd_reset_retry_counter().

◆ const_time_equal()

bool const_time_equal ( const uint8_t * a,
const uint8_t * b,
size_t n )
static

Constant-time comparison of two equal-length byte buffers.

Parameters
aFirst buffer.
bSecond buffer.
nNumber of bytes to compare.
Returns
true if identical.

Definition at line 1693 of file openpgp.cpp.

Referenced by peek_verify_pin().

◆ ehl_parse_one()

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 )
static

Parse one BER-TLV field at pos.

Supports 1- or 2-byte tags and short-form / 0x81 / 0x82 length encodings, which covers everything the OpenPGP 3.4.1 PUT DATA odd payload uses.

Returns
true on success; pos is advanced past the parsed value.

Definition at line 1445 of file openpgp.cpp.

Referenced by cmd_put_data_odd().

◆ encode_pubkey_with_prefix()

void encode_pubkey_with_prefix ( const uint8_t * pubkey,
uint8_t curve,
uint8_t * out,
size_t * out_len )
static

Encodes the public key with the OpenPGP/SEC1 uncompressed prefix.

P-256 keys are normalized to 0x04 || X || Y. Ed25519 keys are returned as their raw 32-byte encoding without prefix.

Parameters
pubkeySource public key buffer.
curveCurve identifier (CDC_CURVE_*).
outDestination buffer (must hold at least P256_PUBKEY_SIZE bytes).
out_lenReceives the encoded length.

Definition at line 2483 of file openpgp.cpp.

References CDC_CURVE_P256, curve, ED25519_PUBKEY_SIZE, and P256_PUBKEY_SIZE.

Referenced by cmd_generate_keypair().

◆ find_put_data_desc()

const put_data_desc_t * find_put_data_desc ( uint16_t tag)
static

Returns descriptor for an OpenPGP PUT DATA tag.

Parameters
tagOpenPGP DO tag.
Returns
Pointer to descriptor or NULL if tag is not handled by the table.

Definition at line 1226 of file openpgp.cpp.

References ca_fp_1, ca_fp_2, ca_fp_3, cardholder_lang, cardholder_login, cardholder_name, cardholder_url, DO_CA_FP_1, DO_CA_FP_2, DO_CA_FP_3, DO_FP_AUT, DO_FP_DEC, DO_FP_SIG, DO_GEN_TIME_AUT, DO_GEN_TIME_DEC, DO_GEN_TIME_SIG, DO_LANG_PREF, DO_LOGIN, DO_NAME, DO_URL, fingerprint_aut, fingerprint_dec, fingerprint_sig, gen_time_aut, gen_time_dec, gen_time_sig, OPENPGP_FINGERPRINT_SIZE, PUT_KIND_BLOB_FIXED, and PUT_KIND_STRING_BOUNDED.

Referenced by cmd_put_data().

◆ fp_is_set()

bool fp_is_set ( const uint8_t fp[OPENPGP_FINGERPRINT_SIZE])
static

Definition at line 900 of file openpgp.cpp.

References OPENPGP_FINGERPRINT_SIZE.

Referenced by openpgp_has_any_key().

◆ generate_dec_key()

uint16_t generate_dec_key ( uint8_t * pubkey_out)
static

Generates a software ECDH P-256 key pair for the DEC slot.

The TROPIC01 secure element does not support ECDH, so the DEC private key is generated and persisted by gpg_storage (encrypted by device key). Memory holding the raw key is zeroized before returning.

Returns
SW_OK on success or an OpenPGP status word on failure.

Definition at line 2382 of file openpgp.cpp.

References ecdh_p256_generate_keypair(), gpg_storage_save_dec_privkey(), P256_PRIVKEY_SIZE, SW_OK, and SW_UNKNOWN.

Referenced by cmd_generate_keypair().

◆ generate_hardware_key()

uint16_t generate_hardware_key ( uint8_t ecc_slot,
uint8_t curve )
static

Generates a hardware ECC key pair in the TROPIC01 secure element.

Parameters
ecc_slotECC slot index targeted by the key generation.
curveCurve identifier (CDC_CURVE_*).
Returns
SW_OK on success or an OpenPGP status word on failure.

Definition at line 2401 of file openpgp.cpp.

References curve, LOG_E, LOG_I, se_ecc_key_generate(), SW_OK, SW_UNKNOWN, and TAG.

Referenced by cmd_generate_keypair().

◆ get_algo_attr()

const uint8_t * get_algo_attr ( key_type_t key_type,
size_t * len )
static

Returns algorithm attributes for a key role based on stored key type.

Parameters
key_typeKey role (signature, decryption, authentication).
lenOutput pointer receiving the attribute length.
Returns
Pointer to the selected algorithm-attribute byte array.

Definition at line 501 of file openpgp.cpp.

References ALGO_ATTR_ED25519, ALGO_ATTR_P256_ECDH, ALGO_ATTR_P256_ECDSA, CDC_CURVE_P256, curve, KEY_TYPE_AUT, KEY_TYPE_DEC, selected_curve_aut, and selected_curve_sig.

Referenced by build_do_app_related(), and cmd_get_data().

◆ get_ecc_slot_for_key_ref()

uint8_t get_ecc_slot_for_key_ref ( uint8_t key_ref)
static

Returns ECC slot mapping for an OpenPGP key reference.

Parameters
key_refOpenPGP key reference value.
Returns
ECC slot index used in secure element storage.

Definition at line 2346 of file openpgp.cpp.

References gpg_storage_aut_slot(), gpg_storage_dec_slot(), gpg_storage_sig_slot(), KEY_AUT, KEY_DEC, and KEY_SIG.

Referenced by cmd_generate_keypair().

◆ get_key_type_for_ref()

key_type_t get_key_type_for_ref ( uint8_t key_ref)
static

Maps an OpenPGP key reference to an internal key type.

Parameters
key_refOpenPGP key reference value.
Returns
Internal key_type_t for algorithm selection.

Definition at line 2364 of file openpgp.cpp.

References KEY_AUT, KEY_DEC, KEY_SIG, KEY_TYPE_AUT, KEY_TYPE_DEC, and KEY_TYPE_SIG.

Referenced by cmd_generate_keypair().

◆ get_se()

cdc::hal::ISecureElement * get_se ( )
static

Returns secure-element instance used by OpenPGP backend.

Returns
Pointer to secure-element abstraction.

Definition at line 41 of file openpgp.cpp.

References cdc::hal::getSecureElementInstance().

Referenced by load_state_from_nvs(), openpgp_factory_reset(), put_data_algo_attr(), save_state_to_nvs(), se_ecc_key_generate(), se_ecc_key_read(), se_ecdsa_sign(), se_eddsa_sign(), and se_random_fill().

◆ init_aid_from_mac()

void init_aid_from_mac ( void )
static

Initializes the OpenPGP AID serial section from the ESP32 MAC address.

Returns
void

Definition at line 833 of file openpgp.cpp.

References LOG_I, LOG_W, s_openpgp_aid, and TAG.

Referenced by openpgp_init().

◆ load_state_from_nvs()

◆ openpgp_factory_reset()

◆ openpgp_get_cardholder_name()

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<<Firstname" or empty when unset.

Returns
number of bytes copied (excluding null terminator).

Definition at line 913 of file openpgp.cpp.

References cardholder_name.

Referenced by gpg_get_status().

◆ openpgp_get_fingerprint()

bool openpgp_get_fingerprint ( uint8_t key_type,
uint8_t * fp_out )

Reads the stored OpenPGP v4 fingerprint for a key role.

Parameters
key_typeOne of KEY_SIG, KEY_DEC, KEY_AUT.
fp_out20-byte output buffer.
Returns
true on success.

Definition at line 890 of file openpgp.cpp.

References fingerprint_aut, fingerprint_dec, fingerprint_sig, KEY_AUT, KEY_DEC, KEY_SIG, and OPENPGP_FINGERPRINT_SIZE.

Referenced by gpg_get_status().

◆ openpgp_get_gen_time()

uint32_t openpgp_get_gen_time ( uint8_t key_type)

Returns the stored Unix timestamp of key generation, or 0 when unset.

Parameters
key_typeOne of KEY_SIG, KEY_DEC, KEY_AUT.

Definition at line 922 of file openpgp.cpp.

References gen_time_aut, gen_time_dec, gen_time_sig, KEY_AUT, KEY_DEC, and KEY_SIG.

Referenced by gpg_get_status().

◆ openpgp_get_sig_count()

uint32_t openpgp_get_sig_count ( void )

Definition at line 886 of file openpgp.cpp.

References sig_count.

Referenced by gpg_get_status().

◆ openpgp_has_any_key()

bool openpgp_has_any_key ( void )

Reports whether any of the SIG / DEC / AUT roles has a non-zero fingerprint configured. Acts as the canonical "card has keys" check.

Definition at line 907 of file openpgp.cpp.

References fingerprint_aut, fingerprint_dec, fingerprint_sig, and fp_is_set().

Referenced by gpg_export_pubkey_pem(), gpg_generate_key(), gpg_get_status(), gpg_is_initialized(), and cdc::mod_gpg::rebuildMenu().

◆ openpgp_init()

bool openpgp_init ( void )

◆ openpgp_is_selected()

bool openpgp_is_selected ( void )

Definition at line 882 of file openpgp.cpp.

References app_selected.

◆ openpgp_process_apdu()

◆ openpgp_set_cardholder_name()

bool openpgp_set_cardholder_name ( const char * name)

Sets the cardholder name (OpenPGP DO 0x5B) and persists state.

Parameters
nameUTF-8 string; truncated to fit the storage buffer.

Definition at line 936 of file openpgp.cpp.

References cardholder_name, name, and save_state_to_nvs().

Referenced by gpg_generate_key().

◆ openpgp_set_key_fingerprint()

bool openpgp_set_key_fingerprint ( uint8_t key_type,
const uint8_t * fingerprint,
uint32_t gen_time )

◆ peek_verify_pin()

bool peek_verify_pin ( pin_slot_t slot,
const char * pin )
static

Compares a candidate PIN against the stored hash without touching retry counters.

Parameters
slotPIN slot to query.
pinCandidate PIN string.
Returns
true if PIN matches the stored hash.

Definition at line 1705 of file openpgp.cpp.

References compute_kdf_hash(), const_time_equal(), cdc::core::PinManager::instance(), and PIN_SLOT_PW1.

Referenced by try_change_pin().

◆ put_data_algo_attr()

int put_data_algo_attr ( uint16_t tag,
const apdu_t * apdu,
uint8_t * resp )
static

Handles APDU PUT DATA command processing.

Parameters
apduParsed APDU request.
respOutput response buffer.
resp_maxMaximum size of resp.
Returns
APDU status/response length result.

Parses and applies a PUT DATA payload addressed to one of the algorithm-attribute Data Objects (C1, C2, C3).

For SIG (C1) and AUT (C3) the selected curve is updated and the existing key in the corresponding slot is wiped to force the host to regenerate it. DEC (C2) is constrained to P-256 ECDH (software path), so we accept the documented attribute and reject everything else.

Definition at line 1307 of file openpgp.cpp.

References ALGO_ATTR_CURVE_ED25519, ALGO_ATTR_CURVE_P256, ALGO_ATTR_ID_ECDH, ALGO_ATTR_OK, algo_attr_parse(), ALGO_ATTR_ROLE_AUT, ALGO_ATTR_ROLE_DEC, ALGO_ATTR_ROLE_SIG, algo_attr_validate_capability(), algo_attr_validate_role(), algo_attr_t::algo_id, apdu_sw(), CDC_CURVE_ED25519, CDC_CURVE_P256, algo_attr_t::curve, DO_ALGO_AUT, DO_ALGO_DEC, DO_ALGO_SIG, fingerprint_aut, fingerprint_sig, gen_time_aut, gen_time_sig, get_se(), gpg_storage_aut_slot(), gpg_storage_sig_slot(), LOG_I, save_state_to_nvs(), selected_curve_aut, selected_curve_sig, SW_FILE_NOT_FOUND, SW_OK, SW_WRONG_DATA, and TAG.

Referenced by cmd_put_data().

◆ read_public_key()

bool read_public_key ( key_type_t key_type,
uint8_t ecc_slot,
uint8_t * pubkey,
uint8_t * curve_out )
static

Reads the public key for a given key role.

For SIG/AUT keys this reads from the TROPIC01 ECC slot. For DEC keys, the public key is derived from the encrypted software-stored private key; the temporary private-key buffer is zeroized after use.

Parameters
key_typeLogical key role.
ecc_slotECC slot index for hardware-backed keys.
pubkeyOutput buffer (must be at least P256_PUBKEY_SIZE bytes).
curve_outCurve detected for the loaded public key.
Returns
true on success, otherwise false.

Definition at line 2445 of file openpgp.cpp.

References CDC_CURVE_P256, ecdh_p256_derive_pubkey(), gpg_storage_has_dec_privkey(), gpg_storage_load_dec_privkey(), KEY_TYPE_DEC, LOG_I, LOG_W, P256_PRIVKEY_SIZE, P256_PUBKEY_SIZE, se_ecc_key_read(), and TAG.

Referenced by cmd_generate_keypair().

◆ save_state_to_nvs()

◆ se_ecc_key_generate()

bool se_ecc_key_generate ( uint8_t slot,
uint8_t curve )
static

Generates ECC key material in secure element slot.

Parameters
slotECC slot index.
curveCurve identifier (CDC_CURVE_*).
Returns
true if key generation succeeded.

Definition at line 74 of file openpgp.cpp.

References CDC_CURVE_ED25519, curve, cdc::hal::ED25519, get_se(), LOG_E, LOG_W, cdc::hal::OK, cdc::hal::P256, and TAG.

Referenced by generate_hardware_key().

◆ se_ecc_key_read()

bool se_ecc_key_read ( uint8_t slot,
uint8_t * pubkey,
size_t max_len,
uint8_t * curve_out )
static

Reads ECC public key from secure element and exposes curve metadata.

Parameters
slotECC slot index.
pubkeyOutput public key buffer.
max_lenCapacity of pubkey.
curve_outOptional output curve identifier.
Returns
true when key exists and output buffer size matches curve format.

Definition at line 53 of file openpgp.cpp.

References CDC_CURVE_ED25519, CDC_CURVE_P256, curve, cdc::hal::ED25519, ED25519_PUBKEY_SIZE, get_se(), cdc::hal::OK, cdc::hal::P256, and P256_PUBKEY_SIZE.

Referenced by cmd_get_data(), cmd_internal_authenticate(), cmd_pso_cds(), and read_public_key().

◆ se_ecdsa_sign()

bool se_ecdsa_sign ( uint8_t slot,
const uint8_t * hash,
size_t hash_len,
uint8_t * sig )
static

Signs a hash using secure-element ECDSA key.

Parameters
slotECC slot index.
hashHash bytes to sign.
hash_lenHash length.
sigOutput 64-byte signature buffer.
Returns
true if signing succeeded.

Definition at line 109 of file openpgp.cpp.

References get_se(), and cdc::hal::OK.

Referenced by cmd_internal_authenticate(), and cmd_pso_cds().

◆ se_eddsa_sign()

bool se_eddsa_sign ( uint8_t slot,
const uint8_t * msg,
size_t msg_len,
uint8_t * sig )
static

Signs a message using secure-element EdDSA key.

Parameters
slotECC slot index.
Message transfer (badge-to-badge)Message bytes.
msg_lenMessage length.
sigOutput signature buffer.
Returns
true if signing succeeded.

Definition at line 124 of file openpgp.cpp.

References get_se(), and cdc::hal::OK.

Referenced by cmd_internal_authenticate(), and cmd_pso_cds().

◆ se_random_fill()

void se_random_fill ( uint8_t * buf,
size_t len )
static

Fills buffer with secure random bytes, with ESP fallback.

Parameters
bufOutput buffer.
lenNumber of bytes to generate.

Definition at line 135 of file openpgp.cpp.

References get_se().

Referenced by cmd_put_data(), and openpgp_process_apdu().

◆ tlv_build()

size_t tlv_build ( uint8_t * buf,
size_t buf_max,
uint16_t tag,
const uint8_t * value,
size_t value_len )
static

Builds complete TLV object and returns total encoded length.

Parameters
bufOutput buffer.
buf_maxMaximum size of buf.
tagTLV tag.
valueOptional value bytes.
value_lenValue length.
Returns
Total bytes written to buf.

Definition at line 470 of file openpgp.cpp.

References tlv_write_len(), and tlv_write_tag().

Referenced by build_do_app_related(), build_do_cardholder(), and cmd_generate_keypair().

◆ tlv_write_len()

size_t tlv_write_len ( uint8_t * buf,
size_t len )
static

Writes a TLV length field using DER length encoding.

Parameters
bufOutput buffer receiving the encoded length.
lenLength value to encode.
Returns
Number of bytes written to buf.

Definition at line 445 of file openpgp.cpp.

Referenced by build_do_app_related(), build_do_cardholder(), cmd_generate_keypair(), and tlv_build().

◆ tlv_write_tag()

size_t tlv_write_tag ( uint8_t * buf,
uint16_t tag )
static

TLV builder helper functions.

Writes a TLV tag using one or two bytes.

Parameters
bufOutput buffer receiving the tag bytes.
tagTLV tag value.
Returns
Number of bytes written to buf.

Definition at line 429 of file openpgp.cpp.

Referenced by build_do_app_related(), build_do_cardholder(), and tlv_build().

◆ try_change_pin()

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 )
static

Searches the split point for CHANGE REFERENCE DATA without consuming retries.

Iterates over candidate old-PIN lengths and uses a non-decrementing hash comparison. Only the matched split is applied through change_fn; the underlying retry counter is left untouched until the caller consumes one retry on overall failure.

Parameters
dataConcatenated old||new PIN bytes.
lenTotal length of data.
min_lenMinimum PIN length for the target slot.
slotPIN slot identifier.
change_fnCallback that applies the new PIN.
Returns
true if a split was found and the change succeeded.

Definition at line 1749 of file openpgp.cpp.

References OPENPGP_PIN_MAX_LEN, and peek_verify_pin().

Referenced by cmd_change_reference_data().

◆ update_generation_timestamp()

void update_generation_timestamp ( uint8_t key_ref)
static

Updates and persists the generation timestamp for a key role.

Parameters
key_refOpenPGP key reference (KEY_SIG/KEY_DEC/KEY_AUT).

Definition at line 2414 of file openpgp.cpp.

References gen_time_aut, gen_time_dec, gen_time_sig, KEY_AUT, KEY_DEC, KEY_SIG, and save_state_to_nvs().

Referenced by cmd_generate_keypair(), and cmd_put_data_odd().

◆ verify_state_signature()

bool verify_state_signature ( cdc::hal::ISecureElement * se,
const uint8_t * payload,
size_t payload_len,
const uint8_t * sig,
size_t sig_len )
static

Verifies the P-256 ECDSA attestation signature over an OpenPGP state payload. Same construction as PinManager.

Definition at line 653 of file openpgp.cpp.

References ATTESTATION_ECC_SLOT, curve, cdc::hal::ISecureElement::eccGetPublicKey(), cdc::hal::OK, OPENPGP_STATE_SIG_SIZE, and cdc::hal::P256.

Referenced by load_state_from_nvs().

Variable Documentation

◆ ALGO_ATTR_ED25519

const uint8_t ALGO_ATTR_ED25519[]
static
Initial value:
= {
0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01
}
#define ALGO_EDDSA
Definition openpgp.h:32

Algorithm attributes for Ed25519 (EdDSA with curve25519).

Format: Algorithm ID (1) + OID bytes (no length prefix per OpenPGP 3.4.1).

Definition at line 381 of file openpgp.cpp.

Referenced by get_algo_attr().

◆ ALGO_ATTR_P256_ECDH

const uint8_t ALGO_ATTR_P256_ECDH[]
static
Initial value:
= {
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07
}
#define ALGO_ECDH
Definition openpgp.h:30

Algorithm attributes for P-256 ECDH (decryption role).

Format: Algorithm ID (1) + OID bytes (no length prefix per OpenPGP 3.4.1).

Definition at line 401 of file openpgp.cpp.

Referenced by get_algo_attr().

◆ ALGO_ATTR_P256_ECDSA

const uint8_t ALGO_ATTR_P256_ECDSA[]
static
Initial value:
= {
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07
}
#define ALGO_ECDSA
Definition openpgp.h:31

Algorithm attributes for P-256 ECDSA (signature/authentication roles).

Format: Algorithm ID (1) + OID bytes (no length prefix per OpenPGP 3.4.1).

Definition at line 391 of file openpgp.cpp.

Referenced by get_algo_attr().

◆ app_selected

bool app_selected = false
static

ATR is defined in ccid.cpp and accessed via ccid_get_atr().

Application session/authentication state.

Definition at line 166 of file openpgp.cpp.

Referenced by cmd_select(), openpgp_is_selected(), and openpgp_process_apdu().

◆ ATTESTATION_ECC_SLOT

uint8_t ATTESTATION_ECC_SLOT = 0
staticconstexpr

Definition at line 646 of file openpgp.cpp.

Referenced by save_state_to_nvs(), and verify_state_signature().

◆ ca_fp_1

uint8_t ca_fp_1[OPENPGP_FINGERPRINT_SIZE] = {0}
static

Optional CA fingerprints for trust-chain metadata.

Definition at line 351 of file openpgp.cpp.

Referenced by build_do_app_related(), cmd_get_data(), find_put_data_desc(), load_state_from_nvs(), openpgp_factory_reset(), and save_state_to_nvs().

◆ ca_fp_2

◆ ca_fp_3

◆ card_terminated

bool card_terminated = false
static

Card lifecycle state per OpenPGP 3.4.1 §7.2.18.

false = operational, true = terminated. While terminated the dispatcher accepts only SELECT and ACTIVATE FILE; everything else returns SW_FILE_TERMINATED (0x6285). ACTIVATE FILE wipes keys, DOs and PINs back to factory defaults.

Definition at line 248 of file openpgp.cpp.

Referenced by cmd_activate_file(), cmd_terminate_df(), load_state_from_nvs(), openpgp_factory_reset(), openpgp_process_apdu(), and save_state_to_nvs().

◆ cardholder_lang

char cardholder_lang[8] = "en"
static

◆ cardholder_login

char cardholder_login[32] = {0}
static

◆ cardholder_name

char cardholder_name[40] = {0}
static

◆ cardholder_sex

uint8_t cardholder_sex = 0x39
static

◆ cardholder_url

char cardholder_url[64] = {0}
static

◆ EXT_CAPABILITIES

const uint8_t EXT_CAPABILITIES[]
static
Initial value:
= {
0x7D,
0x00,
0x00, 0x80,
0x08, 0x00,
0x01, 0x00,
0x00,
0x00,
}

Extended capabilities object per OpenPGP 3.4.1 section 4.2.1.

Definition at line 409 of file openpgp.cpp.

Referenced by build_do_app_related(), and cmd_get_data().

◆ fingerprint_aut

◆ fingerprint_dec

◆ fingerprint_sig

uint8_t fingerprint_sig[OPENPGP_FINGERPRINT_SIZE] = {0}
static

◆ g_chain_active

bool g_chain_active = false
static

Definition at line 275 of file openpgp.cpp.

Referenced by chain_reset(), and openpgp_process_apdu().

◆ g_chain_buffer

uint8_t g_chain_buffer[4096]
static

Command-chaining accumulator (ISO 7816-4 §5.1.1).

The host sets the CLA chaining bit (0x10) on every intermediate APDU and clears it on the last one. We accumulate the command data here until the final chunk arrives, then dispatch a single synthetic APDU. Triggered by gpg key import (RSA / large ECC), large PUT DATA (e.g. cardholder cert), and PSO:DECIPHER with extended Cipher DOs.

Definition at line 273 of file openpgp.cpp.

Referenced by openpgp_process_apdu().

◆ g_chain_ins

uint8_t g_chain_ins = 0
static

Definition at line 276 of file openpgp.cpp.

Referenced by chain_reset(), and openpgp_process_apdu().

◆ g_chain_len

size_t g_chain_len = 0
static

Definition at line 274 of file openpgp.cpp.

Referenced by chain_reset(), and openpgp_process_apdu().

◆ g_chain_p1

uint8_t g_chain_p1 = 0
static

Definition at line 277 of file openpgp.cpp.

Referenced by chain_reset(), and openpgp_process_apdu().

◆ g_chain_p2

uint8_t g_chain_p2 = 0
static

Definition at line 278 of file openpgp.cpp.

Referenced by chain_reset(), and openpgp_process_apdu().

◆ g_resp_buffer

uint8_t g_resp_buffer[4096]
static

Buffered remainder of an APDU response that did not fit into the caller-supplied Le window. Drained one chunk at a time via GET RESPONSE (INS 0xC0) per ISO 7816-4 §5.3.4. Lifetime is bound to the next APDU on the same logical channel: the buffer is invalidated when any non-GET RESPONSE command arrives.

4 kB cap covers the largest OpenPGP DOs (Cardholder Certificate, Application Related Data with full key info) without claiming PSRAM.

Definition at line 260 of file openpgp.cpp.

Referenced by apply_response_chaining(), and cmd_get_response().

◆ g_resp_pos

size_t g_resp_pos = 0
static

Definition at line 262 of file openpgp.cpp.

Referenced by apply_response_chaining(), cmd_get_response(), and openpgp_process_apdu().

◆ g_resp_remaining

size_t g_resp_remaining = 0
static

Definition at line 261 of file openpgp.cpp.

Referenced by apply_response_chaining(), cmd_get_response(), and openpgp_process_apdu().

◆ gen_time_aut

◆ gen_time_dec

◆ gen_time_sig

uint8_t gen_time_sig[4] = {0}
static

◆ HIST_BYTES

const uint8_t HIST_BYTES[]
static
Initial value:
= {
0x00,
0x31,
0xC5,
0x73, 0xC0, 0x01, 0x80,
0x05,
0x90, 0x00
}

Historical bytes used in OpenPGP ATR-related data objects.

Definition at line 367 of file openpgp.cpp.

Referenced by build_do_app_related(), and cmd_get_data().

◆ OPENPGP_AID

const uint8_t* OPENPGP_AID = s_openpgp_aid

Definition at line 156 of file openpgp.cpp.

Referenced by build_do_app_related(), cmd_get_data(), and cmd_select().

◆ OPENPGP_AID_LEN

const uint8_t OPENPGP_AID_LEN = sizeof(s_openpgp_aid)

Definition at line 157 of file openpgp.cpp.

Referenced by build_do_app_related(), and cmd_get_data().

◆ OPENPGP_NVS_SCHEMA_V2

uint8_t OPENPGP_NVS_SCHEMA_V2 = 2
staticconstexpr

Definition at line 332 of file openpgp.cpp.

Referenced by load_state_from_nvs(), and save_state_to_nvs().

◆ OPENPGP_STATE_SIG_SIZE

size_t OPENPGP_STATE_SIG_SIZE = 64
staticconstexpr

Definition at line 647 of file openpgp.cpp.

Referenced by load_state_from_nvs(), save_state_to_nvs(), and verify_state_signature().

◆ pw1_verified

◆ pw3_verified

◆ RC_HASH_SIZE

size_t RC_HASH_SIZE = 32
staticconstexpr

◆ RC_KDF_TOTAL_BYTES

size_t RC_KDF_TOTAL_BYTES = 100000
staticconstexpr

Definition at line 185 of file openpgp.cpp.

Referenced by compute_rc_hash().

◆ RC_SALT_SIZE

size_t RC_SALT_SIZE = 16
staticconstexpr

Definition at line 183 of file openpgp.cpp.

Referenced by cmd_put_data(), compute_rc_hash(), load_state_from_nvs(), and save_state_to_nvs().

◆ s_openpgp_aid

uint8_t s_openpgp_aid[16]
static
Initial value:
= {
0xD2, 0x76, 0x00, 0x01, 0x24, 0x01,
0x03, 0x04,
0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00
}

OpenPGP Application ID (RID + PIX), initialized dynamically.

D2 76 00 01 24 01 = OpenPGP RID. Structure: RID(6) + Version(2) + Manufacturer(2) + Serial(4) + RFU(2) = 16 bytes.

Definition at line 149 of file openpgp.cpp.

Referenced by init_aid_from_mac().

◆ s_rc_hash

uint8_t s_rc_hash[RC_HASH_SIZE] = {0}
static

◆ s_rc_len

◆ s_rc_retries

◆ s_rc_salt

uint8_t s_rc_salt[RC_SALT_SIZE] = {0}
static

◆ s_session_pin

char s_session_pin[OPENPGP_PIN_MAX_LEN+1] = {}
static

Session PIN cache for DEC key decryption (temporary after VERIFY for PSO:DECIPHER).

Definition at line 291 of file openpgp.cpp.

Referenced by __attribute__(), cmd_pso_decipher_aes(), cmd_put_data(), cmd_select(), and cmd_verify().

◆ selected_curve_aut

◆ selected_curve_sig

uint8_t selected_curve_sig = CDC_CURVE_ED25519
static

Host-selected ECC curve per key role. DEC is fixed to P-256 because the TROPIC01 cannot perform ECDH natively and the firmware only carries a software P-256 ECDH path in ecdh.cpp. SIG and AUT default to Ed25519 (the project's preferred curve for signing) and can be flipped to P-256 via PUT DATA C1 / C3 per OpenPGP 3.4.1 §4.4.3.7-9.

Definition at line 237 of file openpgp.cpp.

Referenced by cmd_generate_keypair(), get_algo_attr(), load_state_from_nvs(), openpgp_factory_reset(), put_data_algo_attr(), and save_state_to_nvs().

◆ sig_count

◆ TAG

const char* TAG = "OpenPGP"
static

OpenPGP smart-card application implementation for CDC Badge.

Based on pico-openpgp (https://github.com/polhenarejos/pico-openpgp), adapted for CDC Badge and TROPIC01 secure element integration. Specification target: OpenPGP Smart Card Application 3.4.1.

Definition at line 35 of file openpgp.cpp.