ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
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 chachapoly.cpp
6 * @brief chacha20-poly1305@openssh.com - implementation. See pc_chachapoly.h.
7 */
8
11#include "crypto/crypto_opt.h"
12#include "crypto/crypto_scratch.h" // pc_ct_eq
13#include "crypto/mac/poly1305.h"
14#include "server/mmgr/secure.h" // the secure pool: AEAD working state, wiped on release
15#include <string.h>
17
18namespace
19{
20// The 8-byte ChaCha nonce is the sequence number as a big-endian uint64 (POKE_U64 in OpenSSH); a
21// 32-bit SSH seqnr leaves the high 4 bytes zero.
22void seq_nonce(uint32_t seqnr, uint8_t iv[8])
23{
24 iv[0] = 0;
25 iv[1] = 0;
26 iv[2] = 0;
27 iv[3] = 0;
28 iv[4] = (uint8_t)(seqnr >> 24);
29 iv[5] = (uint8_t)(seqnr >> 16);
30 iv[6] = (uint8_t)(seqnr >> 8);
31 iv[7] = (uint8_t)seqnr;
32}
33
34// chacha20-poly1305 per-op working set (nonce, the derived one-time Poly1305 key, the computed tag, and the
35// decrypted length word) in the shared crypto scratch at the base span - this is a top-level op, so it shares
36// the base with the other one-at-a-time top-level ops; its nested chacha20/poly1305 use their own regions
37// above the base. Wiped per op so the Poly1305 key never lingers.
39{
40 uint8_t iv[8];
41 uint8_t poly_key[32];
42 uint8_t tag[16];
43 uint8_t len[4];
44};
45static_assert(sizeof(ChachapolyWork) <= PC_WORK_CHACHAPOLY,
46 "ChachapolyWork outgrew PC_WORK_CHACHAPOLY - raise it in protocore_config.h, which derives "
47 "PC_SECURE_ARENA_SIZE from it");
48
49} // namespace
50
51uint32_t pc_chachapoly_get_length(const uint8_t key[PC_CHACHAPOLY_KEY_LEN], uint32_t seqnr,
52 const uint8_t enc_len[PC_CHACHAPOLY_AAD_LEN])
53{
54 SecureBorrow ws_b(sizeof(ChachapolyWork), alignof(ChachapolyWork));
55 const pc_span &ws = ws_b.span();
56 if (!pc_span_ok(ws))
57 {
58 return 0; // pool exhausted: a zero length is rejected by every caller
59 }
60 ChachapolyWork *w = reinterpret_cast<ChachapolyWork *>(ws.buf);
61 seq_nonce(seqnr, w->iv);
62 pc_chacha20_xor(key + 32, w->iv, 0, enc_len, w->len, 4); // header key, counter 0
63 uint32_t n = ((uint32_t)w->len[0] << 24) | ((uint32_t)w->len[1] << 16) | ((uint32_t)w->len[2] << 8) | w->len[3];
64 return n;
65}
66
67void pc_chachapoly_encrypt(const uint8_t key[PC_CHACHAPOLY_KEY_LEN], uint32_t seqnr, uint8_t *dest, const uint8_t *src,
68 uint32_t payload_len)
69{
70 SecureBorrow ws_b(sizeof(ChachapolyWork), alignof(ChachapolyWork));
71 const pc_span &ws = ws_b.span();
72 if (!pc_span_ok(ws))
73 {
74 return; // pool exhausted: emit nothing rather than an unauthenticated frame
75 }
76 ChachapolyWork *w = reinterpret_cast<ChachapolyWork *>(ws.buf);
77 seq_nonce(seqnr, w->iv);
78 pc_chacha20_xor(key, w->iv, 0, nullptr, w->poly_key, 32); // Poly1305 key = K_main block 0
79 pc_chacha20_xor(key + 32, w->iv, 0, src, dest, 4); // length field: K_header, counter 0
80 pc_chacha20_xor(key, w->iv, 1, src + 4, dest + 4, payload_len); // payload: K_main, counter 1
81 pc_poly1305(dest + 4 + payload_len, dest, 4 + payload_len, w->poly_key);
82}
83
84bool pc_chachapoly_decrypt(const uint8_t key[PC_CHACHAPOLY_KEY_LEN], uint32_t seqnr, uint8_t *dest, const uint8_t *src,
85 uint32_t payload_len)
86{
87 SecureBorrow ws_b(sizeof(ChachapolyWork), alignof(ChachapolyWork));
88 const pc_span &ws = ws_b.span();
89 if (!pc_span_ok(ws))
90 {
91 return false; // pool exhausted: fail closed
92 }
93 ChachapolyWork *w = reinterpret_cast<ChachapolyWork *>(ws.buf);
94 seq_nonce(seqnr, w->iv);
95 pc_chacha20_xor(key, w->iv, 0, nullptr, w->poly_key, 32);
96 pc_poly1305(w->tag, src, 4 + payload_len, w->poly_key); // MAC over the ciphertext (length || payload)
97 if (!pc_ct_eq(w->tag, src + 4 + payload_len, 16))
98 {
99 return false; // authentication failed - produce no plaintext
100 }
101 pc_chacha20_xor(key + 32, w->iv, 0, src, dest, 4);
102 pc_chacha20_xor(key, w->iv, 1, src + 4, dest + 4, payload_len);
103 return true;
104}
void pc_chacha20_xor(const uint8_t key[PC_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).
Definition chacha20.cpp:102
ChaCha20 stream cipher (D. J. Bernstein; RFC 8439).
bool pc_chachapoly_decrypt(const uint8_t key[PC_CHACHAPOLY_KEY_LEN], uint32_t seqnr, uint8_t *dest, const uint8_t *src, uint32_t payload_len)
Verify+decrypt one packet.
uint32_t pc_chachapoly_get_length(const uint8_t key[PC_CHACHAPOLY_KEY_LEN], uint32_t seqnr, const uint8_t enc_len[PC_CHACHAPOLY_AAD_LEN])
Decrypt just the 4-byte length field to learn the packet length before reading the body.
void pc_chachapoly_encrypt(const uint8_t key[PC_CHACHAPOLY_KEY_LEN], uint32_t seqnr, uint8_t *dest, const uint8_t *src, uint32_t payload_len)
Encrypt+authenticate one packet.
chacha20-poly1305@openssh.com AEAD cipher (OpenSSH PROTOCOL.chacha20poly1305).
#define PC_CHACHAPOLY_AAD_LEN
the encrypted packet-length field
Definition chachapoly.h:32
#define PC_CHACHAPOLY_KEY_LEN
two 256-bit ChaCha20 keys
Definition chachapoly.h:30
A secure borrow whose acquire and release are one call each.
Definition secure.h:153
const pc_span & span() const
The borrowed region; empty if the pool could not satisfy it.
Definition secure.h:161
Per-translation-unit optimization override for hot, pure-integer crypto.
Constant-time comparison for secret-dependent checks.
void seq_nonce(uint32_t seqnr, uint8_t iv[8])
void pc_poly1305(uint8_t tag[PC_POLY1305_TAG_LEN], const uint8_t *msg, size_t len, const uint8_t key[PC_POLY1305_KEY_LEN])
Compute the 16-byte Poly1305 tag over msg under the 32-byte one-time key.
Definition poly1305.cpp:97
Poly1305 one-time authenticator (D. J. Bernstein; RFC 8439 Section 2.5).
#define PC_WORK_CHACHAPOLY
Secure pool accessor - borrows that hold key material.
bool pc_span_ok(const pc_span &s)
True when the span refers to real storage and every write so far has fit.
Definition span.h:114
A writable byte region: the storage, the capacity that belongs to it, and what has been produced into...
Definition span.h:65
uint8_t * buf
first byte, or nullptr when the region could not be obtained
Definition span.h:66