CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
rsa.cpp
Go to the documentation of this file.
1
4
5#include "rsa.h"
6#include "cdc_log.h"
7
8#include <mbedtls/rsa.h>
9#include <mbedtls/bignum.h>
10#include <mbedtls/platform_util.h>
11#include <esp_random.h>
12#include <esp_attr.h>
13#include <string.h>
14
15static const char* TAG = "GPGRsa";
16
20static int rsa_rng(void* ctx, unsigned char* out, size_t len) {
21 (void)ctx;
22 esp_fill_random(out, len);
23 return 0;
24}
25
29static bool rd_u16(const uint8_t* b, size_t len, size_t* pos, uint16_t* out) {
30 if (*pos + 2 > len) return false;
31 *out = static_cast<uint16_t>((b[*pos] << 8) | b[*pos + 1]);
32 *pos += 2;
33 return true;
34}
35
40static bool rsa_load_ctx(const uint8_t* blob, size_t blob_len, mbedtls_rsa_context* rsa) {
41 if (!blob) return false;
42 size_t pos = 0;
43 uint16_t n_bits = 0, e_len = 0, p_len = 0, q_len = 0;
44 if (!rd_u16(blob, blob_len, &pos, &n_bits)) return false;
45 if (!rd_u16(blob, blob_len, &pos, &e_len)) return false;
46 if (e_len == 0 || pos + e_len > blob_len) return false;
47 const uint8_t* e = blob + pos; pos += e_len;
48 if (!rd_u16(blob, blob_len, &pos, &p_len)) return false;
49 if (p_len == 0 || pos + p_len > blob_len) return false;
50 const uint8_t* p = blob + pos; pos += p_len;
51 if (!rd_u16(blob, blob_len, &pos, &q_len)) return false;
52 if (q_len == 0 || pos + q_len > blob_len) return false;
53 const uint8_t* q = blob + pos;
54
55 mbedtls_rsa_init(rsa);
56 mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE);
57 int rc = mbedtls_rsa_import_raw(rsa, nullptr, 0, p, p_len, q, q_len, nullptr, 0, e, e_len);
58 if (rc == 0) rc = mbedtls_rsa_complete(rsa);
59 if (rc == 0) rc = mbedtls_rsa_check_privkey(rsa);
60 if (rc != 0) {
61 mbedtls_rsa_free(rsa);
62 return false;
63 }
64 return true;
65}
66
70static bool rsa_serialize(mbedtls_rsa_context* rsa, uint8_t* out, size_t cap, size_t* out_len) {
71 mbedtls_mpi P, Q, E;
72 mbedtls_mpi_init(&P);
73 mbedtls_mpi_init(&Q);
74 mbedtls_mpi_init(&E);
75 bool ok = false;
76 do {
77 if (mbedtls_rsa_export(rsa, nullptr, &P, &Q, nullptr, &E) != 0) break;
78 const size_t n_len = mbedtls_rsa_get_len(rsa);
79 const uint16_t n_bits = static_cast<uint16_t>(n_len * 8);
80 const size_t half = n_len / 2;
81 size_t e_len = mbedtls_mpi_size(&E);
82 if (e_len == 0) e_len = 1;
83 const size_t total = 2 + 2 + e_len + 2 + half + 2 + half;
84 if (total > cap) break;
85
86 size_t pos = 0;
87 out[pos++] = static_cast<uint8_t>((n_bits >> 8) & 0xFF);
88 out[pos++] = static_cast<uint8_t>(n_bits & 0xFF);
89 out[pos++] = static_cast<uint8_t>((e_len >> 8) & 0xFF);
90 out[pos++] = static_cast<uint8_t>(e_len & 0xFF);
91 if (mbedtls_mpi_write_binary(&E, out + pos, e_len) != 0) break;
92 pos += e_len;
93 out[pos++] = static_cast<uint8_t>((half >> 8) & 0xFF);
94 out[pos++] = static_cast<uint8_t>(half & 0xFF);
95 if (mbedtls_mpi_write_binary(&P, out + pos, half) != 0) break;
96 pos += half;
97 out[pos++] = static_cast<uint8_t>((half >> 8) & 0xFF);
98 out[pos++] = static_cast<uint8_t>(half & 0xFF);
99 if (mbedtls_mpi_write_binary(&Q, out + pos, half) != 0) break;
100 pos += half;
101 *out_len = pos;
102 ok = true;
103 } while (0);
104 mbedtls_mpi_free(&P);
105 mbedtls_mpi_free(&Q);
106 mbedtls_mpi_free(&E);
107 return ok;
108}
109
110bool gpg_rsa_blob_build(uint16_t n_bits, const uint8_t* e, size_t e_len,
111 const uint8_t* p, size_t p_len, const uint8_t* q, size_t q_len,
112 uint8_t* blob_out, size_t blob_cap, size_t* blob_len_out) {
113 if (!e || !p || !q || !blob_out || !blob_len_out) return false;
114 if (e_len == 0 || p_len == 0 || q_len == 0) return false;
115
116 mbedtls_rsa_context rsa;
117 mbedtls_rsa_init(&rsa);
118 mbedtls_rsa_set_padding(&rsa, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE);
119 bool ok = false;
120 int rc = mbedtls_rsa_import_raw(&rsa, nullptr, 0, p, p_len, q, q_len, nullptr, 0, e, e_len);
121 if (rc == 0) rc = mbedtls_rsa_complete(&rsa);
122 if (rc == 0) rc = mbedtls_rsa_check_privkey(&rsa);
123 if (rc == 0) {
124 const size_t bits = mbedtls_rsa_get_len(&rsa) * 8;
125 if ((bits == 2048 || bits == 3072 || bits == 4096) &&
126 (n_bits == 0 || n_bits == bits)) {
127 ok = rsa_serialize(&rsa, blob_out, blob_cap, blob_len_out);
128 } else {
129 LOG_W(TAG, "RSA import rejected: %zu bits (declared %u)", bits, n_bits);
130 }
131 } else {
132 LOG_W(TAG, "RSA import invalid key material (rc=-0x%04x)", -rc);
133 }
134 mbedtls_rsa_free(&rsa);
135 return ok;
136}
137
138bool gpg_rsa_generate(uint16_t n_bits, uint8_t* blob_out, size_t blob_cap, size_t* blob_len_out) {
139 if (n_bits != 2048 && n_bits != 3072 && n_bits != 4096) return false;
140 if (!blob_out || !blob_len_out) return false;
141
142 mbedtls_rsa_context rsa;
143 mbedtls_rsa_init(&rsa);
144 mbedtls_rsa_set_padding(&rsa, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE);
145 bool ok = false;
146 int rc = mbedtls_rsa_gen_key(&rsa, rsa_rng, nullptr, n_bits, 65537);
147 if (rc == 0) {
148 ok = rsa_serialize(&rsa, blob_out, blob_cap, blob_len_out);
149 } else {
150 LOG_E(TAG, "RSA-%u keygen failed (rc=-0x%04x)", n_bits, -rc);
151 }
152 mbedtls_rsa_free(&rsa);
153 return ok;
154}
155
156bool gpg_rsa_blob_public(const uint8_t* blob, size_t blob_len,
157 uint8_t* n_out, size_t n_cap, size_t* n_len_out,
158 uint8_t* e_out, size_t e_cap, size_t* e_len_out) {
159 if (!n_out || !e_out || !n_len_out || !e_len_out) return false;
160 mbedtls_rsa_context rsa;
161 if (!rsa_load_ctx(blob, blob_len, &rsa)) return false;
162
163 bool ok = false;
164 mbedtls_mpi N, E;
165 mbedtls_mpi_init(&N);
166 mbedtls_mpi_init(&E);
167 do {
168 if (mbedtls_rsa_export(&rsa, &N, nullptr, nullptr, nullptr, &E) != 0) break;
169 const size_t n_len = mbedtls_rsa_get_len(&rsa);
170 size_t e_len = mbedtls_mpi_size(&E);
171 if (e_len == 0) e_len = 1;
172 if (n_len > n_cap || e_len > e_cap) break;
173 if (mbedtls_mpi_write_binary(&N, n_out, n_len) != 0) break;
174 if (mbedtls_mpi_write_binary(&E, e_out, e_len) != 0) break;
175 *n_len_out = n_len;
176 *e_len_out = e_len;
177 ok = true;
178 } while (0);
179 mbedtls_mpi_free(&N);
180 mbedtls_mpi_free(&E);
181 mbedtls_rsa_free(&rsa);
182 return ok;
183}
184
185bool gpg_rsa_sign(const uint8_t* blob, size_t blob_len,
186 const uint8_t* digestinfo, size_t di_len,
187 uint8_t* sig_out, size_t sig_cap, size_t* sig_len_out) {
188 if (!digestinfo || di_len == 0 || !sig_out || !sig_len_out) return false;
189 mbedtls_rsa_context rsa;
190 if (!rsa_load_ctx(blob, blob_len, &rsa)) return false;
191
192 bool ok = false;
193 const size_t klen = mbedtls_rsa_get_len(&rsa);
194 if (klen <= sig_cap) {
195 int rc = mbedtls_rsa_pkcs1_sign(&rsa, rsa_rng, nullptr, MBEDTLS_MD_NONE,
196 static_cast<unsigned int>(di_len), digestinfo, sig_out);
197 if (rc == 0) {
198 *sig_len_out = klen;
199 ok = true;
200 } else {
201 LOG_W(TAG, "RSA sign failed (rc=-0x%04x)", -rc);
202 }
203 }
204 mbedtls_rsa_free(&rsa);
205 return ok;
206}
207
208bool gpg_rsa_selftest(uint16_t n_bits) {
209 static EXT_RAM_BSS_ATTR uint8_t blob[GPG_RSA_MAX_MODULUS_BYTES + 64];
210 size_t blob_len = 0;
211 if (!gpg_rsa_generate(n_bits, blob, sizeof(blob), &blob_len)) {
212 LOG_E(TAG, "selftest: keygen failed");
213 return false;
214 }
215
216 mbedtls_rsa_context rsa;
217 if (!rsa_load_ctx(blob, blob_len, &rsa)) {
218 mbedtls_platform_zeroize(blob, sizeof(blob));
219 LOG_E(TAG, "selftest: blob reload failed");
220 return false;
221 }
222
223 bool ok = false;
224 const size_t klen = mbedtls_rsa_get_len(&rsa);
225 static EXT_RAM_BSS_ATTR uint8_t sig[GPG_RSA_MAX_MODULUS_BYTES];
226 static EXT_RAM_BSS_ATTR uint8_t ct[GPG_RSA_MAX_MODULUS_BYTES];
227 static EXT_RAM_BSS_ATTR uint8_t pt[GPG_RSA_MAX_MODULUS_BYTES];
228 uint8_t di[32];
229 memset(di, 0xAB, sizeof(di));
230 uint8_t msg[16];
231 memset(msg, 0x5A, sizeof(msg));
232 do {
233 size_t sig_len = 0;
234 if (!gpg_rsa_sign(blob, blob_len, di, sizeof(di), sig, sizeof(sig), &sig_len)) {
235 LOG_E(TAG, "selftest: sign failed");
236 break;
237 }
238 if (mbedtls_rsa_pkcs1_verify(&rsa, MBEDTLS_MD_NONE, sizeof(di), di, sig) != 0) {
239 LOG_E(TAG, "selftest: verify failed");
240 break;
241 }
242 if (mbedtls_rsa_pkcs1_encrypt(&rsa, rsa_rng, nullptr, sizeof(msg), msg, ct) != 0) {
243 LOG_E(TAG, "selftest: encrypt failed");
244 break;
245 }
246 size_t pt_len = 0;
247 if (!gpg_rsa_decrypt(blob, blob_len, ct, klen, pt, sizeof(pt), &pt_len)) {
248 LOG_E(TAG, "selftest: decrypt failed");
249 break;
250 }
251 if (pt_len != sizeof(msg) || memcmp(pt, msg, sizeof(msg)) != 0) {
252 LOG_E(TAG, "selftest: plaintext mismatch");
253 break;
254 }
255 ok = true;
256 } while (0);
257
258 mbedtls_rsa_free(&rsa);
259 mbedtls_platform_zeroize(blob, sizeof(blob));
260 mbedtls_platform_zeroize(sig, sizeof(sig));
261 mbedtls_platform_zeroize(ct, sizeof(ct));
262 mbedtls_platform_zeroize(pt, sizeof(pt));
263 LOG_I(TAG, "RSA-%u selftest %s", n_bits, ok ? "PASS" : "FAIL");
264 return ok;
265}
266
267bool gpg_rsa_decrypt(const uint8_t* blob, size_t blob_len,
268 const uint8_t* ct, size_t ct_len,
269 uint8_t* pt_out, size_t pt_cap, size_t* pt_len_out) {
270 if (!ct || !pt_out || !pt_len_out) return false;
271 mbedtls_rsa_context rsa;
272 if (!rsa_load_ctx(blob, blob_len, &rsa)) return false;
273
274 bool ok = false;
275 const size_t klen = mbedtls_rsa_get_len(&rsa);
276 if (ct_len == klen) {
277 size_t olen = 0;
278 int rc = mbedtls_rsa_pkcs1_decrypt(&rsa, rsa_rng, nullptr, &olen, ct, pt_out, pt_cap);
279 if (rc == 0) {
280 *pt_len_out = olen;
281 ok = true;
282 } else {
283 LOG_W(TAG, "RSA decrypt failed (rc=-0x%04x)", -rc);
284 }
285 }
286 mbedtls_rsa_free(&rsa);
287 return ok;
288}
static const char * TAG
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145
static bool rsa_serialize(mbedtls_rsa_context *rsa, uint8_t *out, size_t cap, size_t *out_len)
Canonically serialises an RSA context (primes + exponent) into a blob.
Definition rsa.cpp:70
bool gpg_rsa_selftest(uint16_t n_bits)
End-to-end self-test: generate a key, serialise/reload its blob, then sign+verify and encrypt+decrypt...
Definition rsa.cpp:208
bool gpg_rsa_decrypt(const uint8_t *blob, size_t blob_len, const uint8_t *ct, size_t ct_len, uint8_t *pt_out, size_t pt_cap, size_t *pt_len_out)
RSAES-PKCS1-v1.5 decryption of a cryptogram.
Definition rsa.cpp:267
bool gpg_rsa_sign(const uint8_t *blob, size_t blob_len, const uint8_t *digestinfo, size_t di_len, uint8_t *sig_out, size_t sig_cap, size_t *sig_len_out)
RSASSA-PKCS1-v1.5 signature over a host-supplied DigestInfo.
Definition rsa.cpp:185
static bool rd_u16(const uint8_t *b, size_t len, size_t *pos, uint16_t *out)
Reads a big-endian u16 from a buffer and advances the cursor.
Definition rsa.cpp:29
static int rsa_rng(void *ctx, unsigned char *out, size_t len)
mbedTLS RNG callback backed by the ESP hardware RNG.
Definition rsa.cpp:20
bool gpg_rsa_blob_public(const uint8_t *blob, size_t blob_len, uint8_t *n_out, size_t n_cap, size_t *n_len_out, uint8_t *e_out, size_t e_cap, size_t *e_len_out)
Extracts the public modulus and exponent from a private-key blob.
Definition rsa.cpp:156
bool gpg_rsa_generate(uint16_t n_bits, uint8_t *blob_out, size_t blob_cap, size_t *blob_len_out)
Generates a fresh RSA key pair and serialises its private blob. The public exponent is fixed to 65537...
Definition rsa.cpp:138
bool gpg_rsa_blob_build(uint16_t n_bits, const uint8_t *e, size_t e_len, const uint8_t *p, size_t p_len, const uint8_t *q, size_t q_len, uint8_t *blob_out, size_t blob_cap, size_t *blob_len_out)
Serialises raw RSA components into a private-key blob.
Definition rsa.cpp:110
static bool rsa_load_ctx(const uint8_t *blob, size_t blob_len, mbedtls_rsa_context *rsa)
Parses a private-key blob and imports it into an RSA context. On success the caller owns rsa and must...
Definition rsa.cpp:40
#define GPG_RSA_MAX_MODULUS_BYTES
Software RSA backend for the OpenPGP card (mbedTLS).
Definition rsa.h:24