reticiulum-specification/audits/resource-tier1-rns-1.2.4.md
John Poole 7433063bfb Completed the Resource three-tier work unit.
Added deterministic `resources.json` and `regen_resources.py`.
Extended `verify_resource.py` with receiver assembly/proof and requested negative cases.
Updated specification, audit, status, and tool documentation.
Fixed an unrelated nondeterministic wrong-ticket test in verify_stamps.py.
Confirmed vector regeneration is byte-identical.
Confirmed no tracked reliance on specenv or user-specific paths.
git diff --check: pass.
Complete pinned suite: 16 passed, 0 failed.
2026-06-08 13:38:24 -07:00

6.1 KiB

Tier 1 Audit: Resource Fragmentation

Question: Does SPEC.md §10 accurately describe the wire-visible Resource fragmentation behavior of upstream RNS 1.2.4?

Evidence baseline:

  • RNS package: rns==1.2.4
  • Source: installed package paths RNS/Resource.py, RNS/Packet.py, RNS/Link.py, and LXMF/LXMessage.py
  • Audit date: 2026-06-08

This began as a Tier 1 source-analysis report. The completed Tier 2 checks live in tools/verify_resource.py and test-vectors/resources.json; the confirmed F1-F6 corrections were promoted into SPEC.md §10 on 2026-06-08.

Confirmed Core Model

The central §10 model matches RNS 1.2.4:

  • Resource prepends a throwaway 4-byte prefix, encrypts the complete stream once with the Link, then slices the ciphertext into parts (Resource.py:404-428, 453-472; Packet.py:201-204).
  • Advertisement r is a distinct 4-byte salt used for the resource hash and per-part map hashes (Resource.py:440-443, 505-506).
  • Resource integrity is SHA256(uncompressed_segment_plaintext || r), and the expected proof is SHA256(uncompressed_segment_plaintext || resource_hash) (Resource.py:440-443, 681-695, 752-758).
  • RESOURCE_REQ may carry requested part hashes while also requesting a hashmap continuation; the sender fulfils parts before emitting RESOURCE_HMU (Resource.py:994-1027, 1027-1064).
  • RESOURCE parts are matched only within the receiver's current window, while sender lookup is bounded by COLLISION_GUARD_SIZE (Resource.py:863-890, 999-1010).

Findings Requiring Correction or Clarification

F1 — Direct LXMF threshold is 319 bytes, not approximately 360

SPEC.md §10 introduction says Resource carries an LXMF body larger than approximately 360 bytes. With default RNS 1.2.4 / LXMF 0.9.7 parameters:

RNS.Link.MDU                    = 431
LXMessage.LXMF_OVERHEAD         = 112
LXMessage.LINK_PACKET_MAX_CONTENT = 319

LXMessage.pack() selects Resource representation for DIRECT delivery when content_size > 319 (LXMF/LXMessage.py:80-89, 414-421).

Recommended Tier 3 correction: say 319 bytes with default parameters, while noting that the threshold derives from Link MDU and may vary with protocol parameters.

F2 — Resource is not the only possible way to carry larger application data

The §10 introduction calls Resource "the only way" to carry payloads exceeding one Link packet. Resource is the standard RNS mechanism used by LXMF, Link REQUEST/RESPONSE, and rncp, but applications can also stream or sequence data over Channel or their own Link DATA protocol.

Recommended Tier 3 correction: replace "the only way" with "the standard RNS mechanism used by LXMF, REQUEST/RESPONSE, and file-transfer utilities".

F3 — Advertisement d is total logical-resource size

For multi-segment resources, advertisement field d is resource.total_size, the total uncompressed logical transfer size including metadata, not the plaintext size of the advertised segment (Resource.py:281-314, Resource.py:1281-1283).

Each segment still has its own:

  • t: encrypted transfer size for this segment
  • n: part count for this segment
  • h: integrity hash for this segment
  • r: salt for this segment

Recommended Tier 3 correction: define d as total logical-resource size and explicitly distinguish it from the current segment's uncompressed plaintext length, which is not directly advertised.

F4 — RESOURCE_RCL is not a general receiver-side cancel notification

Resource.reject(advertisement_packet) sends RESOURCE_RCL (Resource.py:154-163). A corrupt receiver also calls reject() and tears down the Link (Resource.py:1081-1084).

However, ordinary receiver-side Resource.cancel() only removes the incoming resource locally; it does not send RESOURCE_RCL (Resource.py:1086-1097). The current §10.9 wording implies either side can always notify cancellation on the wire.

Recommended Tier 3 correction: describe RESOURCE_RCL as advertisement rejection / corrupt-resource rejection. Do not claim ordinary receiver cancel always emits it.

F5 — Resource-part packet storage wording is imprecise

SPEC.md §10.2 says packed wire bytes are stored in parts[i]. Upstream stores pre-packed RNS.Packet objects in parts; each packet's data is the raw ciphertext slice and its raw field is the packed Reticulum packet (Resource.py:450-472).

Recommended Tier 3 clarification: distinguish the stored Packet object, its Resource body (part.data), and complete Reticulum wire packet (part.raw).

F6 — Pinned-source mismatch in §10.7 citation

The exhausted-REQ callout cites RNS 1.2.9 while this repository is pinned to RNS 1.2.4. The behavior is already present in RNS 1.2.4 (Resource.py:994-1064).

Recommended Tier 3 correction: cite the pinned 1.2.4 behavior first; retain a later-version note only when documenting an actual version change.

Tier 2 Verifier Scope

The focused verifier is implemented in tools/verify_resource.py and avoids a live threaded transfer. It covers:

  1. Construct a Resource with a deterministic fake Link encryption key, fixed throwaway prefix, and fixed advertisement r.
  2. Verify whole-stream encryption occurs before slicing.
  3. Verify Resource-part Packet bodies are raw slices and are not packet-level re-encrypted.
  4. Verify advertisement dictionary fields, flags, and hashmap first segment.
  5. Verify hash, truncated_hash, expected_proof, and map-hash formulas.
  6. Verify RESOURCE_REQ parsing for both normal and exhausted-with-parts forms, including simultaneous part fulfilment and RESOURCE_HMU generation.
  7. Verify receiver assembly strips the throwaway prefix, decrypts once, validates the hash, and emits the expected RESOURCE_PRF bytes.
  8. Verify a multi-segment fixture proving d remains total logical size while t, n, h, and r describe the current segment.
  9. Verify negative cases: malformed ADV, wrong r, corrupt part, invalid HMU boundary, and oversized decompression.

The deterministic fixture is regenerated by tools/regen_resources.py. Tier 2 Resource work is complete for this audit scope; confirmed claims have been promoted into SPEC.md and the Resource flow documents.