diff --git a/PROGRESS.md b/PROGRESS.md index f0c2db5a3..875de1a1a 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -51,6 +51,12 @@ authenticated ACK mappings for `ok`/`collision`/`duplicate`. The focused SPL crate gate is green with 91 tests. U4's focused split WS-to-loopback pipe function is in review next; the full relay client and U5 supervisor composition remain unaccepted. +- AC-4's supervisor-generated parity corpus is committed in its own pre-cutover commit. It was + generated from `adf2e9c5f`; the Python oracle sources are byte-identical at that commit, this + lane base, and `c3b7cc6e5`. The static vectors prove Python-sealed → Rust-opens only because + HPKE sealing is randomized; the reverse remains the supervisor's live differential. The + short/long header rows are parser vectors, never socket-wire vectors. U6 may use the corpus + when U1–U5 are accepted, but remains blocked on those orchestration units. - Checkpoint gates after the correction-driven units: `cargo fmt --all -- --check`, strict combined clippy, and combined locked tests are green (85 SPL + 12 HPKE); both HPKE and SPL libraries pass the explicit `aarch64-apple-ios` check without an exclusion; `cargo deny` diff --git a/core/crates/solstone-core-spl/tests/vectors/gen_spl_vectors.py b/core/crates/solstone-core-spl/tests/vectors/gen_spl_vectors.py new file mode 100644 index 000000000..5604f396c --- /dev/null +++ b/core/crates/solstone-core-spl/tests/vectors/gen_spl_vectors.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 +"""Generate the SPL home-service parity corpus from the Python oracle. + +Read-only against the shared dev clone: imports solstone.think.spl.* and +cryptography/pyhpke, touches no journal, writes one JSON doc to stdout. + +Every vector is deterministic. HPKE *seal* is randomised (ephemeral key), so +seal is captured as a Python-produced (enc, ct) pair the Rust must be able to +OPEN — the reverse direction (Rust seals, Python opens) is a live differential +the supervisor runs, not a static vector. +""" + +from __future__ import annotations + +import hashlib +import hmac +import json +import uuid + +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec + +from solstone.think.spl import blob_receiver as br +from solstone.think.spl import hpke as H +from solstone.think.spl.health import ( + LINK_HEALTH_EVENT, + OFFLINE_TUNNEL_REASONS, + REASON_HOME_MISSING_MOBILE, + REASON_LOCAL_PRIVATE_LISTENER_UNREACHABLE, + REASON_RELAY_ADMISSION_SATURATED, + REASON_RELAY_TUNNEL_REJECTED, + REASON_RELAY_TUNNEL_UNREACHABLE, + REASON_SERVICE_TOKEN_REJECTED, +) + +hx = bytes.hex + + +def der_priv(k: ec.EllipticCurvePrivateKey) -> bytes: + return k.private_bytes( + serialization.Encoding.DER, + serialization.PrivateFormat.PKCS8, + serialization.NoEncryption(), + ) + + +def der_pub(k: ec.EllipticCurvePublicKey) -> bytes: + return k.public_bytes( + serialization.Encoding.DER, serialization.PublicFormat.SubjectPublicKeyInfo + ) + + +# Deterministic actors. Private scalars are fixed so the corpus is reproducible. +HOME_SCALAR = 0x3F1A2B3C4D5E6F708192A3B4C5D6E7F8091A2B3C4D5E6F708192A3B4C5D6E7F8 +SENDER_SCALAR = 0x11223344556677889900AABBCCDDEEFF00112233445566778899AABBCCDDEEFF +EXT_SCALAR = 0x0A1B2C3D4E5F60718293A4B5C6D7E8F90A1B2C3D4E5F60718293A4B5C6D7E8F9 + +home = ec.derive_private_key(HOME_SCALAR, ec.SECP256R1()) +sender = ec.derive_private_key(SENDER_SCALAR, ec.SECP256R1()) +ext = ec.derive_private_key(EXT_SCALAR, ec.SECP256R1()) + +INSTANCE = "3f9a1c22-0e4b-4d7a-9c11-5b6d8e0f2a34" +instance_16 = uuid.UUID(INSTANCE).bytes +sender_fp = hashlib.sha256(der_pub(sender.public_key())).digest() + +out: dict = { + "_generator": "gen_spl_vectors.py", + "_oracle": "solstone/think/spl at origin/main", + "_note": ( + "Deterministic parity corpus. HPKE seal is randomised, so seal appears " + "only as Python-produced (enc, ct) the Rust must OPEN. Rust-seals-Python-opens " + "is a live differential, not a static vector." + ), + "actors": { + "instance_id": INSTANCE, + "instance_id_16_hex": hx(instance_16), + "home_priv_pkcs8_der_hex": hx(der_priv(home)), + "home_pub_spki_der_hex": hx(der_pub(home.public_key())), + "sender_priv_pkcs8_der_hex": hx(der_priv(sender)), + "sender_pub_spki_der_hex": hx(der_pub(sender.public_key())), + "ext_priv_pkcs8_der_hex": hx(der_priv(ext)), + "ext_pub_spki_der_hex": hx(der_pub(ext.public_key())), + "sender_fp_hex": hx(sender_fp), + "sender_fingerprint": "sha256:" + hx(sender_fp), + }, +} + +# ---------------------------------------------------------------- constants +out["constants"] = { + "OFFER_LEN": br.OFFER_LEN, + "READY_LEN": br.READY_LEN, + "ACK_LEN": br.ACK_LEN, + "ENC_LEN": br.ENC_LEN, + "MAX_CT_LEN": br.MAX_CT_LEN, + "MAX_ENTRIES": br.MAX_ENTRIES, + "MAX_FILE_BYTES": br.MAX_FILE_BYTES, + "MAX_TOTAL_BYTES": br.MAX_TOTAL_BYTES, + "OFFER_MAGIC": br._OFFER_MAGIC.decode(), + "READY_MAGIC": br._READY_MAGIC.decode(), + "ACK_MAGIC": br._ACK_MAGIC.decode(), + "VERSION": br._VERSION, + "KEM_ID": br._KEM_ID, + "KDF_ID": br._KDF_ID, + "AEAD_ID": br._AEAD_ID, + "reasons": { + "home_missing_mobile": REASON_HOME_MISSING_MOBILE, + "service_token_rejected": REASON_SERVICE_TOKEN_REJECTED, + "relay_tunnel_rejected": REASON_RELAY_TUNNEL_REJECTED, + "relay_tunnel_unreachable": REASON_RELAY_TUNNEL_UNREACHABLE, + "local_private_listener_unreachable": REASON_LOCAL_PRIVATE_LISTENER_UNREACHABLE, + "relay_admission_saturated": REASON_RELAY_ADMISSION_SATURATED, + }, + "OFFLINE_TUNNEL_REASONS": sorted(OFFLINE_TUNNEL_REASONS), + "LINK_HEALTH_EVENT": LINK_HEALTH_EVENT, +} + + +def build_offer(blob_id: bytes, ct_len: int, *, magic=None, ver=None, + kem=None, kdf=None, aead=None, fp=None) -> bytes: + return ( + (magic if magic is not None else br._OFFER_MAGIC) + + bytes([ver if ver is not None else br._VERSION]) + + (kem if kem is not None else br._KEM_ID).to_bytes(2, "big") + + (kdf if kdf is not None else br._KDF_ID).to_bytes(2, "big") + + (aead if aead is not None else br._AEAD_ID).to_bytes(2, "big") + + (fp if fp is not None else sender_fp) + + blob_id + + ct_len.to_bytes(8, "big") + ) + + +# ------------------------------------------------------- offer parse table +blob_id = bytes.fromhex("0102030405060708090a0b0c0d0e0f10") +parse_cases = [] + + +def parse_case(name: str, header: bytes, *, note: str = "") -> None: + try: + o = br._parse_offer(header) + res = { + "ok": True, + "sender_fp_hex": hx(o.sender_fp), + "blob_id_hex": o.blob_id_hex, + "ct_len": o.ct_len, + "sender_fingerprint": o.sender_fingerprint, + } + except ValueError as e: + res = {"ok": False, "error": str(e)} + # the wire consequence: SBO1-magic malformed gets READY(0x01); others get nothing + ready = (not res["ok"]) and header[:4] == br._OFFER_MAGIC + parse_cases.append( + { + "name": name, + "note": note, + "header_hex": hx(header), + "header_len": len(header), + "parse": res, + "sends_ready_0x01_before_close": ready, + } + ) + + +parse_case("valid", build_offer(blob_id, 4096), note="baseline") +parse_case("valid_ct_len_at_max", build_offer(blob_id, br.MAX_CT_LEN)) +parse_case("ct_len_over_max", build_offer(blob_id, br.MAX_CT_LEN + 1), + note="oversize ciphertext is refused") +parse_case("bad_magic", build_offer(blob_id, 4096, magic=b"XXXX"), + note="NOT SBO1 -> no READY at all, just close") +parse_case("bad_version", build_offer(blob_id, 4096, ver=0x02)) +parse_case("bad_kem", build_offer(blob_id, 4096, kem=0x0011)) +parse_case("bad_kdf", build_offer(blob_id, 4096, kdf=0x0002)) +parse_case("bad_aead", build_offer(blob_id, 4096, aead=0x0001)) +parse_case("short_header", build_offer(blob_id, 4096)[:66], note="66 bytes") +parse_case("long_header", build_offer(blob_id, 4096) + b"\x00", note="68 bytes") +out["offer_parse"] = parse_cases + +# ---------------------------------------------------------- ready / ack wire +out["ready_frames"] = [ + {"status": s, "bytes_hex": hx(br._READY_MAGIC + bytes([br._VERSION, s]))} + for s in (0x00, 0x01) +] + +# ------------------------------------------------------- HPKE auth (blob v1) +# br._blob_info() reads LinkState from disk; reconstruct its output explicitly so the +# generator touches no journal. Shape is asserted against the source below. +blob_info = b"spl-blob-v1" + instance_16 + sender_fp +out["blob_info_hex"] = hx(blob_info) + +auth_vectors = [] +for idx, plaintext in enumerate( + [b"", b"a", b"the quick brown fox" * 3, bytes(range(256)) * 8] +): + header = build_offer(blob_id, 0) # aad = the whole 67-byte header + suite = H._suite() + enc, sctx = suite.create_sender_context( + H._kem_key_from_public_der(der_pub(home.public_key())), + info=blob_info, + sks=H._kem_key_from_private(sender), + ) + ct = sctx.seal(plaintext, header) + opened = H.open_auth( + enc, home, blob_info, der_pub(sender.public_key()), ct, header + ) + assert opened.plaintext == plaintext + k_ack = opened.export(b"spl-blob-ack-v1", 32) + acks = {} + for status in (0x00, 0x01): + tag = hmac.new( + k_ack, b"spl-blob-ack" + bytes([status]) + blob_id, hashlib.sha256 + ).digest()[:16] + acks[f"status_{status:#04x}"] = hx( + br._ACK_MAGIC + bytes([br._VERSION, status]) + blob_id + tag + ) + auth_vectors.append( + { + "name": f"auth_open_{idx}", + "info_hex": hx(blob_info), + "aad_hex": hx(header), + "enc_hex": hx(enc), + "enc_len": len(enc), + "ct_hex": hx(ct), + "expect_plaintext_hex": hx(plaintext), + "expect_k_ack_hex": hx(k_ack), + "expect_ack_frames": acks, + } + ) +out["hpke_auth_open"] = auth_vectors + +# ------------------------------------------- HPKE base (browser pairing msg3) +base_vectors = [] +for idx, plaintext in enumerate( + [b"{}", json.dumps({"S": "AAAAAAAAAAA", "device_label": "lab"}).encode(), bytes(512)] +): + sealed = H.seal_base(der_pub(home.public_key()), instance_16, plaintext, b"") + got = H.open_base(sealed.enc, home, instance_16, sealed.ciphertext, b"") + assert got == plaintext + base_vectors.append( + { + "name": f"base_open_{idx}", + "info_hex": hx(instance_16), + "aad_hex": "", + "enc_hex": hx(sealed.enc), + "ct_hex": hx(sealed.ciphertext), + "expect_plaintext_hex": hx(plaintext), + } + ) +out["hpke_base_open"] = base_vectors + +# --------------------------------------------------------- health payloads +out["health_payloads"] = { + "all_null": { + "state": "connecting", + "listen_generation": 1, + "last_successful_relay_tunnel_at": None, + "last_relay_tunnel_error": None, + "last_relay_tunnel_error_at": None, + "relay_tunnel_error_status": None, + "relay_admission_saturated_count": 0, + }, + "populated_rejected": { + "state": "connected", + "listen_generation": 7, + "last_successful_relay_tunnel_at": 1_750_000_000_000, + "last_relay_tunnel_error": REASON_RELAY_TUNNEL_REJECTED, + "last_relay_tunnel_error_at": 1_750_000_001_000, + "relay_tunnel_error_status": 502, + "relay_admission_saturated_count": 3, + }, + "_key_order_is_contract": [ + "state", + "listen_generation", + "last_successful_relay_tunnel_at", + "last_relay_tunnel_error", + "last_relay_tunnel_error_at", + "relay_tunnel_error_status", + "relay_admission_saturated_count", + ], +} + +print(json.dumps(out, indent=1, sort_keys=False)) diff --git a/core/crates/solstone-core-spl/tests/vectors/spl-parity-corpus.json b/core/crates/solstone-core-spl/tests/vectors/spl-parity-corpus.json new file mode 100644 index 000000000..d9aeef2fe --- /dev/null +++ b/core/crates/solstone-core-spl/tests/vectors/spl-parity-corpus.json @@ -0,0 +1,300 @@ +{ + "_provenance": { + "generated_from_commit": "adf2e9c5f", + "valid_for_commits": [ + "c3b7cc6e5", + "5ed4dacb8", + "adf2e9c5f" + ], + "equivalence_proof": "git diff --stat 5ed4dacb8 adf2e9c5f -- solstone/think/spl solstone/think/link/browser_pairing.py => empty. The oracle sources are byte-identical across all three commits, so the corpus is valid for any of them.", + "regenerate": "python solstone-core-spl/tests/vectors/gen_spl_vectors.py (requires the pre-cutover Python + pyhpke 0.6.4)", + "warning_offer_parse": "short_header/long_header are PARSER vectors, not wire vectors: the wire read is read_exactly_bounded(67), so a 66- or 68-byte header cannot reach the parser from a socket. Do not build a wire test from those two rows." + }, + "_generator": "gen_spl_vectors.py", + "_oracle": "solstone/think/spl at origin/main", + "_note": "Deterministic parity corpus. HPKE seal is randomised, so seal appears only as Python-produced (enc, ct) the Rust must OPEN. Rust-seals-Python-opens is a live differential, not a static vector.", + "actors": { + "instance_id": "3f9a1c22-0e4b-4d7a-9c11-5b6d8e0f2a34", + "instance_id_16_hex": "3f9a1c220e4b4d7a9c115b6d8e0f2a34", + "home_priv_pkcs8_der_hex": "308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104203f1a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8a144034200048271287a705261adeba2990e91b878877e7ceafea580c5070308e97c629c94453ab61c9b54ed481e95d1ddba1945ff2280cf4abaa6499ca2257265141036209e", + "home_pub_spki_der_hex": "3059301306072a8648ce3d020106082a8648ce3d030107034200048271287a705261adeba2990e91b878877e7ceafea580c5070308e97c629c94453ab61c9b54ed481e95d1ddba1945ff2280cf4abaa6499ca2257265141036209e", + "sender_priv_pkcs8_der_hex": "308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b020101042011223344556677889900aabbccddeeff00112233445566778899aabbccddeeffa1440342000440f080196b1ee67c4e7bca861d94ed20c8af0187ae9080332f7025dfd31130ce47a2f07fad3dd3d5f69ab97dfeec56d9accaf5efb58d5ce9f5facbfd08dfed66", + "sender_pub_spki_der_hex": "3059301306072a8648ce3d020106082a8648ce3d0301070342000440f080196b1ee67c4e7bca861d94ed20c8af0187ae9080332f7025dfd31130ce47a2f07fad3dd3d5f69ab97dfeec56d9accaf5efb58d5ce9f5facbfd08dfed66", + "ext_priv_pkcs8_der_hex": "308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104200a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9a14403420004160b0615159ebbdf7fd4b1194d42b2986c6b2cccad799521b8b326292b6419b393a20e124ad2a56ecea74f8dae84d8d91d0f5ac4a33f221226d1681cd12552ca", + "ext_pub_spki_der_hex": "3059301306072a8648ce3d020106082a8648ce3d03010703420004160b0615159ebbdf7fd4b1194d42b2986c6b2cccad799521b8b326292b6419b393a20e124ad2a56ecea74f8dae84d8d91d0f5ac4a33f221226d1681cd12552ca", + "sender_fp_hex": "09dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69", + "sender_fingerprint": "sha256:09dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69" + }, + "constants": { + "OFFER_LEN": 67, + "READY_LEN": 6, + "ACK_LEN": 38, + "ENC_LEN": 65, + "MAX_CT_LEN": 83886080, + "MAX_ENTRIES": 64, + "MAX_FILE_BYTES": 16777216, + "MAX_TOTAL_BYTES": 67108864, + "OFFER_MAGIC": "SBO1", + "READY_MAGIC": "SBR1", + "ACK_MAGIC": "SBA1", + "VERSION": 1, + "KEM_ID": 16, + "KDF_ID": 1, + "AEAD_ID": 2, + "reasons": { + "home_missing_mobile": "home_missing_mobile", + "service_token_rejected": "service_token_rejected", + "relay_tunnel_rejected": "relay_tunnel_rejected", + "relay_tunnel_unreachable": "relay_tunnel_unreachable", + "local_private_listener_unreachable": "local_private_listener_unreachable", + "relay_admission_saturated": "relay_admission_saturated" + }, + "OFFLINE_TUNNEL_REASONS": [ + "local_private_listener_unreachable", + "service_token_rejected" + ], + "LINK_HEALTH_EVENT": "health" + }, + "offer_parse": [ + { + "name": "valid", + "note": "baseline", + "header_hex": "53424f310100100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000000001000", + "header_len": 67, + "parse": { + "ok": true, + "sender_fp_hex": "09dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69", + "blob_id_hex": "0102030405060708090a0b0c0d0e0f10", + "ct_len": 4096, + "sender_fingerprint": "sha256:09dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69" + }, + "sends_ready_0x01_before_close": false + }, + { + "name": "valid_ct_len_at_max", + "note": "", + "header_hex": "53424f310100100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000005000000", + "header_len": 67, + "parse": { + "ok": true, + "sender_fp_hex": "09dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69", + "blob_id_hex": "0102030405060708090a0b0c0d0e0f10", + "ct_len": 83886080, + "sender_fingerprint": "sha256:09dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69" + }, + "sends_ready_0x01_before_close": false + }, + { + "name": "ct_len_over_max", + "note": "oversize ciphertext is refused", + "header_hex": "53424f310100100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000005000001", + "header_len": 67, + "parse": { + "ok": false, + "error": "blob ciphertext too large" + }, + "sends_ready_0x01_before_close": true + }, + { + "name": "bad_magic", + "note": "NOT SBO1 -> no READY at all, just close", + "header_hex": "585858580100100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000000001000", + "header_len": 67, + "parse": { + "ok": false, + "error": "blob offer magic mismatch" + }, + "sends_ready_0x01_before_close": false + }, + { + "name": "bad_version", + "note": "", + "header_hex": "53424f310200100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000000001000", + "header_len": 67, + "parse": { + "ok": false, + "error": "unsupported blob HPKE suite" + }, + "sends_ready_0x01_before_close": true + }, + { + "name": "bad_kem", + "note": "", + "header_hex": "53424f310100110001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000000001000", + "header_len": 67, + "parse": { + "ok": false, + "error": "unsupported blob HPKE suite" + }, + "sends_ready_0x01_before_close": true + }, + { + "name": "bad_kdf", + "note": "", + "header_hex": "53424f310100100002000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000000001000", + "header_len": 67, + "parse": { + "ok": false, + "error": "unsupported blob HPKE suite" + }, + "sends_ready_0x01_before_close": true + }, + { + "name": "bad_aead", + "note": "", + "header_hex": "53424f310100100001000109dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000000001000", + "header_len": 67, + "parse": { + "ok": false, + "error": "unsupported blob HPKE suite" + }, + "sends_ready_0x01_before_close": true + }, + { + "name": "short_header", + "note": "66 bytes", + "header_hex": "53424f310100100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f1000000000000010", + "header_len": 66, + "parse": { + "ok": false, + "error": "blob offer header length mismatch" + }, + "sends_ready_0x01_before_close": true + }, + { + "name": "long_header", + "note": "68 bytes", + "header_hex": "53424f310100100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f10000000000000100000", + "header_len": 68, + "parse": { + "ok": false, + "error": "blob offer header length mismatch" + }, + "sends_ready_0x01_before_close": true + } + ], + "ready_frames": [ + { + "status": 0, + "bytes_hex": "534252310100" + }, + { + "status": 1, + "bytes_hex": "534252310101" + } + ], + "blob_info_hex": "73706c2d626c6f622d76313f9a1c220e4b4d7a9c115b6d8e0f2a3409dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69", + "hpke_auth_open": [ + { + "name": "auth_open_0", + "info_hex": "73706c2d626c6f622d76313f9a1c220e4b4d7a9c115b6d8e0f2a3409dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69", + "aad_hex": "53424f310100100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000000000000", + "enc_hex": "048dfc4fb5cc1cb974dd17af75a6256d40dabf57654770737e6df744711485ec82e572bd0ce9b55e22c084c5dea22337f258dba2876754817157a0a2ba5e475bac", + "enc_len": 65, + "ct_hex": "90642ae2c49d0ef88ba3bcbcec2ff1bd", + "expect_plaintext_hex": "", + "expect_k_ack_hex": "d234f6e4278f9f3732c4fefd7966109818ee4ccc96547db9a714bd86dce58fbe", + "expect_ack_frames": { + "status_0x00": "5342413101000102030405060708090a0b0c0d0e0f10806274282360cb51a30a449dc89fcbef", + "status_0x01": "5342413101010102030405060708090a0b0c0d0e0f109f5bf2079e532dd73ca27615c0ce5f9f" + } + }, + { + "name": "auth_open_1", + "info_hex": "73706c2d626c6f622d76313f9a1c220e4b4d7a9c115b6d8e0f2a3409dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69", + "aad_hex": "53424f310100100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000000000000", + "enc_hex": "04a4110117955e0d76751eb14d03d16143739a3243f25d6629a4727adba186b3307a7ed424bcbb62b841454b81282b0949e44bfe0e9912a58ec0731bbff19a1051", + "enc_len": 65, + "ct_hex": "980e5b5711b8247af1bb376bb21a6624fa", + "expect_plaintext_hex": "61", + "expect_k_ack_hex": "8484c36ea86910cfa45b1765d4e8ff58b66f576ae44e064f5f4b1bbd0b3f84e0", + "expect_ack_frames": { + "status_0x00": "5342413101000102030405060708090a0b0c0d0e0f104d4f65c34b6d67250317781f80edbbeb", + "status_0x01": "5342413101010102030405060708090a0b0c0d0e0f10259ae16b30da77f6958d186e1396a01d" + } + }, + { + "name": "auth_open_2", + "info_hex": "73706c2d626c6f622d76313f9a1c220e4b4d7a9c115b6d8e0f2a3409dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69", + "aad_hex": "53424f310100100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000000000000", + "enc_hex": "04705e626a2063e43c5d69f469f21d60df13572db448aa29bb14b0a6e9d625163786067a98313a9be4be76268e76bd43eb4475453b00118de5caaa71f11705b34e", + "enc_len": 65, + "ct_hex": "34e1416a9959f2f33136388cb38b7c6913a11fdbd728a905a770a2db619b7b42ccaeb46a3ee7285aec510dec0d1c64a054896ace1c78cd9c6e1be7d1a75c0f370ede2f800801f1e4d6", + "expect_plaintext_hex": "74686520717569636b2062726f776e20666f7874686520717569636b2062726f776e20666f7874686520717569636b2062726f776e20666f78", + "expect_k_ack_hex": "59615c9696bfbd0306171a0d5db55a82a0bf17d6bb2f2724c16df4fd1211949a", + "expect_ack_frames": { + "status_0x00": "5342413101000102030405060708090a0b0c0d0e0f1010da3fa3069cada50bfe1806fb3b2336", + "status_0x01": "5342413101010102030405060708090a0b0c0d0e0f10ebb2797ceea02af7729b71c6119ef5b4" + } + }, + { + "name": "auth_open_3", + "info_hex": "73706c2d626c6f622d76313f9a1c220e4b4d7a9c115b6d8e0f2a3409dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb69", + "aad_hex": "53424f310100100001000209dde95b376b02c75d749f0686b5c796bab8c3d056cee2b8b8fd68856d2edb690102030405060708090a0b0c0d0e0f100000000000000000", + "enc_hex": "04dd2eed65736e7bd7047f09132d8dad311458f1d3d537db209a9c8a6e6ad41c39f855eae990443b27daec48cc03de80706576e9b05dcd81dc318a5758d403c0fb", + "enc_len": 65, + "ct_hex": "f15b326961af05ec9595dcfc1b790091665e073830778f7de7ac1762b2f3299c0f343ed41d049b2efa59ba1ed6af36faa42c2e3b27fd9913831646594e26bfe6d7de0c64b42c453fa887d4f7703d71a259b437d81d71a028d015f501abe1f379ff67f9e7c30c7b7bdc92bf14ae6f4244e02e61ccbef1451028470e59ac223e2008cf3822a1b2b8ac9964b0bd3e08ed0443ee198d0470c0c3ab6134186924b711e307fb0620708a280795e6755a4ad7a162a5256c507b8a693c8f3991a7e5b1a3627f976e6d4c741e9e65447f3f330aa38158f007e24717effbebeb164c70b804ca01bcbf909d519efccc124598583936d2ce2e33a1001e353359b17154904f4ab276dd7f1f3db9071533e469c40a2edd78ecd9e1a9183672cce7407c70bfda5f702dfe0c7e4e6fbbb1ce6adcba1ddfe7eb3f683f57aa33f9879e04aff3e7ff83755ac67a5bfa5cca4ea1bf22ae230439a856347099253059fcd8fc4133f7690d580e588ad855b62f4b936bc47e8f06c5281decc7c665eaa2d57f757a6d09d09f6192971e9aa5aeff2b839b35196f99d9ddb222013ac9fa6daa4eaf8dbe02c7d87f6515fb383627f6b14c487c926665830f596bd8dbd54ef3036d8b21e16c8b8b1275910c33746591f0b9b5881a9303974ccc5c90e71559541993c270ffbf24890db3eec8a9dc1ac0c7846bf451389cbac68fd1e388c4a8123c8af9bd601648ccb1f86db74e6beb9843bada7be1126c4a9a532a55dda03436d76f8fdda003e75a7be26f3f9b7c145787d381678c8731f9bba0ccf0fe4b4b88591fcea570840f9669e97d43ca34bc8c30a4e7424c6da2c92109071d7069a680986f3d546192b54582ac9d4fc28144a5a2372b3fa50c49930985bdffbfcb1036f58d809bea53b72894ec4fd539c23ea4d3cd4653dd038466f97d08fe66299c8c64ff2717e95627b85ddcdd012c9b6b9f66c770758801ca277553f959b2935a9cf096ac9e9b70b02de946cdb237a0f8edae2575d29173cadc0c08809eb566de58e21e35ba3ae299378d70f3e26540ac7ff4e623da302b9f0cfd44eb7f96a9a1ed0cd4c9ab7d79bb040b247832f83484469cde084992371e1957267bccfd34bc4c4c07428f4213cf0febab8420c9fe55727b0380688dc31a0736ebcaa40ea50d71a194b644b758b9b06b9f981015ab93aa611a4dce5e36cd0a10c4e747dc10909b38ae5e297c4ce6b60d15a23dcf22aa56ea4583bfa74ab09391165ed596b8fd160dd0bf400802d9729a3daceaa8b48993c69ce2bb95e33611736b893c0c1e3b1205956ee4e9eb5fd6d288ec9e891bfeb3b4311c5f0408a6b758e09f10a51711f198f387dba3c2d331d5cc6cece9ffc582896d94e486d87a56b6907aa40c40b110b37ae98c4b1a3a0049d0d5810a788ba559164d64ebf366bcc86509263c8e7c21beba006c254261265db7e627528783479330ed3039ae788463a3d8595933a5c0123494b2c56155a496a4a893b353fe637e1ebdd23f128ed8f1e84a3a4a18e397ca595c1ba66583ada4fef6997c81fdf7efb66f44ff06c4399fc7da5df6162cd828723e224cac335cd2f644f0163a8a40ab2714965c1e1c2b99fff8395c3544aecc06c9ab4cf55276a1133dc34b0f2b73e77f6c0baafb4b5cb734e63b11ada0e4409f9b4e879cdd34631d4e0ac721a0a0c6f941cbf07cff10c1860f946cd2d19819bcf58beb178e31462183e869ebf78be2ba0f1fe3807f9c8945500ab2792751fe4e23a085b05037742ad5d9e4e7af84eaba4f5fecff02b10235820452c37e328c676c78f30952ada97002a44b969fcacb57adfbf41e44744f224ba8e18e9ba15421ced71cb159109809ea5b3efbc87945d87ad9eaed622b5891adec12817541b1f04635cff6024a6105e0fa5feee5bc3f9c73a2a6a8c31cde8c917fe64c46db9ecc248c8a838073ef5eb6a1bbabdf09894057732707795100b23622ec9182f89445b86005d2602f4794ca8a258d7be6123ba4d5eb191dbc8c25719462520c73338bb92b0b39e131dbf9712723671abe46ed0c53864c64549149dc970fbc013ee4d10dc6e1b29ea214b415d1009250ea800e42344396c5ece1dacfc801ec743fee160be0d5620caf325cfe7b5e80d330288f2a9925a1b9de9f512a0159b38534eabb1cf1a346f9bd4494a46c3bd2a9a416573ee1acd6583487c8573c93da3f5cd01438653d9bc44220f102629558f0df32010c462fea5200902ff0096229d4778c09c0b206c21723c8d67701f8791067acadf58c68b144471f06ee60633a52a552d61aee83a7e20e438395d57d1955dfe21aa3781d8605d1922d6087053b7d1e5393cea7d62751be6470c1546af623dcc0229e2d51ef0c8643235abc9824e82471b343b94301a129d6d5774649d752a7e6b4a9537e7fcfb32bdc18d0f62b1856d622859dcad3e851dd16397fd53001030dcea465fd720f968e1d5f8ee2709be845c61163c7f2a30d5441a54213fdf17fa34de079a5cf1a9889bf563bf618d42d614663b973def722b54aaab70d5a35653ba36a392d2e0ecf8ad729473e841e32f194d5032959a6db621373c540b7fe8fb81677a32669c62c3f627411f2d17fae81c853ddec8dbfcc7f2e97234763d06a7ef09ee6417398324c5cda2f84a382ea2ce0586c8543367ffe2902b9ae9d67b68237ef1c70cfffd8896862c86fa81959e1a88c5e72cc31e126121a5abc873660b54011c106560602f43eebe0473b4dc7193eb8433a4b9f640ec6a97cc84f6bed588635c1dc2d11595c1271d90ec4722b94685eee3d3bc29d93bd531b2bd55f72cd6e3122a2e61a7c1ba9ac6df5262727e00dc7b6c2bd1ac42aedf07c71eaa9ec8f2a684f4dfb38d1d7667af4e13b4560e44fc0b05ee77dca1f5e877de9317a1ea3da5d2aa5877558", + "expect_plaintext_hex": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "expect_k_ack_hex": "29c7bc38f5fa1677e3ae9d52f756db42c0d623e6bb139b7e301acb325053df81", + "expect_ack_frames": { + "status_0x00": "5342413101000102030405060708090a0b0c0d0e0f1021bfc9801af252f1575ce99e691bd655", + "status_0x01": "5342413101010102030405060708090a0b0c0d0e0f10164d02bf571afc24a13857d247af1483" + } + } + ], + "hpke_base_open": [ + { + "name": "base_open_0", + "info_hex": "3f9a1c220e4b4d7a9c115b6d8e0f2a34", + "aad_hex": "", + "enc_hex": "04b767962066ec0e6415fcc829d949da86563f8807737797daa5e39af3288c43b39ada4f5680d03cf36d029c9e8314ce7baaae1d85bdee4f93dd0aacc53925cf99", + "ct_hex": "d3ff33c049682d20c917fb14afa70adabc05", + "expect_plaintext_hex": "7b7d" + }, + { + "name": "base_open_1", + "info_hex": "3f9a1c220e4b4d7a9c115b6d8e0f2a34", + "aad_hex": "", + "enc_hex": "0436068fb90f53088740882d9095bbab538e8826da70d899f0575ee73f6f6bbc4424314776050ac3cb6661568c712d0b1df2c65160aa50dd53968df7b0d0da4e8f", + "ct_hex": "f5fc97f8e5ae9e5d3aeef3479780b471f9523104fa90c21d99fd80c7ac8677d7bb5c09ca23390e8a61493da3e7af1b4876fe89508c1eb2d7b85c66", + "expect_plaintext_hex": "7b2253223a20224141414141414141414141222c20226465766963655f6c6162656c223a20226c6162227d" + }, + { + "name": "base_open_2", + "info_hex": "3f9a1c220e4b4d7a9c115b6d8e0f2a34", + "aad_hex": "", + "enc_hex": "046a621abc94437c8e472cf064397fd95b915c57c673074c85e1eba90872f1a39ece0c2259c73fbe88e402b0cf106b83029b08e92e9f45c4819a936708254556d1", + "ct_hex": "042442a901391012a72f40b868bfd41d445e2e89c73ebe813d444cbedd4706996f9fd11920edf4b390b5e86ee7f3767061ee3ef70f4cf2d2dfd6b93e5ee606ad4475964b3fa508e6b383461a0fee2621c6bc7b4ad2dd7337818e69db2a824314f5a356faaaf980cbfc630e0e6f6f5a02c33f3d175f9f3f2b7165fb6b206f0a9b88fa096d3f4dd929f0ee82ef5da0d656a7554484a52e72186c2f0b3d9662d16c1fa9495eb5e7bd1e02cefe93fd5bdb7f601a9061c08eda8cb5de19d28c04cd7f711b9ee3453cc817269a981f808a5cb368aee98f01089cac0b2af83eea75b2a81c45991e7c9d22b120c92ff2544a0194a45ff202239dfd2a775e87bc0a3ce0a5981bc777fa567902df4b34259ca49c521b5c39c4425799f7aeacc7a05d06a34f71a837304c6653f68673a4581f72c266f61772af525ec723a44364d2b07adafc157c3d8c077c0c09f5c5c5053eaa6cac5d3a364e862e9704d9b3eb90789dc361773e846db6d94b7779373ee57fed1b995dfe8b58e56b0f0fbc6b070b2771f63808b728cca8b69ed7ab66b4500fd2f098264caf918026068a14d844d93b1fef96bec3c446105265c1bd8c8232b7a2f1e9abbf965cae33ef55ba2e47c28c06f511170668cae55afb31112e0bf9da0196299cc9e0cf79ee3b9cb45c130873dc831cac94d49080a1a8bc5aa0607ca06f05e113592560e236f58dced43b96d20341b0cc6ae743c7b37940fffd6211e12bcb79", + "expect_plaintext_hex": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + ], + "health_payloads": { + "all_null": { + "state": "connecting", + "listen_generation": 1, + "last_successful_relay_tunnel_at": null, + "last_relay_tunnel_error": null, + "last_relay_tunnel_error_at": null, + "relay_tunnel_error_status": null, + "relay_admission_saturated_count": 0 + }, + "populated_rejected": { + "state": "connected", + "listen_generation": 7, + "last_successful_relay_tunnel_at": 1750000000000, + "last_relay_tunnel_error": "relay_tunnel_rejected", + "last_relay_tunnel_error_at": 1750000001000, + "relay_tunnel_error_status": 502, + "relay_admission_saturated_count": 3 + }, + "_key_order_is_contract": [ + "state", + "listen_generation", + "last_successful_relay_tunnel_at", + "last_relay_tunnel_error", + "last_relay_tunnel_error_at", + "relay_tunnel_error_status", + "relay_admission_saturated_count" + ] + } +} \ No newline at end of file