21 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
30 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
47#include <Preferences.h>
48#include <esp_random.h>
49#include <freertos/FreeRTOS.h>
50#include <freertos/semphr.h>
51#include <mbedtls/md.h>
52#include <mbedtls/pk.h>
53#include <mbedtls/rsa.h>
58static int ssh_mbedtls_rng(
void *ctx,
unsigned char *buf,
size_t len)
61 esp_fill_random(buf, len);
74 mbedtls_pk_context
pk;
85 s_rsa.
lock = xSemaphoreCreateMutex();
88 if (!prefs.begin(
"ssh_host_key",
true))
92 size_t der_len = prefs.getBytesLength(
"priv_der");
98 prefs.getBytes(
"priv_der", der, der_len);
104 mbedtls_pk_free(&s_rsa.
pk);
107 mbedtls_pk_init(&s_rsa.
pk);
108 int rc = mbedtls_pk_parse_key(&s_rsa.
pk, der, der_len,
nullptr, 0
109#
if MBEDTLS_VERSION_MAJOR >= 3
111 ssh_mbedtls_rng,
nullptr
115 ssh_wipe(der, der_len);
119 mbedtls_pk_free(&s_rsa.
pk);
123 mbedtls_rsa_context *rsa = mbedtls_pk_rsa(s_rsa.
pk);
126 mbedtls_pk_free(&s_rsa.
pk);
133 mbedtls_mpi_init(&n_mpi);
134 mbedtls_mpi_init(&e_mpi);
135 mbedtls_rsa_export(rsa, &n_mpi,
nullptr,
nullptr,
nullptr, &e_mpi);
139 mbedtls_mpi_free(&n_mpi);
140 mbedtls_mpi_free(&e_mpi);
157 const mbedtls_md_type_t md = sha512 ? MBEDTLS_MD_SHA512 : MBEDTLS_MD_SHA256;
167 xSemaphoreTake(s_rsa.
lock, portMAX_DELAY);
169#if MBEDTLS_VERSION_MAJOR >= 3
170 int rc = mbedtls_pk_sign(&s_rsa.
pk, md, digest, dlen, sig,
SSH_RSA_SIG_BYTES, &sig_len, ssh_mbedtls_rng,
nullptr);
172 int rc = mbedtls_pk_sign(&s_rsa.
pk, md, digest, dlen, sig, &sig_len, ssh_mbedtls_rng,
nullptr);
175 xSemaphoreGive(s_rsa.
lock);
176 ssh_wipe(digest,
sizeof(digest));
182 const uint8_t *sig,
size_t sig_len,
SshRsaHash hash)
187 mbedtls_rsa_context rsa;
188#if MBEDTLS_VERSION_MAJOR >= 3
189 mbedtls_rsa_init(&rsa);
191 mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0);
196 mbedtls_mpi_init(&N);
197 mbedtls_mpi_init(&E);
199 mbedtls_mpi_read_binary(&E, e_be4, 4);
201 int rc = mbedtls_rsa_import(&rsa, &N,
nullptr,
nullptr,
nullptr, &E);
203 rc = mbedtls_rsa_complete(&rsa);
206 const mbedtls_md_type_t md = sha512 ? MBEDTLS_MD_SHA512 : MBEDTLS_MD_SHA256;
215#if MBEDTLS_VERSION_MAJOR >= 3
216 rc = mbedtls_rsa_pkcs1_verify(&rsa, md, dlen, digest, sig);
218 rc = mbedtls_rsa_pkcs1_verify(&rsa,
nullptr,
nullptr, MBEDTLS_RSA_PUBLIC, md, dlen, digest, sig);
222 mbedtls_mpi_free(&N);
223 mbedtls_mpi_free(&E);
224 mbedtls_rsa_free(&rsa);
225 return rc == 0 ? 0 : -1;
239uint8_t _test_rsa_e[4];
256 size_t *digest_len,
const uint8_t **di,
size_t *di_len)
263 *di_len = SSH_PKCS1_SHA512_DIGESTINFO_LEN;
270 *di_len = SSH_PKCS1_DIGESTINFO_LEN;
277static void pkcs1v15_encode(
const uint8_t *digest,
size_t digest_len,
const uint8_t *di,
size_t di_len,
280 const size_t total = di_len + digest_len;
284 memset(em + 2, 0xFF, pad_len);
285 em[2 + pad_len] = 0x00;
286 memcpy(em + 3 + pad_len, di, di_len);
287 memcpy(em + 3 + pad_len + di_len, digest, digest_len);
308 uint64_t cur = (uint64_t)p[i + j] + (uint64_t)a[i] * b[j] + carry;
309 p[i + j] = (uint32_t)cur;
316 uint64_t cur = (uint64_t)p[k] + carry;
317 p[k] = (uint32_t)cur;
332 for (
int bit = 2 *
SSH_BN_LIMBS * 32 - 1; bit >= 0; bit--)
338 uint32_t nc = r[k] >> 31;
339 r[k] = (r[k] << 1) | carry;
343 r[0] |= (p[bit >> 5] >> (bit & 31)) & 1u;
364 uint64_t v = (uint64_t)r[k] - m[k] - borrow;
366 borrow = (v >> 32) & 1u;
384 prod[k] = base->
d[k];
387 bn_reduce_full(prod, n->
d, b.
d);
390 memset(r.
d, 0,
sizeof(r.
d));
394 while (top >= 0 && !((e >> top) & 1u))
396 for (
int i = top; i >= 0; i--)
398 bn_mul_full(r.
d, r.
d, prod);
399 bn_reduce_full(prod, n->
d, r.
d);
402 bn_mul_full(r.
d, b.
d, prod);
403 bn_reduce_full(prod, n->
d, r.
d);
421 prod[k] = base->
d[k];
424 bn_reduce_full(prod, n->
d, b.
d);
427 memset(r.
d, 0,
sizeof(r.
d));
432 while (top_limb >= 0 && exp->
d[top_limb] == 0)
440 while (top_bit >= 0 && !((exp->
d[top_limb] >> top_bit) & 1u))
443 for (
int limb = top_limb; limb >= 0; limb--)
445 int start = (limb == top_limb) ? top_bit : 31;
446 for (
int bit = start; bit >= 0; bit--)
448 bn_mul_full(r.
d, r.
d, prod);
449 bn_reduce_full(prod, n->
d, r.
d);
450 if ((exp->
d[limb] >> bit) & 1u)
452 bn_mul_full(r.
d, b.
d, prod);
453 bn_reduce_full(prod, n->
d, r.
d);
466 memcpy(priv.
e_bytes, _test_rsa_e, 4);
470 size_t digest_len = 0;
471 const uint8_t *di =
nullptr;
473 rsa_digest(msg, msg_len, hash, digest, &digest_len, &di, &di_len);
477 pkcs1v15_encode(digest, digest_len, di, di_len, em);
478 ssh_wipe(digest,
sizeof(digest));
489 ssh_wipe(em,
sizeof(em));
491 bn_modexp_full(&m_bn, &d_bn, &n_bn, &s_bn);
497 ssh_wipe(&priv,
sizeof(priv));
498 ssh_wipe(&n_bn,
sizeof(n_bn));
499 ssh_wipe(&d_bn,
sizeof(d_bn));
500 ssh_wipe(&m_bn,
sizeof(m_bn));
501 ssh_wipe(&s_bn,
sizeof(s_bn));
507 const uint8_t *sig,
size_t sig_len,
SshRsaHash hash)
520 uint32_t e = ((uint32_t)e_be4[0] << 24) | ((uint32_t)e_be4[1] << 16) | ((uint32_t)e_be4[2] << 8) | e_be4[3];
521 bn_modexp_pub(&s, e, &n, &m);
528 size_t digest_len = 0;
529 const uint8_t *di =
nullptr;
531 rsa_digest(msg, msg_len, hash, digest, &digest_len, &di, &di_len);
533 pkcs1v15_encode(digest, digest_len, di, di_len, expected);
537 diff |= (uint8_t)(em[k] ^ expected[k]);
538 return diff == 0 ? 0 : -1;
548static uint8_t *put_u32(uint8_t *p, uint32_t v)
550 p[0] = (uint8_t)(v >> 24);
551 p[1] = (uint8_t)(v >> 16);
552 p[2] = (uint8_t)(v >> 8);
559static uint8_t *put_mpint(uint8_t *p,
const uint8_t *data,
size_t data_len)
563 while (off < data_len && data[off] == 0)
565 const uint8_t *src = data + off;
566 size_t src_len = data_len - off;
567 bool need_pad = (src_len > 0) && (src[0] & 0x80u);
568 uint32_t mpint_len = (uint32_t)src_len + (need_pad ? 1u : 0u);
569 p = put_u32(p, mpint_len);
572 memcpy(p, src, src_len);
591 p = put_u32(p, (uint32_t)alg_len);
592 memcpy(p, alg, alg_len);
601 *out_len = (size_t)(p - out);
int bn_cmp(const SshBigNum *a, const SshBigNum *b)
Compare two SshBigNum values.
void bn_to_bytes(uint8_t bytes[256], const SshBigNum *in)
Write a SshBigNum as a 256-byte big-endian array.
void bn_from_bytes(SshBigNum *out, const uint8_t *bytes, size_t len)
Read a big-endian byte array of len bytes into a SshBigNum.
#define SSH_BN_LIMBS
Number of 32-bit limbs in a 2048-bit integer.
SSH session key material - types, pools, and security model.
int ssh_rsa_verify(const uint8_t n_be[SSH_RSA_KEY_BYTES], const uint8_t e_be4[4], const uint8_t *msg, size_t msg_len, const uint8_t *sig, size_t sig_len, SshRsaHash hash)
Verify an RSA PKCS#1 v1.5 signature (rsa-sha2-256/512) with a public key.
int ssh_rsa_sign(const uint8_t *msg, size_t msg_len, SshRsaHash hash, uint8_t sig[SSH_RSA_SIG_BYTES])
Sign msg using the RSA host key (PKCS#1 v1.5, rsa-sha2-256/512).
SshRsaPubKey ssh_host_pubkey
Static host public key (BSS). Set by ssh_rsa_load_pubkey().
const uint8_t ssh_pkcs1_sha512_digestinfo[SSH_PKCS1_SHA512_DIGESTINFO_LEN]
The DER-encoded DigestInfo wrapper for SHA-512.
int ssh_rsa_load_pubkey(void)
Load the public portion of the RSA host key into ssh_host_pubkey.
int ssh_rsa_encode_pubkey(uint8_t *out, size_t *out_len, size_t out_cap)
Serialize the RSA public host key into an SSH "ssh-rsa" key blob.
const uint8_t ssh_pkcs1_sha256_digestinfo[SSH_PKCS1_DIGESTINFO_LEN]
The DER-encoded DigestInfo wrapper for SHA-256.
RSA-SHA2-256/512 host-key signing and public-key serialization.
#define SSH_RSA_PUBKEY_ALG_LEN
Length of SSH_RSA_PUBKEY_ALG ("ssh-rsa" = 7 bytes).
#define SSH_RSA_KEY_DER_MAX
Maximum DER size for a PKCS#1 RSAPrivateKey with 2048-bit fields.
SshRsaHash
Hash algorithm selecting the RSA signature scheme (RFC 8332).
#define SSH_RSA_SIG_BYTES
PKCS#1 v1.5 signature size for RSA-2048 in bytes.
#define SSH_RSA_KEY_BYTES
RSA modulus and private exponent size in bytes (RSA-2048).
#define SSH_RSA_PUBKEY_BLOB_MAX
Maximum byte length of the serialized RSA public key blob.
#define SSH_RSA_PUBKEY_ALG
Key-blob type string for an RSA host key.
void ssh_sha256(const uint8_t *data, size_t len, uint8_t digest[SSH_SHA256_DIGEST_LEN])
One-shot SHA-256: hash len bytes and write the digest.
#define SSH_SHA256_DIGEST_LEN
SHA-256 digest length in bytes.
void ssh_sha512(const uint8_t *data, size_t len, uint8_t digest[SSH_SHA512_DIGEST_LEN])
One-shot SHA-512: hash len bytes of data into digest (64 bytes).
#define SSH_SHA512_DIGEST_LEN
SHA-512 digest length in bytes.
A 2048-bit unsigned integer stored as 64 little-endian 32-bit limbs.
uint32_t d[SSH_BN_LIMBS]
256 bytes of magnitude, little-endian limbs.
SemaphoreHandle_t lock
serialises signs on the shared context
bool ready
pk holds a valid parsed key
mbedtls_pk_context pk
parsed host key + cached blinding state
RSA-2048 private key parameters.
uint8_t e_bytes[4]
Public exponent e (typically 65537).
uint8_t d[SSH_RSA_KEY_BYTES]
Private exponent d (SENSITIVE).
uint8_t n[SSH_RSA_KEY_BYTES]
Modulus n (256 bytes, big-endian).
RSA-2048 public key parameters for the host key blob.
bool loaded
True after ssh_rsa_load_pubkey() succeeds.
uint8_t n[SSH_RSA_KEY_BYTES]
Modulus n (256 bytes, big-endian).
uint8_t e_bytes[4]
Public exponent e (big-endian uint32).