Worked for 4m 50s

Completed the destination-routed DATA and reverse-PROOF three-tier unit.

Key findings:

Intermediate relays retarget HEADER_2; the final relay delivers HEADER_1.
Each relay records independent reverse-table state.
PROOF destination hashes remain invariant across transport transformations.
A wrong-interface PROOF consumes the reverse entry before being dropped.
REVERSE_TIMEOUT is 480 seconds, not 30 seconds.
Added Tier 1 audit, two-relay flow, deterministic vectors, regenerator, and runtime verifier. Corrected affected specification, flows, playbook, and status documentation.

Verification:

Deterministic regeneration: identical SHA-256
Full pinned suite: 22 passed, 0 failed
git diff --check: passed
No commit created.
This commit is contained in:
John Poole 2026-06-08 18:32:26 -07:00
commit 9c3b628c6a
14 changed files with 578 additions and 16 deletions

View file

@ -0,0 +1,111 @@
"""
Regenerator for test-vectors/transport-data.json.
Builds a deterministic two-relay opportunistic DATA and returning PROOF path
from the existing LXMF and identity vectors.
"""
from __future__ import annotations
import json
import os
import RNS
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LXMF_PATH = os.path.join(REPO_ROOT, "test-vectors", "lxmf.json")
IDS_PATH = os.path.join(REPO_ROOT, "test-vectors", "identities.json")
OUT_PATH = os.path.join(REPO_ROOT, "test-vectors", "transport-data.json")
def load_json(path: str):
with open(path, "r", encoding="utf-8") as input_file:
return json.load(input_file)
def header_two(raw: bytes, transport_id: bytes) -> bytes:
flags = (
(RNS.Packet.HEADER_2 << 6)
| (RNS.Transport.TRANSPORT << 4)
| (raw[0] & 0x0F)
)
return bytes([flags, raw[1]]) + transport_id + raw[2:]
def packet(raw: bytes) -> RNS.Packet:
parsed = RNS.Packet(None, raw)
if not parsed.unpack():
raise RuntimeError("generated invalid packet")
return parsed
def main() -> None:
lxmf = load_json(LXMF_PATH)["vectors"][0]
identities = load_json(IDS_PATH)["vectors"]
alice = next(item for item in identities if item["label"] == "alice")
bob = next(item for item in identities if item["label"] == "bob")
relay_one = bytes.fromhex(alice["expected"]["identity_hash_hex"])
relay_two = bytes.fromhex(bob["expected"]["identity_hash_hex"])
destination_hash = bytes.fromhex(
lxmf["expected"]["fields_layout"]["destination_hash_hex"]
)
ciphertext = bytes.fromhex(lxmf["expected"]["token_ciphertext_hex"])
header_one_data = (
bytes([(RNS.Destination.SINGLE << 2) | RNS.Packet.DATA, 0])
+ destination_hash
+ bytes([RNS.Packet.NONE])
+ ciphertext
)
inbound_relay_one = header_two(header_one_data, relay_one)
forwarded_relay_one = (
inbound_relay_one[:1] + b"\x01" + relay_two + inbound_relay_one[18:]
)
delivered_header_one = header_one_data[:1] + b"\x02" + header_one_data[2:]
proof_destination = packet(header_one_data).getTruncatedHash()
implicit_proof_body = bytes.fromhex("ab" * 64)
proof = (
bytes([(RNS.Destination.SINGLE << 2) | RNS.Packet.PROOF, 0])
+ proof_destination
+ bytes([RNS.Packet.NONE])
+ implicit_proof_body
)
vector = {
"_about": (
"Two-relay ordinary DATA and reverse-table PROOF fixtures derived "
"from the deterministic opportunistic LXMF vector."
),
"inputs": {
"lxmf_vector_label": lxmf["label"],
"relay_one_identity_label": "alice",
"relay_one_identity_hash_hex": relay_one.hex(),
"relay_two_identity_label": "bob",
"relay_two_identity_hash_hex": relay_two.hex(),
},
"expected": {
"destination_hash_hex": destination_hash.hex(),
"proof_destination_hash_hex": proof_destination.hex(),
"origin_header1_data_raw_hex": header_one_data.hex(),
"relay_one_inbound_header2_data_raw_hex": inbound_relay_one.hex(),
"relay_one_forwarded_header2_data_raw_hex": forwarded_relay_one.hex(),
"relay_two_delivered_header1_data_raw_hex": delivered_header_one.hex(),
"destination_proof_raw_hex": proof.hex(),
"relay_two_forwarded_proof_raw_hex": (proof[:1] + b"\x01" + proof[2:]).hex(),
"relay_one_forwarded_proof_raw_hex": (proof[:1] + b"\x02" + proof[2:]).hex(),
},
"rns_version_at_generation": RNS.__version__,
"lxmf_version_at_generation": "0.9.7",
"generator_script": "tools/regen_transport_data.py",
"verifies_spec_sections": ["2.3", "6.5", "12.2", "12.5.3"],
}
with open(OUT_PATH, "w", encoding="utf-8") as output_file:
json.dump(vector, output_file, indent=2)
output_file.write("\n")
print(f"Wrote {OUT_PATH}")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,288 @@
"""
Verifier for ordinary DATA and PROOF routing through transport relays.
Exercises a deterministic two-relay path against upstream RNS 1.2.4
Transport.inbound. This distinguishes path_table forwarding from link_table
forwarding and verifies the one-shot reverse_table return path.
"""
from __future__ import annotations
import json
import os
import sys
import tempfile
import time
import RNS
from RNS import Transport
from RNS.Transport import IDX_RT_OUTB_IF, IDX_RT_RCVD_IF, IDX_RT_TIMESTAMP
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
VECTORS_PATH = os.path.join(REPO_ROOT, "test-vectors", "transport-data.json")
IDS_PATH = os.path.join(REPO_ROOT, "test-vectors", "identities.json")
def fail(message: str) -> None:
print(f"FAIL: {message}")
sys.exit(1)
def load_json(path: str):
with open(path, "r", encoding="utf-8") as input_file:
return json.load(input_file)
def init_minimal_rns():
config_dir = tempfile.mkdtemp(prefix="rns-verify-transport-data-")
config_path = os.path.join(config_dir, "config")
with open(config_path, "w", encoding="utf-8") as config:
config.write("[reticulum]\nenable_transport = No\nshare_instance = No\n")
return RNS.Reticulum(configdir=config_dir, loglevel=0)
class FakeInterface:
OUT = True
IN = True
HW_MTU = RNS.Reticulum.MTU
AUTOCONFIGURE_MTU = True
FIXED_MTU = True
bitrate = 1_000_000
mode = RNS.Interfaces.Interface.Interface.MODE_FULL
def __init__(self, name: str):
self.name = name
def __str__(self):
return self.name
def clear_state() -> None:
Transport.packet_hashlist = set()
Transport.packet_hashlist_prev = set()
Transport.path_table.clear()
Transport.reverse_table.clear()
Transport.link_table.clear()
Transport.destinations_map.clear()
Transport.receipts.clear()
Transport.local_client_interfaces.clear()
def seed_path(destination_hash: bytes, next_hop: bytes, hops: int, outbound_if) -> None:
Transport.path_table[destination_hash] = [
time.time(), next_hop, hops, time.time() + 60, [], outbound_if, None,
]
def expect_forward(captured: list, interface, raw: bytes, label: str) -> None:
if len(captured) != 1:
fail(f"{label}: expected one forwarded packet, got {len(captured)}")
if captured[0][0] is not interface:
fail(f"{label}: wrong outbound interface")
if captured[0][1] != raw:
fail(f"{label}: forwarded bytes mismatch")
def packet(raw: bytes) -> RNS.Packet:
parsed = RNS.Packet(None, raw)
if not parsed.unpack():
fail("could not unpack fixture")
return parsed
def check_reverse_entry(entry: list, ingress, egress, label: str) -> None:
if entry[IDX_RT_RCVD_IF] is not ingress or entry[IDX_RT_OUTB_IF] is not egress:
fail(f"{label}: reverse-table interfaces mismatch")
if entry[IDX_RT_TIMESTAMP] > time.time():
fail(f"{label}: reverse-table timestamp is in the future")
def verify_data_path(vector: dict, relay_one_identity, relay_two_identity,
origin_if, between_if, destination_if, captured: list) -> tuple[list, list]:
expected = vector["expected"]
destination_hash = bytes.fromhex(expected["destination_hash_hex"])
proof_hash = bytes.fromhex(expected["proof_destination_hash_hex"])
Transport.identity = relay_one_identity
seed_path(destination_hash, relay_two_identity.hash, 2, between_if)
Transport.inbound(
bytes.fromhex(expected["relay_one_inbound_header2_data_raw_hex"]), origin_if
)
expect_forward(
captured, between_if,
bytes.fromhex(expected["relay_one_forwarded_header2_data_raw_hex"]),
"intermediate relay DATA",
)
if proof_hash not in Transport.reverse_table:
fail("intermediate relay did not create reverse-table entry")
relay_one_reverse = Transport.reverse_table[proof_hash]
check_reverse_entry(relay_one_reverse, origin_if, between_if, "intermediate relay")
captured.clear()
Transport.packet_hashlist.clear()
Transport.packet_hashlist_prev.clear()
Transport.path_table.clear()
Transport.reverse_table.clear()
Transport.identity = relay_two_identity
seed_path(destination_hash, destination_hash, 1, destination_if)
Transport.inbound(
bytes.fromhex(expected["relay_one_forwarded_header2_data_raw_hex"]), between_if
)
expect_forward(
captured, destination_if,
bytes.fromhex(expected["relay_two_delivered_header1_data_raw_hex"]),
"last-hop relay DATA",
)
if proof_hash not in Transport.reverse_table:
fail("last-hop relay did not create reverse-table entry")
relay_two_reverse = Transport.reverse_table[proof_hash]
check_reverse_entry(relay_two_reverse, between_if, destination_if, "last-hop relay")
forms = [
bytes.fromhex(expected["origin_header1_data_raw_hex"]),
bytes.fromhex(expected["relay_one_inbound_header2_data_raw_hex"]),
bytes.fromhex(expected["relay_one_forwarded_header2_data_raw_hex"]),
bytes.fromhex(expected["relay_two_delivered_header1_data_raw_hex"]),
]
hashes = {packet(raw).getTruncatedHash() for raw in forms}
if hashes != {proof_hash}:
fail("DATA proof destination changed across header/hop transformations")
print("PASS S12.2 two-relay DATA path retargets HEADER_2 then delivers HEADER_1")
print("PASS S6.5/S12.2.5 proof destination is invariant and each relay records reverse state")
return relay_one_reverse, relay_two_reverse
def verify_proof_path(vector: dict, relay_one_identity, relay_two_identity,
origin_if, between_if, destination_if, captured: list,
relay_one_reverse: list, relay_two_reverse: list) -> None:
expected = vector["expected"]
proof_hash = bytes.fromhex(expected["proof_destination_hash_hex"])
proof = bytes.fromhex(expected["destination_proof_raw_hex"])
captured.clear()
Transport.packet_hashlist.clear()
Transport.packet_hashlist_prev.clear()
Transport.reverse_table = {proof_hash: relay_two_reverse}
Transport.identity = relay_two_identity
Transport.inbound(proof, destination_if)
expect_forward(
captured, between_if,
bytes.fromhex(expected["relay_two_forwarded_proof_raw_hex"]),
"last-hop relay PROOF",
)
if proof_hash in Transport.reverse_table:
fail("last-hop relay did not consume reverse-table entry")
captured.clear()
Transport.packet_hashlist.clear()
Transport.packet_hashlist_prev.clear()
Transport.reverse_table = {proof_hash: relay_one_reverse}
Transport.identity = relay_one_identity
Transport.inbound(
bytes.fromhex(expected["relay_two_forwarded_proof_raw_hex"]), between_if
)
expect_forward(
captured, origin_if,
bytes.fromhex(expected["relay_one_forwarded_proof_raw_hex"]),
"intermediate relay PROOF",
)
if proof_hash in Transport.reverse_table:
fail("intermediate relay did not consume reverse-table entry")
wrong_if = FakeInterface("wrong-proof-interface")
captured.clear()
Transport.packet_hashlist.clear()
Transport.packet_hashlist_prev.clear()
Transport.reverse_table = {proof_hash: relay_two_reverse}
Transport.identity = relay_two_identity
Transport.inbound(proof, wrong_if)
if captured:
fail("wrong-interface PROOF was forwarded")
if proof_hash in Transport.reverse_table:
fail("wrong-interface PROOF did not consume reverse-table entry")
captured.clear()
Transport.packet_hashlist.clear()
Transport.packet_hashlist_prev.clear()
Transport.inbound(proof, destination_if)
if captured:
fail("PROOF routed after wrong-interface packet consumed reverse state")
print("PASS S12.5.3 PROOF returns through both relays and consumes one-shot reverse entries")
print("PASS S12.5.3 wrong-interface PROOF is dropped after consuming its reverse entry")
def verify_guards(vector: dict, relay_one_identity, origin_if, between_if,
captured: list) -> None:
expected = vector["expected"]
destination_hash = bytes.fromhex(expected["destination_hash_hex"])
incoming = bytes.fromhex(expected["relay_one_inbound_header2_data_raw_hex"])
captured.clear()
clear_state()
Transport.identity = relay_one_identity
Transport.inbound(incoming, origin_if)
if captured or Transport.reverse_table:
fail("DATA with no path was forwarded or recorded")
captured.clear()
clear_state()
Transport.identity = RNS.Identity()
seed_path(destination_hash, bytes(16), 1, between_if)
Transport.inbound(incoming, origin_if)
if captured or Transport.reverse_table:
fail("DATA addressed to another transport identity was forwarded or recorded")
if Transport.REVERSE_TIMEOUT != 8 * 60:
fail(f"REVERSE_TIMEOUT is {Transport.REVERSE_TIMEOUT}, expected 480 seconds")
print("PASS S12.2 unknown-path/wrong-transport DATA drops without reverse state")
print("PASS S12.5.3 RNS 1.2.4 REVERSE_TIMEOUT is 480 seconds")
def main() -> None:
print(f"verify_transport_data.py against RNS {RNS.__version__}")
init_minimal_rns()
vector = load_json(VECTORS_PATH)
identities = load_json(IDS_PATH)["vectors"]
alice = next(item for item in identities if item["label"] == "alice")
bob = next(item for item in identities if item["label"] == "bob")
relay_one_identity = RNS.Identity.from_bytes(bytes.fromhex(alice["inputs"]["private_key_hex"]))
relay_two_identity = RNS.Identity.from_bytes(bytes.fromhex(bob["inputs"]["private_key_hex"]))
origin_if = FakeInterface("origin-side")
between_if = FakeInterface("between-relays")
destination_if = FakeInterface("destination-side")
captured: list[tuple[object, bytes]] = []
original_transmit = Transport.transmit
original_transport_enabled = RNS.Reticulum.transport_enabled
original_identity = Transport.identity
try:
clear_state()
RNS.Reticulum.transport_enabled = staticmethod(lambda: True)
Transport.transmit = staticmethod(lambda interface, raw: captured.append((interface, raw)))
relay_one_reverse, relay_two_reverse = verify_data_path(
vector, relay_one_identity, relay_two_identity,
origin_if, between_if, destination_if, captured,
)
verify_proof_path(
vector, relay_one_identity, relay_two_identity,
origin_if, between_if, destination_if, captured,
relay_one_reverse, relay_two_reverse,
)
verify_guards(vector, relay_one_identity, origin_if, between_if, captured)
print("ALL PASS")
finally:
Transport.transmit = original_transmit
RNS.Reticulum.transport_enabled = original_transport_enabled
Transport.identity = original_identity
clear_state()
try:
RNS.Reticulum.exit_handler()
except Exception:
pass
if __name__ == "__main__":
main()