ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
aes256ctr.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 aes256ctr.h
6 * @brief AES-256-CTR stream cipher (aes256-ctr, RFC 4344 §4) - stateless one-shot API.
7 *
8 * AES-256-CTR is the mandatory cipher for this SSH implementation. CTR mode turns AES into a stream
9 * cipher: each 16-byte counter block is AES-ECB encrypted to produce a keystream block, and data is
10 * XOR'd with the keystream. Encrypt and decrypt are the identical operation.
11 *
12 * STATELESS API (mirrors chachapoly.h)
13 * ────────────────────────────────────
14 * There is no context object. The caller keeps the two things that must persist across packets - the
15 * 32-byte key and the 16-byte counter - as plain bytes (SshKeyMat holds them, exactly as it holds the
16 * raw chacha20-poly1305 key), and passes both in on every call. The AES key schedule is rebuilt per call
17 * in the secure pool and wiped when released, so expanded key material never lives in
18 * BSS or on the stack - every crypto secret is funneled through the one hardened, wiped scratch region
19 * (crypto_scratch.h). @p counter is advanced in place by ceil(@p len / 16) blocks so successive calls
20 * continue the stream.
21 *
22 * COUNTER FORMAT (RFC 4344 §4)
23 * The 16-byte counter increments as a big-endian 128-bit integer after each 16-byte keystream block.
24 * The initial counter is the IV from the key exchange (RFC 4253 §7.2, labels 'A'/'B').
25 *
26 * @note The SSH binary packet is always a whole number of cipher blocks, so every call is block-aligned
27 * and the counter alone is sufficient state. A non-block-aligned @p len is permitted only as the
28 * final call of a stream (any leftover keystream in the last block is discarded, not carried).
29 *
30 * On Arduino (ESP32) the AES block is mbedtls, routed to the hardware AES accelerator; on native host
31 * builds a compact software AES-256 is used so the cipher is unit-testable off-target.
32 *
33 * @author Douglas Quigg (dstroy0)
34 * @date 2026
35 */
36
37#ifndef PROTOCORE_AES256CTR_H
38#define PROTOCORE_AES256CTR_H
39
40#include <stddef.h>
41#include <stdint.h>
42
43/** @brief AES-256-CTR key length (bytes). */
44#define PC_AES256CTR_KEY_LEN 32
45/** @brief AES-256-CTR counter/IV block length (bytes). */
46#define PC_AES256CTR_CTR_LEN 16
47
48/**
49 * @brief Encrypt or decrypt @p len bytes with AES-256-CTR (the two are identical).
50 *
51 * Rebuilds the key schedule from @p key on the stack, produces the CTR keystream starting at @p counter,
52 * and XORs it into @p out. @p counter is advanced in place by ceil(@p len / 16) blocks so the next call
53 * continues the same stream. @p in and @p out may alias (in-place is the mode ssh_packet.cpp uses).
54 *
55 * @param key 32-byte AES-256 key.
56 * @param counter 16-byte counter block (big-endian); updated in place to the next unused block.
57 * @param in Input bytes.
58 * @param out Output bytes (may equal @p in).
59 * @param len Number of bytes to process.
60 */
61void pc_aes256ctr_crypt(const uint8_t key[PC_AES256CTR_KEY_LEN], uint8_t counter[PC_AES256CTR_CTR_LEN],
62 const uint8_t *in, uint8_t *out, size_t len);
63
64/**
65 * @brief Decrypt only the 4-byte SSH packet_length prefix WITHOUT advancing @p counter.
66 *
67 * Mirrors pc_chachapoly_get_length: lets the receiver learn a packet's length (and thus how many bytes
68 * to wait for) before the whole packet has arrived, without consuming counter state. The key schedule
69 * and keystream block live in the shared crypto scratch; no cipher state touches the stack.
70 *
71 * @param key 32-byte AES-256 key.
72 * @param counter current 16-byte counter block (read only; not advanced).
73 * @param enc4 the 4 encrypted length bytes (start of the packet).
74 * @return the decrypted big-endian SSH packet_length.
75 */
76uint32_t pc_aes256ctr_get_length(const uint8_t key[PC_AES256CTR_KEY_LEN], const uint8_t counter[PC_AES256CTR_CTR_LEN],
77 const uint8_t enc4[4]);
78
79#endif // PROTOCORE_AES256CTR_H
void pc_aes256ctr_crypt(const uint8_t key[PC_AES256CTR_KEY_LEN], uint8_t counter[PC_AES256CTR_CTR_LEN], const uint8_t *in, uint8_t *out, size_t len)
Encrypt or decrypt len bytes with AES-256-CTR (the two are identical).
Definition aes256ctr.cpp:45
#define PC_AES256CTR_CTR_LEN
AES-256-CTR counter/IV block length (bytes).
Definition aes256ctr.h:46
#define PC_AES256CTR_KEY_LEN
AES-256-CTR key length (bytes).
Definition aes256ctr.h:44
uint32_t pc_aes256ctr_get_length(const uint8_t key[PC_AES256CTR_KEY_LEN], const uint8_t counter[PC_AES256CTR_CTR_LEN], const uint8_t enc4[4])
Decrypt only the 4-byte SSH packet_length prefix WITHOUT advancing counter.