ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
common.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file common.h
6 * @brief Root infrastructure: fixed widths, serializers, opcodes and sizes, for every layer above.
7 */
8
9#ifndef PROTOCORE_SSH_COMMON_H
10#define PROTOCORE_SSH_COMMON_H
11
12#include "cellularum_laboro/cellularum_laboro.h" // cellul.len: the length prefix on a written string
13#include "crypto/aead/chachapoly/chachapoly.h" // PROTOCORE_CHACHAPOLY_KEY_LEN - the chacha keys in the memory map
14#include "crypto/asymmetric/bignum/bignum.h" // protocore_bignum - the DH ephemeral in the memory map
15#include "crypto/cipher/aes256ctr/aes256ctr.h" // PROTOCORE_AES256CTR_KEY_LEN / _CTR_LEN - the aes keys and IVs
16#include "crypto/mac/hmac_sha256/hmac_sha256.h" // PROTOCORE_HMAC_SHA256_BORROW - the packet MAC scratch
17#include "crypto/pqc/sntrup761/sntrup761.h" // PROTOCORE_SNTRUP761_PK_BYTES - the PQC public key in the memory map
18#include "memoria_operor/memoria_operor.h" // memor.cpy: the copy out of a read string
19#include "network_drivers/presentation/ssh/transport/ssh_kexhash/ssh_kexhash.h" // SSH_KEXHASH_MAX_LEN - the session id span
20#include "octetus_introitus_exitus/octetus_introitus_exitus.h" // mmgr_span, byteio.* writers, byteio.rd_str / byteio.take_be readers
21
22#include "protocore_config.h" // protocore_types.h for the fixed widths, PROTOCORE_INLINE, the SSH sizing constants
23
24// ---------------------------------------------------------------------------
25// Sizing
26// ---------------------------------------------------------------------------
27
28/** @brief Max stored length of an SSH identification string (RFC 4253 sec 4.2: 255). */
29#define SSH_VERSION_MAX 256
30
31/** @brief Longest identification string RFC 4253 sec 4.2 admits: 255 on the wire, CR and LF counted. */
32#define SSH_VERSION_CONTENT_MAX 253
33
34/**
35 * @brief Max stored size of our own KEXINIT (I_S). Sized for the full advertised suite: the
36 * kex list (mlkem + dh + ecdsa-nistp256 + curve25519 x2 + ext-info-s), all three host-key types,
37 * the cipher (chacha + 2x aes) and MAC (2x aes + 2x plain) lists, and zlib s2c compression
38 * (worst case ~580 bytes; 704 leaves headroom for future algorithm additions).
39 */
40#define PROTOCORE_SSH_KEXINIT_S_MAX 704
41
42/**
43 * @brief Capacity of I_C and I_S. A buffer takes the peer bound in the role that receives into it,
44 * our own bound in the role that writes it.
45 */
46#if PROTOCORE_ENABLE_SSH_CLIENT && PROTOCORE_ENABLE_SSH_SERVER
47#define PROTOCORE_SSH_I_C_MAX SSH_KEXINIT_MAX
48#define PROTOCORE_SSH_I_S_MAX SSH_KEXINIT_MAX
49#elif PROTOCORE_ENABLE_SSH_CLIENT
50#define PROTOCORE_SSH_I_C_MAX PROTOCORE_SSH_KEXINIT_S_MAX
51#define PROTOCORE_SSH_I_S_MAX SSH_KEXINIT_MAX
52#else
53#define PROTOCORE_SSH_I_C_MAX SSH_KEXINIT_MAX
54#define PROTOCORE_SSH_I_S_MAX PROTOCORE_SSH_KEXINIT_S_MAX
55#endif
56
57/** @brief Server identification string (no CR LF; appended on the wire). */
58#define SSH_SERVER_VERSION "SSH-2.0-1.0"
59
60/** @brief Client identification string (no CR LF; appended on the wire). */
61#define SSH_CLIENT_VERSION "SSH-2.0-PROTOCORE_client_1.0"
62
64
65/** @brief Protocol opcodes, by the number each RFC assigns it (RFC 4250 sec 4.1.2). */
67{
68 // Transport layer, RFC 4253: generic 1 to 19, negotiation 20 to 29, method specific 30 to 49.
75 SSH_MSG_EXT_INFO = 7, // RFC 8308 extension negotiation
80
81 // User authentication, RFC 4252: generic 50 to 59, method specific 60 to 79.
85 SSH_MSG_USERAUTH_BANNER = 53, // RFC 4252 sec 5.4: text for the user, any time before success
87 // 60 is method specific: PK_OK for publickey, INFO_REQUEST for keyboard-interactive
88 // (RFC 4256 sec 3.2). The current auth phase decides which handler owns an inbound 60.
90 SSH_MSG_USERAUTH_INFO_RESPONSE = 61, // RFC 4256 sec 3.4
91
92 // Connection protocol, RFC 4254: global 80 to 89, channel 90 to 127.
101 SSH_MSG_CHANNEL_EXTENDED_DATA = 95, // data_type_code + string
108
109/** @brief Disconnect reason codes (RFC 4253 sec 11.1, numbered by RFC 4250 sec 4.2.2). */
119
120/** @brief Channel open failure reason codes (RFC 4254 sec 5.1). */
128
129/**
130 * @brief The connection's memory map: every byte it uses, at a named offset from its slot base.
131 *
132 * Laid out kmt | constants | control packet | data packet, each offset the previous one plus that
133 * member's size. The storage is ssh.c's; a translation unit takes its pointer as
134 * @c ssh_conn_slot(i) + the offset and already knows the member's size.
135 */
136
137// One key epoch: the six RFC 4253 sec 7.2 keys in every cipher mode negotiation can pick, at
138// offsets from the epoch's own base. Both epochs are laid out this way.
139#define SSH_OFF_GCM_C2S 0u
140#define SSH_OFF_GCM_S2C (SSH_OFF_GCM_C2S + PROTOCORE_AESGCM_BORROW)
141#define SSH_OFF_CHACHA_C2S (SSH_OFF_GCM_S2C + PROTOCORE_AESGCM_BORROW)
142#define SSH_OFF_CHACHA_S2C (SSH_OFF_CHACHA_C2S + PROTOCORE_CHACHAPOLY_KEY_LEN)
143#define SSH_OFF_MAC_C2S (SSH_OFF_CHACHA_S2C + PROTOCORE_CHACHAPOLY_KEY_LEN)
144#define SSH_OFF_MAC_S2C (SSH_OFF_MAC_C2S + 64u)
145#define SSH_OFF_AES_KEY_C2S (SSH_OFF_MAC_S2C + 64u)
146#define SSH_OFF_AES_KEY_S2C (SSH_OFF_AES_KEY_C2S + PROTOCORE_AES256CTR_KEY_LEN)
147#define SSH_OFF_AES_IV_C2S (SSH_OFF_AES_KEY_S2C + PROTOCORE_AES256CTR_KEY_LEN)
148#define SSH_OFF_AES_IV_S2C (SSH_OFF_AES_IV_C2S + PROTOCORE_AES256CTR_CTR_LEN)
149#define SSH_EPOCH_STRIDE (SSH_OFF_AES_IV_S2C + PROTOCORE_AES256CTR_CTR_LEN)
150
151// Order: wire | session | exchange | packet | rx. Slots are contiguous, so the region an overrun
152// carries into the next slot is the one at offset 0 - the wire, which is framing, not key material.
153// A run off the end of rx therefore kills both connections rather than reaching either one's keys.
154// Bounds are enforced where bytes enter; this ordering is what remains if one is ever missed.
155//
156// The regions are grouped by how long their contents live, because that is what decides how many
157// copies a build needs:
158//
159// wire, rx one per connection, and the only regions whose size follows the packet size.
160// session one per connection, alive from the first key exchange to the last packet.
161// exchange alive only from KEXINIT to NEWKEYS (RFC 4253 sec 7.1), so one per exchange in
162// flight rather than one per connection.
163// packet alive only for the message being framed or verified, so one per worker.
164//
165// exchange and packet are still per-slot here; they are grouped so that stays visible, and so
166// lifting them out is a change of base pointer rather than a re-layout.
167
168// wire: the framed packet. The payload is written at SSH_WIRE_PAYLOAD_OFF and framed in place.
169#define SSH_OFF_WIRE 0u
170
171// session: what outlives a single exchange - the identification strings both ends hash into every
172// exchange hash, the session id the first KEX fixes (RFC 4253 sec 7.2), and the two key epochs. The
173// second epoch holds the keys a re-key derives while the first still decrypts, until both
174// directions have switched (sec 7.3).
175#define SSH_OFF_V_C (SSH_OFF_WIRE + SSH_WIRE_CAP)
176#define SSH_OFF_V_S (SSH_OFF_V_C + SSH_VERSION_MAX)
177#define SSH_OFF_SESSION_ID (SSH_OFF_V_S + SSH_VERSION_MAX)
178#define SSH_OFF_EPOCH_0 (SSH_OFF_SESSION_ID + SSH_KEXHASH_MAX_LEN)
179#define SSH_OFF_EPOCH_1 (SSH_OFF_EPOCH_0 + SSH_EPOCH_STRIDE)
180#define SSH_SESSION_END (SSH_OFF_EPOCH_1 + SSH_EPOCH_STRIDE)
181
182// exchange: everything an exchange needs and nothing else reads once NEWKEYS is sent - the peer
183// identification being collected, both KEXINIT payloads the exchange hash covers, the client's
184// public value, and every ephemeral private. One exchange runs at a time per worker.
185#define SSH_OFF_IDENT SSH_SESSION_END
186#define SSH_OFF_I_C (SSH_OFF_IDENT + SSH_VERSION_MAX)
187#define SSH_OFF_I_S (SSH_OFF_I_C + PROTOCORE_SSH_I_C_MAX)
188#define SSH_OFF_KEXINIT (SSH_OFF_I_S + PROTOCORE_SSH_I_S_MAX)
189#define SSH_OFF_CPUB (SSH_OFF_KEXINIT + PROTOCORE_SSH_KEXINIT_S_MAX)
190#define SSH_OFF_DH_Y (SSH_OFF_CPUB + PROTOCORE_SSH_CPUB_MAX)
191#define SSH_OFF_DH_F (SSH_OFF_DH_Y + sizeof(protocore_bignum))
192#define SSH_OFF_DH_K (SSH_OFF_DH_F + sizeof(protocore_bignum))
193#define SSH_OFF_ECDH_SK (SSH_OFF_DH_K + sizeof(protocore_bignum))
194#define SSH_OFF_ECDH_PK (SSH_OFF_ECDH_SK + 32u)
195/** @brief The ECDH ephemeral pair, private then public: what one wipe covers. */
196#define SSH_ECDH_PAIR_LEN 64u
197#define SSH_OFF_CRYPTO_WORK (SSH_OFF_ECDH_PK + 32u)
198#define SSH_EXCHANGE_END (SSH_OFF_CRYPTO_WORK + PROTOCORE_CRYPTO_BORROW_MAX)
199
200// packet: the bytes one message's MAC works out of, then the bytes its cipher does. Live for that
201// message only. The two are separate regions rather than one shared max, so a MAC and a cipher on
202// the same packet cannot reach each other's bytes whatever order a mode runs them in. The cipher's
203// width is the wider of the two negotiable ones, since a connection runs one of them.
204#define SSH_OFF_MAC_WORK SSH_EXCHANGE_END
205#define SSH_OFF_CIPHER_WORK (SSH_OFF_MAC_WORK + PROTOCORE_HMAC_SHA256_BORROW)
206#if PROTOCORE_CHACHAPOLY_BORROW > PROTOCORE_AES256CTR_BORROW
207#define SSH_CIPHER_WORK_LEN PROTOCORE_CHACHAPOLY_BORROW
208#endif
209#if PROTOCORE_CHACHAPOLY_BORROW <= PROTOCORE_AES256CTR_BORROW
210#define SSH_CIPHER_WORK_LEN PROTOCORE_AES256CTR_BORROW
211#endif
212#define SSH_PACKET_END (SSH_OFF_CIPHER_WORK + SSH_CIPHER_WORK_LEN)
213
214// rx: the bytes drained off the transport ring, then the reassembly they feed. Last, so what it
215// runs into is the next slot's wire.
216#define SSH_OFF_RX_READ SSH_PACKET_END
217#define SSH_OFF_RX_ASM (SSH_OFF_RX_READ + RX_BUF_SIZE)
218
219/** @brief Capacity of the reassembly region at SSH_OFF_RX_ASM: what rx_buf actually spans. */
220#define SSH_RX_ASM_CAP ((size_t)SSH_RFC_MAX_PAYLOAD)
221
222/** @brief One connection's whole span, and the stride between slots. */
223#define SSH_SLOT_BORROW (SSH_OFF_RX_ASM + SSH_RX_ASM_CAP)
224
225// What each region costs, so the count a build needs is arithmetic rather than a guess. exchange
226// and packet are the two that do not have to be replicated per connection: one exchange runs at a
227// time per worker, and one message is framed at a time per worker.
228#define SSH_SESSION_SIZE (SSH_SESSION_END - SSH_OFF_V_C)
229#define SSH_EXCHANGE_SIZE (SSH_EXCHANGE_END - SSH_OFF_IDENT)
230#define SSH_PACKET_SIZE (SSH_PACKET_END - SSH_OFF_MAC_WORK)
231#define SSH_RX_SIZE (SSH_SLOT_BORROW - SSH_OFF_RX_READ)
232
233// ---------------------------------------------------------------------------
234// SSH wire types (RFC 4251 sec 5, RFC 4253 sec 7.1)
235// ---------------------------------------------------------------------------
236
237/** @brief Read a uint32 in network byte order (RFC 4251 sec 5). */
238static inline uint32_t read_u32_be(const uint8_t *p)
239{
240 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
241}
242
243/** @brief Write a uint32 in network byte order (RFC 4251 sec 5). */
244static inline void write_u32_be(uint8_t *p, uint32_t v)
245{
246 p[0] = (uint8_t)(v >> 24);
247 p[1] = (uint8_t)(v >> 16);
248 p[2] = (uint8_t)(v >> 8);
249 p[3] = (uint8_t)(v);
250}
251
252/** @brief Append a string: uint32 length, then @p n bytes of @p data. */
253PROTOCORE_INLINE void protocore_ssh_wr_str(mmgr_span *w, const void *data, size_t n)
254{
255 EMBED_CALL(byteio.put_be, OctetusCfg, .write_span = w, .value = (uint64_t)n, .bytes = 4);
256 EMBED_CALL(byteio.raw, OctetusCfg, .write_span = w, .src = data, .bytes = n);
257}
258
259/**
260 * @brief Append a NUL-terminated @p s as a string, its length taken up to the span's capacity.
261 *
262 * A comma-separated name-list (RFC 4253 sec 7.1) is one of these.
263 */
264PROTOCORE_INLINE void protocore_ssh_wr_cstr(mmgr_span *w, const char *s)
265{
266 protocore_ssh_wr_str(w, s, EMBED_CALL(cellul.len, CatenaFinitaCfg, .src = s, .cap = w->cap));
267}
268
269/**
270 * @brief Append @p len big-endian bytes as an mpint: leading zero bytes stripped, a 0x00 prepended
271 * when the top bit is set, and a zero value written as the empty string.
272 */
273PROTOCORE_INLINE void protocore_ssh_wr_mpint(mmgr_span *w, const uint8_t *be, size_t len)
274{
275 size_t off = 0;
276 while (off < len && be[off] == 0)
277 {
278 off++;
279 }
280 if (off == len)
281 {
282 EMBED_CALL(byteio.put_be, OctetusCfg, .write_span = w, .value = 0, .bytes = 4);
283 return;
284 }
285 proto_bool pad = (be[off] & 0x80u) != 0;
286 uint64_t mlen = (uint64_t)(len - off);
287 if (pad)
288 {
289 mlen++;
290 }
291 EMBED_CALL(byteio.put_be, OctetusCfg, .write_span = w, .value = mlen, .bytes = 4);
292 if (pad)
293 {
294 EMBED_CALL(byteio.put, OctetusCfg, .write_span = w, .byte = 0x00);
295 }
296 EMBED_CALL(byteio.raw, OctetusCfg, .write_span = w, .src = be + off, .bytes = len - off);
297}
298
299// ---------------------------------------------------------------------------
300// Sequence number overflow threshold
301// ---------------------------------------------------------------------------
302
303/**
304 * @brief Close the connection when seq_no reaches this value.
305 *
306 * Set to 0xFFFFFFF0 (16 below the 32-bit wrap) as a conservative margin.
307 * This prevents CTR keystream reuse that would occur at wrap. The counter is
308 * never reset; a re-key at SSH_REKEY_PACKET_THRESHOLD keeps it far from here.
309 */
310#define SSH_SEQ_CLOSE_THRESHOLD 0xFFFFFFF0u
311
312// ---------------------------------------------------------------------------
313// Wire buffer sizing
314// ---------------------------------------------------------------------------
315
316// Worst-case on-wire bytes for a payload of up to SSH_PKT_BUF_SIZE: the 4-byte packet_length, the
317// 1-byte padding_length, the effective payload, worst-case padding, and the largest MAC tag. When
318// s2c compression is built in, the "effective payload" is the compressor's worst-case output
319// (ssh_deflate_bound of a full payload) since fixed-Huffman can slightly expand incompressible data.
320// Callers MUST size the wire buffer with this so a compressed packet never overflows and desyncs the
321// stateful cipher / compression stream (a dropped packet mid-stream would corrupt the session).
322#if PROTOCORE_ENABLE_SSH_ZLIB
323#define SSH_MAX_EFFECTIVE_PAYLOAD (2 + SSH_RFC_MAX_PAYLOAD + (SSH_RFC_MAX_PAYLOAD >> 3) + 32) // = ssh_deflate_bound()
324#else
325#define SSH_MAX_EFFECTIVE_PAYLOAD (SSH_RFC_MAX_PAYLOAD)
326#endif
327#define SSH_MAX_PAD 32 // worst-case padding across block-8 / block-16 modes (min-4 rule)
328#define SSH_MAX_MAC 64 // largest MAC tag (hmac-sha2-512); chacha's Poly1305 tag is 16
329#define SSH_PKT_WIRE_MAX ((size_t)(4 + 1 + SSH_MAX_EFFECTIVE_PAYLOAD + SSH_MAX_PAD + SSH_MAX_MAC))
330
331/**
332 * @brief Uncompressed payload RFC 4253 sec 6.1 requires an implementation to process.
333 *
334 * "All implementations MUST be able to process packets with an uncompressed payload length of
335 * 32768 bytes or less". This tree sizes its own spans on this rather than on SSH_PKT_BUF_SIZE,
336 * which the pre-move tree still shares.
337 */
338#define SSH_RFC_MAX_PAYLOAD 32768u
339
340/** @brief Largest total packet RFC 4253 sec 6.1 requires an implementation to process. */
341#define SSH_RFC_MAX_PACKET 35000u
342
343// Two framed packets. ssh_pkt_emit() appends at tx_len when a packet is framed and not yet drained,
344// so a pair (KEXDH_REPLY then NEWKEYS at the kex boundary) leaves on one drain instead of the second
345// being refused. The payload is written at SSH_WIRE_PAYLOAD_OFF and framed in place by
346// ssh_pkt_send_at(), so each packet costs its own bytes and no copy of them.
347#define SSH_WIRE_CAP ((size_t)131072u)
348static_assert(SSH_WIRE_CAP >= 2u * SSH_PKT_WIRE_MAX, "the wire span must frame two of this end's largest packets");
349static_assert(SSH_WIRE_CAP >= 2u * SSH_RFC_MAX_PACKET,
350 "the wire span must hold two of the 35000-byte packets RFC 4253 sec 6.1 requires processing");
351static_assert((SSH_WIRE_CAP & (SSH_WIRE_CAP - 1u)) == 0u, "SSH_WIRE_CAP must stay a power of two");
352
353// Scratch the transport layer (RFC 4253) borrows to frame one packet, and nothing more - the wire
354// buffer and the payload being framed belong to whoever called in, because this layer is the framer,
355// not the wire. The receive side is the peak: a plaintext scratch (largest across the cipher modes)
356// is live while the payload is decompressed into a second buffer. The send side borrows only the
357// compressor's output bound. RFC 4251 sec 1 stacks auth and connection on top of this, so the arena
358// sums the layers; it does not fold them into each other.
359#if PROTOCORE_ENABLE_SSH_ZLIB
360#define PROTOCORE_PLAINTEXT_WORK_SSH_TRANSPORT ((size_t)(SSH_PKT_BUF_SIZE + 64 + SSH_PKT_BUF_SIZE))
361#else
362#define PROTOCORE_PLAINTEXT_WORK_SSH_TRANSPORT ((size_t)(SSH_PKT_BUF_SIZE + 64))
363#endif
364
365// The secure-pool term the connection declares against PROTOCORE_SECURE_ARENA_SIZE, proved against what
366// is actually borrowed. The wire is not borrowed: it is the slot's own span at SSH_OFF_WIRE, framed in
367// place by ssh_pkt_send_at(). What remains on the pool is a payload-sized transient.
368static_assert(PROTOCORE_WORK_SSH_CONN >= (size_t)SSH_PKT_BUF_SIZE,
369 "PROTOCORE_WORK_SSH_CONN must cover one transient payload: raise it in protocore_config.h");
370
371// PROTOCORE_SSH_SLOT_BYTES is sized in protocore_config.h, which cannot see the offset chain above.
372// This is the translation unit that includes both, so it is where the number is checked against the
373// span it has to cover.
375 "PROTOCORE_SSH_SLOT_BYTES must cover one connection's whole span: raise it in protocore_config.h, "
376 "which sums it into the secure arena");
377
378// PROTOCORE_SSH_CPUB_MAX is sized in protocore_config.h, which cannot see the PQC key sizes. This is the
379// translation unit that includes both, so it is where the two spellings are checked against it.
380#if PROTOCORE_ENABLE_PQC_KEX
381static_assert(PROTOCORE_SSH_CPUB_MAX >= MLKEM768_EK_BYTES + 32u,
382 "PROTOCORE_SSH_CPUB_MAX must cover an ML-KEM-768 C_INIT: raise it in protocore_config.h");
383#endif
384#if PROTOCORE_ENABLE_SSH_SNTRUP761
386 "PROTOCORE_SSH_CPUB_MAX must cover an sntrup761 C_INIT: raise it in protocore_config.h");
387#endif
388
389// The library's own caller is the connection: every KDF here runs out of slot i's crypto_work.
391 "a slot's crypto_work must cover the RFC 4253 sec 7.2 KDF: raise PROTOCORE_CRYPTO_BORROW_MAX");
392
393/**
394 * @brief Where a payload sits inside a wire buffer: past packet_length and padding_length.
395 *
396 * Every cipher mode lays those two fields down ahead of the payload and encrypts from there, so a
397 * caller that writes its message at this offset hands ssh_pkt_send_at() a packet the framer never
398 * has to move. A pipe (forwarding, a channel data pump) reads its source straight into that slot and
399 * the bytes are copied once, into the packet they leave in.
400 */
401#define SSH_WIRE_PAYLOAD_OFF 5
402
403// ---------------------------------------------------------------------------
404// RFC 4251 sec 5 - bounded reader over a payload
405// ---------------------------------------------------------------------------
406
407// Reader over a payload with bounds checking.
408typedef struct
409{
410 const uint8_t *buf;
411 size_t len;
412 size_t off;
414} Rd;
416{
417 if (r->off + 1 > r->len)
418 {
419 r->ok = PROTO_FALSE;
420 return 0;
421 }
422 return r->buf[r->off++];
423}
425{
426 mmgr_cspan c = {.buf = r->buf, .len = r->len, .pos = r->off, .err = EMBED_FALSE};
427 uint64_t v = 0;
428 if (!EMBED_CALL(byteio.take_be, OctetusCfg, .read_span = &c, .bytes = 4, .out = &v))
429 {
430 r->ok = PROTO_FALSE;
431 return 0;
432 }
433 r->off = c.pos;
434 return (uint32_t)v;
435}
436// Returns a pointer to an in-place string of length *n; advances past it. Fails closed on overflow.
437PROTOCORE_INLINE const uint8_t *protocore_ssh_rd_string(Rd *r, uint32_t *n)
438{
439 mmgr_cspan c = {.buf = r->buf, .len = r->len, .pos = r->off, .err = EMBED_FALSE};
440 const uint8_t *p = NULL;
441 size_t got = 0;
442 if (!EMBED_CALL(byteio.rd_str, OctetusCfg, .read_span = &c, .blob = &p, .blob_bytes = &got))
443 {
444 r->ok = PROTO_FALSE;
445 *n = 0;
446 return NULL;
447 }
448 r->off = c.pos;
449 *n = (uint32_t)got; // the length was read as four bytes, so it fits
450 return p;
451}
452
453// ---------------------------------------------------------------------------
454// Wire helpers
455// ---------------------------------------------------------------------------
456
457// Copy an SSH string into a fixed buffer and null-terminate it. Advances *off.
458// Returns false on truncation or if the string does not fit (buffer too small).
459//
460// Reading the field by reference is byteio.rd_str()'s job; this only adds the copy and the terminator,
461// which is what separates it from the by-reference reads below.
462static proto_bool read_string(const uint8_t *p, size_t len, size_t *off, char *out, size_t outcap)
463{
464 mmgr_cspan c = {.buf = p, .len = len, .pos = *off, .err = EMBED_FALSE};
465 const uint8_t *s = NULL;
466 size_t n = 0;
467 if (!EMBED_CALL(byteio.rd_str, OctetusCfg, .read_span = &c, .blob = &s, .blob_bytes = &n))
468 {
469 return PROTO_FALSE;
470 }
471 if (n >= outcap)
472 {
473 return PROTO_FALSE; // does not fit our fixed buffer; same contract as byteio.rd_str: *off stays on its own field
474 }
475 *off = c.pos;
476 EMBED_CALL(memor.cpy, MemoriaCfg, .dst = out, .src = s, .bytes = n);
477 out[n] = '\0';
478 return PROTO_TRUE;
479}
480
482
483#endif // PROTOCORE_SSH_COMMON_H
AES-256-CTR stream cipher (aes256-ctr, RFC 4344 §4).
2048-bit big-integer arithmetic for DH-group14 and RSA-2048.
#define PROTOCORE_CRYPTO_BORROW_MAX
#define PROTOCORE_SSH_KDF_BORROW
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
#define SSH_PKT_BUF_SIZE
Packet assembly buffer per SSH connection (bytes).
#define PROTOCORE_SSH_SLOT_BYTES
One connection's whole span: the wire, the session, the exchange, the packet and the rx regions end t...
chacha20-poly1305@openssh.com AEAD cipher (OpenSSH PROTOCOL.chacha20poly1305).
#define PROTOCORE_INLINE
Linkage for a leaf primitive whose body is cheaper than the call that reaches it.
#define PROTOCORE_WORK_SSH_CONN
#define PROTOCORE_SSH_CPUB_MAX
Reverse-SSH tunnel: max concurrent forwarded-tcpip channels bridged at once. A relay that forwards to...
HMAC-SHA2-256 (RFC 2104 + FIPS 198-1) - streaming context and one-shot API.
#define MLKEM768_EK_BYTES
encapsulation key (public key): 384*k + 32
Definition mlkem.h:52
PROTOCORE_INLINE void protocore_ssh_wr_cstr(mmgr_span *w, const char *s)
Append a NUL-terminated s as a string, its length taken up to the span's capacity.
Definition common.h:264
#define SSH_RFC_MAX_PACKET
Largest total packet RFC 4253 sec 6.1 requires an implementation to process.
Definition common.h:341
enum PROTO_ENUM_PACKED SshOpenFailureReason
Channel open failure reason codes (RFC 4254 sec 5.1).
enum PROTO_ENUM_PACKED SshDisconnectReason
Disconnect reason codes (RFC 4253 sec 11.1, numbered by RFC 4250 sec 4.2.2).
PROTOCORE_INLINE void protocore_ssh_wr_str(mmgr_span *w, const void *data, size_t n)
Append a string: uint32 length, then n bytes of data.
Definition common.h:253
PROTOCORE_INLINE const uint8_t * protocore_ssh_rd_string(Rd *r, uint32_t *n)
Definition common.h:437
PROTOCORE_INLINE void protocore_ssh_wr_mpint(mmgr_span *w, const uint8_t *be, size_t len)
Append len big-endian bytes as an mpint: leading zero bytes stripped, a 0x00 prepended when the top b...
Definition common.h:273
PROTOCORE_BEGIN_DECLS enum PROTO_ENUM_PACKED SshMsgId
Protocol opcodes, by the number each RFC assigns it (RFC 4250 sec 4.1.2).
#define SSH_PKT_WIRE_MAX
Definition common.h:329
#define SSH_SLOT_BORROW
One connection's whole span, and the stride between slots.
Definition common.h:223
@ SSH_DISCONNECT_SERVICE_NOT_AVAILABLE
Definition common.h:114
@ SSH_MSG_CHANNEL_CLOSE
Definition common.h:103
@ SSH_DISCONNECT_MAC_ERROR
Definition common.h:113
@ SSH_MSG_REQUEST_FAILURE
Definition common.h:95
@ SSH_MSG_USERAUTH_REQUEST
Definition common.h:82
@ SSH_MSG_CHANNEL_EXTENDED_DATA
Definition common.h:101
@ SSH_MSG_USERAUTH_BANNER
Definition common.h:85
@ SSH_MSG_IGNORE
Definition common.h:70
@ SSH_MSG_KEXDH_INIT
Definition common.h:78
@ SSH_OPEN_CONNECT_FAILED
Definition common.h:124
@ SSH_MSG_GLOBAL_REQUEST
Definition common.h:93
@ SSH_OPEN_ADMINISTRATIVELY_PROHIBITED
Definition common.h:123
@ SSH_MSG_CHANNEL_WINDOW_ADJUST
Definition common.h:99
@ SSH_MSG_NEWKEYS
Definition common.h:77
@ SSH_MSG_USERAUTH_FAILURE
Definition common.h:83
@ SSH_MSG_REQUEST_SUCCESS
Definition common.h:94
@ SSH_DISCONNECT_BY_APPLICATION
Definition common.h:115
@ SSH_MSG_CHANNEL_FAILURE
Definition common.h:106
@ SSH_MSG_SERVICE_ACCEPT
Definition common.h:74
@ SSH_MSG_DISCONNECT
Definition common.h:69
@ SSH_OPEN_UNKNOWN_CHANNEL_TYPE
Definition common.h:125
@ SSH_MSG_CHANNEL_OPEN_CONFIRMATION
Definition common.h:97
@ SSH_MSG_EXT_INFO
Definition common.h:75
@ SSH_MSG_USERAUTH_INFO_RESPONSE
Definition common.h:90
@ SSH_OPEN_RESOURCE_SHORTAGE
Definition common.h:126
@ SSH_MSG_DEBUG
Definition common.h:72
@ SSH_MSG_KEXINIT
Definition common.h:76
@ SSH_MSG_SERVICE_REQUEST
Definition common.h:73
@ SSH_MSG_USERAUTH_PK_OK
Definition common.h:86
@ SSH_MSG_CHANNEL_REQUEST
Definition common.h:104
@ SSH_MSG_CHANNEL_DATA
Definition common.h:100
@ SSH_MSG_CHANNEL_OPEN_FAILURE
Definition common.h:98
@ SSH_MSG_USERAUTH_INFO_REQUEST
Definition common.h:89
@ SSH_MSG_CHANNEL_OPEN
Definition common.h:96
@ SSH_DISCONNECT_TOO_MANY_CONNECTIONS
Definition common.h:116
@ SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE
Definition common.h:117
@ SSH_MSG_CHANNEL_EOF
Definition common.h:102
@ SSH_MSG_CHANNEL_SUCCESS
Definition common.h:105
@ SSH_MSG_USERAUTH_SUCCESS
Definition common.h:84
@ SSH_DISCONNECT_PROTOCOL_ERROR
Definition common.h:112
@ SSH_MSG_KEXDH_REPLY
Definition common.h:79
@ SSH_MSG_UNIMPLEMENTED
Definition common.h:71
PROTOCORE_INLINE uint32_t protocore_ssh_rd_u32(Rd *r)
Definition common.h:424
#define SSH_WIRE_CAP
Definition common.h:347
PROTOCORE_INLINE uint8_t protocore_ssh_rd_u8(Rd *r)
Definition common.h:415
Streamlined NTRU Prime sntrup761 KEM - keygen, encapsulation, decapsulation.
#define PROTOCORE_SNTRUP761_PK_BYTES
public key (Rq-encoded h)
Definition sntrup761.h:41
One key-exchange digest that dispatches SHA-256 or SHA-512 by the negotiated method.
Definition common.h:409
const uint8_t * buf
Definition common.h:410
size_t off
Definition common.h:412
proto_bool ok
Definition common.h:413
size_t len
Definition common.h:411
#define PROTO_FALSE
the false value
Definition types.h:68
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTO_TRUE
the true value, spelled so a caller never writes a bare 1
Definition types.h:67
#define PROTOCORE_END_DECLS
Definition types.h:97