DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ssh_chacha20.cpp
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_chacha20.cpp
6 * @brief ChaCha20 (RFC 8439) - implementation. See ssh_chacha20.h.
7 */
8
11
12// ChaCha20 is a hot, pure-integer (add/xor/rotate) keystream generator. The ESP32-S3 has no usable
13// vector path (its PIE unit has only a *saturating* 32-bit add, `ee.vadds.s32`; ChaCha needs modular
14// wrap-around, so it cannot be vectorized). The real lever is optimization level: the library ships at
15// the arduino framework's -Os, and ChaCha runs ~2.36x faster at -O2 (measured on-device, CCOUNT). It is
16// constant-time by structure (no secret-dependent branches), so forcing a higher level is side-channel
17// safe - see the caveats in crypto_opt.h. Byte-exact; the SIMD investigation is in docs/FEATURE_PERFORMANCE.md.
19
20namespace
21{
22uint32_t rd_le32(const uint8_t *p)
23{
24 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
25}
26uint32_t rotl32(uint32_t v, int c)
27{
28 return (v << c) | (v >> (32 - c));
29}
30
31#define QR(a, b, c, d) \
32 a += b; \
33 d ^= a; \
34 d = rotl32(d, 16); \
35 c += d; \
36 b ^= c; \
37 b = rotl32(b, 12); \
38 a += b; \
39 d ^= a; \
40 d = rotl32(d, 8); \
41 c += d; \
42 b ^= c; \
43 b = rotl32(b, 7)
44
45// The ChaCha20 core: 20 rounds over the 16-word state, add the original state, serialize LE.
46void chacha_core(const uint32_t in[16], uint8_t out[64])
47{
48 uint32_t x[16];
49 for (int i = 0; i < 16; i++)
50 x[i] = in[i];
51 for (int i = 0; i < 10; i++) // 10 double-rounds = 20 rounds
52 {
53 QR(x[0], x[4], x[8], x[12]);
54 QR(x[1], x[5], x[9], x[13]);
55 QR(x[2], x[6], x[10], x[14]);
56 QR(x[3], x[7], x[11], x[15]);
57 QR(x[0], x[5], x[10], x[15]);
58 QR(x[1], x[6], x[11], x[12]);
59 QR(x[2], x[7], x[8], x[13]);
60 QR(x[3], x[4], x[9], x[14]);
61 }
62 for (int i = 0; i < 16; i++)
63 {
64 uint32_t v = x[i] + in[i];
65 out[4 * i + 0] = (uint8_t)v;
66 out[4 * i + 1] = (uint8_t)(v >> 8);
67 out[4 * i + 2] = (uint8_t)(v >> 16);
68 out[4 * i + 3] = (uint8_t)(v >> 24);
69 }
70}
71
72// "expand 32-byte k"
73const uint32_t SIGMA0 = 0x61707865;
74const uint32_t SIGMA1 = 0x3320646e;
75const uint32_t SIGMA2 = 0x79622d32;
76const uint32_t SIGMA3 = 0x6b206574;
77} // namespace
78
79void ssh_chacha20_xor(const uint8_t key[SSH_CHACHA20_KEY_LEN], const uint8_t iv[8], uint64_t counter, const uint8_t *in,
80 uint8_t *out, size_t len)
81{
82 uint32_t st[16];
83 st[0] = SIGMA0;
84 st[1] = SIGMA1;
85 st[2] = SIGMA2;
86 st[3] = SIGMA3;
87 for (int i = 0; i < 8; i++)
88 st[4 + i] = rd_le32(key + 4 * i);
89 st[14] = rd_le32(iv + 0); // OpenSSH layout: 64-bit nonce in words 14-15
90 st[15] = rd_le32(iv + 4);
91 uint8_t ks[64];
92 size_t off = 0;
93 while (off < len)
94 {
95 st[12] = (uint32_t)(counter & 0xffffffffu); // 64-bit little-endian counter in words 12-13
96 st[13] = (uint32_t)(counter >> 32);
97 chacha_core(st, ks);
98 size_t n = (len - off < 64) ? (len - off) : 64;
99 for (size_t i = 0; i < n; i++)
100 out[off + i] = (uint8_t)((in ? in[off + i] : 0) ^ ks[i]);
101 off += n;
102 counter++;
103 }
104}
105
106void ssh_chacha20_block_ietf(const uint8_t key[SSH_CHACHA20_KEY_LEN], uint32_t counter, const uint8_t nonce[12],
107 uint8_t out[SSH_CHACHA20_BLOCK_LEN])
108{
109 uint32_t st[16];
110 st[0] = SIGMA0;
111 st[1] = SIGMA1;
112 st[2] = SIGMA2;
113 st[3] = SIGMA3;
114 for (int i = 0; i < 8; i++)
115 st[4 + i] = rd_le32(key + 4 * i);
116 st[12] = counter; // RFC 8439 layout: 32-bit counter, 96-bit nonce
117 st[13] = rd_le32(nonce + 0);
118 st[14] = rd_le32(nonce + 4);
119 st[15] = rd_le32(nonce + 8);
120 chacha_core(st, out);
121}
Per-translation-unit optimization override for hot, pure-integer crypto.
const uint32_t SIGMA1
uint32_t rotl32(uint32_t v, int c)
uint32_t rd_le32(const uint8_t *p)
const uint32_t SIGMA3
void chacha_core(const uint32_t in[16], uint8_t out[64])
const uint32_t SIGMA0
const uint32_t SIGMA2
void ssh_chacha20_block_ietf(const uint8_t key[SSH_CHACHA20_KEY_LEN], uint32_t counter, const uint8_t nonce[12], uint8_t out[SSH_CHACHA20_BLOCK_LEN])
One 64-byte ChaCha20 keystream block in the RFC 8439 layout (for the KAT).
#define QR(a, b, c, d)
void ssh_chacha20_xor(const uint8_t key[SSH_CHACHA20_KEY_LEN], const uint8_t iv[8], uint64_t counter, const uint8_t *in, uint8_t *out, size_t len)
XOR len bytes of in with the ChaCha20 keystream into out (OpenSSH layout).
ChaCha20 stream cipher (D. J. Bernstein; RFC 8439).
#define SSH_CHACHA20_BLOCK_LEN
#define SSH_CHACHA20_KEY_LEN