diff -uNr a/pest_spec/MANIFEST.TXT b/pest_spec/MANIFEST.TXT
--- a/pest_spec/MANIFEST.TXT a7dd03c240b2526fb6e93ffcb3140cc4027071df86a6eaccdb2878deeabcdcf98fa0e01180ec4b639f0fcd1550747f08b12940a6790d3ff841a83d45a49e6f29
+++ b/pest_spec/MANIFEST.TXT 58b8961087d179fd8dce0e1cf3b5fcdb5b652a521c596414c3db08bb726707684d682cde95d3086dd4693cc969d4605e846e2aa4b76dca3eee07e2ca238d6421
@@ -1,3 +1,4 @@
699183 pest_spec_genesis asciilifeform "Genesis."
699220 pest_spec_ver_fe asciilifeform "First revision."
710434 pest_spec_ver_fd asciilifeform "Second revision."
+718649 pest_spec_ver_fc asciilifeform "Third revision."
diff -uNr a/pest_spec/pest.md b/pest_spec/pest.md
--- a/pest_spec/pest.md a6b726e6c662bdb95d05e9442153a6473ee436ac364a68c875f3708752bd3fd52a2f6d4d002f45112c54414d7a590eda2dc6782c8511cdcadb11eaf8023c0277
+++ b/pest_spec/pest.md 50255e53a9690e00b97c7527131cbcd0fe864b53d22dd628b2a505ad1df0278b7fd22218e2ab7e2f859c1f1e4e3047d69bee7ec7a078aa61b6098771a0544e39
@@ -2,7 +2,7 @@
## Version.
-*This document represents Protocol Version 0xFD.*
+*This document represents Protocol Version 0xFC.*
## 1.1. What is Pest?
@@ -10,7 +10,7 @@
## 1.2. How Pest Differs from IRC and Other Chat Protocols.
-Pest explicitly rejects the inherently-centralizing concepts of IRC (not even speaking of the even more noxious "walled-garden" atrocities perpetrated in recent years.) In place of IRC's collectivist concept of the *server*, every Pest user instead inhabits a stand-alone *station* of which he is the sole *operator*. Pest stations exchange [authenticably-encrypted](#322-black-packet) messages exclusively with an operator-configured set of *peers* known as a [WOT](#23-the-wot) (*web of trust*.)
+Pest explicitly rejects the inherently-centralizing concepts of IRC (not even speaking of the even more noxious "walled-garden" atrocities perpetrated in recent years.) In place of IRC's collectivist concept of the *server*, every Pest user is instead the sole *operator* of a stand-alone *station*. A Pest station exchanges [authenticably-encrypted](#322-black-packet) messages exclusively with an operator-configured set of *peers* known as a [`WOT`](#23-the-wot) (*web of trust*.)
### 1.2.1. One Station -- One Operator.
@@ -22,11 +22,13 @@
A [*broadcast*](#412-broadcast-message) message will reach every reachable member of its sender's net. Nets may easily and organically undergo schismatic splits, or, on the contrary, combine into larger nets, whenever the individual station operators so desire.
-### 1.2.3. Identity is not Centrally-Registered.
+### 1.2.3. Identity is Decentralized.
-To join a Pest net, an operator must simply find one or more current members of that net who would peer with his station, and securely exchange a small amount of information.
+A prospective Pest station operator need not ask permission from *anyone*; and the only other people who will need to know about the mere existence of the station will be its *peers*.
-He may choose any *handle* he likes, so long as it does not [collide](#3121-selfchain-broadcast-messages) with that of a peer. Importantly, one person may easily operate *multiple* Pest stations, and inhabit multiple *disjoint* nets; and may use, if he wishes, a different handle on each net.
+To join a Pest net, an operator must simply find one or more current members of that net who would peer with his station, and securely exchange a small amount of information. An active Pest station may have as few as *one* peer, or as many as its [hardware is able to service at the desired bandwidth capacity.](#95-valid-packets-and-station-capacity)
+
+A Pest station operator may choose any *handle* he likes, so long as it does not [collide](#3121-selfchain-broadcast-messages) with that of a peer. Importantly, one person may easily operate *multiple* Pest stations, and inhabit multiple *disjoint* nets; and may use, if he wishes, a different handle on each net.
### 1.2.4. Messages are Authenticable, but not Opposable.
@@ -52,10 +54,12 @@
Unlike virtually all traditional Internet protocols, Pest *does not talk to strangers*. At all.
-Strangers may, of course, *send* whatever packets where they will -- including to a Pest station. However, because a stranger (by definition) does not know any of the [*keys*](#22-peers-and-keys) in that station's [WOT](#23-the-wot), all [such packets](#1271-martians) will *simply get thrown away*[^3], as they are *invalid*.
+Strangers may, of course, *send* whatever packets where they will -- including to a Pest station. However, because a stranger (by definition) does not know any of the [*keys*](#22-peers-and-keys) in that station's [`WOT`](#23-the-wot), all [such packets](#1271-martians) will *simply get thrown away*[^3], as they are *invalid*.
An *invalid* packet is one which is determined to be [*martian*](#1271-martians), [*duplicate*](#1272-duplicates), or [*stale*](#1273-stales). Conversely, a packet which is neither *martian*, *duplicate*, nor *stale* is considered *valid*.
+It is recommended that a Pest station be provisioned with sufficient hardware resources to [process incoming packets at *line rate*](#95-valid-packets-and-station-capacity) (i.e. at the maximum rate at which they may arrive via the station's physical network interfaces.)
+
#### 1.2.7.1. "Martians".
An [incorrectly-sized](#322-black-packet) packet received by a Pest station -- or a correctly-sized one which [does not bear a valid signature from one of its peers](#421-common-prologue-for-all-packets)[^4] is referred to as a **martian**. All such packets are silently discarded.
@@ -68,14 +72,6 @@
Any packet bearing a [Message](#31-message) which has [expired, or appears to come "from the future"](#311-timestamp) is considered **stale** and -- even though it may be valid in every other respect -- is silently discarded.[^6]
-#### 1.2.7.4. Valid Packets and Station Capacity.
-
-When provisioning hardware for a Pest station, the operator must provide sufficient CPU cores, so that it may:
-
-* Accept and process all *valid* packets, at a frequency at or below some planned maximum *FPMax*. If valid packets arrive faster than the planned maximum frequency, a certain portion will be discarded. *FPMax* valid packets per second, however, *will* be processed under any possible circumstances, and in particular regardless of the frequency at which *invalid* packets arrive.
-* Reject all *invalid* packets at the maximum physically-possible rate at which they may arrive (i.e. up to and including the *line rate*) without affecting processing of *valid* packets.
-
-
### 1.2.8. Nothing to the Snoop.
A [stranger](#127-nothing-to-the-stranger) who is able to capture some or all of the traffic emitted by a Pest station is referred to as a *snoop*.
@@ -106,15 +102,15 @@
## 2.3. The WOT.
-A Pest station may have any number of peers. One or more[^8] `K` for each peer is kept in a data structure referred to as the station's **WOT**. The operator may edit this structure at any time, and changes take effect *immediately*. The WOT is *never* altered by the station *except* by direct command of the operator.
+A Pest station may have any number of peers. One or more[^8] `K` for each peer is kept in a data structure referred to as the station's **`WOT`**. The operator may edit this structure at any time, and changes take effect *immediately*. The `WOT` is *never* altered by the station *except* by direct command of the operator.
Each peer entry in the WOT also contains one or more *handles*, as well as certain other information concerning that peer.
## 2.4. The AT.
-A Pest station has another data structure, the **AT** (*Address Table*), which holds the *last known address*[^9] of each WOT peer. The AT is used exclusively for determining where to send *outgoing* packets.
+A Pest station has another data structure, the **`AT`** (*Address Table*), which holds the *last known address*[^9] of each WOT peer. The `AT` is used exclusively for determining where to send *outgoing* packets.
-Like the WOT, the AT may also be edited by the operator at any time. Unlike the WOT, the AT is *automatically updated* by the station when a [cryptographically-valid](#322-black-packet) packet is received from a new address.
+Like the `WOT`, the `AT` may also be edited by the operator at any time. Unlike the `WOT`, the `AT` is *automatically updated* by the station when **any** [cryptographically-valid](#322-black-packet) packet is received from a new (i.e. not currently in the `AT`) address. The `AT` contains *one* entry per `WOT` peer.
## 2.5. Operator Console.
@@ -132,7 +128,7 @@
|:--|--|:--|
|**`USER`**|*username* *hostname* *servername* *realname*|The string *username* must equal a preconfigured value stored on disk, or the console connection will be terminated. It is not used for any other purpose. None of the other parameters are used for any purpose, but *may* be present.|
|**`PASS`**|*password*|This is a password, or a derivative thereof; the exact authentication mechanism is **unspecified**. If the password is found to be invalid, the console connection will be terminated immediately.|
-|**`NICK`**|`HANDLE`|Every message originating at this station will have a [`Speaker`](#314-speaker) equal to `HANDLE`. Importantly, `HANDLE` **may not** collide with any peer handle found in the station's WOT; nor may `PEER` or `AKA` later be invoked with an argument equal to this `HANDLE`.|
+|**`NICK`**|`HANDLE`|Every message originating at this station will have a [`Speaker`](#314-speaker) equal to `HANDLE`. Importantly, `HANDLE` **may not** collide with any peer handle found in the station's `WOT`; nor may `PEER` or `AKA` later be invoked with an argument equal to this `HANDLE`.|
|**`JOIN`**|`#pest`|Valid `USER`, `PASS`, and `NICK` commands *must* be received prior to accepting this command. The argument *must* start with **`#`**. This is a *pseudochannel* which must be used in all subsequent IRC commands which require a channel (e.g. `PRIVMSG`, `PART`.) In all examples henceforth -- will be illustratively `#pest`, but in fact it may be any string of up to 128 bytes in length.|
|**`PART`**|`#pest`|Has no effect.|
|**`VERSION`**||Display a description of the implementation and [version](#version) of the protocol currently in use.|
@@ -142,7 +138,7 @@
**Control** commands allow a Pest station's operator to view or update the station's configuration.
-All control commands must be found at the beginning of a string entered into the operator console, and are prefixed with `%` (e.g. `%WOT`). Any line entered into the operator console which begins with `%` (i.e. appearing as an IRC message *originating from the operator* of the form `PRIVMSG foo %bar`, irregardless of `foo`) *must* be treated as a control command, and a correct Pest implementation *must* take care to filter these lines to prevent emission of the command or its payload as a [Pest *message*](#41-message-origination). Whitespace may precede the `%` command prefix and is to be disregarded. If a station operator finds it necessary to transmit a Pest message starting with `%`, he may prefix it with the traditional Unix escape character: `\%`.
+All control commands must be found at the beginning of a string entered into the operator console, and are prefixed with `%` (e.g. `%WOT`). Any line entered into the operator console which begins with `%` (i.e. appearing as an IRC message *originating from the operator* of the form `PRIVMSG foo %bar`, regardless of `foo`) *must* be treated as a control command, and a correct Pest implementation *must* take care to filter these lines to prevent emission of the command or its payload as a [Pest *message*](#41-message-origination). Whitespace may precede the `%` command prefix and is to be disregarded. If a station operator finds it necessary to transmit a Pest message starting with `%`, he may prefix it with the traditional Unix escape character: `\%`.
A control command may be issued at any time, and *must* take effect (including preserving any state change to persistent storage) *immediately*. Responses to control commands will be emitted through the console in the form of IRC *NOTICE* messages.
@@ -150,29 +146,33 @@
|Command|Arguments|Description|
|:--|--|:--|
-|**`WOT`**||Display the current WOT, with complete (apart from keys) data concerning each peer. This includes the peer's handles; whether the peer is *paused*; the timestamp of the most recent packet received from the peer; and the current address stored in the AT for the peer.|
-|**`WOT`**|`HANDLE`|Display all WOT data concerning the peer identified by `HANDLE`, *including all known keys*, *starting with the most recently used*, for that peer.|
+|**`WOT`**||Display the current `WOT`, with complete (apart from keys) data concerning each peer. This includes the peer's handles; whether the peer is *paused*; the timestamp of the most recent packet received from the peer; and the current address stored in the AT for the peer.|
+|**`WOT`**|`HANDLE`|Display all `WOT` data concerning the peer identified by `HANDLE`, *including all known keys*, *starting with the most recently used*, for that peer.|
|**`PEER`**|`HANDLE`|Declare a new peer, identified by `HANDLE`. This command is required but not sufficient to establish communication with the peer (see also `KEY` and `AT`.)|
|**`UNPEER`**|`HANDLE`|Permanently discard *all* data concerning the peer identified by `HANDLE`, including keys and AT entries.|
|**`AKA`**|`HANDLE` `ALIAS`|Permit the use of the string `ALIAS` synonymously with the previously-known `HANDLE` (which may in turn be the peer's original handle, or an alias added via this command.)|
-|**`UNAKA`**|`HANDLE`|Remove `HANDLE` from the WOT. However if it is any peer's *only* known handle, the command has no effect and a warning is displayed.|
+|**`UNAKA`**|`HANDLE`|Remove `HANDLE` from the `WOT`. However if it is any peer's *only* known handle, the command has no effect and a warning is displayed.|
|**`PAUSE`**|`HANDLE`|Temporarily disable all communication with the peer identified by `HANDLE`, without discarding any data.|
|**`UNPAUSE`**|`HANDLE`|Re-enable communication with the peer identified by `HANDLE`.|
-|**`KEY`**|`HANDLE` `KEY`|Add a `KEY` for the peer identified by `HANDLE`. `KEY` [is in all cases a **base64**-encoded **512-byte** value](#51-keys), and *may not* previously exist anywhere in the WOT. If `HANDLE` is unknown, a warning is displayed.|
-|**`UNKEY`**|`KEY`|Remove the given `KEY` from the WOT. However if `KEY` is any peer's *only* known key, the command has no effect and a warning is printed.|
-|**`GENKEY`**||Use the system [TRNG](http://nosuchlabs.com/) to generate and display a new `KEY` suitable for use with **`KEY`**. No change is made to the WOT.|
-|**`REKEY`**|`HANDLE`|Attempt a [Rekeying](#6-rekeying) with the peer specified by `HANDLE`. If no handle is specified, attempt rekeying *with each peer in the WOT, in random order.* All successful rekeyings are reported to the operator console (without displaying keys.)|
+|**`KEY`**|`HANDLE` `KEY`|Add a `KEY` for the peer identified by `HANDLE`. `KEY` [is in all cases a **base64**-encoded **512-byte** value](#51-keys), and *may not* previously exist anywhere in the `WOT`. (If it was found to exist, *an error message is displayed and nothing further happens.*) If `HANDLE` is unknown, a warning is displayed.|
+|**`UNKEY`**|`KEY`|Remove the given `KEY` from the `WOT`. However if `KEY` is any peer's *only* known key, the command has no effect and a warning is printed.|
+|**`GENKEY`**||Use the system [TRNG](http://nosuchlabs.com/) to generate and display a new `KEY` suitable for use with **`KEY`**. No change is made to the `WOT`.|
+|**`REKEY`**|`HANDLE`|Attempt a [Rekeying](#6-rekeying) with the peer specified by `HANDLE`. If no handle is specified, attempt rekeying *with each peer in the `WOT`, in random order.* All successful rekeyings are reported to the operator console (without displaying keys.)|
|**`RKTOG`**|`ENABLE` or `DISABLE`|Toggle processing of [Rekeying](#6-rekeying) requests. Defaults to disabled.|
-|**`GAG`**|`HANDLE`|Add `HANDLE` (which *may not* correspond to a WOT peer!) to the *killfile*. Any incoming message where [`Speaker`](#314-speaker) matches a killfile entry will not be processed.|
+|**`GAG`**|`HANDLE`|Add `HANDLE` (which *may not* correspond to a `WOT` peer!) to the *killfile*. Any incoming message where [`Speaker`](#314-speaker) matches a killfile entry will not be processed.|
|**`UNGAG`**|`HANDLE`|Undo the effect of a previously-issued `GAG` command.|
|**`AT`**||Display the current AT.|
|**`AT`**|`HANDLE`|Display the current AT entry for the peer identified by `HANDLE`.|
-|**`AT`**|`HANDLE` `ADDRESS`|Set the current `ADDRESS` in the AT for the WOT peer identified by `HANDLE`. In order for two peers to communicate, at least one of them must issue this command to add the other's address to his AT. Subsequently, this value will be updated as required as packets are received.|
+|**`AT`**|`HANDLE` `ADDRESS`|Set the current `ADDRESS` in the AT for the `WOT` peer identified by `HANDLE`. In order for two peers to communicate, at least one of them must issue this command to add the other's address to his AT. Subsequently, this value will be updated as required as packets are received.|
|**`KNOB`**||Displays a list of all implemented *knobs* (operator-configured constants.)|
|**`KNOB`**|`KNOB`|Displays the current value of `KNOB`.|
|**`KNOB`**|`KNOB` `VALUE`|Sets the knob identified by `KNOB` to `VALUE`.|
|**`CUT`**|`INTEGER`|Sets the station's [bounce cutoff](#3212-bounces) to the given `INTEGER` between 0 and 255. If equal to 0, the station will process only [direct messages](#411-direct-message-to-a-peer).|
|**`RESOLVE`**|`HANDLE`|Resolve [a *fork*](#3121-selfchain-broadcast-messages) afflicting the peer identified by `HANDLE`.|
+|**`SLAVE`**|`HANDLE`|Intended strictly for use with [bots](#94-bots). Set the station into *slave mode* with respect to the peer identified by `HANDLE`. The latter is referred to as a *master*. A [broadcast message](#3121-selfchain-broadcast-messages) received *solely* via one or more peers identified as a *master* is treated as an [immediate message](#4231-receiving-a-broadcast-message-immediate-messages); when rebroadcast, its [bounce count](#3212-bounces) will consequently be equal to *one*. More than one *master* may be set.|
+|**`SLAVE`**||Display a list of *masters* that have been set via the `SLAVE` command. If this list is currently *empty*, the message *"station is not in slave mode."* is displayed.|
+|**`UNSLAVE`**|`HANDLE`|Removes the peer identified by `HANDLE` from the *masters* list. If the peer was *not* in the *masters* list, an error message is displayed and there is no other effect.|
+|**`UNSLAVE`**||Removes *all* entries from the *masters* list.|
### 2.5.3. Optional Commands.
@@ -281,7 +281,7 @@
#### 3.2.1.2. Bounces.
-This 1-byte field represents the number of times the message in this packet had been relayed between stations. A station *must* reject messages which have experienced more than a preconfigured number of bounces. The recommended cutoff value is **3**.
+This 1-byte field represents the number of times the message in this packet had been relayed between stations. A station *must* reject messages which have experienced more than a preconfigured number of bounces. The recommended cutoff value is **5**.
#### 3.2.1.3. Version.
@@ -317,7 +317,7 @@
##### 3.2.1.6.1. Broadcast Text.
-A human-readable message, intended for the broadest possible dissemination. A *broadcast* message is sent to *every* peer in the originator station's WOT, and will be propagated to *their* peers, and so on.
+A human-readable message, intended for the broadest possible dissemination. A *broadcast* message is sent to *every* peer in the originator station's `WOT`, and will be propagated to *their* peers, and so on.
A *broadcast* message will often reach stations which *are not peers of the originator*. From the point of view of these stations, such a message is termed [*hearsay*](#4232-receiving-a-broadcast-message-hearsay-messages); and, when displayed, it is specially marked so as to distinguish it from an [*immediate*](#4231-receiving-a-broadcast-message-immediate-messages) message.
@@ -363,31 +363,40 @@
|Bytes|Description|
|-|-|
-|276|Ciphertext|
+|272|Ciphertext|
|48|Signature|
+|4|*Padding of null bytes*|
-A station which receives an address cast will attempt to verify and decrypt it (into a `Red Address Cast`) *strictly on a best-effort basis* (i.e. when the CPU would otherwise be idle.) The logic is identical to that used for decoding [Broadcast Text](#423-receiving-a-broadcast-message) messages, with the exception of the epilogue, where, instead of treating the (decoded) payload as human-readable text, it will be treated as a Red Address Cast, i.e. *276 bytes* of plaintext, which take the following format:
+A station which receives an address cast will attempt to verify and decrypt it (into a `Red Address Cast`) *strictly on a best-effort basis* (i.e. when the CPU would otherwise be idle.) The logic is identical to that used for decoding [Broadcast Text](#423-receiving-a-broadcast-message) messages, with the exception of the epilogue, where, instead of treating the (decoded) payload as human-readable text, it will be treated as a Red Address Cast, i.e. *272 bytes* of plaintext, which take the following format:
|Bytes|Description|
|-|-|
|4|Cast Command|
|2|Port|
|4|IP|
-|266|*Padded with null bytes*|
+|262|*Padding of null bytes*|
`Cast Command` must equal `0` (signifying `Address Cast`).
+The `Port` field represents the *port number* on which the originator station wishes to receive Pest packets from the addressee. The *first* byte of this field will consist of the *least-significant byte* of the port number; the *second* byte will contain its *most-significant byte*. (E.g. if the port number is equal to `1337`, the *first* byte will equal `0x39`, and the *second* byte will equal `0x05`.)
+
+The `IP` field represents the *four bytes* of the originator's IPv4 address, *in order of descending significance.* (E.g. if the `IP` to be encoded is `1.2.3.4`, the *first* byte will be equal to `0x01`; the second: `0x02`, the third: `0x03`, the fourth: `0x04`.)
+
+`IP` **must** represent a *publicly-routable IPv4 address*. (If a receiving station determines that it does *not* such an address, it *must* silently reject the message.)
+
A station which successfully validated and decrypted an Address Cast from one of its peers will execute the equivalent of the `AT` command, henceforth using the supplied *address* for all outgoing packets intended to reach that peer.
-The circumstances under which a station will *originate* an address cast are presently *undefined*. (A reasonable behaviour may be to generate one periodically for *every* peer in the station's [WOT](#23-the-wot)).
+The circumstances under which a station will *originate* an address cast are presently *undefined*. (A reasonable behaviour may be to generate one periodically for *every* peer in the station's [`WOT`](#23-the-wot)).
+
+The [Speaker](#314-speaker) field of a [Message](#31-message) bearing an `Address Cast` payload must match a [`WOT`](#23-the-wot) handle corresponding to its apparent originator.
##### 3.2.1.6.8. Ignore.
-A garbage message. A station *may* transmit garbage messages to its peers, to frustrate traffic analysis by snoops. In such cases, it will consist of arbitrary random bytes. A recipient of such a message *may* relay it to an arbitrary subset of his WOT. Receipt of a garbage message *must not* result in any console output.
+A garbage message. A station *may* transmit garbage messages to its peers, to frustrate traffic analysis by snoops. In such cases, it will consist of arbitrary random bytes. A recipient of such a message *may* relay it to an arbitrary subset of his `WOT`. Receipt of a garbage message *must not* result in any console output.
### 3.2.2. *Black* Packet.
-A station transmits messages exclusively to peers. Prior to transmission, a *red* packet is *ciphered and signed* using a `K` found in the WOT for the peer to whom it is to be sent. After this happens, the packet is referred to as *black*.
+A station transmits messages exclusively to peers. Prior to transmission, a *red* packet is *ciphered and signed* using a `K` found in the `WOT` for the peer to whom it is to be sent. After this happens, the packet is referred to as *black*.
A *black* Pest packet consists of **496** bytes (excluding IP and UDP headers) representing the following fields:
@@ -398,7 +407,7 @@
A third party without knowledge of `K` is unable to read such a packet; to distinguish it from arbitrary random noise; or to generate a spurious replacement (including via replay, in whole or in part, of a previously-sent packet) that could be accepted by the addressee.
-Every incoming (without exception, *black*) packet has its `Signature` *verified against* each `K` in the receiving station's WOT, *in random order*.
+Every incoming (without exception, *black*) packet has its `Signature` *verified against* each `K` in the receiving station's `WOT`, *in random order*.
If verification of the packet against a particular `K` succeeds, the packet is then known to have been *signed* by the peer associated with that `K`. The packet is then decrypted (with `K`) and becomes *red* once again; at this point, the receiving station is able to process the original message.
@@ -420,7 +429,7 @@
The following actions will be performed:
-1. The station's WOT is searched for a peer with a handle `nebuchadnezzar`. If *there isn't one*, or `nebuchadnezzar` is currently *paused*, nothing further happens, and a warning will be emitted to the console.
+1. The station's `WOT` is searched for a peer with a handle `nebuchadnezzar`. If *there isn't one*, or `nebuchadnezzar` is currently *paused*, nothing further happens, and a warning will be emitted to the console.
2. The most-recently used key `K` for `nebuchadnezzar` is found. (If *no keys are known* for `nebuchadnezzar`, nothing further happens, and a warning will be emitted to the console.)
3. The first 32 bytes of `K` represent `K(S)` -- the *signing key*; while the remaining 32 bytes of `K` are `K(C)` -- the *cipher key*.
4. The [AT](#24-the-at) entry for `nebuchadnezzar` is found. (If *one such does not exist*... see above.)
@@ -430,7 +439,7 @@
|--:|:--|:--|
|`16`|`Nonce`|*random bytes*|
|`1`|`Bounces`|`0`|
- |`1`|`Version`|`253`|
+ |`1`|`Version`|*a single byte representing the [Pest protocol version](#version) supported by the originating station.*|
|`1`|`Reserved`|`0`|
|`1`|`Command`|`0x01` (*Direct Message*)|
|`428`|`Message`|*as given below:*|
@@ -450,7 +459,9 @@
|448|Ciphertext|`SERPENT_ENCRYPT(K(C),`*the 448-byte red packet*`)`|
|48|Signature|`HMAC384(Ciphertext, K(S))`|
-7. The black packet is transmitted to `nebuchadnezzar`'s current *address*, determined in step 4.
+7. The message is [hashed](#fn-11), and its hash is entered into the station's deduplication buffer.
+
+8. The black packet bearing the message is transmitted to `nebuchadnezzar`'s current `AT` *address*, determined in step 4.
### 4.1.2. Broadcast Message.
@@ -460,17 +471,17 @@
The following actions will be performed:
-1. **For each WOT peer `P`** in `shalmaneser`'s WOT, **in random order**:
+1. A list of *addressees*, **`A`**, is generated. By default, it will consist of *all* of the peers in `shalmaneser`'s `WOT` (with the exception of any *paused* peers). However, if the message is a *rebroadcast*, `A` will *not* contain any peers from which [copies of the message to be rebroadcast](#4232-receiving-a-broadcast-message-hearsay-messages) are known to have been received. For each peer **`P`** in `A`, **in random order**:
2. The most-recently used key `Pk` for `P` is found. (If *no keys are known* for `P`, we move on to the next peer.)
3. The first 32 bytes of `Pk` represent `Pk(S)` -- the *signing key*; while the remaining 32 bytes of `Pk` are `Pk(C)` -- the *cipher key*.
4. The [AT](#24-the-at) entry for `P` is found. (If *one such does not exist*, we move on to the next peer.)
-5. For **each peer**, a [red packet](#321-red-packet) is created, where:
+5. For **each P** in `A`, a [red packet](#321-red-packet) is created, where:
|Bytes|Description|Value|
|--:|:--|:--|
|`16`|`Nonce`|*random bytes*|
|`1`|`Bounces`|`0`|
- |`1`|`Version`|`253`|
+ |`1`|`Version`|*a single byte representing the [Pest protocol version](#version) supported by the originating station.*|
|`1`|`Reserved`|`0`|
|`1`|`Command`|**`0x00` (*Broadcast Message*)**|
|`428`|`Message`|*as given below:*|
@@ -489,7 +500,9 @@
|448|Ciphertext|`SERPENT_ENCRYPT(Pk(C),`*the 448-byte red packet*`)`|
|48|Signature|`HMAC384(Ciphertext, Pk(S))`|
-7. Each black packet (one for each peer) is transmitted to its destination, determined in step 4.
+7. The message is [hashed](#fn-11), and the its is entered into the station's deduplication buffer.
+
+8. The above [black packet](#322-black-packet) is transmitted to its addressee (i.e. to the [Address](#22-peers-and-keys) currently known via the `AT` for the peer `P`) as determined in step 4.
## 4.2. Message Receipt.
@@ -504,15 +517,15 @@
`nebuchadnezzar`'s station will carry out the following procedure:
-1. **For each WOT peer `P`** in `nebuchadnezzar`'s WOT, **in random order**:
-2. For each `Pk` known for `P`... (If *no keys are known* for `P`, `P` is simply ignored.)
+1. **For each key `Pk`** in `nebuchadnezzar`'s `WOT`, **in random order**:
+2. `P` represents the peer to whom the key `Pk` belongs.
3. The first 32 bytes of `Pk` represent `Pk(S)` -- the *signing key*; while the remaining 32 bytes of `Pk` are `Pk(C)` -- the *cipher key*.
4. `HMAC384(Ciphertext, Pk(S))` is compared to `Signature`. If they do not match, the next one is tried. If none match, the packet is declared *martian* and *silently discarded*.
-5. If a match was found, `Pk(C)` is used to decrypt `Ciphertext` and reproduce the original [red packet](#321-red-packet).
-6. `Timestamp` is [compared to the current system time](#311-timestamp) and if it is **more than 15 minutes** in the past or the future, the packet is considered *stale* and *discarded*.
-7. The *deduplication queue* is a ring buffer holding the last **hour**'s worth of messages. If an identical message is found in the queue, the packet is considered *stale* and is *discarded*.
-8. If, in the previous step, the deduplication queue was *not* found to already contain a copy of the message, a copy is placed there.
-9. Let **`H`** be the set of all handles found in the station's WOT for the peer who was found to have sent the packet; e.g. `{shalmaneser, ShalmaneserTheGreat}`.
+5. If a match was found, `Pk(C)` is used to decrypt `Ciphertext` and reproduce the original [red packet](#321-red-packet). The packet is now considered to have been sent by the station operated by peer `P`.
+6. If a response to a [`GetData` request](#32164-getdata) is anticipated, the [message](#3216-message) is [hashed](#fn-11); and if its hash is found in the list of currently-anticipated `GetData` responses, the message is deemed *not stale*. However: if there are *no* currently-anticipated `GetData` responses, or there are and the message was not found to be one of them, its `Timestamp` is [compared to the current system time](#311-timestamp) and if it is **more than 15 minutes** in the past or the future, the packet is considered *stale* and *discarded*.
+7. The *deduplication buffer* is a ring buffer holding the last **hour**'s worth of messages (including ones *transmitted by* the station.) If an incoming message is found to be identical to one found in the buffer, the packet bearing said message is considered a [duplicate](#1272-duplicates) and is *discarded*. For fast retrieval, messages are indexed by their hash.
+8. If the incoming message was *not* rejected as a duplicate, it is entered into the deduplication buffer.
+9. Let **`H`** be the set of all handles found in the station's `WOT` for the peer who was found to have sent the packet; e.g. `{shalmaneser, ShalmaneserTheGreat}`.
10. At this point, the next step in the algorithm depends on the value of the `Command` field...
### 4.2.2. Receiving a Direct Message.
@@ -523,8 +536,8 @@
1. If `Bounces` is not equal to `0x00`, the packet is discarded.
2. The [`SelfChain` warning](#3121-selfchain-broadcast-messages) is displayed if required.
-3. If `Speaker` *is not* in `H`, the console will display the `Speaker`, followed by the entirety of `H` (in parentheses), followed by the `Text`:
- `bob (shalmaneser): hello`
+3. If `Speaker` *is not* in `H`, the console will display the `Speaker`, followed by the entirety of `H` (in *square brackets*), followed by the `Text`:
+ `bob[shalmaneser]: hello`
... in the standard format for IRC *private messages*.
4. If `Speaker` *is* in `H`, the console will display the `Speaker`, followed by the `Text`, e.g:
`shalmaneser: Come to tea.`
@@ -551,13 +564,13 @@
A packet bearing a hearsay message is placed into the station's **hearsay buffer** and will remain there for an *operator-configurable* interval (recommended: **1 second**).
-If packets bearing duplicates of the message are received, they must be discarded, but the [bounce](#3212-bounces) count of the retained copy *must* be considered equal to that of the copy bearing the *lowest* bounce count.
+If packets bearing duplicates of the message are received, they must be discarded, but the [bounce](#3212-bounces) count of the retained copy [*must* be considered equal to that of the copy bearing the *lowest* bounce count](#4233-receiving-a-broadcast-message-common-epilogue).
-Tthe [count of peers who have supplied copies of a particular message](#4233-receiving-a-broadcast-message-common-epilogue) is tallied; following which, the message is processed in one of the two ways given below, depending on whether `Speaker` is in the receiver's WOT:
+The [count of peers who have supplied copies of a particular message](#4233-receiving-a-broadcast-message-common-epilogue) is tallied; following which, the message is processed in one of the two ways given below, depending on whether `Speaker` is in the receiver's `WOT`:
##### 4.2.3.2.1. Simple Hearsay.
-Consider a message received by `nebuchadnezzar` where `H = {shalmaneser}`, but `Speaker` is in fact `hammurabi`; and there is **not** a `hammurabi` in `nebuchadnezzar`'s WOT.
+Consider a message received by `nebuchadnezzar` where `H = {shalmaneser}`, but `Speaker` is in fact `hammurabi`; and there is **not** a `hammurabi` in `nebuchadnezzar`'s `WOT`.
`nebuchadnezzar`'s station knows that the message was *sent* by `shalmaneser`, because a `K` belonging to `shalmaneser` had verified it. However, it also knows that this message had **not** been *originated* by `shalmaneser`. (*Or, if it had been, he is perhaps suffering from multiple personality disease.*)
@@ -565,13 +578,13 @@
Therefore the `Text` will be displayed in `nebuchadnezzar`'s console (in channel `#pest`) in the following form:
-`hammurabi(shalmaneser): hey listen!`
+`hammurabi[shalmaneser]: hey listen!`
-... where the handle in parentheses corresponds to the peer from whom the packet had come.
+... where the handle in *square brackets* corresponds to the peer from whom the packet had come.
##### 4.2.3.2.2. In-WOT Hearsay.
-Now suppose that `Speaker` is not in `H`, i.e. the message is a *hearsay* message, but that `Speaker` in fact **is in the receiver's WOT**. For instance, a message exactly like the previous example's, but where `hammurabi` is in fact a member of `nebuchadnezzar`'s WOT.
+Now suppose that `Speaker` is not in `H`, i.e. the message is a *hearsay* message, but that `Speaker` in fact **is in the receiver's WOT**. For instance, a message exactly like the previous example's, but where `hammurabi` is in fact a member of `nebuchadnezzar`'s `WOT`.
This can happen if the *immediate* copy of `hammurabi`'s packet (i.e. received directly from its originator) has been lost or delayed in transit.
@@ -583,34 +596,42 @@
Broadcast messages which survived deduplication are processed as follows:
-1. If any duplicates of the message were found to have been received upon the elapse of the embargo interval, the copy of the message bearing *the lowest* [bounce](#3212-bounces) *must* be selected for further processing, and all others discarded.
+1. If any duplicates of the message were found to have been received upon the elapse of the embargo interval, the copy of the message bearing *the lowest* [bounce](#3212-bounces) *must* be selected for further processing, and all others discarded after having been counted. This minimal bounce count is referred to as `Lb`.
+
+2. A list **`R`** is generated, consisting of *relayers*, i.e. peers (identified by the [`Speaker`](#314-speaker) field in the message) who have supplied duplicates of the message.
+
+3. A subset of `R`, `Rb`, is determined. It consists of such members of `R` whose packet's [bounce](#3212-bounces) count was equal to `Lb`.
-2. The `Text` of the message will be displayed to the console.
+4. The `Text` of the message is displayed to the console.
-3. The message will be relayed to all peers.
+5. The message is relayed to all `WOT` peers, *other than those found in `R`*.
-If the message had been a deemed [Hearsay](#4232-receiving-a-broadcast-message-hearsay-messages), its `Text` will be displayed to the console in one of the two following formats, depending on *how many peers had sent in duplicates of this message during the embargo interval* :
+6. The message is displayed to the station operator's console, as follows:
-If **three or fewer** peers had sent in such duplicates, the `Text` of the message will be displayed in the console preceded by a set of parentheses listing the handle of each such peer.
+If the message had been a deemed [Hearsay](#4232-receiving-a-broadcast-message-hearsay-messages), its `Text` is displayed to the console in one of the two following formats, depending on *how many peers had provided `Lb`-bounced (*min-bounce*) duplicates of this message during the embargo interval* :
-For instance, if copies of a hearsay message purporting to originate from `hammurabi` had been received from `shalmaneser`, `nebuchadnezzar`, and `bob`, it will appear as:
+If `Rb` consists of **three or fewer** peers, the `Text` of the message is displayed in the console preceded by a set of *square brackets* listing the members of `Rb`.
-`hammurabi(shalmaneser, nebuchadnezzar, bob): hi there`
+For instance, if copies of a hearsay message purporting to originate from `hammurabi` had been received from `shalmaneser`, `nebuchadnezzar`, and `bob`, the list of *min-bounce* relayers will appear in *square brackets*, separated by `|` (*pipe*) characters:
-But if **four or more** duplicates of a hearsay message were received during the embargo interval, the parentheses will instead contain simply *the total number* of peers who had sent them, e.g.:
+`hammurabi[shalmaneser|nebuchadnezzar|bob]: hi there`
-`hammurabi(11): hi`
+But if `Rb` contains **four or more** members, the brackets will instead contain simply *their total number*:
+
+`hammurabi[11]: hi`
In both [Immediate](#4231-receiving-a-broadcast-message-immediate-messages) and [Hearsay](#4232-receiving-a-broadcast-message-hearsay-messages) cases, after `Text` is sent to the console:
1. `Bounces` is incremented by **1**.
-2. The message is rebroadcast (see [4.1.2. Broadcast Message](#412-broadcast-message)) to every WOT peer -- in *random* order.
+2. The message is rebroadcast (see [4.1.2. Broadcast Message](#412-broadcast-message)) to every active (i.e. not *paused*, and having a valid `AT` entry and key) `WOT` peer (in the case of an *immediate message*) -- or every member of `Rb` (in the case of a *hearsay* message), in *random* order.
# 6. Rekeying.
-All symmetric cipher keys "age" with use. A Pest station *may* send rekey requests to a peer. The receiving station *may* process such requests, or silently reject them.
+All symmetric cipher keys "age" with use. A Pest station *may* send rekey requests to a particular peer. The receiving station *may* process such requests, or silently reject them.
+
+It is recommended that a *rekey* be conducted *immediately after an initial peering* and at regular intervals thereafter. Station operators may disable the processing of incoming *rekey* requests (see the [`RKTOG`](#252-control-commands) control command) to preserve the validity of scheduled `WOT` backups; and re-enable it, when necessary, by mutual verbal agreement.
-The Pest rekeying process is such that each peer is required to generate a *xor-slice* of the proposed new key `Kn` **independently**. The new key will be equal to the `xor` of the previous key `K` shared by the peers with both of the proposed slices: `Kn == K xor Sa xor Sb`. Consequently, the new key `Kn` will have an expected entropy greater than or equal to that of the old key `K` and each of the slices `Sa`, `Sb`.
+The Pest rekeying process is such that the initiator and receiver are each required to generate a *xor-slice* of the proposed new key `Kn` **independently**. The new key will be equal to the `xor` of the previous key `K` shared by the two peers, with both of the proposed slices: `Kn == K xor Sa xor Sb`. Consequently, the new key `Kn` will have an expected entropy greater than or equal to that of the old key `K` and each of the slices `Sa`, `Sb`.
The algorithm takes the following form:
@@ -620,10 +641,10 @@
4. When station `A` receives `B`'s [`Key Offer`](#32165-key-offer), it will verify that the latter *does not equal* its own previously-sent `Key Offer`. (If they were found to be identical, `B`'s `Key Offer` is deemed *invalid*, and `A` will abort the rekeying.) If `A` proceeds with the rekeying, it will now encode its slice `Sa` into a [`Key Slice`](#32166-key-slice) message and transmit said message to station `B`.
5. Station `B` will determine whether the hash in `A`'s [`Key Offer`](#32165-key-offer) corresponds to `H(Sa)` where `H` is the hash function. If it does not, the offer is deemed invalid and station `B` carries on as before, as if no rekeying request had been made.
6. However, if the hash matched, station `B` will reply by similarly encoding its slice `Sb` into a [`Key Slice`](#32166-key-slice) message and transmitting said message to station `A`.
-7. At this time, station `B` is able to calculate the *new key* `Kn` using the equation: `Kn == K xor Sa xor Sb` where `K` is the previous key used for peer `A`, `Sa` is `A`'s `Key Slice`, and `Sb` is `B`'s `Key Slice`. `B` records this key in its WOT entry for `A`, but does not discard the old key `K` yet.
+7. At this time, station `B` is able to calculate the *new key* `Kn` using the equation: `Kn == K xor Sa xor Sb` where `K` is the previous key used for peer `A`, `Sa` is `A`'s `Key Slice`, and `Sb` is `B`'s `Key Slice`. `B` records this key in its `WOT` entry for `A`, but does not discard the old key `K` yet.
8. Station `A` will perform the same comparison operation as described in step 5. If the hash does not match, `B`'s offer is deemed invalid, and nothing further happens.
9. If the hash matched, the rekey handshake is deemed successful.
-10. Station `A` will calculate the *new key* `Kn`, similarly to `B` in step 7: `Kn == K xor Sa xor Sb` where `K` is the previous key shared with peer `B`, `Sa` is `A`'s `Key Slice`, and `Sb` is `B`'s `Key Slice`. `A` records this key in its WOT entry for `B`, but does not discard the old key `K` yet.
+10. Station `A` will calculate the *new key* `Kn`, similarly to `B` in step 7: `Kn == K xor Sa xor Sb` where `K` is the previous key shared with peer `B`, `Sa` is `A`'s `Key Slice`, and `Sb` is `B`'s `Key Slice`. `A` records this key in its `WOT` entry for `B`, but does not discard the old key `K` yet.
11. Station `A` will transmit an [`Ignore`](#32168-ignore) packet to station `B` using the new key `Kn`.
12. Station `B`, upon receiving this packet, will succeed in decoding it using `Kn`. It will reply with an [`Ignore`](#32168-ignore) packet to station `A`, ciphered and signed using `Kn`.
13. Each of the stations will record `Kn` to their `WOT`s under the entry for the respective peer.
@@ -633,7 +654,7 @@
A rekeying is deemed to have aborted (any slice `Sx`, as well as `Kn` if it has been generated -- discarded by the station) if it does not complete within an operator-specified interval `Tk`.
-A station which has successfully rekeyed a peer (irregardless of which operator initiated the rekeying process) will warn its operator, so that he is made aware of the need to back up his WOT.
+A station which has successfully rekeyed a peer (regardless of which operator initiated the rekeying process) will warn its operator, so that he is made aware of the need to back up his `WOT`.
# 7. GetData.
@@ -717,6 +738,16 @@
If an operator wishes to run bots, guest accounts, etc., additional stations may be set up to peer with his primary one; on the same physical machine, if so desired.
+## 9.5. Valid Packets and Station Capacity.
+
+When provisioning hardware for a Pest station, the operator must provide sufficient CPU cores, so that it may:
+
+* Accept and process all *valid* packets, at a frequency at or below some planned maximum *FPMax*. If valid packets arrive faster than the planned maximum frequency, a certain portion will be discarded. *FPMax* valid packets per second, however, *will* be processed under any possible circumstances, and in particular regardless of the frequency at which *invalid* packets arrive.
+* Reject all *invalid* packets at the maximum physically-possible rate at which they may arrive (i.e. up to and including the *line rate*) without affecting processing of *valid* packets.
+
+## 9.6. ICMP.
+
+The processing of [ICMP](https://datatracker.ietf.org/doc/html/rfc777) packets by a machine housing a Pest station exposes the latter to [demasking](http://logs.nosuchlabs.com/log/asciilifeform/2022-01-13#1072667), violating the [*nothing to the stranger*](#127-nothing-to-the-stranger) dictum; it is also a traditional DDOS vector. Therefore it is recommended that Pest station operators **disable the processing of ICMP packets under any pretext whatsoever** by whatever means required under their operating system or firewall setup.
# Notes.
@@ -730,7 +761,7 @@
[^5]: However, sometimes it is necessary to [count the number of distinct peers who provided copies of a given message](#4233-receiving-a-broadcast-message-common-epilogue).
-[^6]: Unless the message is an expected response to a *getdata*.
+[^6]: Unless the message is an expected response to a *[GetData](#32164-getdata)*.
[^7]: Via secure means external to Pest, such as GPG, [Peh](http://www.loper-os.org/?p=1913), or an in-person meeting.
@@ -738,7 +769,7 @@
[^9]: That is, the *source address* of the most recent packet received from this peer.
-[^10]: *Current time* shall be defined as the value of a station's *64-bit monotonic epoch clock*. Station operators must take measures to synchronize their clocks within *15 minutes*, a precision entirely achievable without recourse to centralized time servers.
+[^10]: *Current time* shall be defined as the value of a station's *64-bit monotonic epoch clock*, traditionally defined as *"a 64-bit unsigned fixed-point number, in seconds relative to 00:00:00 January 1, 1970, UTC".* Station operators must take measures to synchronize their clocks within *15 minutes*, a precision entirely achievable without recourse to centralized time servers.
[^11]: In the current protocol: SHA256. The hash encompasses all message fields, in the order they are listed in the table. Any trailing padding bytes required by the hash are to equal zero.
diff -uNr a/pest_spec/pest.svg b/pest_spec/pest.svg
--- a/pest_spec/pest.svg false
+++ b/pest_spec/pest.svg 6be6323d82ab165e535a2bb6ed9e1dadbb7bf788abd0dcdcbb069352e228776321118cb672a6d9ae07bfa110df0a912ba4c59b31194c29bf38e528bc2c798309
@@ -0,0 +1,1012 @@
+
+