DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ssh_chachapoly.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_chachapoly.cpp
6 * @brief chacha20-poly1305@openssh.com - implementation. See ssh_chachapoly.h.
7 */
8
12#include <string.h>
13
14namespace
15{
16// The 8-byte ChaCha nonce is the sequence number as a big-endian uint64 (POKE_U64 in OpenSSH); a
17// 32-bit SSH seqnr leaves the high 4 bytes zero.
18void seq_nonce(uint32_t seqnr, uint8_t iv[8])
19{
20 iv[0] = 0;
21 iv[1] = 0;
22 iv[2] = 0;
23 iv[3] = 0;
24 iv[4] = (uint8_t)(seqnr >> 24);
25 iv[5] = (uint8_t)(seqnr >> 16);
26 iv[6] = (uint8_t)(seqnr >> 8);
27 iv[7] = (uint8_t)seqnr;
28}
29
30// Constant-time equality of two 16-byte tags.
31bool ct_eq16(const uint8_t *a, const uint8_t *b)
32{
33 uint8_t d = 0;
34 for (int i = 0; i < 16; i++)
35 d |= (uint8_t)(a[i] ^ b[i]);
36 return d == 0;
37}
38} // namespace
39
40uint32_t ssh_chachapoly_get_length(const uint8_t key[SSH_CHACHAPOLY_KEY_LEN], uint32_t seqnr,
41 const uint8_t enc_len[SSH_CHACHAPOLY_AAD_LEN])
42{
43 uint8_t iv[8];
44 seq_nonce(seqnr, iv);
45 uint8_t len[4];
46 ssh_chacha20_xor(key + 32, iv, 0, enc_len, len, 4); // header key, counter 0
47 return ((uint32_t)len[0] << 24) | ((uint32_t)len[1] << 16) | ((uint32_t)len[2] << 8) | len[3];
48}
49
50void ssh_chachapoly_encrypt(const uint8_t key[SSH_CHACHAPOLY_KEY_LEN], uint32_t seqnr, uint8_t *dest,
51 const uint8_t *src, uint32_t payload_len)
52{
53 uint8_t iv[8];
54 seq_nonce(seqnr, iv);
55 uint8_t poly_key[32];
56 ssh_chacha20_xor(key, iv, 0, nullptr, poly_key, 32); // Poly1305 key = K_main block 0
57 ssh_chacha20_xor(key + 32, iv, 0, src, dest, 4); // length field: K_header, counter 0
58 ssh_chacha20_xor(key, iv, 1, src + 4, dest + 4, payload_len); // payload: K_main, counter 1
59 ssh_poly1305(dest + 4 + payload_len, dest, 4 + payload_len, poly_key);
60}
61
62bool ssh_chachapoly_decrypt(const uint8_t key[SSH_CHACHAPOLY_KEY_LEN], uint32_t seqnr, uint8_t *dest,
63 const uint8_t *src, uint32_t payload_len)
64{
65 uint8_t iv[8];
66 seq_nonce(seqnr, iv);
67 uint8_t poly_key[32];
68 ssh_chacha20_xor(key, iv, 0, nullptr, poly_key, 32);
69 uint8_t tag[16];
70 ssh_poly1305(tag, src, 4 + payload_len, poly_key); // MAC over the ciphertext (length || payload)
71 if (!ct_eq16(tag, src + 4 + payload_len))
72 return false; // authentication failed - produce no plaintext
73 ssh_chacha20_xor(key + 32, iv, 0, src, dest, 4);
74 ssh_chacha20_xor(key, iv, 1, src + 4, dest + 4, payload_len);
75 return true;
76}
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).
void ssh_chachapoly_encrypt(const uint8_t key[SSH_CHACHAPOLY_KEY_LEN], uint32_t seqnr, uint8_t *dest, const uint8_t *src, uint32_t payload_len)
Encrypt+authenticate one packet.
uint32_t ssh_chachapoly_get_length(const uint8_t key[SSH_CHACHAPOLY_KEY_LEN], uint32_t seqnr, const uint8_t enc_len[SSH_CHACHAPOLY_AAD_LEN])
Decrypt just the 4-byte length field to learn the packet length before reading the body.
bool ssh_chachapoly_decrypt(const uint8_t key[SSH_CHACHAPOLY_KEY_LEN], uint32_t seqnr, uint8_t *dest, const uint8_t *src, uint32_t payload_len)
Verify+decrypt one packet.
chacha20-poly1305@openssh.com AEAD cipher (OpenSSH PROTOCOL.chacha20poly1305).
#define SSH_CHACHAPOLY_AAD_LEN
the encrypted packet-length field
#define SSH_CHACHAPOLY_KEY_LEN
two 256-bit ChaCha20 keys
void ssh_poly1305(uint8_t tag[SSH_POLY1305_TAG_LEN], const uint8_t *msg, size_t len, const uint8_t key[SSH_POLY1305_KEY_LEN])
Compute the 16-byte Poly1305 tag over msg under the 32-byte one-time key.
Poly1305 one-time authenticator (D. J. Bernstein; RFC 8439 Section 2.5).