DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ssh_aesgcm.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_aesgcm.h
6 * @brief AES-256-GCM AEAD for SSH (aes256-gcm@openssh.com, RFC 5647).
7 *
8 * The OpenSSH AES-GCM cipher is an AEAD: the 4-byte SSH packet_length is sent in the clear and
9 * authenticated as additional data (AAD), the rest of the binary packet (padding_length || payload
10 * || padding) is encrypted, and a 16-byte GCM tag follows. No separate MAC is negotiated - the AEAD
11 * tag *is* the integrity check (RFC 5647 sec 7.3).
12 *
13 * NONCE / INVOCATION COUNTER (RFC 5647 sec 7.1)
14 * The 12-byte GCM nonce is `fixed_field(4) || invocation_counter(8)`. The initial nonce is the first
15 * 12 bytes of the IV derived from the key exchange (RFC 4253 sec 7.2, labels 'A'/'B'). After every
16 * packet the 8-byte invocation_counter is incremented as a big-endian integer; the 4-byte fixed field
17 * never changes. The counter therefore lives in the context and advances once per sealed/opened packet
18 * - this is what makes the cipher stateful and requires whole-packet atomicity in the packet layer.
19 *
20 * PLATFORM SELECTION (mirrors ssh_aes256ctr / quic_aead)
21 * On Arduino (ESP32) the AES-256 block is mbedtls, routed to the hardware AES accelerator. On native
22 * host builds a compact software AES-256 is used so the whole AEAD is unit-testable off-target. GHASH
23 * and the counter loop are the same software on both targets.
24 *
25 * Pure, zero heap; host-tested against the NIST/McGrew AES-256-GCM test vectors and round-tripped
26 * through the SSH packet layer.
27 *
28 * @author Douglas Quigg (dstroy0)
29 * @date 2026
30 */
31
32#ifndef DETERMINISTICESPASYNCWEBSERVER_SSH_AESGCM_H
33#define DETERMINISTICESPASYNCWEBSERVER_SSH_AESGCM_H
34
36#include <stddef.h>
37#include <stdint.h>
38
39/** @brief AES-256-GCM key length (bytes). */
40static constexpr size_t SSH_AESGCM_KEY_LEN = 32;
41/** @brief GCM nonce length (bytes) = fixed_field(4) || invocation_counter(8). */
42static constexpr size_t SSH_AESGCM_IV_LEN = 12;
43/** @brief GCM authentication tag length (bytes). */
44static constexpr size_t SSH_AESGCM_TAG_LEN = 16;
45
46#ifdef ARDUINO
47#include <mbedtls/aes.h>
48/** @brief AES-256-GCM context for one SSH direction (HW AES on ESP32). */
50{
51 mbedtls_aes_context mbed; ///< mbedtls context (HW-accelerated on ESP32), encrypt key schedule.
52 uint8_t h[16]; ///< GHASH subkey H = E(K, 0^128).
53 GhashKey ghk; ///< 4-bit GHASH table built from H (once at init).
54 uint8_t iv[SSH_AESGCM_IV_LEN]; ///< current nonce; low 8 bytes (invocation counter) ++ per packet.
55 bool ready; ///< true once a key/IV is installed.
56};
57#else
58/** @brief AES-256-GCM context for one SSH direction (software AES on host). */
59struct SshAesGcmCtx
60{
61 uint32_t rk[60]; ///< AES-256 expanded round-key schedule (60 words, 240 bytes).
62 uint8_t h[16]; ///< GHASH subkey H = E(K, 0^128).
63 GhashKey ghk; ///< 4-bit GHASH table built from H (once at init).
64 uint8_t iv[SSH_AESGCM_IV_LEN]; ///< current nonce; low 8 bytes (invocation counter) ++ per packet.
65 bool ready; ///< true once a key/IV is installed.
66};
67#endif
68
69/**
70 * @brief Initialize an AES-256-GCM context: expand the key, precompute H, latch the initial nonce.
71 * @param ctx Uninitialized context.
72 * @param key 32-byte AES-256 key (KEX label 'C'/'D').
73 * @param iv 12-byte initial nonce (first 12 bytes of the KEX 'A'/'B' IV).
74 */
75void ssh_aesgcm_init(SshAesGcmCtx *ctx, const uint8_t key[SSH_AESGCM_KEY_LEN], const uint8_t iv[SSH_AESGCM_IV_LEN]);
76
77/**
78 * @brief Seal one packet: AES-256-GCM encrypt @p pt (@p pt_len bytes) and authenticate it together
79 * with @p aad. Writes @p pt_len ciphertext bytes then the 16-byte tag into @p out (so @p out
80 * must hold @p pt_len + ::SSH_AESGCM_TAG_LEN bytes). @p out may alias @p pt (in place).
81 * The context's invocation counter is advanced by one afterwards.
82 */
83void ssh_aesgcm_seal(SshAesGcmCtx *ctx, const uint8_t *aad, size_t aad_len, const uint8_t *pt, size_t pt_len,
84 uint8_t *out);
85
86/**
87 * @brief Open one packet: verify the 16-byte @p tag over @p aad || @p ct in constant time, and only
88 * on success decrypt @p ct (@p ct_len bytes) into @p out (@p out may alias @p ct). The
89 * invocation counter is advanced by one on success. @return true iff the tag is valid.
90 */
91bool ssh_aesgcm_open(SshAesGcmCtx *ctx, const uint8_t *aad, size_t aad_len, const uint8_t *ct, size_t ct_len,
92 const uint8_t tag[SSH_AESGCM_TAG_LEN], uint8_t *out);
93
94/** @brief Zero the key schedule, H, and nonce (volatile wipe). Call on disconnect. */
96
97#endif // DETERMINISTICESPASYNCWEBSERVER_SSH_AESGCM_H
GHASH (the GF(2^128) universal hash under AES-GCM, NIST SP 800-38D sec 6.3), 4-bit table.
void ssh_aesgcm_seal(SshAesGcmCtx *ctx, const uint8_t *aad, size_t aad_len, const uint8_t *pt, size_t pt_len, uint8_t *out)
Seal one packet: AES-256-GCM encrypt pt (pt_len bytes) and authenticate it together with aad....
void ssh_aesgcm_init(SshAesGcmCtx *ctx, const uint8_t key[SSH_AESGCM_KEY_LEN], const uint8_t iv[SSH_AESGCM_IV_LEN])
Initialize an AES-256-GCM context: expand the key, precompute H, latch the initial nonce.
void ssh_aesgcm_wipe(SshAesGcmCtx *ctx)
Zero the key schedule, H, and nonce (volatile wipe). Call on disconnect.
bool ssh_aesgcm_open(SshAesGcmCtx *ctx, const uint8_t *aad, size_t aad_len, const uint8_t *ct, size_t ct_len, const uint8_t tag[SSH_AESGCM_TAG_LEN], uint8_t *out)
Open one packet: verify the 16-byte tag over aad || ct in constant time, and only on success decrypt ...
4-bit GHASH table for a fixed subkey H = E(K, 0^128): M[i] = i*H as four big-endian uint32 words (M[i...
Definition ghash.h:33
AES-256-GCM context for one SSH direction (HW AES on ESP32).
Definition ssh_aesgcm.h:50
uint8_t h[16]
GHASH subkey H = E(K, 0^128).
Definition ssh_aesgcm.h:52
GhashKey ghk
4-bit GHASH table built from H (once at init).
Definition ssh_aesgcm.h:53
uint8_t iv[SSH_AESGCM_IV_LEN]
current nonce; low 8 bytes (invocation counter) ++ per packet.
Definition ssh_aesgcm.h:54
bool ready
true once a key/IV is installed.
Definition ssh_aesgcm.h:55
mbedtls_aes_context mbed
mbedtls context (HW-accelerated on ESP32), encrypt key schedule.
Definition ssh_aesgcm.h:51