aboutsummaryrefslogtreecommitdiffstats
path: root/link/link-protocol.h
diff options
context:
space:
mode:
Diffstat (limited to 'link/link-protocol.h')
-rw-r--r--link/link-protocol.h61
1 files changed, 45 insertions, 16 deletions
diff --git a/link/link-protocol.h b/link/link-protocol.h
index f121b2e..f54514b 100644
--- a/link/link-protocol.h
+++ b/link/link-protocol.h
@@ -16,18 +16,35 @@
*
* 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).
+ * u8 0x70 (version 0) with the low four bits of the epoch it is sealed
+ * in, 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 an epoch's secret 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).
+ *
+ * Epoch 0's secret is the one the two nodes' keys share (X25519). A sender
+ * offers the public half of a pair made for the next epoch (OFFER) in its
+ * packets until the receiver accepts it with a half of its own (ACCEPT); the
+ * X25519 of the two halves and the epoch's secret make the next one's
+ * (HKDF, "pure link 0 epoch" and its number), and the private halves are let
+ * go. The sender seals in it from the ACCEPT on; the receiver takes it once
+ * a packet of it opens, and opens the late ones of the epoch before. So what
+ * went in epoch 1 and on cannot be opened by one who later has both nodes'
+ * keys: only a run's first packets, before its first answer, are sealed
+ * under the static keys. A new epoch every hour or GiB
+ * (pure_link_sender_epochs). A receiver that remembers (pure_link_remember)
+ * writes the epoch it accepted for each sender's run, and goes on in it after
+ * it starts again; a sender that hears nothing for 2 s over 4 probes (the
+ * receiver forgot) offers again in epoch 0, with no records, and the chain
+ * begins again from the nodes' keys.
*
* What an attacker on the path may do, and what it gets (link/test-link.c
* does each):
@@ -41,7 +58,11 @@
* 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.)
+ * it, in a run of its own: static-static keys, as Noise KK's first
+ * message. Epochs do not change this; they keep it out of others'
+ * runs.)
+ * - keep what went, and later get both nodes' keys: it opens only the
+ * packets of epoch 0.
* - 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).
@@ -70,6 +91,11 @@
* 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)
+ * 0x09 OFFER u32 epoch, a public key (X25519): the sender's half of that
+ * epoch's secret, until it is accepted
+ * 0x0a ACCEPT u32 epoch, a public key: the receiver's half, until a
+ * packet of that epoch opens (the same offer again gets the
+ * same; one a second for a run)
* 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
@@ -87,13 +113,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; runs keyed (keys made from a secret)
- * and secrets made (X25519); a receiver's senders woken (their active
+ * congestion events undone for it; runs keyed (keys made from a secret),
+ * secrets made (X25519 of the nodes' keys), epochs begun; 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;
+ epochs, woken, remembered, swept, active;
} PureLinkCounts;
/* ---- The sending side: one output's saved sends to a receiver's input, as
@@ -132,6 +158,9 @@ size_t pure_link_sender_datagram(const PureLinkSender *);
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 *);
+/* How often it moves on to a new epoch (ns), and after how many bytes sent
+ * (an hour and a GiB at first). */
+void pure_link_sender_epochs(PureLinkSender *, uint64_t every_ns, uint64_t bytes);
/* 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 *);