aboutsummaryrefslogtreecommitdiffstats
path: root/link/link-protocol.h
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-29 12:05:58 +0100
committereunhhu <[email protected]>2026-09-29 12:05:58 +0100
commit319c2e61fabc89e2e64add218be448a9c9b6b776 (patch)
tree050e2df51e3615a5031405dda30edcc0aa01d3e1 /link/link-protocol.h
parent95ce26b67aa6827b97a7fd0a9580a5d7be5dff37 (diff)
downloadpure-319c2e61fabc89e2e64add218be448a9c9b6b776.tar.gz
pure-319c2e61fabc89e2e64add218be448a9c9b6b776.zip
Close what an attacker on the path could do to a link, each checked
test-link now also plays an attacker: packets changed on the way, packets of the past sent again from the sender's address and its own, keys and sources not allowed, and a flood of made-up packets. It found: - A nonce used twice: a receiver sealed every answer in its one run, and a sender at rest coming back with the same run got a new slot whose answers began at number 0 again under the same key. Each slot now draws the run its answers are sealed in. - A link stopped for good by one packet of the past: a stream that did not know its last heard (after a restart or a rest) handed over from its first record held, and a stale part of an old one never came whole. It hands over from its first whole record. - Answers taken away: any packet that opened, however old, moved where answers went. Only the newest of its run moves it now; a new address is sent a challenge the sender says back (CHALLENGE, RESPONSE), and until then gets at most three times the bytes that came from it. - An answer made by a packet that did not open: a number heard before was acknowledged before the packet was opened. It is opened first. - An old answer lowering the sender's credit, or its gap making it send again: the sender now sends up to the most heard and credit it was told, and takes a gap only from a word not older than it knows. - Ten thousand made-up packets made ten thousand keys: keys made for a run that did not open now cost a token, 256 a second. Counts now say runs keyed and secrets made.
Diffstat (limited to 'link/link-protocol.h')
-rw-r--r--link/link-protocol.h46
1 files changed, 35 insertions, 11 deletions
diff --git a/link/link-protocol.h b/link/link-protocol.h
index f51260b..9b4e63d 100644
--- a/link/link-protocol.h
+++ b/link/link-protocol.h
@@ -23,9 +23,28 @@
* 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 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).
+ * 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
@@ -40,16 +59,20 @@
* 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)
+ * 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)
* 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; congestion control is its NewReno
- * (RFC 9002 §7). */
+ * 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
@@ -61,12 +84,13 @@ enum { PURE_LINK_DATAGRAM = 1200, PURE_LINK_DATAGRAM_MOST = 1452, PURE_LINK_NAME
/* 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; a receiver's senders woken (their
- * active state made), of them with a secret remembered (no X25519), let go
- * when idle, and active now. */
+ * 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, woken, remembered,
- swept, active;
+ 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