12#include <mbedtls/base64.h>
13#include <mbedtls/sha256.h>
23constexpr const char*
TAG =
"GPG_XSIG";
25static const uint8_t kOidEd25519[] = {0x09, 0x2B, 0x06, 0x01, 0x04, 0x01,
26 0xDA, 0x47, 0x0F, 0x01};
27static const uint8_t kOidP256[] = {0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D,
30constexpr uint8_t kHashAlgoSha256 = 0x08;
31constexpr uint8_t kSigTypeGenericCert = 0x10;
32constexpr uint8_t kSigTypeSubkeyBinding = 0x18;
33constexpr uint8_t kSubpktTypeSigCreated = 0x02;
34constexpr uint8_t kSubpktTypeIssuerKeyId = 0x10;
35constexpr uint8_t kSubpktTypeKeyFlags = 0x1B;
36constexpr uint8_t kKeyFlagEncrypt = 0x0C;
39bool isAllZero(
const uint8_t* p,
size_t n)
41 for (
size_t i = 0; i < n; ++i) {
42 if (p[i] != 0)
return false;
49size_t buildPubkeyBody(
const gpg_recv_key_t& key, uint8_t* out,
size_t out_size)
54 if (!is_ed25519 && key.pubkey_len < 64)
return 0;
57 const uint8_t* oid = is_ed25519 ? kOidEd25519 : kOidP256;
58 const size_t oid_len = is_ed25519 ?
sizeof(kOidEd25519) : sizeof(kOidP256);
66 mpi[0] =
static_cast<uint8_t
>((bits >> 8) & 0xFF);
67 mpi[1] =
static_cast<uint8_t
>(bits & 0xFF);
73 mpi[0] =
static_cast<uint8_t
>((bits >> 8) & 0xFF);
74 mpi[1] =
static_cast<uint8_t
>(bits & 0xFF);
80 const size_t total = 1 + 4 + 1 + oid_len + mpi_len;
81 if (total > out_size)
return 0;
85 out[off++] = (key.created_at >> 24) & 0xFF;
86 out[off++] = (key.created_at >> 16) & 0xFF;
87 out[off++] = (key.created_at >> 8) & 0xFF;
88 out[off++] = key.created_at & 0xFF;
90 std::memcpy(out + off, oid, oid_len);
92 std::memcpy(out + off, mpi, mpi_len);
102size_t buildSigSubpackets(
bool hashed,
103 const uint8_t fp_self[20],
104 uint32_t sig_creation_time,
105 uint8_t* out,
size_t out_size)
109 if (out_size < 6)
return 0;
112 out[1] = kSubpktTypeSigCreated;
113 out[2] = (sig_creation_time >> 24) & 0xFF;
114 out[3] = (sig_creation_time >> 16) & 0xFF;
115 out[4] = (sig_creation_time >> 8) & 0xFF;
116 out[5] = sig_creation_time & 0xFF;
120 if (out_size < 10)
return 0;
122 out[1] = kSubpktTypeIssuerKeyId;
123 std::memcpy(out + 2, fp_self + 12, 8);
128size_t writeNewFormatLength(uint8_t* out,
size_t len)
131 out[1] = (len >> 24) & 0xFF;
132 out[2] = (len >> 16) & 0xFF;
133 out[3] = (len >> 8) & 0xFF;
141size_t writeMpi(
const uint8_t* data,
size_t len, uint8_t* out,
size_t out_size)
145 while (start < len && data[start] == 0) ++start;
146 const size_t real_len = len - start;
148 if (out_size < 2)
return 0;
153 if (out_size < 2 + real_len)
return 0;
155 uint16_t bits =
static_cast<uint16_t
>(real_len) * 8;
156 uint8_t high = data[start];
157 for (
int b = 7; b >= 0; --b) {
158 if (high & (1u << b))
break;
161 out[0] = (bits >> 8) & 0xFF;
162 out[1] = bits & 0xFF;
163 std::memcpy(out + 2, data + start, real_len);
168uint32_t crc24(
const uint8_t* data,
size_t len)
170 constexpr uint32_t kCrc24Init = 0x00B704CEu;
171 constexpr uint32_t kCrc24Poly = 0x01864CFBu;
172 uint32_t crc = kCrc24Init;
173 for (
size_t i = 0; i < len; ++i) {
174 crc ^=
static_cast<uint32_t
>(data[i]) << 16;
175 for (
int b = 0; b < 8; ++b) {
177 if (crc & 0x01000000u) crc ^= kCrc24Poly;
180 return crc & 0x00FFFFFFu;
184size_t armorBase64(
const uint8_t* data,
size_t len,
char* out,
size_t out_size)
187 if (mbedtls_base64_encode(
reinterpret_cast<unsigned char*
>(out), out_size,
188 &enc_len, data, len) != 0) {
194 if (enc_len + (enc_len / 64) * 2 + 4 >
sizeof(tmp))
return 0;
197 for (
size_t i = 0; i < enc_len; i += 64) {
198 size_t chunk = std::min<size_t>(64, enc_len - i);
199 std::memcpy(tmp + out_idx, out + i, chunk);
201 tmp[out_idx++] =
'\r';
202 tmp[out_idx++] =
'\n';
204 if (out_idx > out_size)
return 0;
205 std::memcpy(out, tmp, out_idx);
212bool armorBinaryBlock(
const uint8_t* binary,
size_t binary_off,
213 char* out,
size_t out_size,
size_t* out_len)
215 static constexpr const char* kBegin =
"-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n\r\n";
216 static constexpr const char* kEnd =
"-----END PGP PUBLIC KEY BLOCK-----\r\n";
217 const size_t begin_len = std::strlen(kBegin);
218 const size_t end_len = std::strlen(kEnd);
220 if (begin_len + end_len + (binary_off * 2) + 16 > out_size)
return false;
223 std::memcpy(out + off, kBegin, begin_len);
226 size_t body_written = armorBase64(binary, binary_off, out + off, out_size - off);
227 if (body_written == 0)
return false;
231 uint8_t crc_bytes[3];
232 uint32_t crc = crc24(binary, binary_off);
233 crc_bytes[0] = (crc >> 16) & 0xFF;
234 crc_bytes[1] = (crc >> 8) & 0xFF;
235 crc_bytes[2] = crc & 0xFF;
237 if (off + 8 > out_size)
return false;
240 if (mbedtls_base64_encode(
reinterpret_cast<unsigned char*
>(out + off),
241 out_size - off, &crc_len,
242 crc_bytes,
sizeof(crc_bytes)) != 0) {
249 if (off + end_len > out_size)
return false;
250 std::memcpy(out + off, kEnd, end_len);
253 if (off < out_size) out[off] =
'\0';
261size_t buildUidCertPacket(
const uint8_t* pk_body,
size_t pk_body_len,
262 const char*
user_id,
size_t uid_len,
263 uint8_t signer_curve,
const uint8_t signer_fp[20],
264 uint32_t sig_created_at,
const uint8_t sig_rs[64],
265 uint8_t* out,
size_t out_size)
270 uint8_t hashed_subs[8];
271 const size_t hashed_subs_len = buildSigSubpackets(
true,
nullptr, sig_created_at,
272 hashed_subs,
sizeof(hashed_subs));
273 uint8_t unhashed_subs[10];
274 const size_t unhashed_subs_len = buildSigSubpackets(
false, signer_fp, 0,
275 unhashed_subs,
sizeof(unhashed_subs));
277 if (out_size < 6 + hashed_subs_len + 2 + unhashed_subs_len + 2)
return 0;
281 out[off++] = kSigTypeGenericCert;
282 out[off++] = sig_algo;
283 out[off++] = kHashAlgoSha256;
284 out[off++] = (hashed_subs_len >> 8) & 0xFF;
285 out[off++] = hashed_subs_len & 0xFF;
286 std::memcpy(out + off, hashed_subs, hashed_subs_len);
287 off += hashed_subs_len;
288 out[off++] = (unhashed_subs_len >> 8) & 0xFF;
289 out[off++] = unhashed_subs_len & 0xFF;
290 std::memcpy(out + off, unhashed_subs, unhashed_subs_len);
291 off += unhashed_subs_len;
295 const uint8_t pk_prefix[3] = {
297 static_cast<uint8_t
>((pk_body_len >> 8) & 0xFF),
298 static_cast<uint8_t
>(pk_body_len & 0xFF),
300 const uint8_t uid_prefix[5] = {
302 static_cast<uint8_t
>((uid_len >> 24) & 0xFF),
303 static_cast<uint8_t
>((uid_len >> 16) & 0xFF),
304 static_cast<uint8_t
>((uid_len >> 8) & 0xFF),
305 static_cast<uint8_t
>(uid_len & 0xFF),
307 uint8_t sig_data_header[6] = {
308 0x04, kSigTypeGenericCert, sig_algo, kHashAlgoSha256,
309 static_cast<uint8_t
>((hashed_subs_len >> 8) & 0xFF),
310 static_cast<uint8_t
>(hashed_subs_len & 0xFF),
312 const size_t sig_data_total =
sizeof(sig_data_header) + hashed_subs_len;
313 const uint8_t trailer[6] = {
315 static_cast<uint8_t
>((sig_data_total >> 24) & 0xFF),
316 static_cast<uint8_t
>((sig_data_total >> 16) & 0xFF),
317 static_cast<uint8_t
>((sig_data_total >> 8) & 0xFF),
318 static_cast<uint8_t
>(sig_data_total & 0xFF),
321 mbedtls_sha256_context ctx;
322 mbedtls_sha256_init(&ctx);
323 mbedtls_sha256_starts(&ctx, 0);
324 mbedtls_sha256_update(&ctx, pk_prefix,
sizeof(pk_prefix));
325 mbedtls_sha256_update(&ctx, pk_body, pk_body_len);
326 mbedtls_sha256_update(&ctx, uid_prefix,
sizeof(uid_prefix));
327 mbedtls_sha256_update(&ctx,
reinterpret_cast<const uint8_t*
>(
user_id), uid_len);
328 mbedtls_sha256_update(&ctx, sig_data_header,
sizeof(sig_data_header));
329 mbedtls_sha256_update(&ctx, hashed_subs, hashed_subs_len);
330 mbedtls_sha256_update(&ctx, trailer,
sizeof(trailer));
332 mbedtls_sha256_finish(&ctx, hash);
333 mbedtls_sha256_free(&ctx);
335 if (off + 2 > out_size)
return 0;
336 out[off++] = hash[0];
337 out[off++] = hash[1];
340 size_t mpi_off = writeMpi(sig_rs, 32, out + off, out_size - off);
341 if (mpi_off == 0)
return 0;
343 mpi_off = writeMpi(sig_rs + 32, 32, out + off, out_size - off);
344 if (mpi_off == 0)
return 0;
352size_t buildBindingHashedSubpackets(uint32_t sig_created_at, uint8_t* out)
356 out[off++] = kSubpktTypeSigCreated;
357 out[off++] = (sig_created_at >> 24) & 0xFF;
358 out[off++] = (sig_created_at >> 16) & 0xFF;
359 out[off++] = (sig_created_at >> 8) & 0xFF;
360 out[off++] = sig_created_at & 0xFF;
362 out[off++] = kSubpktTypeKeyFlags;
363 out[off++] = kKeyFlagEncrypt;
369void hashSubkeyBinding(
const uint8_t* primary_body,
size_t primary_len,
370 const uint8_t* subkey_body,
size_t subkey_len,
372 const uint8_t* hashed_subs,
size_t hashed_subs_len,
373 uint8_t out_hash[32])
375 const uint8_t sig_data_header[6] = {
376 0x04, kSigTypeSubkeyBinding, sig_algo, kHashAlgoSha256,
377 static_cast<uint8_t
>((hashed_subs_len >> 8) & 0xFF),
378 static_cast<uint8_t
>(hashed_subs_len & 0xFF),
380 const size_t sig_data_total =
sizeof(sig_data_header) + hashed_subs_len;
381 const uint8_t trailer[6] = {
383 static_cast<uint8_t
>((sig_data_total >> 24) & 0xFF),
384 static_cast<uint8_t
>((sig_data_total >> 16) & 0xFF),
385 static_cast<uint8_t
>((sig_data_total >> 8) & 0xFF),
386 static_cast<uint8_t
>(sig_data_total & 0xFF),
388 const uint8_t primary_prefix[3] = {
390 static_cast<uint8_t
>((primary_len >> 8) & 0xFF),
391 static_cast<uint8_t
>(primary_len & 0xFF),
393 const uint8_t subkey_prefix[3] = {
395 static_cast<uint8_t
>((subkey_len >> 8) & 0xFF),
396 static_cast<uint8_t
>(subkey_len & 0xFF),
399 mbedtls_sha256_context ctx;
400 mbedtls_sha256_init(&ctx);
401 mbedtls_sha256_starts(&ctx, 0);
402 mbedtls_sha256_update(&ctx, primary_prefix,
sizeof(primary_prefix));
403 mbedtls_sha256_update(&ctx, primary_body, primary_len);
404 mbedtls_sha256_update(&ctx, subkey_prefix,
sizeof(subkey_prefix));
405 mbedtls_sha256_update(&ctx, subkey_body, subkey_len);
406 mbedtls_sha256_update(&ctx, sig_data_header,
sizeof(sig_data_header));
407 mbedtls_sha256_update(&ctx, hashed_subs, hashed_subs_len);
408 mbedtls_sha256_update(&ctx, trailer,
sizeof(trailer));
409 mbedtls_sha256_finish(&ctx, out_hash);
410 mbedtls_sha256_free(&ctx);
415bool signSubkeyBinding(
const uint8_t* primary_body,
size_t primary_len,
416 const uint8_t* subkey_body,
size_t subkey_len,
417 uint32_t sig_created_at, uint8_t out_sig[64])
424 uint8_t hashed_subs[16];
425 const size_t hashed_subs_len = buildBindingHashedSubpackets(sig_created_at, hashed_subs);
428 hashSubkeyBinding(primary_body, primary_len, subkey_body, subkey_len,
429 sig_algo, hashed_subs, hashed_subs_len, hash);
432 if (!se)
return false;
438 return se->ecdsaSign(slot, hash,
sizeof(hash), out_sig, &sig_len)
444size_t buildSubkeyBindingSigPacket(
const uint8_t* primary_body,
size_t primary_len,
445 const uint8_t* subkey_body,
size_t subkey_len,
446 uint8_t signer_curve,
const uint8_t signer_fp[20],
447 uint32_t sig_created_at,
const uint8_t sig_rs[64],
448 uint8_t* out,
size_t out_size)
453 uint8_t hashed_subs[16];
454 const size_t hashed_subs_len = buildBindingHashedSubpackets(sig_created_at, hashed_subs);
455 uint8_t unhashed_subs[10];
456 const size_t unhashed_subs_len = buildSigSubpackets(
false, signer_fp, 0,
457 unhashed_subs,
sizeof(unhashed_subs));
459 if (out_size < 6 + hashed_subs_len + 2 + unhashed_subs_len + 2)
return 0;
463 out[off++] = kSigTypeSubkeyBinding;
464 out[off++] = sig_algo;
465 out[off++] = kHashAlgoSha256;
466 out[off++] = (hashed_subs_len >> 8) & 0xFF;
467 out[off++] = hashed_subs_len & 0xFF;
468 std::memcpy(out + off, hashed_subs, hashed_subs_len);
469 off += hashed_subs_len;
470 out[off++] = (unhashed_subs_len >> 8) & 0xFF;
471 out[off++] = unhashed_subs_len & 0xFF;
472 std::memcpy(out + off, unhashed_subs, unhashed_subs_len);
473 off += unhashed_subs_len;
476 hashSubkeyBinding(primary_body, primary_len, subkey_body, subkey_len,
477 sig_algo, hashed_subs, hashed_subs_len, hash);
478 if (off + 2 > out_size)
return 0;
479 out[off++] = hash[0];
480 out[off++] = hash[1];
482 size_t mpi_off = writeMpi(sig_rs, 32, out + off, out_size - off);
483 if (mpi_off == 0)
return 0;
485 mpi_off = writeMpi(sig_rs + 32, 32, out + off, out_size - off);
486 if (mpi_off == 0)
return 0;
500 if (!se)
return false;
502 uint8_t pubkey[64 + 1] = {0};
511 std::memmove(pubkey, pubkey + 1, 64);
517 std::memcpy(self.pubkey, pubkey, self.pubkey_len);
518 self.created_at = status.created_at;
519 std::memcpy(self.fingerprint_v4, status.fingerprint, 20);
520 std::strncpy(self.user_id, status.user_id,
sizeof(self.user_id) - 1);
528bool buildReceivedKeyArmored(
const gpg_recv_key_t& key,
bool include_our_cert,
529 char* out,
size_t out_size,
size_t* out_len)
531 if (!out || !out_len || out_size < 256)
return false;
533 uint8_t pk_body[128];
534 const size_t pk_body_len = buildPubkeyBody(key, pk_body,
sizeof(pk_body));
535 if (pk_body_len == 0)
return false;
536 const size_t uid_len = strnlen(key.user_id,
sizeof(key.user_id));
538 uint8_t binary[1024];
539 size_t binary_off = 0;
540 auto emit = [&](uint8_t tag,
const uint8_t* body,
size_t len) ->
bool {
541 if (binary_off + 1 + 5 + len >
sizeof(binary))
return false;
542 binary[binary_off++] = tag;
543 binary_off += writeNewFormatLength(binary + binary_off, len);
544 std::memcpy(binary + binary_off, body, len);
549 if (!emit(0xC6, pk_body, pk_body_len))
return false;
550 if (!emit(0xCD,
reinterpret_cast<const uint8_t*
>(key.user_id), uid_len))
return false;
553 if (!isAllZero(key.owner_self_sig, 64)) {
555 const size_t n = buildUidCertPacket(pk_body, pk_body_len, key.user_id, uid_len,
556 key.curve, key.fingerprint_v4,
557 key.created_at, key.owner_self_sig,
559 if (n > 0) emit(0xC2, sig, n);
563 if (include_our_cert && key.sig_len == 64) {
566 if (n > 0) emit(0xC2, sig, n);
570 if (!isAllZero(key.pubkey_dec, 64) && !isAllZero(key.dec_binding_sig, 64)) {
571 uint8_t dec_body[128];
573 dec_body,
sizeof(dec_body));
577 bind_len = buildSubkeyBindingSigPacket(pk_body, pk_body_len, dec_body, dec_len,
578 key.curve, key.fingerprint_v4,
579 key.created_at_dec, key.dec_binding_sig,
583 emit(0xCE, dec_body, dec_len);
584 emit(0xC2, bind, bind_len);
588 return armorBinaryBlock(binary, binary_off, out, out_size, out_len);
594 uint32_t sig_creation_time,
597 if (!out_sig)
return false;
601 LOG_W(
TAG,
"gpgCrossSign: own GPG key not available");
605 uint8_t pk_body[128];
606 const size_t pk_body_len = buildPubkeyBody(target, pk_body,
sizeof(pk_body));
607 if (pk_body_len == 0)
return false;
609 const size_t uid_len = strnlen(target.
user_id,
sizeof(target.
user_id));
613 uint8_t hashed_subs[8];
614 const size_t hashed_subs_len = buildSigSubpackets(
615 true,
nullptr, sig_creation_time, hashed_subs,
sizeof(hashed_subs));
620 uint8_t sig_data_header[6];
621 sig_data_header[0] = 0x04;
622 sig_data_header[1] = kSigTypeGenericCert;
623 sig_data_header[2] = sig_algo;
624 sig_data_header[3] = kHashAlgoSha256;
625 sig_data_header[4] = (hashed_subs_len >> 8) & 0xFF;
626 sig_data_header[5] = hashed_subs_len & 0xFF;
628 const size_t sig_data_total =
sizeof(sig_data_header) + hashed_subs_len;
629 const uint8_t trailer[6] = {
631 static_cast<uint8_t
>((sig_data_total >> 24) & 0xFF),
632 static_cast<uint8_t
>((sig_data_total >> 16) & 0xFF),
633 static_cast<uint8_t
>((sig_data_total >> 8) & 0xFF),
634 static_cast<uint8_t
>(sig_data_total & 0xFF),
641 mbedtls_sha256_context ctx;
642 mbedtls_sha256_init(&ctx);
643 mbedtls_sha256_starts(&ctx, 0);
645 const uint8_t pk_prefix[3] = {
647 static_cast<uint8_t
>((pk_body_len >> 8) & 0xFF),
648 static_cast<uint8_t
>(pk_body_len & 0xFF),
650 mbedtls_sha256_update(&ctx, pk_prefix,
sizeof(pk_prefix));
651 mbedtls_sha256_update(&ctx, pk_body, pk_body_len);
653 const uint8_t uid_prefix[5] = {
655 static_cast<uint8_t
>((uid_len >> 24) & 0xFF),
656 static_cast<uint8_t
>((uid_len >> 16) & 0xFF),
657 static_cast<uint8_t
>((uid_len >> 8) & 0xFF),
658 static_cast<uint8_t
>(uid_len & 0xFF),
660 mbedtls_sha256_update(&ctx, uid_prefix,
sizeof(uid_prefix));
661 mbedtls_sha256_update(&ctx,
reinterpret_cast<const uint8_t*
>(target.
user_id), uid_len);
663 mbedtls_sha256_update(&ctx, sig_data_header,
sizeof(sig_data_header));
664 mbedtls_sha256_update(&ctx, hashed_subs, hashed_subs_len);
665 mbedtls_sha256_update(&ctx, trailer,
sizeof(trailer));
668 mbedtls_sha256_finish(&ctx, hash);
669 mbedtls_sha256_free(&ctx);
672 if (!se)
return false;
679 return se->ecdsaSign(slot, hash,
sizeof(hash), out_sig, &sig_len)
685 if (!out || key.
sig_len != 64)
return 0;
690 uint8_t pk_body[128];
691 const size_t pk_body_len = buildPubkeyBody(key, pk_body,
sizeof(pk_body));
692 if (pk_body_len == 0)
return 0;
697 return buildUidCertPacket(pk_body, pk_body_len, key.
user_id, uid_len,
703 char* out,
size_t out_size,
706 return buildReceivedKeyArmored(key,
true, out, out_size, out_len);
710 char* out,
size_t out_size,
713 return buildReceivedKeyArmored(key,
false, out, out_size, out_len);
718 if (!out || !out_len || out_size < 256)
return false;
721 if (!loadOwnSigKey(self))
return false;
723 uint8_t pk_body[128];
724 const size_t pk_body_len = buildPubkeyBody(self, pk_body,
sizeof(pk_body));
725 if (pk_body_len == 0)
return false;
726 const size_t uid_len = strnlen(self.
user_id,
sizeof(self.
user_id));
728 constexpr size_t kBinaryCap = 3072;
730 if (!binary)
return false;
731 size_t binary_off = 0;
732 auto emit = [&](uint8_t tag,
const uint8_t* body,
size_t len) ->
bool {
733 if (binary_off + 1 + 5 + len > kBinaryCap)
return false;
734 binary[binary_off++] = tag;
735 binary_off += writeNewFormatLength(binary.get() + binary_off, len);
736 std::memcpy(binary.get() + binary_off, body, len);
741 if (!emit(0xC6, pk_body, pk_body_len))
return false;
742 if (!emit(0xCD,
reinterpret_cast<const uint8_t*
>(self.
user_id), uid_len))
return false;
747 uint8_t self_sig[64];
751 std::memcpy(self.
my_signature, self_sig,
sizeof(self_sig));
752 uint8_t sig_body[256];
754 if (sig_body_off > 0) emit(0xC2, sig_body, sig_body_off);
759 const uint8_t n = store.count();
760 for (uint8_t i = 0; i < n; ++i) {
762 if (!store.getCert(i, &cert))
continue;
771 uint8_t dec_body[128];
773 dec_body,
sizeof(dec_body));
774 uint8_t binding_sig[64];
778 signSubkeyBinding(pk_body, pk_body_len, dec_body, dec_len, dec_time, binding_sig)) {
779 bind_len = buildSubkeyBindingSigPacket(pk_body, pk_body_len, dec_body, dec_len,
781 dec_time, binding_sig, bind,
sizeof(bind));
784 emit(0xCE, dec_body, dec_len);
785 emit(0xC2, bind, bind_len);
789 return armorBinaryBlock(binary.get(), binary_off, out, out_size, out_len);
793 uint8_t self_sig[64], uint8_t binding_sig[64])
795 if (!dec_pubkey || !self_sig || !binding_sig)
return false;
798 if (!loadOwnSigKey(self))
return false;
806 std::memcpy(dec_pubkey, dec65 + 1, 64);
808 if (dec_created_at) *dec_created_at = dec_time;
810 uint8_t pk_body[128];
811 const size_t pk_len = buildPubkeyBody(self, pk_body,
sizeof(pk_body));
812 uint8_t dec_body[128];
813 const size_t dec_len =
buildEcdhPubkeyBody(dec_pubkey, dec_time, dec_body,
sizeof(dec_body));
814 if (pk_len == 0 || dec_len == 0)
return false;
816 return signSubkeyBinding(pk_body, pk_len, dec_body, dec_len, dec_time, binding_sig);
uint8_t gpg_storage_sig_slot(void)
Shared RAII wrappers for firmware resources.
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
static GpgSelfCertStore & instance()
#define ED25519_PUBKEY_SIZE
Ed25519 raw public key size in bytes.
#define OPENPGP_ALGO_ECDSA
OpenPGP algorithm ID for ECDSA (RFC 4880 Section 9.1).
#define P256_PUBKEY_BITS
Canonical OpenPGP MPI bit-length of an uncompressed P-256 point.
#define P256_PUBKEY_SIZE
P-256 uncompressed public key size: 0x04 || X(32) || Y(32).
#define MPI_HEADER_SIZE
OpenPGP MPI header size (2-byte length prefix).
#define OPENPGP_ALGO_EDDSA
OpenPGP algorithm ID for EdDSA (Ed25519).
#define MPI_FULL_SIZE_P256
Full P-256 MPI buffer size: 2 byte length prefix + 65 byte uncompressed key.
#define CDC_CURVE_ED25519
uint8_t user_id[FIDO2_USER_ID_MAX_LEN]
bool gpg_get_status(gpg_status_t *status)
Fills status from the OpenPGP card-application state.
bool gpg_get_dec_pubkey(uint8_t *pub65)
Derives the DEC (decryption) public key as an uncompressed P-256 point.
PsramUniquePtr< T > psramAlloc(std::size_t count) noexcept
Allocate count elements of T in PSRAM (8-bit capable region).
ISecureElement * getSecureElementInstance()
Returns singleton secure-element stub instance.
size_t buildCertSigPacket(const gpg_recv_key_t &key, uint8_t *out, size_t out_size)
Build the OpenPGP Signature Packet body certifying key.
bool gpgBuildOwnSubkeyMaterial(uint8_t dec_pubkey[64], uint32_t *dec_created_at, uint8_t self_sig[64], uint8_t binding_sig[64])
Gather the badge's own encryption-subkey material for transfer.
constexpr size_t kGpgSelfCertSigMax
Maximum length of a stored certification signature packet body (Tag 2).
bool gpgBuildOwnSignedKeyArmored(char *out, size_t out_size, size_t *out_len)
Build an ASCII-armored OpenPGP block for the badge's own public key, appending every stored third-par...
bool gpgCrossSign(const gpg_recv_key_t &target, uint32_t sig_creation_time, uint8_t out_sig[64])
Cross-sign a received key with the badge's own SIG ECC slot.
bool gpgBuildPublicKeyArmored(const gpg_recv_key_t &key, char *out, size_t out_size, size_t *out_len)
Build an ASCII-armored OpenPGP public-key block (no signature).
size_t buildEcdhPubkeyBody(const uint8_t *pubkey, uint32_t created_at, uint8_t *out, size_t out_size)
Serialise an RFC 6637 ECDH Public Key Packet body (algorithm 18).
bool gpgBuildReceivedKeyArmored(const gpg_recv_key_t &key, char *out, size_t out_size, size_t *out_len)
Build an ASCII-armored OpenPGP block for a received peer key.
uint32_t openpgp_get_gen_time(uint8_t key_type)
Returns the stored Unix timestamp of key generation, or 0 when unset.
One GPG public key received from another badge.
uint8_t fingerprint_v4[20]
One third-party certification received over the badge's own key.
uint8_t sig_pkt[kGpgSelfCertSigMax]
uint16_t sig_pkt_len
Length of sig_pkt.
Snapshot of the current OpenPGP card-application state for UI display.