# FOG Sphinx Profiles Status: Draft 0.1 Date: 2026-08-08 ## 1. Purpose This document defines `FOG-SPHINX-PROFILES`, the authenticated profile framework for fixed-size KEMSphinx packets used by FOG. It fixes the structural packet contract, route shape, routing commands, SURB rules, replay behavior, wire ownership, SDK boundary, transition behavior, and conformance evidence required before a concrete packet suite can be activated. It also records one calculated but non-active candidate geometry named `FOG-SPHINX-CANDIDATE-MLKEM768-X25519-1`. The candidate is an engineering input for benchmarks and review. It is not yet `FOG-SPHINX-1`, is not authorized for a public network, and does not make a deployed security claim. The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, and MAY describe normative requirements in the sense of BCP 14 when they appear in uppercase. ## 2. Scope `FOG-SPHINX-PROFILES` owns: - immutable packet-profile identifiers and their consensus authorization; - fixed KEMSphinx packet geometry and exact route length; - the mapping from PKI nodes to 32-byte packet node identifiers; - per-hop routing command sequences and command padding; - forward payload and SURB slots; - single-use reply blocks and private reply tokens; - replay-tag derivation, durable replay-state behavior, and retirement; - packet lifetime and epoch binding; - the logical bodies carried by FOG-WIRE packet commands; - parser limits, uniform failures, and key lifecycle; - conformance-vector and SDK requirements; - isolation requirements for foreign Sphinx-family bridges. This document does not own: - message-level end-to-end encryption; - contact, retry, acknowledgement, or application deduplication semantics; - entry-capsule encryption and blind-relay entry selection; - rendezvous capability allocation and offline return import; - storage capabilities or replica protocols; - adjacent-link Noise framing or its record size; - the concrete delay distribution and cover rate; - consensus production, signatures, or topology assignment. Those contracts belong to FOG-MESSAGING, FOG-STORAGE, the entry-capsule and return-rendezvous specifications, FOG-WIRE, the cover profile, and FOG-PKI. ## 3. Security Boundaries KEMSphinx protects per-hop routing information and transforms the packet at every hop. It does not replace message-level encryption. The terminal receives the fixed outer FOG payload and MUST still treat the native message or storage operation as end-to-end encrypted unless the owning application protocol explicitly defines a different public object. FOG-WIRE authenticates adjacent links but does not replace KEMSphinx. A valid Noise connection is only a carrier for one packet whose KEMSphinx authentication, route command, replay status, epoch, and geometry must still be validated. Fixed packet size alone does not provide anonymity against timing, intersection, n-1, volume, active tagging, or global observation. Delay, traffic volume, cover traffic, topology, endpoint safety, and operator independence remain necessary parts of any anonymity claim. ## 4. Protocol Invariants ### SPHINX-INV-01: One exact profile per epoch context A packet is processed under exactly one packet profile selected by the accepted consensus and the authenticated FOG-WIRE epoch context. A node MUST NOT guess a profile from packet length, try a list of suites, negotiate a suite inside a packet, or fall back after failure. ### SPHINX-INV-02: Four KEMSphinx hops Every core forward route and every core reply route has exactly four KEMSphinx hops. A shorter or longer core route is invalid even if an underlying Sphinx library can pad a variable path to the same header length. ### SPHINX-INV-03: Entry is outside the forward mix route The forward entry is not a KEMSphinx hop. The blind relay submits an entry-bound capsule; the entry releases the already constructed packet only to its bound layer-1 node. ### SPHINX-INV-04: One transform per hop An accepted KEMSphinx hop performs one and only one unwrap. No role unwraps two layers, forwards a packet without the required unwrap, or changes a route outside the authenticated command obtained from that unwrap. ### SPHINX-INV-05: Fixed external geometry Every packet using one packet profile has one exact byte length. Forward, reply, padding, loop, drop, mailbox, and asynchronous-message packets use the same geometry. Source applications do not select packet sizes. ### SPHINX-INV-06: Durable replay before action After successful KEM decapsulation and header authentication, a hop MUST atomically check and durably insert its replay identifier before scheduling, forwarding, terminal delivery, or returning success to an upstream worker. ### SPHINX-INV-07: Single-use replies A SURB, its private decryption token, its `surb_id`, and the associated return rendezvous are single-use. Uncertain delivery burns them. Retrying with the same reply material is forbidden. ### SPHINX-INV-08: No packet-layer fragmentation identity KEMSphinx packets contain no application message identifier, fragment index, or fragment count. Application fragmentation occurs inside the fixed opaque user payload under the owning end-to-end protocol. ### SPHINX-INV-09: Purpose-separated keys and state Mix KEM keys, terminal KEM keys, Noise keys, entry-capsule keys, replay-state protection keys, SURB private tokens, message keys, and storage keys are distinct. Sharing one private key or replay database across roles is forbidden. ### SPHINX-INV-10: Failure never changes topology or profile Failure cannot remove a layer, substitute the entry as a mix, route directly to storage, activate an old profile, send plaintext, or hand a foreign packet to a core parser. ## 5. Terminology and Encoding Terms used in this specification: - **packet profile**: permanent mapping from a numeric identifier to exact KEMSphinx primitives, geometry, commands, epoch rules, limits, and SDK behavior; - **packet epoch context**: the epoch and accepted consensus already bound by the FOG-WIRE connection carrying the packet; - **packet node ID**: fixed 32-byte KEMSphinx routing identifier derived from one consensus-authorized node and one packet profile; - **forward route**: layer 1, layer 2, layer 3, then a courier or native terminal; - **reply route**: layer 3, layer 2, layer 1, then an entry return rendezvous; - **SURB**: a public Single Use Reply Block given to a terminal; - **private reply token**: Composer-only payload decryption material paired with one SURB; - **raw replay tag**: profile-domain-separated digest of the current hop KEM ciphertext; - **stored replay ID**: keyed local representation committed to durable replay state. All integers defined directly by FOG are unsigned and encoded in network byte order. Fixed arrays have exactly the declared length. Reserved and padding bytes are zero after decryption. Parsers reject non-zero reserved bytes, truncation, trailing bytes, duplicate commands, reordered commands, unknown commands, and lengths other than the active profile's exact values. ## 6. Packet Profile Registry Every `packet_profile_id` is a non-zero unsigned 32-bit integer whose meaning is permanent. An identifier MUST NOT be reused for changed bytes, algorithms, command rules, route shape, limits, or behavior. An exact profile record contains at least: ```text [ packet_profile_id, kemsphinx_format_id, kem_suite_id, primitive_suite_id, packet_node_id_hash_id, replay_hash_id, replay_store_mac_id, additional_data, hop_count, route_shape_id, command_registry_id, kem_ciphertext_length, header_mac_length, payload_tag_length, sprp_key_material_length, per_hop_routing_info_length, routing_info_length, header_length, surb_length, sphinx_plaintext_header_length, user_forward_payload_length, forward_payload_length, packet_length, delay_profile_id, maximum_packet_lifetime, epoch_drain_limit, parser_limits_profile_id, sdk_contract_id, conformance_vector_set_id ] ``` Registry metadata MAY label a reviewed profile `alpha`, `active`, `draining`, or `retired`, but that mutable label is not part of the immutable profile meaning. Only the accepted consensus authorizes actual use. An incomplete candidate does not receive a numeric `packet_profile_id`. The profile record is published as an immutable release artifact and its identifier is listed by FOG-PKI. Consensus `active_profile_ids` selects the exact record. Numeric parameters are not operator-tunable. One packet profile MUST be compatible with the FOG-WIRE command-shape registry active on every link that carries it. A consensus containing an incompatible pair is invalid. ## 7. Epoch and Profile Selection The packet bytes do not carry a negotiable profile identifier or epoch. The authenticated FOG-WIRE connection context supplies: - network identity; - epoch; - accepted consensus hash; - link context; - wire profile; - the one packet profile mapped to that epoch and command shape. For a given link context and packet class, one epoch maps to exactly one packet profile. Old-epoch packets continue only on old-epoch connections during the bounded drain interval. A node MUST NOT trial-decrypt an old packet with several epoch keys. A Composer MUST NOT create a packet unless: ```text current_time_upper_bound + maximum_route_delay + relay_and_entry_queue_budget + wire_delivery_budget < packet_epoch_hard_expiry ``` The same check applies to a SURB's expected round trip. A packet or SURB that cannot complete within its epoch budget is not created. ## 8. Core Route Shape ### 8.1 Forward route The only core forward shape is: ```text entry capsule -> L1 -> L2 -> L3 -> terminal ``` The entry capsule is outside this four-hop KEMSphinx packet. Its specification must cryptographically bind the exact packet to the exact L1 packet node ID without revealing that ID to the blind relay. The four KEMSphinx hops are: | Hop | Required role | Required routing result | |---:|---|---| | 0 | mix layer 1 | delay, then layer 2 | | 1 | mix layer 2 | delay, then layer 3 | | 2 | mix layer 3 | delay, then selected terminal | | 3 | courier or native terminal | terminal recipient and payload | ### 8.2 Reply route The only core reply shape is: ```text terminal uses SURB -> L3 -> L2 -> L1 -> entry rendezvous ``` The four KEMSphinx hops are: | Hop | Required role | Required routing result | |---:|---|---| | 0 | mix layer 3 | delay, then layer 2 | | 1 | mix layer 2 | delay, then layer 1 | | 2 | mix layer 1 | delay, then entry | | 3 | entry | return rendezvous plus `surb_id` | The reply sender learns the public first-hop packet node ID contained in the SURB. It does not learn later hops, the destination relay, or the Composer's private reply token. ### 8.3 Eligibility checks The Composer builds routes only from one accepted full consensus. Every hop must have the required role, exact layer, active packet profile, current epoch KEM key, endpoint reachability through the next role, and no effective revocation. The route must also pass the consensus topology and diversity rules. Each online hop independently verifies that the authenticated upstream role, its own role and layer, the next packet node ID, and the next FOG-WIRE context match the consensus. A valid header is not sufficient authorization for a role-invalid edge. ## 9. Packet Node IDs KEMSphinx routing uses a fixed 32-byte `packet_node_id`. It is not a private key and is not a substitute for the PKI `node_id`. For profile `P`, it is derived as: ```text packet_node_id = HASH32( "FOG-SPHINX-NODE-ID-1" || u32be(length(network_id)) || network_id || u32be(P.packet_profile_id) || u32be(length(node_id)) || node_id ) ``` `HASH32` is fixed by `packet_node_id_hash_id`. Consensus validation derives all active packet node IDs and rejects any duplicate. Nodes build an immutable epoch-local lookup table from packet node ID to the exact authorized role, layer, KEM key ID, and link endpoint. Packet node IDs may be logged only in coarse configuration validation. They MUST NOT be logged per packet. ## 10. Routing Commands The core profile uses the maintained Sphinx command model with these exact tags: | Tag | Command | Body length | Meaning | |---:|---|---:|---| | `0x00` | `NULL` | 0 | terminates command parsing; remaining bytes are zero | | `0x01` | `NEXT_NODE` | 64 | 32-byte next node ID and 32-byte next header MAC | | `0x02` | `RECIPIENT` | 32 | opaque terminal recipient or return capability | | `0x03` | `SURB_REPLY` | 16 | random single-use `surb_id` | | `0x80` | `NODE_DELAY` | 4 | unsigned delay in profile-defined units | Unknown tags are invalid. No vendor command range is accepted by core nodes. ### 10.1 Intermediate mix command sequence Every nonterminal mix hop contains exactly: ```text NODE_DELAY || NEXT_NODE || NULL || zero padding || next KEM ciphertext ``` The implementation may build commands in an internal representation, but the encrypted routing bytes and parser result must preserve this semantic order. There is exactly one delay and one next node. Zero delay is valid only if the active cover profile explicitly includes it in the same public distribution used for real and cover packets. The `NODE_DELAY` value is validated before queue insertion. Values outside the active delay profile, arithmetic overflow, or a deadline past epoch expiry cause a uniform drop. ### 10.2 Forward terminal command sequence The terminal hop contains exactly: ```text RECIPIENT || NULL || zero padding || unused zero KEM slot ``` `RECIPIENT` is a random or derived 32-byte capability defined by the terminal contract. It must not be a human address, username, public mailbox name, or application-specific string. The terminal validates it before acting on the payload. ### 10.3 Reply terminal command sequence The entry terminal hop contains exactly: ```text RECIPIENT || SURB_REPLY || NULL || zero padding || unused zero KEM slot ``` Here `RECIPIENT` is an opaque short-lived return-rendezvous capability and `SURB_REPLY` selects the Composer's private reply token. The entry atomically consumes the rendezvous before queueing the returned opaque payload. ### 10.4 Terminal padding The current maintained KEMSphinx geometry reserves one KEM ciphertext-sized tail in every per-hop routing block, including the terminal block. The terminal tail is all zero before routing encryption. FOG does not apply the possible one-ciphertext optimization until a new separately identified profile has complete vectors and interoperability review. ## 11. Forward Plaintext Block After the terminal KEMSphinx unwrap and payload-tag validation, a forward payload has this exact layout: ```text offset length field 0 1 surb_flag 1 1 reserved 2 S surb_slot 2+S U user_payload ``` `reserved` is zero. `S` is the profile's exact `surb_length`. `U` is the exact `user_forward_payload_length`. `surb_flag` is: - `0x00`: no usable SURB; the entire `surb_slot` is zero after decryption; - `0x01`: `surb_slot` contains one valid SURB for the same packet profile and epoch. All other values are invalid. The slot always exists and always occupies the same bytes. Application code receives exactly `U` opaque bytes and an optional validated public SURB. It does not receive short lengths from the packet layer. The 4,096-byte candidate user payload is owned internally by the next protocol. FOG-MESSAGING or FOG-STORAGE defines authentication, actual body length, padding, fragmentation, retries, deduplication, and acknowledgements inside those bytes. ## 12. SURB Contract ### 12.1 Public SURB The public SURB encoding is exactly: ```text prebuilt_kemsphinx_header[header_length] || first_hop_packet_node_id[32] || reply_payload_key_material[sprp_key_material_length] ``` The packet profile and epoch are supplied by the containing authenticated context and the local SURB object. They are not inserted into the opaque SURB and do not change its size. The prebuilt reply path has exactly four hops and ends in the entry terminal commands described in section 10.3. ### 12.2 Private reply token The Composer stores, separately from the public SURB: ```text [ token_version, packet_profile_id, epoch, surb_id, hard_expiry, status, reverse_order_payload_key_material ] ``` For four hops, `reverse_order_payload_key_material` contains five fixed key and IV pairs: one per KEMSphinx hop plus the final reply payload pair. In the candidate geometry this secret field is 320 bytes. The token is secret Composer state. It is encrypted and integrity-protected at rest under the Composer state profile, excluded from logs and routine backups unless the backup design explicitly protects it, and erased after successful use, expiry, cancellation, or uncertain duplicate handling. ### 12.3 Single-use state machine The only valid state transitions are: ```text AVAILABLE -> COMMITTED -> CONSUMED AVAILABLE -> EXPIRED COMMITTED -> BURNED ``` The terminal changes a public SURB from `AVAILABLE` to `COMMITTED` before building or submitting a reply packet. A confirmed local construction error before any packet or KEM operation may return it to `AVAILABLE`; after packet construction or any send attempt, uncertainty results in `BURNED`. The Composer atomically changes the private token to `CONSUMED` before releasing successfully authenticated plaintext to an application. A second return with the same `surb_id` is discarded without another decryption attempt. Applications needing retries provide multiple independently generated SURBs. They never clone a SURB or its private token. ### 12.4 Tagging and compulsion limits SURBs do not remove active-tagging or reply-compulsion risk. Implementations MUST apply the same fixed packet schedule, reply size, and terminal queue policy to successful replies, errors, and cover outcomes. A terminal cannot send arbitrary immediate diagnostic replies outside the cover schedule. ## 13. Replay Protection ### 13.1 Raw replay tag For an authenticated hop, the raw tag is: ```text raw_replay_tag = HASH( "FOG-SPHINX-REPLAY-TAG-1" || network_id || u32be(packet_profile_id) || u64be(epoch) || kem_key_id || current_hop_kem_ciphertext ) ``` Lengths for variable fields are fixed by the active PKI and packet profiles. `HASH` and its output length are fixed by `replay_hash_id`. The current hop KEM ciphertext is the public ciphertext at the front of the current KEMSphinx header, not a future ciphertext hidden in routing data. ### 13.2 Stored replay ID The durable database stores: ```text stored_replay_id = MAC( replay_state_key_epoch, "FOG-SPHINX-REPLAY-STORE-1" || raw_replay_tag ) ``` This local keyed representation reduces direct correlation between a stolen database and previously captured packet headers. It is not a substitute for disk, process, or host protection. `replay_state_key_epoch` is independently generated per node, packet profile, and epoch. It is not derived from a KEMSphinx private key and is never shared with another role or node. ### 13.3 Processing order A hop processes an incoming packet in this order: 1. verify authenticated FOG-WIRE context, exact command body, and packet length; 2. enforce current epoch, profile, role, layer, upstream, and local key ID; 3. parse only fixed header offsets; 4. decapsulate the current KEM ciphertext; 5. derive hop keys and verify the current header MAC; 6. derive the raw replay tag and stored replay ID; 7. atomically check and durably insert the stored replay ID; 8. parse and validate the exact routing command sequence; 9. validate next-hop authorization and delay bounds; 10. transform the packet exactly once; 11. queue the transformed packet or terminal payload; 12. erase per-hop shared secrets and temporary keys. An unauthenticated random ciphertext is not inserted. A header that successfully authenticates is inserted before later command validation, even when the command, next hop, delay, or terminal capability is invalid. This prevents repeated authenticated malformed work from bypassing replay state. ### 13.4 Durable database Replay insertion is a crash-consistent transaction. The packet is not made eligible for forwarding until the write-ahead record or equivalent durable commit succeeds. Group commit is permitted only if scheduling and forwarding wait for the corresponding durable barrier. A keyed in-memory filter MAY avoid many exact lookups, but it is only a front cache. A positive filter result is confirmed against the exact durable set; therefore filter false positives do not discard valid packets. Replay databases are separated by node, role, packet profile, epoch, and KEM key ID. They are excluded from telemetry, snapshots shared across nodes, and ordinary backups. Aggregate counts may be exported only under the observer privacy profile. ### 13.5 Restart and loss On restart, a node verifies database integrity, replay-state key availability, epoch ownership, and committed sequence state before accepting packets. It rebuilds any in-memory filter from the exact durable set. Missing, rolled-back, corrupt, or unverifiable replay state is fail-closed. The node stops packet processing for that profile and epoch. It does not start with an empty cache. Service can resume only from safely restored monotonic state or a new epoch with fresh KEM and replay-state keys. ### 13.6 Retirement An epoch replay database and its local key are retained until: ```text epoch_hard_expiry + maximum_packet_lifetime + maximum_clock_uncertainty + crash_recovery_margin ``` has passed. Retirement erases the replay-state key and removes the database through the deployment's recoverable secure-deletion policy. No node accepts new work merely because an old database still exists. ## 14. KEMSphinx Hop Processing Every mix worker has fixed-size buffers from its active geometry. It does not allocate based on decrypted command values. For a valid intermediate hop it: - decrypts the current routing block; - obtains the authenticated delay and next node; - shifts the routing information according to KEMSphinx; - copies the hidden next-hop KEM ciphertext into the public KEM field; - applies one payload permutation; - commits replay state; - enters the bounded delay queue; - emits the exact transformed packet on the authorized next link. For a valid terminal it: - verifies the terminal command sequence; - verifies the final payload integrity tag; - validates the fixed forward plaintext block for a forward packet, or returns the still SURB-protected payload and `surb_id` for a reply packet; - consumes any terminal or rendezvous capability atomically; - hands only the bounded opaque object to the owning role contract. No mix exposes whether failure was KEM, MAC, replay, command, delay, route, queue, payload tag, recipient, or epoch. Remote behavior is the same uniform drop class and is subject to the cover schedule. ## 15. Delay Contract `NODE_DELAY` contains a 32-bit count in the unit fixed by the active delay profile. Implementations convert using checked arithmetic and a monotonic clock. The delay profile fixes at least: ```text [ delay_profile_id, unit_nanoseconds, maximum_encoded_delay, maximum_per_hop_delay, maximum_route_delay, sampling_distribution_id, quantization_rule, queue_deadline_rule, cover_schedule_profile_id ] ``` The Composer samples every mix delay from this authenticated distribution. Operators do not alter it locally. Mixes validate the encoded value but do not resample it. Queue pressure does not convert delayed traffic to immediate traffic; overload follows the profile's uniform drop or shutdown behavior. The initial distribution and numeric limits remain simulation outputs. No anonymity or latency claim follows from this structural specification. `FOG-SIMULATION.md` records an initial 500 ms versus 5 second exponential-delay sensitivity comparison. It selects neither value. The longer value improved local pool overlap under one high-cover scenario while increasing modeled latency by roughly an order of magnitude; formal end-to-end and operational evidence remains open. ## 16. Padding and Fragmentation ### 16.1 Packet padding Routing blocks, forward SURB slots, user payloads, reply payloads, and unused fields always occupy their full profile lengths. Plain structural padding is zero before the applicable cryptographic layer. Random bytes are used only where the selected reviewed construction requires randomness. Application code MUST NOT create a shorter KEMSphinx payload. It supplies an exact fixed-size inner envelope whose internal padding is authenticated by the owning end-to-end protocol. ### 16.2 Application fragmentation Messages larger than `user_forward_payload_length` are fragmented by FOG-MESSAGING or FOG-STORAGE before KEMSphinx construction. Fragment metadata is inside the end-to-end protected envelope. Each fragment becomes an independent fixed-size KEMSphinx packet with independent route randomness, KEM ciphertexts, replay tags, and optional SURB. KEMSphinx does not retransmit or deduplicate fragments. Reusing a packet for a retry is forbidden. A retry constructs a fresh packet under the application protocol's idempotency rules. ### 16.3 FOG-WIRE fragmentation `PACKET_FORWARD` and the packet portion released from `PACKET_SUBMIT` are one logical FOG-WIRE message with an exact KEMSphinx packet body. FOG-WIRE may split that logical message across its fixed DATA records when the active wire profile requires it. Wire fragments are link-local and are fully reassembled, bounded, and authenticated before KEMSphinx parsing. They are never individually queued, replayed, forwarded, or stored as KEMSphinx packets. ## 17. FOG-WIRE Command Bodies This document owns these logical command shapes: ```text PACKET_FORWARD_BODY = kemsphinx_packet[packet_length] PACKET_RETURN_BODY = surb_id[16] || returned_payload[payload_tag_length + forward_payload_length] ``` `PACKET_FORWARD_BODY` is used for entry-to-L1, mix-to-mix, and L3-to-terminal links. Link context and direction determine which role pair is legal. `PACKET_RETURN_BODY` is created only after the entry completes the final reply hop and atomically consumes the return rendezvous. The returned payload remains protected by the Composer's private reply token. The relay cannot decrypt or modify it successfully. `PACKET_SUBMIT_BODY` remains owned by the entry-capsule specification because it must hide and bind the first internal hop from the blind relay. A compatible FOG-WIRE command-shape registry has compile-time exact body limits for these commands. Generic byte-string RPCs are not conforming. ## 18. Candidate Geometry ### 18.1 Status The following profile is a calculated candidate: ```text name = FOG-SPHINX-CANDIDATE-MLKEM768-X25519-1 packet_profile_id = UNASSIGNED candidate_status = geometry-only ``` The candidate name is permanently bound to this calculated geometry. It is not a complete packet profile because final primitive IDs, dependency revisions, delay limits, replay lifetime, and conformance vector IDs are still unresolved. A numeric packet profile ID is assigned only after every field in section 6 is frozen. It will not reuse an identifier from another candidate. This candidate MUST NOT be placed in a claim-bearing public consensus until section 24's activation gates pass. `FOG-CRYPTO-SUITES.md` admits the exact calculated construction to complete-packet benchmarking but does not activate it. The unresolved non-KEM primitives and implementation evidence keep the complete profile non-active. No active FOG implementation benchmark exists for this geometry. Every future implementation MUST enforce the exact path, packet, payload, SURB, encrypted reply, and reply-key lengths at the FOG boundary before cryptographic processing. This requirement follows from the profile contract itself, not from an archived external implementation experiment. ### 18.2 Candidate primitive inputs The geometry calculation uses: - KEMSphinx with one KEM ciphertext per hop; - exactly four hops; - hybrid `MLKEM768-X25519` built by the maintained HPQC security-preserving split-PRF combiner; - component and ciphertext concatenation order of X25519 hashed-ElGamal KEM first, then ML-KEM-768, matching the evaluated HPQC registry despite the display name; the split-PRF is order-sensitive; - ML-KEM-768 ciphertext length of 1,088 bytes; - X25519 hashed-ElGamal KEM ciphertext length of 32 bytes; - combined KEM ciphertext length of 1,120 bytes; - 32-byte packet node IDs; - 32-byte header MACs; - 16-byte SURB IDs; - 32-byte payload integrity tags; - 48-byte SPRP keys and 16-byte SPRP IVs; - two additional-data bytes fixed to `0x0000` for compatibility with the evaluated maintained KEMSphinx format; - a two-byte forward plaintext header; - a 4,096-byte user forward payload; - one fixed SURB slot in every forward plaintext block. The primitive suite currently evaluated with this geometry includes the maintained Katzenpost KDF, header MAC, header stream, and AEZ-based payload SPRP parameterization. Geometry compatibility does not constitute approval of that primitive suite. In particular, the exact dependency revisions, side-channel behavior, licensing, AEZ usage, deterministic vectors, and independent review remain activation gates. ### 18.3 Exact calculation ```text KEM_CIPHERTEXT_LENGTH = 1088 + 32 = 1120 NEXT_NODE_LENGTH = 1 + 32 + 32 = 65 SURB_REPLY_LENGTH = 1 + 16 = 17 PER_HOP_ROUTING_INFO = 65 + 17 + 1120 = 1202 ROUTING_INFO_LENGTH = 4 * 1202 = 4808 HEADER_LENGTH = 2 + 1120 + 4808 + 32 = 5962 SPRP_KEY_MATERIAL = 48 + 16 = 64 SURB_LENGTH = 5962 + 32 + 64 = 6058 FORWARD_PAYLOAD = 2 + 6058 + 4096 = 10156 PACKET_LENGTH = 5962 + 32 + 10156 = 16150 PRIVATE_REPLY_KEYS = (4 + 1) * 64 = 320 ``` ### 18.4 Candidate geometry table | Field | Bytes | |---|---:| | Hop count | 4 hops | | Additional data | 2 | | KEM ciphertext | 1,120 | | Per-hop routing information | 1,202 | | Routing information | 4,808 | | Header MAC | 32 | | Header | 5,962 | | Payload tag | 32 | | Public SURB | 6,058 | | Forward plaintext header | 2 | | User forward payload | 4,096 | | Forward payload | 10,156 | | Complete KEMSphinx packet | 16,150 | | Private reply key material | 320 | Every arithmetic value is a protocol constant for this candidate. Runtime configuration cannot change it. ## 19. Key Lifecycle Every mix and terminal has an independently generated KEMSphinx key for each authorized packet profile and epoch. Descriptors bind the public key, key ID, purpose, owner node, role, layer, profile, and validity interval. Private keys: - are generated with an approved operating-system randomness source; - are written only to role-local protected storage; - are loaded only by the owning role process; - are never copied to another layer, co-located role, authority, relay, or observer; - are never used for Noise, entry capsules, storage, signatures, or messages; - remain available only for the exact old-epoch drain interval; - are erased after packet and replay retirement conditions both hold. Key generation and persistence are crash-safe. A descriptor is not published until the private key is durably available to its owner. A node never creates a fresh private key under an already published key ID. KEM decapsulation failures, malformed public keys, and component failures in a hybrid KEM are fatal to that packet. A hybrid implementation must reject incorrect component lengths before component decapsulation and must not reveal which component failed. ## 20. Parser and Resource Limits Before cryptographic work, an implementation enforces: - exact logical message length; - exact packet profile from connection context; - one bounded packet buffer; - no recursive, compressed, map-based, or self-describing packet data; - no allocation based on routing commands; - no profile or algorithm name supplied by the peer; - per-connection, per-peer, per-key, and global cryptographic work budgets; - bounded queues and deadlines. After decryption, it enforces: - exact command count, type, order, and zero padding; - one legal next role and layer; - delay bounds and epoch completion bounds; - terminal capability length and one-time state; - exact forward block flags and zero reserved bytes; - exact payload and SURB lengths. Memory containing shared secrets, per-hop keys, private reply keys, and decrypted routing blocks is cleared promptly using the reviewed library's supported mechanism. Memory clearing is defense in depth and does not replace process isolation. ## 21. Logging and Observability Core roles MUST NOT log: - packet bytes or packet digests; - raw replay tags or stored replay IDs; - KEM ciphertexts or shared secrets; - `surb_id`, SURBs, private reply tokens, or rendezvous capabilities; - per-packet routes, next-hop IDs, delay values, or fine timing; - terminal recipient capabilities or user payloads. Permitted local diagnostics are coarse reason counters, queue occupancy buckets, bounded latency histograms, replay database health state, and profile-level totals under FOG-OBSERVER privacy rules. Operators cannot enable packet tracing on a claim-bearing profile. Test builds may use deterministic vectors and verbose traces only with public test keys and synthetic packets. Such builds and keys are rejected by production configuration. ## 22. SDK Boundary The stable public SDK exposes typed operations, not raw cryptographic knobs. It contains at least: ```text ValidatePacketProfile(profile, consensus_context) DerivePacketNodeID(profile, node_id) BuildForwardRoute(consensus, terminal, rng) BuildReplyRoute(consensus, rendezvous, surb_id, rng) CreateSURB(profile, epoch, reply_route, rng) CreateForwardPacket(profile, route, fixed_payload, optional_surb, rng) CreateReplyPacket(profile, public_surb, fixed_payload) UnwrapOneHop(profile, epoch_context, private_key, exact_packet) DecryptSURBReply(private_token, surb_id, exact_returned_payload) ``` The SDK does not expose: - arbitrary hop counts for core profiles; - operator-selected algorithms or geometry; - raw private-key serialization through ordinary application APIs; - reuse or cloning of SURBs or reply tokens; - packet parsing without an authenticated profile and epoch context; - automatic foreign-profile detection; - application-dependent packet sizes. SURB and private-token types are move-only or guarded by an atomic persistent state abstraction. Copyable byte slices are not the primary API. The same reference codec and vector set are used by Composer, entry, mix, terminal, relay import, simulator, and conformance tools. Each executable still imports only the operations needed by its role. ## 23. Conformance Evidence Each packet profile release includes machine-readable vectors generated with fixed public test entropy. The vector manifest records dependency versions, source revision, profile record hash, and generation command. The required vector set covers: 1. packet node ID derivation; 2. hybrid KEM component key and ciphertext lengths; 3. complete candidate geometry arithmetic; 4. every routing command encoding and invalid tag; 5. zero padding and terminal unused KEM slot; 6. one complete four-hop forward packet at every unwrap; 7. one complete four-hop SURB reply at every unwrap and final decryption; 8. replay tags at each hop and durable duplicate rejection; 9. changed ciphertext, MAC, command, padding, payload tag, and SURB failures; 10. wrong profile, epoch, role, layer, route length, and upstream failures; 11. maximum and invalid delay values; 12. SURB state transitions, double use, expiry, and uncertain send; 13. replay database restart, rollback, corruption, and retirement; 14. FOG-WIRE fragmentation and exact logical body reassembly; 15. profile overlap with separate old and new epoch connections; 16. cross-implementation byte equality. Property and fuzz tests additionally cover all fixed-offset parsers, truncations at every byte boundary, trailing bytes, unknown commands, command reordering, integer boundaries, malformed KEM component lengths, and queue resource limits. At least two independently integrated implementations must reproduce the complete vectors before a claim-bearing profile becomes active. Calling the same library through two thin wrappers is not independent evidence. ## 24. Activation Gates `FOG-SPHINX-CANDIDATE-MLKEM768-X25519-1` remains non-active until all of the following are complete: - reproducible create, unwrap, SURB, and replay benchmarks on every supported hardware class using the exact 16,150-byte geometry; the first older x86-64 create, unwrap, and SURB baseline is complete, while replay and the remaining hardware classes are open; - memory, queue, storage, and maximum-throughput budgets for every role; - dependency revision pinning, license review, and reproducible builds; - review of the ML-KEM-768 implementation and X25519 adapter; - confirmation that the exact security-preserving combiner meets the KEMSphinx hybrid-KEM requirement; - review of KDF, MAC, stream, payload SPRP, fragile payload-tag construction, and side-channel behavior; - complete deterministic vectors and negative corpus; - fuzzing and restart-safe replay tests; - simulator evidence for packet size, delay, cover traffic, n-1, and long-term disclosure behavior; - compatibility verification with the selected maintained KEMSphinx target; - independent protocol and implementation review; - a separately documented decision promoting an exact immutable record to `alpha` and later `active`. Failure of a gate produces a newly named geometry candidate or a newly numbered complete packet profile, as applicable. It does not silently change this candidate. ## 25. Profile Transitions A packet-profile transition follows the FOG-PKI preannouncement and bounded overlap rules. During overlap: - new work uses the profile mapped to the new epoch; - old work drains only on old-epoch FOG-WIRE connections; - each profile has separate KEM keys, replay-state keys, replay databases, queues, command-shape registry, and SDK object types; - a packet is never converted in place from one profile to another; - a failure in the new profile does not reactivate the old profile; - minimum accepted profile state is monotonic. After hard expiry, old connections close, old packets and SURBs are rejected, private reply tokens expire, replay state completes its retention period, and private KEM keys are erased. Emergency retirement can stop creation and acceptance immediately, but it cannot downgrade. In-flight delivery may be lost. ## 26. External Sphinx-Family Bridges Foreign Sphinx, KEMSphinx, Katzenpost, Nym, YAMN, SMTP, NNTP, and other network formats never enter a core packet parser. A bridge has: - a separate `fog-bridge-*` executable and service identity; - separate ports, FOG-WIRE link context, keys, writable state, queues, replay domains, metrics, and deployment policy; - one explicitly named foreign profile and version; - strict parsing and resource bounds for that profile only; - no access to Composer plaintext or core private keys; - a fresh core packet constructed after policy validation, never a header reinterpretation or in-place conversion; - explicit disclosure as a correlation and availability point. A bridge terminates one network anonymity context and originates another. It cannot claim end-to-end mix-path unlinkability across the boundary, even when the application payload remains end-to-end encrypted. Automatic port sharing, packet sniffing, trial parsing, or silent downgrade between core and foreign profiles is forbidden. ## 27. Failure Matrix | Condition | Required behavior | |---|---| | Wrong packet length | reject before KEM work | | Wrong epoch or profile context | reject, no alternate-key trial | | KEM decapsulation failure | uniform drop | | Header MAC failure | uniform drop, no durable replay insertion | | Authenticated duplicate | uniform drop after exact replay lookup | | Authenticated invalid command | durable replay insert, then uniform drop | | Invalid next role or layer | durable replay insert, then uniform drop | | Invalid delay or expired deadline | durable replay insert, then uniform drop | | Replay commit failure | stop affected profile and epoch processing | | Replay state missing or rolled back | fail closed until safe restore or new epoch | | Delay queue overload | profile-defined uniform drop or role shutdown | | Final payload tag failure | uniform drop, no terminal action | | Invalid terminal capability | uniform drop after one-time-state rules | | SURB reused or expired | discard without constructing another reply | | Private token missing or consumed | discard returned payload | | Old profile failure during transition | do not fall back or extend expiry | | Foreign packet on core port | reject before foreign parsing | Local diagnostics use coarse stable reason classes. Remote peers receive no fine-grained error response. ## 28. Threat Traceability | Threat-model concern | Packet-profile response | |---|---| | `TM-NET-01` traffic correlation | one fixed geometry, four hops, authenticated delay and cover profile | | `TM-NET-02` low anonymity set | no packet-format claim; simulator and deployment gates remain required | | `TM-NET-03` compromised mixes | stratified route, one transform, role validation, purpose-separated keys | | `TM-NET-04` replay and compulsion | durable per-hop replay, single-use SURBs and rendezvous state | | `TM-NET-05` tagging | authenticated header and payload processing, uniform terminal failure | | `TM-NET-06` profile fingerprinting | consensus-selected immutable profiles, no autodetection or negotiation | | `TM-PKI-02` stale consensus | epoch-bound packet context and monotonic transition state | | `TM-ROLE-01` role collapse | separate KEM keys, replay state, ports, commands, and bridge processes | | `TM-ROLE-02` relay knowledge | entry capsule hides L1; relay handles only opaque fixed packets | | `TM-ROLE-03` terminal exposure | terminal gets fixed opaque application payload, not source address or full route | | `TM-CRYPTO-01` primitive misuse | reviewed parameterized library, immutable suite record, activation gates | | `TM-CRYPTO-02` key reuse | role, purpose, profile, and epoch separation | | `TM-AVAIL-01` resource exhaustion | fixed buffers, crypto budgets, durable replay, bounded queues and failures | ## 29. Residual Risks Even a conforming implementation remains exposed to: - traffic analysis from timing, volume, routes, endpoints, and sparse use; - n-1 and active-delay attacks by sufficiently placed malicious nodes; - denial of service through connection, KEM, replay-store, queue, or terminal exhaustion within residual budgets; - endpoint or Composer compromise; - malicious or colluding entry, mix, terminal, relay, storage, and authority operators within the threat model's residual cases; - reply tagging and compulsion not eliminated by SURBs; - implementation, dependency, side-channel, randomness, and erasure defects; - correlation introduced by external bridges; - loss caused by fail-closed replay and single-use reply behavior; - incorrect anonymity conclusions from a local PoC or low-traffic network. These risks must appear in deployment documentation and public claims. ## 30. References - George Danezis and Ian Goldberg, *Sphinx: A Compact and Provably Secure Mix Format*, 2009: - Katzenpost, *The KEMSphinx Cryptographic Packet Format*: - Katzenpost, *The Sphinx Cryptographic Packet Format*: - Katzenpost, *The Katzenpost Mix Network Wire Protocol*: - Katzenpost, *Katzenpost Mix Network Replay Detection*: - Federico Giacon, Felix Heuer, and Bertram Poettering, *KEM Combiners*, 2018: - NIST, *FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard*, 2024: - RFC 7748, *Elliptic Curves for Security*: - FOG Composer protocol: `FOG-COMPOSER.md` - FOG cryptographic suite evaluation: `FOG-CRYPTO-SUITES.md` - FOG traffic and topology simulation: `FOG-SIMULATION.md` ## 31. Completion Checklist Before implementation work for an active packet profile begins, reviewers must be able to answer yes to all of these questions: - Is one immutable profile selected by authenticated consensus and epoch context without packet guessing? - Are both directions exactly four KEMSphinx hops with the entry outside the forward route? - Are packet, header, routing, payload, SURB, and reply-token sizes proven by arithmetic and tests? - Are command sequences exact, padded, role-valid, and parser-bounded? - Is replay state durable before forwarding and fail-closed after rollback or loss? - Are SURBs, private tokens, IDs, and rendezvous capabilities atomic and single-use? - Is application fragmentation inside end-to-end protection and independent of packet size? - Are KEM, replay, Noise, entry, storage, and message keys purpose-separated? - Do FOG-WIRE command shapes and packet geometry agree exactly? - Are complete vectors, fuzzing, benchmarks, simulation, and independent review available for the exact dependency set? - Are external formats isolated in separate bridge processes and disclosed as correlation points? - Do failures stop safely without topology, profile, or plaintext fallback?