#ifndef PURE_LINK_PROTOCOL_H #define PURE_LINK_PROTOCOL_H #include "seal.h" /* Links without connections (docs/design/011-pure-transport.md), as state * alone: no socket, thread or clock of their own. The owner gives each side * the packets that came and the time (ns, monotonic), and sends the packets * each side makes; link/link.h is such an owner over UDP. So one thread may * carry many links, and a test may carry both sides through a network of its * own making. * * A link's lasting state is the apps' own: the sender's outbox (what to send * and send again) and the receiver's heard numbers (what it applied, saved * with the events). What either side keeps in memory is a cache, so either * may restart and the other goes on with the next packet. * * A packet is a header in the clear, then frames sealed with AES-128-GCM * (the header as associated data): * u8 0x70 (version 0), u32le the key id of the node that sent it (FNV-1a * of its public key; two keys may share an id, and a receiver tries the * keys it allows with it), u64le that node's run (random, new each time it * starts, so no nonce repeats under a key across restarts), u64le the * packet's number in its run. * Keys come from the secret the two nodes' keys share (X25519) by * HKDF-SHA256, with both public keys and the sending node's run: a sender's * packets use its own run. A receiver's answers use a run drawn for each * sender's run it keeps (made again when a sender at rest comes back, so no * nonce repeats), and add the run they answer, so an answer opens only in * that run. The nonce is the key's base with the packet's number in its * last eight bytes (XOR, big-endian, as QUIC does). * * What an attacker on the path may do, and what it gets (link/test-link.c * does each): * - change a packet: it does not open, and it is answered by nothing * (a packet is opened before it is looked at). * - send a packet of the past again: its records are applied once (the * heard numbers), its word is taken only as the most said (STORED's * limit), and it moves nothing: answers go where the newest packet of * the run came from. A new address is sent a challenge the sender says * back (the attacker cannot); until then answers there are at most * three times the bytes that came from it (RFC 9000 §8). * - send as a key or a source not allowed: nothing opens or is delivered. * (Anyone who has the receiver's private key can seal as any sender to * it: static-static keys, as Noise KK's first message.) * - flood made-up packets under a known key's id, runs never seen: keys * made for a run that did not open cost a token, 256 a second, so at * most that many HKDFs a second; X25519 once a sender (remembered). * * Frames (numbers little-endian): * 0x01 STREAM source (u8 length, bytes), input (u8 length, bytes), u32 the * shape of the messages (the sender's output and the * receiver's input must have the same): the stream the * records after it are of * 0x02 RECORD u64 number, u32 size, u32 offset, u16 length, bytes: part * of a saved send (a send too large for what is left of a * packet goes in parts, cut where the packet is full) * 0x03 ACK u64 largest number, u32 delay (µs), u8 ranges, each as u64 * first and u64 last, newest first * 0x04 STORED source, input (as STREAM), u64 the last number heard * (applied and saved), u8 flags (1: a record after a gap was * not taken: send what follows it again), u32 credit (records * it takes past that: the sender sends up to the most heard * and credit it was told) * 0x05 PING to be acknowledged * 0x06 RECORDS u64 the first one's number, u8 count, then whole records one * after another (numbers following on), each its size (a * varint: LEB128) and its bytes: a send that fits the packet * whole, with 1 to 3 bytes of framing, not the 19 of RECORD * 0x07 CHALLENGE u64: a receiver's, until its sender says it back from * the address the answers go to * 0x08 RESPONSE u64: a challenge said back (the last few had), in the next * packet that goes (records, or a probe) * 0x00 PADDING to the end of the packet: a full packet sent in a burst is * a whole datagram * Losses are detected as RFC 9002 says, with RACK's patience for packets * that come late; congestion control is CUBIC (RFC 9438), paced. */ /* A packet is PURE_LINK_DATAGRAM bytes at most at first, and up to * PURE_LINK_DATAGRAM_MOST once the path is found to carry that many (a * probe of that size acknowledged: DPLPMTUD, RFC 8899; a link probes once * a packet of its was full, else a larger one would carry nothing more); a * source's or an input's name is 1 to PURE_LINK_NAME bytes; a receiver * keeps where a packet came from (the socket's address) in * PURE_LINK_ADDRESS bytes. */ enum { PURE_LINK_DATAGRAM = 1200, PURE_LINK_DATAGRAM_MOST = 1452, PURE_LINK_NAME = 64, PURE_LINK_ADDRESS = 28 }; /* Counts for a host's report: packets and records sent, records sent again, * packets lost and acknowledged, packets received, deliveries handed over, * the last number stored; packets called lost that came late, and * congestion events undone for it; runs keyed (keys made from a secret) * and secrets made (X25519); a receiver's senders woken (their active * state made), of them with a secret remembered (no X25519), let go when * idle, and active now. */ typedef struct { uint64_t packets, records, resent, lost, acknowledged, received, deliveries, stored, late, undone, keyed, secrets, woken, remembered, swept, active; } PureLinkCounts; /* ---- The sending side: one output's saved sends to a receiver's input, as * source (the receiver's name for it), their messages of shape (the * receiver's input must have the same: core/module.h's pure_module_shape), * from own (this node's key, whose public half is own_public; NULL when * the secret comes from elsewhere). NULL when the names are not 1 to * PURE_LINK_NAME bytes, or memory runs out. */ typedef struct PureLinkSender PureLinkSender; PureLinkSender *pure_link_sender(const char *source, const char *input, uint32_t shape, const PureSealKey *own, const unsigned char own_public[PURE_SEAL_KEY]); /* The receiver's public key: the keys made from it (one X25519). A sender * makes no packet before it has one. */ bool pure_link_sender_peer(PureLinkSender *, const unsigned char peer[PURE_SEAL_KEY]); /* The same with the secret own's key shares with peer's made elsewhere (by * a key kept in hardware, say, or a test's many keys): no X25519 here. */ bool pure_link_sender_shared(PureLinkSender *, const unsigned char peer[PURE_SEAL_KEY], const unsigned char secret[32]); /* A saved send, numbered one past the last queued (any number when none * waits); false when it cannot be kept. */ bool pure_link_sender_queue(PureLinkSender *, uint64_t seq, const unsigned char *bytes, size_t size); /* A packet that came (the receiver's answers; others are let be). */ void pure_link_sender_take(PureLinkSender *, const unsigned char *packet, size_t size, uint64_t now); /* The timers due by now: losses found, probes, records sent again. */ void pure_link_sender_tick(PureLinkSender *, uint64_t now); /* The next packet to send into out, and its number: its size, or 0 when * there is none (nothing to send, or the window is full). whole: a full * packet is padded to the sender's datagram (so packets may go in a burst * of whole datagrams, as UDP GSO sends them). A probe of a larger size is * larger than the datagram: it goes alone. */ size_t pure_link_sender_packet(PureLinkSender *, unsigned char out[PURE_LINK_DATAGRAM_MOST], bool whole, uint64_t now, uint64_t *number); /* The size of a full packet now. */ size_t pure_link_sender_datagram(const PureLinkSender *); /* A packet made but not sent (the socket refused it): what it carried is * sent again, and it counts as never sent. */ void pure_link_sender_refused(PureLinkSender *, uint64_t number); /* When the next timer is due (ns), or 0 for none. */ uint64_t pure_link_sender_due(const PureLinkSender *); /* The last number queued; the last the receiver stored. */ uint64_t pure_link_sender_queued(const PureLinkSender *); uint64_t pure_link_sender_stored(const PureLinkSender *); void pure_link_sender_counts(const PureLinkSender *, PureLinkCounts *); void pure_link_sender_free(PureLinkSender *); /* ---- The receiving side: packets from the senders allowed (a source name * and the key it seals with, a few bytes each however many there are: see * pure_link_idle). A stream (a source to an input) goes to the host's input * its name and shape find (its number, or -1: the stream is refused). Whole * records of a stream, in order, go to deliver as one delivery; the host * applies them from the one after the source's last heard number, stops at * a gap, saves, and then says so with a done (pure_link_receiver_turn). * Records wait here until then, and only the saved ones are acknowledged as * stored. */ typedef struct { uint32_t input; const char *source; uint64_t first; unsigned count; const unsigned char *records; /* count of: u32le size, bytes */ size_t size; void *token; /* for its done */ } PureLinkDelivery; typedef struct { int (*input)(void *context, const char *name, uint32_t shape); bool (*deliver)(void *context, const PureLinkDelivery *); void *context; } PureLinkHost; /* A delivery's end: heard, the last number heard; stored, its save * succeeded (else it is handed over again); gap, it stopped at a gap. */ typedef struct { void *token; uint64_t heard; bool stored, gap; } PureLinkDone; /* A packet to send to an address a packet came from: whether it went. */ typedef bool PureLinkSend(void *context, const unsigned char *packet, size_t size, const unsigned char *to, size_t to_size); typedef struct PureLinkReceiver PureLinkReceiver; PureLinkReceiver *pure_link_receiver(const PureSealKey *own, const unsigned char own_public[PURE_SEAL_KEY], const PureLinkHost *host, char *error, size_t size); bool pure_link_allow(PureLinkReceiver *, const char *source, const unsigned char key[PURE_SEAL_KEY], char *error, size_t size); /* A sender quiet for ms (30 s at first) with nothing of it waiting lets its * keys, addresses and streams go: an idle link costs the receiver only its * allowing, and the sender's next packet makes them again (one X25519). */ void pure_link_idle(PureLinkReceiver *, uint64_t ms); /* Remember the secret each sender's key shares with the node's in a file * (path, 0600; begun again for another node's key): a place a sender in * the order allowed, mapped, so a sender at rest takes no memory for it. * A node that starts again then makes no X25519 for a sender it heard from * before. After the senders are allowed, before the first packet. */ bool pure_link_remember(PureLinkReceiver *, const char *path, char *error, size_t size); /* A packet that came from an address (from_size bytes, at most * PURE_LINK_ADDRESS kept): answers to its sender go there. */ void pure_link_receiver_take(PureLinkReceiver *, const unsigned char *packet, size_t size, const void *from, size_t from_size, uint64_t now); /* After the packets that came: the dones (count of them) applied, whole * records handed over, answers sent through send, and senders at rest let * go. */ void pure_link_receiver_turn(PureLinkReceiver *, const PureLinkDone *done, size_t count, uint64_t now, PureLinkSend *send, void *context); /* When a turn is due with no packet (ns), or 0 for none. */ uint64_t pure_link_receiver_due(const PureLinkReceiver *); void pure_link_receiver_counts(const PureLinkReceiver *, PureLinkCounts *); void pure_link_receiver_free(PureLinkReceiver *); #endif