DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ssh_rsa.h File Reference

RSA-SHA2-256/512 host-key signing and public-key serialization. More...

Go to the source code of this file.

Classes

struct  SshRsaPrivKey
 RSA-2048 private key parameters. More...
 
struct  SshRsaPubKey
 RSA-2048 public key parameters for the host key blob. More...
 

Macros

#define SSH_RSA_KEY_DER_MAX   1700
 Maximum DER size for a PKCS#1 RSAPrivateKey with 2048-bit fields.
 
#define SSH_RSA_KEY_BYTES   256
 RSA modulus and private exponent size in bytes (RSA-2048).
 
#define SSH_RSA_SIG_BYTES   256
 PKCS#1 v1.5 signature size for RSA-2048 in bytes.
 
#define SSH_RSA_PUBKEY_ALG   "ssh-rsa"
 Key-blob type string for an RSA host key.
 
#define SSH_RSA_PUBKEY_ALG_LEN   7
 Length of SSH_RSA_PUBKEY_ALG ("ssh-rsa" = 7 bytes).
 
#define SSH_RSA_PUBKEY_BLOB_MAX   (4 + 7 + 4 + 1 + 4 + 4 + 1 + 256)
 Maximum byte length of the serialized RSA public key blob.
 

Enumerations

enum class  SshRsaHash : uint8_t { SHA256 = 0 , SHA512 = 1 }
 Hash algorithm selecting the RSA signature scheme (RFC 8332). More...
 

Functions

int ssh_rsa_load_pubkey (void)
 Load the public portion of the RSA host key into ssh_host_pubkey.
 
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).
 
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.
 
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.
 

Variables

const uint8_t ssh_pkcs1_sha256_digestinfo [SSH_PKCS1_DIGESTINFO_LEN]
 The DER-encoded DigestInfo wrapper for SHA-256.
 
const uint8_t ssh_pkcs1_sha512_digestinfo [SSH_PKCS1_SHA512_DIGESTINFO_LEN]
 The DER-encoded DigestInfo wrapper for SHA-512.
 
SshRsaPubKey ssh_host_pubkey
 Static host public key (BSS). Set by ssh_rsa_load_pubkey().
 

Detailed Description

RSA-SHA2-256/512 host-key signing and public-key serialization.

═══════════════════════════════════════════════════════════════════════════ SECURITY MODEL - PRIVATE KEY LIFETIME ═══════════════════════════════════════════════════════════════════════════

The RSA-2048 private key (d, p, q, dp, dq, qinv) MUST NEVER live in static or global memory. Reasons:

  1. A linear heap/BSS overflow from any direction can reach a static variable; the stack is a separate region with its own growth direction and is far harder to reach from a single-direction overflow.
  2. A use-after-free or dangling-pointer bug that reads static memory can silently expose a key that "should have been zeroed" but was not.
  3. The private key is only needed for the KEX (once per connection, during the handshake). There is no reason to keep it resident between uses.

MANDATED LIFETIME: load (from NVS) → stack → sign → volatile-wipe → return

The struct SshRsaPrivKey is declared here for documentation purposes; it must only ever be declared as a local variable inside ssh_rsa_sign().

═══════════════════════════════════════════════════════════════════════════ PKCS#1 v1.5 SIGNATURE SCHEME ═══════════════════════════════════════════════════════════════════════════

The signature produced by ssh_rsa_sign() follows PKCS#1 v1.5 (RFC 8017 §8.2), which is what SSH uses for "rsa-sha2-256" and "rsa-sha2-512" (RFC 8332) - the only difference is the hash and its DigestInfo OID:

  1. Compute digest = SHA256(msg) or SHA512(msg), per hash.
  2. Encode digest in a DER DigestInfo wrapper: SHA-256: 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 <32 bytes> SHA-512: 30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40 <64 bytes> The OID 2.16.840.1.101.3.4.2.1 identifies SHA-256, .2.3 SHA-512 (RFC 5754 §3.2).
  3. Pad to the RSA modulus length (256 bytes for RSA-2048): 0x00 0x01 <0xFF padding> 0x00 <DigestInfo> The 0xFF padding fills bytes [2 .. 256-1-len(DigestInfo)-1]. SHA-256 DigestInfo = 51 bytes (padding 202); SHA-512 DigestInfo = 83 bytes (padding 170).
  4. Interpret the 256-byte padded message M as a bignum m.
  5. Compute s = m^d mod n (RSA private-key operation).
  6. Output s as a 256-byte big-endian integer.

═══════════════════════════════════════════════════════════════════════════ ARDUINO VS NATIVE ═══════════════════════════════════════════════════════════════════════════

Arduino: uses mbedtls_pk_sign() with MBEDTLS_MD_SHA256 or MBEDTLS_MD_SHA512, which performs the full PKCS#1 v1.5 pad-and-sign in hardware-accelerated multiprecision arithmetic via ESP-IDF.

Native: software path - SHA-256/512 via ssh_sha256()/ssh_sha512(), PKCS#1 v1.5 padding built by hand, RSA exponentiation via bn_expmod_group14() (same Montgomery path used for DH). Both paths use the same key layout.

═══════════════════════════════════════════════════════════════════════════ NVS KEY FORMAT ═══════════════════════════════════════════════════════════════════════════

On Arduino the private key is stored in NVS namespace "ssh_host_key" under key "priv_der", as a DER-encoded PKCS#1 RSAPrivateKey (RFC 8017 App. C):

RSAPrivateKey ::= SEQUENCE { version Version (INTEGER: 0), modulus INTEGER, – n publicExponent INTEGER, – e privateExponent INTEGER, – d prime1 INTEGER, – p prime2 INTEGER, – q exponent1 INTEGER, – dp = d mod (p-1) exponent2 INTEGER, – dq = d mod (q-1) coefficient INTEGER, – qinv = q^(-1) mod p }

The DER blob is parsed by mbedtls_rsa_parse_key() on Arduino. On native builds, the test fixture injects n, e, d directly as raw 256-byte arrays.

Author
Douglas Quigg (dstroy0)
Date
2026

Definition in file ssh_rsa.h.

Macro Definition Documentation

◆ SSH_RSA_KEY_DER_MAX

#define SSH_RSA_KEY_DER_MAX   1700

Maximum DER size for a PKCS#1 RSAPrivateKey with 2048-bit fields.

Definition at line 116 of file ssh_rsa.h.

◆ SSH_RSA_KEY_BYTES

#define SSH_RSA_KEY_BYTES   256

RSA modulus and private exponent size in bytes (RSA-2048).

Definition at line 119 of file ssh_rsa.h.

◆ SSH_RSA_SIG_BYTES

#define SSH_RSA_SIG_BYTES   256

PKCS#1 v1.5 signature size for RSA-2048 in bytes.

Definition at line 122 of file ssh_rsa.h.

◆ SSH_RSA_PUBKEY_ALG

#define SSH_RSA_PUBKEY_ALG   "ssh-rsa"

Key-blob type string for an RSA host key.

Per RFC 8332 §3, the RSA public-key blob always carries the type string "ssh-rsa" - even when the negotiated signature algorithm is "rsa-sha2-256" or "rsa-sha2-512". Only the signature and authentication algorithm-name fields use "rsa-sha2-256"; the key blob format is unchanged from RFC 4253 §6.6. Emitting "rsa-sha2-256" here would make compliant clients (e.g. OpenSSH) fail to parse the host key.

Definition at line 134 of file ssh_rsa.h.

◆ SSH_RSA_PUBKEY_ALG_LEN

#define SSH_RSA_PUBKEY_ALG_LEN   7

Length of SSH_RSA_PUBKEY_ALG ("ssh-rsa" = 7 bytes).

Definition at line 137 of file ssh_rsa.h.

◆ SSH_RSA_PUBKEY_BLOB_MAX

#define SSH_RSA_PUBKEY_BLOB_MAX   (4 + 7 + 4 + 1 + 4 + 4 + 1 + 256)

Maximum byte length of the serialized RSA public key blob.

uint32 len + "ssh-rsa"(7) + mpint e (4 len + 1 pad + up to 4 bytes)

  • mpint n (4 len + 1 pad + 256 bytes)

Definition at line 301 of file ssh_rsa.h.

Enumeration Type Documentation

◆ SshRsaHash

enum class SshRsaHash : uint8_t
strong

Hash algorithm selecting the RSA signature scheme (RFC 8332).

The RSA public-key blob is "ssh-rsa" for both; only the message hash and its DigestInfo OID differ. SHA256 = "rsa-sha2-256", SHA512 = "rsa-sha2-512".

Enumerator
SHA256 

rsa-sha2-256

SHA512 

rsa-sha2-512

Definition at line 105 of file ssh_rsa.h.

Function Documentation

◆ ssh_rsa_load_pubkey()

int ssh_rsa_load_pubkey ( void  )

Load the public portion of the RSA host key into ssh_host_pubkey.

Arduino: reads the DER blob from NVS ("ssh_host_key"/"priv_der"), parses n and e via mbedtls, stores them in ssh_host_pubkey.

Native: reads n and e from the test fixture arrays (see ssh_rsa.cpp).

Call once at startup (or after begin()). Must succeed before any SSH connections are accepted.

Returns
0 on success, -1 if the key is absent or malformed.

Definition at line 82 of file ssh_rsa.cpp.

References SshRsaPubKey::e_bytes, SshRsaPubKey::loaded, SshRsaCtx::lock, SshRsaPubKey::n, SshRsaCtx::pk, SshRsaCtx::ready, ssh_host_pubkey, SSH_RSA_KEY_BYTES, and SSH_RSA_KEY_DER_MAX.

Referenced by ssh_rsa_sign().

◆ ssh_rsa_sign()

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).

The private key is loaded from NVS directly into a stack-local SshRsaPrivKey struct, used, then zeroed before this function returns. The key NEVER touches static or global memory.

On Arduino: delegates to mbedtls_pk_sign() (hardware-accelerated). On native: software SHA-256/512 + hand-built PKCS#1 pad + bn_expmod_group14().

Parameters
[in]msgMessage to sign (typically the exchange hash H, 32 bytes).
[in]msg_lenLength of msg.
[in]hashSignature hash: SHA256 (rsa-sha2-256) or SHA512 (rsa-sha2-512).
[out]sigOutput buffer, must be SSH_RSA_SIG_BYTES (256) bytes.
Returns
0 on success, -1 on failure (NVS read error, RSA error).

Definition at line 147 of file ssh_rsa.cpp.

References SshRsaCtx::lock, SshRsaCtx::pk, SshRsaCtx::ready, SHA512, ssh_rsa_load_pubkey(), SSH_RSA_SIG_BYTES, ssh_sha256(), SSH_SHA256_DIGEST_LEN, ssh_sha512(), and SSH_SHA512_DIGEST_LEN.

◆ ssh_rsa_encode_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.

Format (RFC 4253 §6.6, RFC 8332 §3): uint32 len("ssh-rsa") = 7 byte[7] "ssh-rsa" mpint e mpint n

Writes into out; sets *out_len to the number of bytes written. out must be at least SSH_RSA_PUBKEY_BLOB_MAX bytes.

Parameters
[out]outDestination buffer.
[out]out_lenNumber of bytes written.
[in]out_capCapacity of out.
Returns
0 on success, -1 if pubkey not loaded or buffer too small.

Definition at line 576 of file ssh_rsa.cpp.

References SshRsaPubKey::e_bytes, SshRsaPubKey::loaded, SshRsaPubKey::n, ssh_host_pubkey, SSH_RSA_KEY_BYTES, SSH_RSA_PUBKEY_ALG, SSH_RSA_PUBKEY_ALG_LEN, and SSH_RSA_PUBKEY_BLOB_MAX.

◆ ssh_rsa_verify()

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.

Used for client publickey authentication (RFC 4252 §7): n_be / e_be4 come from the client-supplied key blob, not the host key. The public exponent is small (typically 65537), so the modular exponentiation s^e mod n is cheap and is performed for real on both platforms (native: schoolbook bignum; Arduino: mbedTLS). The hash is selected by the client's signature algorithm name (rsa-sha2-256 -> SHA256, rsa-sha2-512 -> SHA512), not by the key blob.

Parameters
[in]n_beRSA modulus n, big-endian, 256 bytes.
[in]e_be4RSA public exponent e, big-endian, 4 bytes.
[in]msgSigned message.
[in]msg_lenLength of msg.
[in]sigSignature bytes (256 for RSA-2048).
[in]sig_lenLength of sig.
[in]hashSignature hash: SHA256 (rsa-sha2-256) or SHA512 (rsa-sha2-512).
Returns
0 if the signature is valid, -1 otherwise.

Definition at line 181 of file ssh_rsa.cpp.

References SHA512, SSH_RSA_KEY_BYTES, ssh_sha256(), SSH_SHA256_DIGEST_LEN, ssh_sha512(), and SSH_SHA512_DIGEST_LEN.

Variable Documentation

◆ ssh_pkcs1_sha256_digestinfo

const uint8_t ssh_pkcs1_sha256_digestinfo[SSH_PKCS1_DIGESTINFO_LEN]
extern

The DER-encoded DigestInfo wrapper for SHA-256.

Prepend this to the 32-byte SHA-256 digest to form the 51-byte DigestInfo structure for PKCS#1 v1.5.

Definition at line 17 of file ssh_rsa.cpp.

◆ ssh_pkcs1_sha512_digestinfo

const uint8_t ssh_pkcs1_sha512_digestinfo[SSH_PKCS1_SHA512_DIGESTINFO_LEN]
extern

The DER-encoded DigestInfo wrapper for SHA-512.

Prepend this to the 64-byte SHA-512 digest to form the 83-byte DigestInfo structure for PKCS#1 v1.5.

Definition at line 26 of file ssh_rsa.cpp.

◆ ssh_host_pubkey

SshRsaPubKey ssh_host_pubkey
extern

Static host public key (BSS). Set by ssh_rsa_load_pubkey().

Definition at line 39 of file ssh_rsa.cpp.

Referenced by ssh_rsa_encode_pubkey(), and ssh_rsa_load_pubkey().