8#include <mbedtls/rsa.h>
9#include <mbedtls/bignum.h>
10#include <mbedtls/platform_util.h>
11#include <esp_random.h>
15static const char*
TAG =
"GPGRsa";
20static int rsa_rng(
void* ctx,
unsigned char* out,
size_t len) {
22 esp_fill_random(out, len);
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]);
40static bool rsa_load_ctx(
const uint8_t* blob,
size_t blob_len, mbedtls_rsa_context* rsa) {
41 if (!blob)
return false;
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;
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);
61 mbedtls_rsa_free(rsa);
70static bool rsa_serialize(mbedtls_rsa_context* rsa, uint8_t* out,
size_t cap,
size_t* out_len) {
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;
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;
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;
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;
104 mbedtls_mpi_free(&P);
105 mbedtls_mpi_free(&Q);
106 mbedtls_mpi_free(&E);
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;
116 mbedtls_rsa_context rsa;
117 mbedtls_rsa_init(&rsa);
118 mbedtls_rsa_set_padding(&rsa, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE);
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);
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)) {
129 LOG_W(
TAG,
"RSA import rejected: %zu bits (declared %u)", bits, n_bits);
132 LOG_W(
TAG,
"RSA import invalid key material (rc=-0x%04x)", -rc);
134 mbedtls_rsa_free(&rsa);
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;
142 mbedtls_rsa_context rsa;
143 mbedtls_rsa_init(&rsa);
144 mbedtls_rsa_set_padding(&rsa, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE);
146 int rc = mbedtls_rsa_gen_key(&rsa,
rsa_rng,
nullptr, n_bits, 65537);
150 LOG_E(
TAG,
"RSA-%u keygen failed (rc=-0x%04x)", n_bits, -rc);
152 mbedtls_rsa_free(&rsa);
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;
165 mbedtls_mpi_init(&N);
166 mbedtls_mpi_init(&E);
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;
179 mbedtls_mpi_free(&N);
180 mbedtls_mpi_free(&E);
181 mbedtls_rsa_free(&rsa);
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;
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);
201 LOG_W(
TAG,
"RSA sign failed (rc=-0x%04x)", -rc);
204 mbedtls_rsa_free(&rsa);
212 LOG_E(
TAG,
"selftest: keygen failed");
216 mbedtls_rsa_context rsa;
218 mbedtls_platform_zeroize(blob,
sizeof(blob));
219 LOG_E(
TAG,
"selftest: blob reload failed");
224 const size_t klen = mbedtls_rsa_get_len(&rsa);
229 memset(di, 0xAB,
sizeof(di));
231 memset(msg, 0x5A,
sizeof(msg));
234 if (!
gpg_rsa_sign(blob, blob_len, di,
sizeof(di), sig,
sizeof(sig), &sig_len)) {
235 LOG_E(
TAG,
"selftest: sign failed");
238 if (mbedtls_rsa_pkcs1_verify(&rsa, MBEDTLS_MD_NONE,
sizeof(di), di, sig) != 0) {
239 LOG_E(
TAG,
"selftest: verify failed");
242 if (mbedtls_rsa_pkcs1_encrypt(&rsa,
rsa_rng,
nullptr,
sizeof(msg), msg, ct) != 0) {
243 LOG_E(
TAG,
"selftest: encrypt failed");
247 if (!
gpg_rsa_decrypt(blob, blob_len, ct, klen, pt,
sizeof(pt), &pt_len)) {
248 LOG_E(
TAG,
"selftest: decrypt failed");
251 if (pt_len !=
sizeof(msg) || memcmp(pt, msg,
sizeof(msg)) != 0) {
252 LOG_E(
TAG,
"selftest: plaintext mismatch");
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");
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;
275 const size_t klen = mbedtls_rsa_get_len(&rsa);
276 if (ct_len == klen) {
278 int rc = mbedtls_rsa_pkcs1_decrypt(&rsa,
rsa_rng,
nullptr, &olen, ct, pt_out, pt_cap);
283 LOG_W(
TAG,
"RSA decrypt failed (rc=-0x%04x)", -rc);
286 mbedtls_rsa_free(&rsa);
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
#define LOG_I(tag, fmt,...)
#define LOG_E(tag, fmt,...)
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.
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...
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.
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.
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.
static int rsa_rng(void *ctx, unsigned char *out, size_t len)
mbedTLS RNG callback backed by the ESP hardware RNG.
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.
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...
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.
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...
#define GPG_RSA_MAX_MODULUS_BYTES
Software RSA backend for the OpenPGP card (mbedTLS).