10#include <mbedtls/sha1.h>
11#include <mbedtls/base64.h>
12#include <mbedtls/platform_util.h>
18constexpr const char*
TAG =
"GPG";
26uint32_t get_unix_time(
void) {
27 return static_cast<uint32_t
>(time(
nullptr));
34bool se_get_pubkey(uint8_t slot, uint8_t* pubkey,
size_t max_len, uint8_t* curve_out) {
36 if (!se || !pubkey)
return false;
38 LOG_E(
TAG,
"se_get_pubkey: buffer too small (slot=%u max_len=%zu)", slot, max_len);
42 auto res = se->eccGetPublicKey(slot, pubkey, &
curve);
44 LOG_E(
TAG,
"se_get_pubkey: slot=%u failed (%d)", slot,
static_cast<int>(res));
54bool se_generate_key(uint8_t slot, uint8_t
curve) {
57 LOG_E(
TAG,
"se_generate_key: no SE (slot=%u)", slot);
64 LOG_W(
TAG,
"se_generate_key: slot %u initial fail (%d), retry after delete",
65 slot,
static_cast<int>(res));
67 res = se->eccGenerate(slot, c);
70 LOG_E(
TAG,
"se_generate_key: slot %u curve=%u failed (%d)",
71 slot,
curve,
static_cast<int>(res));
77bool calculate_fingerprint(
const uint8_t *pubkey, uint8_t
curve,
78 uint32_t created_at, uint8_t *fp_out) {
80 return ::cdc::mod_gpg::calculateFingerprintV4(
curve, pubkey, pubkey_len,
87 s_pending_user_id[0] =
'\0';
96 if (!status)
return false;
97 memset(status, 0,
sizeof(*status));
100 status->initialized =
true;
104 memcpy(status->fingerprint, fp,
sizeof(status->fingerprint));
110 uint8_t sig_pub[64] = {0};
120 strncpy(status->user_id,
name,
sizeof(status->user_id) - 1);
127 strncpy(s_pending_user_id,
user_id,
sizeof(s_pending_user_id) - 1);
128 s_pending_user_id[
sizeof(s_pending_user_id) - 1] =
'\0';
133 return s_pending_user_id[0] !=
'\0';
138 LOG_E(
TAG,
"generate: storage not ready");
142 LOG_E(
TAG,
"generate: missing user-id");
149 LOG_I(
TAG,
"generate: curve=%u sig_slot=%u dec_slot=%u aut_slot=%u",
150 curve, sig_slot, dec_slot, aut_slot);
153 if (!se_generate_key(sig_slot,
curve)) {
154 LOG_E(
TAG,
"generate: SIG key generation failed");
157 if (!se_generate_key(aut_slot,
curve)) {
158 LOG_E(
TAG,
"generate: AUT key generation failed");
167 se->eccDelete(dec_slot);
172 LOG_E(
TAG,
"generate: DEC ECDH keypair failed");
176 mbedtls_platform_zeroize(dec_priv,
sizeof(dec_priv));
178 LOG_E(
TAG,
"generate: DEC privkey save failed");
182 uint32_t created_at = get_unix_time();
184 uint8_t pub_sig[64] = {};
185 uint8_t pub_aut[64] = {};
186 uint8_t curve_sig =
curve;
187 uint8_t curve_aut =
curve;
188 if (!se_get_pubkey(sig_slot, pub_sig,
sizeof(pub_sig), &curve_sig)) {
189 LOG_E(
TAG,
"generate: read SIG pubkey failed");
192 if (!se_get_pubkey(aut_slot, pub_aut,
sizeof(pub_aut), &curve_aut)) {
193 LOG_E(
TAG,
"generate: read AUT pubkey failed");
200 if (!calculate_fingerprint(pub_sig, curve_sig, created_at, fp_sig)) {
201 LOG_E(
TAG,
"generate: SIG fingerprint failed");
209 LOG_E(
TAG,
"generate: DEC fingerprint failed");
212 if (!calculate_fingerprint(pub_aut, curve_aut, created_at, fp_aut)) {
213 LOG_E(
TAG,
"generate: AUT fingerprint failed");
221 if (s_pending_user_id[0]) {
225 s_pending_user_id[0] =
'\0';
235 s_pending_user_id[0] =
'\0';
240 if (!buf || size < 256 || !out_len) {
245 LOG_W(
TAG,
"export: no key configured");
260 auto res = se->eccGetPublicKey(sig_slot, pubkey, &eccCurve);
262 LOG_W(
TAG,
"export: eccGetPublicKey slot=%u failed (%d)",
263 sig_slot,
static_cast<int>(res));
273 static const uint8_t ed25519_prefix[] = {
276 0x06, 0x03, 0x2b, 0x65, 0x70,
280 uint8_t der[
sizeof(ed25519_prefix) + 32];
281 memcpy(der, ed25519_prefix,
sizeof(ed25519_prefix));
282 memcpy(der +
sizeof(ed25519_prefix), pubkey, 32);
286 if (mbedtls_base64_encode(
reinterpret_cast<unsigned char*
>(b64),
sizeof(b64),
287 &b64_len, der,
sizeof(der)) != 0) {
291 int written = snprintf(buf, size,
292 "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
294 if (written < 0 ||
static_cast<size_t>(written) >= size)
return false;
295 *out_len =
static_cast<size_t>(written);
299 static const uint8_t p256_prefix[] = {
302 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
303 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
306 uint8_t der[
sizeof(p256_prefix) + 65];
307 memcpy(der, p256_prefix,
sizeof(p256_prefix));
308 der[
sizeof(p256_prefix)] = 0x04;
309 memcpy(der +
sizeof(p256_prefix) + 1, pubkey, 64);
313 if (mbedtls_base64_encode(
reinterpret_cast<unsigned char*
>(b64),
sizeof(b64),
314 &b64_len, der,
sizeof(der)) != 0) {
317 int written = snprintf(buf, size,
318 "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
320 if (written < 0 ||
static_cast<size_t>(written) >= size)
return false;
321 *out_len =
static_cast<size_t>(written);
326 if (!pub65)
return false;
329 LOG_W(
TAG,
"dec pubkey: load privkey failed");
333 mbedtls_platform_zeroize(priv,
sizeof(priv));
uint8_t gpg_storage_dec_slot(void)
bool gpg_storage_ready(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_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)
uint8_t gpg_storage_sig_slot(void)
#define KEY_FINGERPRINT_MAX_LEN
bool key_fingerprint_generate(uint8_t slot, char *buf, size_t len)
Reads public key from secure element slot and generates fingerprint.
char name[cdc::hal::ISecureElement::RMEM_NAME_LEN]
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
#define LOG_I(tag, fmt,...)
#define LOG_E(tag, fmt,...)
#define ED25519_PUBKEY_SIZE
Ed25519 raw public key size in bytes.
#define P256_PUBKEY_SIZE
P-256 uncompressed public key size: 0x04 || X(32) || Y(32).
#define P256_PRIVKEY_SIZE
P-256 private key (scalar) size in bytes.
bool ecdh_p256_generate_keypair(uint8_t *privkey_out, uint8_t *pubkey_out)
bool ecdh_p256_derive_pubkey(const uint8_t *privkey, uint8_t *pubkey_out)
#define CDC_CURVE_ED25519
uint8_t user_id[FIDO2_USER_ID_MAX_LEN]
bool gpg_has_pending_user_id(void)
Returns whether a user-id was staged via gpg_set_pending_user_id().
bool gpg_init(void)
Initializes the GPG module bookkeeping.
bool gpg_set_pending_user_id(const char *user_id)
Stages a user-id string for the next on-device key generation. The string is forwarded to OpenpgpNvsS...
bool gpg_generate_key(uint8_t curve)
Generates SIG / DEC / AUT keys on the device and announces them to the OpenPGP card application (fing...
bool gpg_alchemy_fingerprint(char *buf, size_t len)
Writes the alchemical-word fingerprint of the SIG public key.
bool gpg_get_status(gpg_status_t *status)
Fills status from the OpenPGP card-application state.
bool gpg_is_initialized(void)
Reports whether at least one OpenPGP key role has a configured fingerprint on the card.
bool gpg_export_pubkey_pem(char *buf, size_t size, size_t *out_len)
Renders the current SIG public key as a SubjectPublicKeyInfo PEM. The key is read straight from the s...
bool gpg_get_dec_pubkey(uint8_t *pub65)
Derives the DEC (decryption) public key as an uncompressed P-256 point.
bool gpg_reset(void)
Factory-resets all GPG key material and metadata.
#define GPG_FINGERPRINT_LEN
ISecureElement * getSecureElementInstance()
Returns singleton secure-element stub instance.
bool calculateFingerprintV4Ecdh(const uint8_t *pubkey, uint32_t created_at, uint8_t out_fp[20])
Compute the V4 fingerprint of an ECDH (DEC) subkey.
bool openpgp_get_fingerprint(uint8_t key_type, uint8_t *fp_out)
Reads the stored OpenPGP v4 fingerprint for a key role.
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_key_fingerprint(uint8_t key_type, const uint8_t *fingerprint, uint32_t gen_time)
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...
bool openpgp_set_cardholder_name(const char *name)
Sets the cardholder name (OpenPGP DO 0x5B) and persists state.
uint32_t openpgp_get_sig_count(void)
bool openpgp_has_any_key(void)
Reports whether any of the SIG / DEC / AUT roles has a non-zero fingerprint configured....
void openpgp_factory_reset(void)