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

2048-bit big-integer arithmetic for DH-group14 and RSA-2048. More...

#include "ServerConfig.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>

Go to the source code of this file.

Classes

struct  SshBigNum
 A 2048-bit unsigned integer stored as 64 little-endian 32-bit limbs. More...
 

Macros

#define SSH_BN_LIMBS   64
 Number of 32-bit limbs in a 2048-bit integer.
 

Functions

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.
 
void bn_to_bytes (uint8_t bytes[256], const SshBigNum *in)
 Write a SshBigNum as a 256-byte big-endian array.
 
int bn_cmp (const SshBigNum *a, const SshBigNum *b)
 Compare two SshBigNum values.
 
int bn_is_zero (const SshBigNum *a)
 Return non-zero if a is zero (all limbs zero).
 
void bn_expmod_group14 (SshBigNum *out, const SshBigNum *base, const SshBigNum *exp)
 Compute out = base^exp mod group14_p.
 
int bn_dh_validate (const SshBigNum *v)
 Validate a received DH public value.
 

Variables

uint8_t crypto_work [SSH_CRYPTO_WORK_SIZE]
 Global scratch buffer for big-number temporaries.
 
const SshBigNum group14_p
 The RFC 3526 MODP group-14 prime (2048-bit).
 
const SshBigNum group14_g
 Generator for group-14: g = 2.
 

Detailed Description

2048-bit big-integer arithmetic for DH-group14 and RSA-2048.

═══════════════════════════════════════════════════════════════════════════ DESIGN RATIONALE ═══════════════════════════════════════════════════════════════════════════

SshBigNum is a fixed-width 2048-bit integer stored as 64 little-endian 32-bit limbs (d[0] = least significant). Fixed width means:

  • Struct size is a compile-time constant: 256 bytes.
  • No dynamic allocation - both DH scalars and RSA key fragments fit in the same type and can live in BSS or on the stack.
  • Array indexing is bounds-safe; no VLA or pointer arithmetic hazards.

On Arduino (ESP32), DH uses mbedtls_mpi from ESP-IDF (heap-allocated, variable-length bignum), which has hardware-accelerated multiplication. On native builds, the software Montgomery path is used - correct but slower (~200 ms for a 2048-bit exponentiation on x86 at test time). Since DH happens once per connection and ESP32 uses the HW path in production, the native speed is acceptable for testing.

═══════════════════════════════════════════════════════════════════════════ MONTGOMERY MULTIPLICATION ═══════════════════════════════════════════════════════════════════════════

For DH-group14 the modulus p ends in ...FFFFFFFF FFFFFFFF (little-endian d[0]=0xFFFFFFFF). This gives Montgomery parameter:

p_inv = (-(p mod 2^32))^(-1) mod 2^32 = (-(0xFFFFFFFF))^(-1) mod 2^32 = (0x00000001)^(-1) mod 2^32 = 1

p_inv = 1 simplifies the inner reduction loop: m_i = t[i] * 1 = t[i].

Montgomery product: MonPro(a,b) = a·b·R^-1 mod p where R = 2^2048.

To compute a·b mod p normally:

  1. Convert a, b to Montgomery form: aR = a·R mod p (= MonPro(a, R²mod p))
  2. Compute MonPro(aR, bR) = a·b·R mod p
  3. Convert back: MonPro(a·b·R, 1) = a·b mod p

R² mod p is a precomputed 2048-bit constant (see ssh_bignum.cpp).

═══════════════════════════════════════════════════════════════════════════ SCRATCH BUFFER ═══════════════════════════════════════════════════════════════════════════

The Montgomery SOS multiplication uses a 129-word (516-byte) temporary array. The expmod function needs three SshBigNum temporaries (768 bytes). All of these live in crypto_work[] (SSH_CRYPTO_WORK_SIZE = 1536 bytes) defined in ServerConfig.h and allocated in ssh_bignum.cpp.

Layout during bn_expmod_group14(): [0..255] base_mont (SshBigNum) [256..511] result (SshBigNum) [512..767] tmp (SshBigNum) [768..1283] mont_t (uint32_t[129])

crypto_work[] is zeroed via ssh_wipe() immediately after bn_expmod_group14() returns so intermediate DH/RSA products do not persist in memory.

═══════════════════════════════════════════════════════════════════════════

Author
Douglas Quigg (dstroy0)
Date
2026

Definition in file ssh_bignum.h.

Macro Definition Documentation

◆ SSH_BN_LIMBS

#define SSH_BN_LIMBS   64

Number of 32-bit limbs in a 2048-bit integer.

Definition at line 87 of file ssh_bignum.h.

Function Documentation

◆ bn_from_bytes()

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.

If len < 256 the most-significant limbs are zeroed. If len > 256 only the least-significant 256 bytes are read.

Parameters
outDestination bignum.
bytesBig-endian source bytes.
lenNumber of source bytes (typically 256 for 2048-bit).

Definition at line 210 of file ssh_bignum.cpp.

References SshBigNum::d.

Referenced by bn_expmod_group14(), and ssh_kexdh_handle().

◆ bn_to_bytes()

void bn_to_bytes ( uint8_t  bytes[256],
const SshBigNum in 
)

Write a SshBigNum as a 256-byte big-endian array.

Parameters
bytesDestination buffer (exactly 256 bytes).
inSource bignum.

Definition at line 222 of file ssh_bignum.cpp.

References SshBigNum::d, and SSH_BN_LIMBS.

Referenced by bn_expmod_group14(), and ssh_kexdh_handle().

◆ bn_cmp()

int bn_cmp ( const SshBigNum a,
const SshBigNum b 
)

Compare two SshBigNum values.

Returns
-1 if a < b, 0 if a == b, 1 if a > b.

Definition at line 234 of file ssh_bignum.cpp.

References SshBigNum::d, and SSH_BN_LIMBS.

Referenced by bn_dh_validate().

◆ bn_is_zero()

int bn_is_zero ( const SshBigNum a)

Return non-zero if a is zero (all limbs zero).

Definition at line 239 of file ssh_bignum.cpp.

References SshBigNum::d, and SSH_BN_LIMBS.

◆ bn_expmod_group14()

void bn_expmod_group14 ( SshBigNum out,
const SshBigNum base,
const SshBigNum exp 
)

Compute out = base^exp mod group14_p.

Uses Montgomery modular exponentiation with left-to-right binary scan. Uses crypto_work[] for all temporaries; zeros crypto_work[] on exit.

On Arduino the computation is delegated to mbedtls_mpi_exp_mod() which uses hardware multiplication and blinding.

Parameters
outResult (base^exp mod p, 2048-bit).
baseBase value; must satisfy 1 < base < p-1.
expExponent (e.g. the 2048-bit private DH scalar y).

Definition at line 276 of file ssh_bignum.cpp.

References bn_from_bytes(), bn_to_bytes(), and group14_p.

Referenced by ssh_dh_generate(), and ssh_kexdh_handle().

◆ bn_dh_validate()

int bn_dh_validate ( const SshBigNum v)

Validate a received DH public value.

RFC 4253 §8: the received value e (or f) must satisfy 1 < e < p-1. Returns 0 if the value is valid, -1 otherwise.

Parameters
vReceived public DH value.

Definition at line 247 of file ssh_bignum.cpp.

References bn_cmp(), SshBigNum::d, group14_p, and SSH_BN_LIMBS.

Referenced by ssh_kexdh_handle().

Variable Documentation

◆ crypto_work

uint8_t crypto_work[SSH_CRYPTO_WORK_SIZE]
extern

Global scratch buffer for big-number temporaries.

Only one SSH KEX or RSA-sign operation runs at a time (single Arduino loop task, synchronous handshake). Zeroed after every use via ssh_wipe().

See the layout comment in the file header for the exact field map.

Definition at line 36 of file ssh_bignum.cpp.

◆ group14_p

const SshBigNum group14_p
extern

The RFC 3526 MODP group-14 prime (2048-bit).

Definition at line 43 of file ssh_bignum.cpp.

Referenced by bn_dh_validate(), and bn_expmod_group14().

◆ group14_g

const SshBigNum group14_g
extern

Generator for group-14: g = 2.

Definition at line 55 of file ssh_bignum.cpp.

Referenced by ssh_dh_generate().