CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
GpgKeyPayload.cpp
Go to the documentation of this file.
1
5
8#include "mod_gpg/gpg.h"
10#include "openpgp/xsig.h"
11
13
14#include <cstring>
15#include <ctime>
16
17namespace cdc::mod_gpg {
18
19namespace {
20
21void writeBe32(uint8_t* out, uint32_t v) {
22 out[0] = (v >> 24) & 0xFF;
23 out[1] = (v >> 16) & 0xFF;
24 out[2] = (v >> 8) & 0xFF;
25 out[3] = v & 0xFF;
26}
27
28uint32_t readBe32(const uint8_t* in) {
29 return (static_cast<uint32_t>(in[0]) << 24) |
30 (static_cast<uint32_t>(in[1]) << 16) |
31 (static_cast<uint32_t>(in[2]) << 8) |
32 static_cast<uint32_t>(in[3]);
33}
34
35} // namespace
36
37size_t gpgBuildOwnKeyPayload(uint8_t* out, size_t out_size) {
38 gpg_status_t status = {};
39 if (!gpg_get_status(&status)) return 0;
40
41 const uint8_t curve = status.curve;
42 const uint8_t pubkey_len = (curve == CDC_CURVE_ED25519) ? 32 : 64;
43
45 if (!se) return 0;
46
47 uint8_t pubkey[64] = {0};
49 if (se->eccGetPublicKey(gpg_storage_sig_slot(), pubkey, &hal_curve)
51 return 0;
52 }
53
54 // DEC encryption-subkey material (public point + the two signatures the
55 // peer cannot reproduce locally). Required for the exchanged key to import
56 // as a usable encryption key.
57 uint8_t dec_pubkey[64] = {0};
58 uint32_t dec_created_at = 0;
59 uint8_t self_sig[64] = {0};
60 uint8_t binding_sig[64] = {0};
61 if (!gpgBuildOwnSubkeyMaterial(dec_pubkey, &dec_created_at, self_sig, binding_sig)) {
62 return 0;
63 }
64
65 const size_t uid_len = strnlen(status.user_id, sizeof(status.user_id));
66 const size_t total = 1 + 1 + pubkey_len + 4 + 20 + 1 + uid_len + kGpgKeyDecBlock;
67 if (total > out_size) return 0;
68
69 size_t off = 0;
70 out[off++] = curve;
71 out[off++] = pubkey_len;
72 std::memcpy(out + off, pubkey, pubkey_len);
73 off += pubkey_len;
74 writeBe32(out + off, status.created_at);
75 off += 4;
76 std::memcpy(out + off, status.fingerprint, 20);
77 off += 20;
78 out[off++] = static_cast<uint8_t>(uid_len);
79 std::memcpy(out + off, status.user_id, uid_len);
80 off += uid_len;
81 writeBe32(out + off, dec_created_at);
82 off += 4;
83 std::memcpy(out + off, dec_pubkey, 64);
84 off += 64;
85 std::memcpy(out + off, self_sig, 64);
86 off += 64;
87 std::memcpy(out + off, binding_sig, 64);
88 off += 64;
89 return off;
90}
91
92size_t gpgBuildRecvKeyPayload(const gpg_recv_key_t& key, uint8_t* out, size_t out_size) {
93 const uint8_t pubkey_len = key.pubkey_len;
94 if (pubkey_len != 32 && pubkey_len != 64) return 0;
95
96 const size_t uid_len = strnlen(key.user_id, sizeof(key.user_id));
97 const size_t total = 1 + 1 + pubkey_len + 4 + 20 + 1 + uid_len + kGpgKeyDecBlock;
98 if (total > out_size) return 0;
99
100 size_t off = 0;
101 out[off++] = key.curve;
102 out[off++] = pubkey_len;
103 std::memcpy(out + off, key.pubkey, pubkey_len);
104 off += pubkey_len;
105 writeBe32(out + off, key.created_at);
106 off += 4;
107 std::memcpy(out + off, key.fingerprint_v4, 20);
108 off += 20;
109 out[off++] = static_cast<uint8_t>(uid_len);
110 std::memcpy(out + off, key.user_id, uid_len);
111 off += uid_len;
112 writeBe32(out + off, key.created_at_dec);
113 off += 4;
114 std::memcpy(out + off, key.pubkey_dec, 64);
115 off += 64;
116 std::memcpy(out + off, key.owner_self_sig, 64);
117 off += 64;
118 std::memcpy(out + off, key.dec_binding_sig, 64);
119 off += 64;
120 return off;
121}
122
123bool gpgParseKeyPayload(const uint8_t* data, size_t len, gpg_recv_key_t* out) {
124 if (!data || !out) return false;
125 if (len < kGpgKeyPayloadMin) return false;
126
127 size_t off = 0;
128 const uint8_t curve = data[off++];
129 if (curve != CDC_CURVE_ED25519 && curve != CDC_CURVE_P256) return false;
130
131 const uint8_t pubkey_len = data[off++];
132 if (curve == CDC_CURVE_ED25519 && pubkey_len != 32) return false;
133 if (curve == CDC_CURVE_P256 && pubkey_len != 64) return false;
134 if (off + pubkey_len + 4 + 20 + 1 > len) return false;
135
136 std::memset(out, 0, sizeof(*out));
137 out->curve = curve;
138 out->pubkey_len = pubkey_len;
139 std::memcpy(out->pubkey, data + off, pubkey_len);
140 off += pubkey_len;
141 out->created_at = readBe32(data + off);
142 off += 4;
143 std::memcpy(out->fingerprint_v4, data + off, 20);
144 off += 20;
145
146 const uint8_t uid_len = data[off++];
147 if (uid_len > 63 || off + uid_len > len) return false;
148 std::memcpy(out->user_id, data + off, uid_len);
149 out->user_id[uid_len] = '\0';
150 off += uid_len;
151
152 // DEC encryption-subkey block: created_at_dec, pubkey_dec, owner_self_sig,
153 // dec_binding_sig. The binding signature is verified by GnuPG on import.
154 if (off + kGpgKeyDecBlock > len) return false;
155 out->created_at_dec = readBe32(data + off);
156 off += 4;
157 std::memcpy(out->pubkey_dec, data + off, 64);
158 off += 64;
159 std::memcpy(out->owner_self_sig, data + off, 64);
160 off += 64;
161 std::memcpy(out->dec_binding_sig, data + off, 64);
162 off += 64;
163
164 // Reject the payload unless the transmitted fingerprint reproduces from the
165 // transmitted creation time, so the stored key (and any certification over
166 // it) binds to the peer's real OpenPGP key.
167 uint8_t recomputed_fp[20] = {0};
169 out->created_at, recomputed_fp)) {
170 return false;
171 }
172 if (std::memcmp(recomputed_fp, out->fingerprint_v4, 20) != 0) return false;
173
174 out->received_at = static_cast<uint32_t>(std::time(nullptr));
176 out->created_at, out->fingerprint_v5);
177 return true;
178}
179
180} // namespace cdc::mod_gpg
Transport-agnostic (de)serialisation of a GPG public key.
uint8_t gpg_storage_sig_slot(void)
#define CDC_CURVE_ED25519
Definition fido2.h:23
#define CDC_CURVE_P256
Definition fido2.h:24
uint8_t curve
bool gpg_get_status(gpg_status_t *status)
Fills status from the OpenPGP card-application state.
Definition gpg.cpp:95
uint32_t readBe32(const uint8_t *in)
Reads a 32-bit value from a buffer in big-endian order.
Definition Bytes.h:29
void writeBe32(uint8_t *out, uint32_t v)
Writes a 32-bit value to a buffer in big-endian order.
Definition Bytes.h:17
ISecureElement * getSecureElementInstance()
Returns singleton secure-element stub instance.
constexpr size_t kGpgKeyDecBlock
Fixed-size trailing DEC encryption-subkey block.
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.
Definition xsig.cpp:792
bool calculateFingerprintV4(uint8_t curve, const uint8_t *pubkey, size_t pubkey_len, uint32_t created_at, uint8_t out_fp[20])
Compute the RFC 4880 V4 OpenPGP fingerprint (SHA-1, 20 bytes).
size_t gpgBuildRecvKeyPayload(const gpg_recv_key_t &key, uint8_t *out, size_t out_size)
Serialise a stored received key into the wire payload (for forwarding).
size_t gpgBuildOwnKeyPayload(uint8_t *out, size_t out_size)
Serialise the badge's own public key into the wire payload.
constexpr size_t kGpgKeyPayloadMin
Minimum payload size (Ed25519 + empty user id + DEC block).
bool gpgParseKeyPayload(const uint8_t *data, size_t len, gpg_recv_key_t *out)
Parse a wire payload into a key record, computing the V5 fingerprint.
bool calculateFingerprintV5(uint8_t curve, const uint8_t *pubkey, size_t pubkey_len, uint32_t created_at, uint8_t out_fp[32])
Compute the V5 / RFC 9580 OpenPGP fingerprint (SHA-256, 32 bytes).
One GPG public key received from another badge.
Snapshot of the current OpenPGP card-application state for UI display.
Definition gpg.h:25
uint8_t fingerprint[20]
Definition gpg.h:29