ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ssh_rsa.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ssh_rsa.h
6 * @brief SSH RSA host-key layer: NVS-backed host key, host-key signing, and "ssh-rsa" blob encoding.
7 *
8 * This is the SSH-specific wrapper around the shared RSA primitive (crypto/rsa): it owns the device's
9 * RSA-2048 host key (loaded from NVS on Arduino, a test fixture on native), signs handshake data with
10 * it, and serializes the public key into the RFC 4253 / RFC 8332 "ssh-rsa" blob. The
11 * protocol-agnostic RSASSA-PKCS1-v1.5 math (verify, software sign, PKCS#1 encoding, modexp) lives in
12 * crypto/rsa (pc_rsa_verify / pc_rsa_sign_sw); peers' signatures are verified via that primitive.
13 *
14 * ═══════════════════════════════════════════════════════════════════════════
15 * SECURITY MODEL - PRIVATE KEY LIFETIME
16 * ═══════════════════════════════════════════════════════════════════════════
17 * The RSA-2048 private key MUST NEVER live in static or global memory. On Arduino the parsed key is
18 * held in a private mbedtls context for the server lifetime (as an SSH host key normally is) and every
19 * sign runs under a mutex; on native the test fixture injects n/d/e and the software sign
20 * (pc_rsa_sign_sw) wipes its own temporaries. The signature scheme is PKCS#1 v1.5 (RFC 8017 §8.2),
21 * "rsa-sha2-256" / "rsa-sha2-512" (RFC 8332) - only the hash and its DigestInfo OID differ.
22 *
23 * NVS: on Arduino the private key is a DER PKCS#1 RSAPrivateKey in namespace "ssh_host_key" / key
24 * "priv_der", parsed by mbedtls. On native, the test fixture injects n, e, d as raw 256-byte arrays.
25 *
26 * @author Douglas Quigg (dstroy0)
27 * @date 2026
28 */
29
30#ifndef PROTOCORE_SSH_RSA_H
31#define PROTOCORE_SSH_RSA_H
32
33#include "crypto/asymmetric/rsa.h" // pc_rsa_hash, PC_RSA_KEY_BYTES/SIG_BYTES, pc_rsa_verify / pc_rsa_sign_sw
34#include <stddef.h>
35#include <stdint.h>
36
37/** @brief Maximum DER size for a PKCS#1 RSAPrivateKey with 2048-bit fields. */
38#define SSH_RSA_KEY_DER_MAX 1700
39
40/**
41 * @brief Key-blob type string for an RSA host key.
42 *
43 * Per RFC 8332 §3, the RSA *public-key blob* always carries the type string "ssh-rsa" - even when the
44 * negotiated *signature* algorithm is "rsa-sha2-256" / "rsa-sha2-512". Only the signature and
45 * authentication algorithm-name fields use "rsa-sha2-256"; the key blob format is unchanged from
46 * RFC 4253 §6.6.
47 */
48#define SSH_RSA_PUBKEY_ALG "ssh-rsa"
49
50/** @brief Length of SSH_RSA_PUBKEY_ALG ("ssh-rsa" = 7 bytes). */
51#define SSH_RSA_PUBKEY_ALG_LEN 7
52
53/** @brief Signature algorithm name for SHA-256 (RFC 8332). Used in the signature blob. */
54static constexpr char SSH_RSA_SIG_ALG_SHA256[] = "rsa-sha2-256";
55
56/** @brief Signature algorithm name for SHA-512 (RFC 8332). Used in the signature blob. */
57static constexpr char SSH_RSA_SIG_ALG_SHA512[] = "rsa-sha2-512";
58
59// ---------------------------------------------------------------------------
60// RSA public key (safe to keep in static/flash - no secret material)
61// ---------------------------------------------------------------------------
62
63/**
64 * @brief RSA-2048 public host key parameters. Allocated in BSS; contains only n and e.
65 */
67{
68 uint8_t n[PC_RSA_KEY_BYTES]; ///< Modulus n (256 bytes, big-endian).
69 uint8_t e_bytes[4]; ///< Public exponent e (big-endian uint32).
70 bool loaded; ///< True after pc_ssh_rsa_load_pubkey() succeeds.
71};
72
73/** @brief Static host public key (BSS). Set by pc_ssh_rsa_load_pubkey(). */
75
76/** @brief Upper bound on the encoded "ssh-rsa" public-key blob (len+alg + mpint e + mpint n). */
77#define SSH_RSA_PUBKEY_BLOB_MAX (4 + 7 + 4 + 1 + 4 + 4 + 1 + 256)
78
79// ---------------------------------------------------------------------------
80// Public API
81// ---------------------------------------------------------------------------
82
83/**
84 * @brief Load the public portion of the RSA host key into ssh_host_pubkey.
85 *
86 * Arduino: reads + parses the DER blob from NVS ("ssh_host_key"/"priv_der") and caches the signer
87 * context. Native: reads n/e from the test fixture. Call once at startup (single-threaded).
88 * @return 0 on success, -1 if the key is absent or malformed.
89 */
91
92/**
93 * @brief Sign @p msg with the RSA host key (PKCS#1 v1.5, rsa-sha2-256/512).
94 * @return 0 on success, -1 on failure. @p sig receives PC_RSA_SIG_BYTES big-endian.
95 */
96int ssh_rsa_sign(const uint8_t *msg, size_t msg_len, pc_rsa_hash hash, uint8_t sig[PC_RSA_SIG_BYTES]);
97
98/**
99 * @brief Encode ssh_host_pubkey as the RFC 4253 §6.6 "ssh-rsa" public-key blob.
100 * @return 0 on success (writing @p out_len), -1 if the key is not loaded or @p out_cap is too small.
101 */
102int ssh_rsa_encode_pubkey(uint8_t *out, size_t *out_len, size_t out_cap);
103
104#endif // PROTOCORE_SSH_RSA_H
RSA-2048 PKCS#1 v1.5 signature primitive (RFC 8017) - verify + software sign.
pc_rsa_hash
Hash algorithm selecting the RSA signature scheme (RFC 8017 §9.2).
Definition rsa.h:39
#define PC_RSA_SIG_BYTES
PKCS#1 v1.5 signature size for RSA-2048 in bytes.
Definition rsa.h:31
#define PC_RSA_KEY_BYTES
RSA modulus / signature size in bytes (RSA-2048).
Definition rsa.h:28
int pc_ssh_rsa_load_pubkey(void)
Load the public portion of the RSA host key into ssh_host_pubkey.
Definition ssh_rsa.cpp:58
SshRsaPubKey ssh_host_pubkey
Static host public key (BSS). Set by pc_ssh_rsa_load_pubkey().
Definition ssh_rsa.cpp:29
int ssh_rsa_sign(const uint8_t *msg, size_t msg_len, pc_rsa_hash hash, uint8_t sig[PC_RSA_SIG_BYTES])
Sign msg with the RSA host key (PKCS#1 v1.5, rsa-sha2-256/512).
Definition ssh_rsa.cpp:126
int ssh_rsa_encode_pubkey(uint8_t *out, size_t *out_len, size_t out_cap)
Encode ssh_host_pubkey as the RFC 4253 §6.6 "ssh-rsa" public-key blob.
Definition ssh_rsa.cpp:230
RSA-2048 public host key parameters. Allocated in BSS; contains only n and e.
Definition ssh_rsa.h:67
uint8_t n[PC_RSA_KEY_BYTES]
Modulus n (256 bytes, big-endian).
Definition ssh_rsa.h:68
bool loaded
True after pc_ssh_rsa_load_pubkey() succeeds.
Definition ssh_rsa.h:70
uint8_t e_bytes[4]
Public exponent e (big-endian uint32).
Definition ssh_rsa.h:69