Independent Pi/OpenRouter review: meta/muse-spark-1.3
Source: a10323dede4413fbf295916b8ad12e3dbad7514e. Status: completed.
Independent defensive review — a10323dede4413fbf295916b8ad12e3dbad7514e
v2_main.rs reachability only. No execution; all tests cited NOT EXECUTED.
Verdicts
A. Early recovery/inference of request/response CONTENTS — SUPPORTED CONDITIONALLY
No practical pre-delay plaintext/key recovery without breaking RandomX found in frozen code, iff:
- client pins exact PCR0 from independent source, does fresh live Nitro verification bound to actual inner-TLS peer SPKI, nonce,
ready,graviton5_verified, and never sends app data beforeverify(); - AWS/Nitro isolation, NSM entropy/time/PKI, EC2 API auth, WebPKI for upstream/DoH/EC2 are trusted as stated;
- accepted leakages are understood: parent/front sees IPs, timing, sizes, connection behaviour, SNI when ECH absent/fallback, resolver sees DNS questions; puzzles/records/attestations public by design; decrypted data intended public after solve; destination necessarily sees its own request/response; delay is publication-relative, not per-request; no anonymity; no storage-durability proof; no post-release origin proof.
This is not proof of impossibility.
B. Early epoch-key/seed capability or forced use of substantially-solved puzzle without breaking RandomX — SUPPORTED CONDITIONALLY
Generation/solving asymmetry, publication gating, monotonic expiry, bounded leases, sequence exhaustion, content-addressing and fail-closed paths block the obvious in-scope vectors in code, iff same trusts plus enclave-memory isolation and correct FFI/kernel/RNG/Instant behaviour, which were not proved.
No VIOLATED for practical early key/plaintext recovery. Availability/retention/provenance gaps below are real limits, separated from content-confidentiality.
Cross-cutting — INSUFFICIENT EVIDENCE
Hardware-independent timing, absence of microarchitectural side-channels, complete memory erasure, native/kernel/library correctness, full-duration production generation/rollover/recovery. threatmodel.md §7 itself states warming only, ~25.14h generation vs 24h epoch, unverified full run, S3 without Object Lock/retention verification, Cloudflare/third-provider pending. These limit reliance.
Traces checked
Request/response bytes
python/.../client.py:42-123 InnerTLS over transport.py:122-174 exchange/send (/relay?reqid&seq&ack&payload&send, stop-and-wait, identical-retry) -> crates/host/src/http_relay.rs:89-163 Session::exchange, 221-264 handle, 271-321 serve (opaque, bounded MAX_CHUNK 4096, MAX_PENDING 256K, MAX_OUTPUT 32K) -> crates/host/src/main.rs:181-206 forwarder (TCP:443 -> vsock 8443) -> crates/enclave/src/v2_main.rs:184-197 TlsAcceptor + http1 -> crates/enclave/src/v2_proxy.rs:42-120 handle/dispatch:
54-59GET-only, no body;61-68/v1/attestation,/v1/status,/;73-77 validate_target->127-137https/443/no-creds/no-fragment/not-own/not-local/public;78-84try_acquire+trusted_time_ms_uncached+active...accepts(now).cloned()— no work without published live epoch;87next_sequencebefore outbound;92-97fetchwith30stimeout,outcome complete/upstream_error/timeout;98-107canonical_record(CBOR canonical,29-33) ->encrypt_record->publish(...record.json)before returning upstream body;502withoutx-attested-relay-recordon persistence failure;108-119stripsHOP_BY_HOP+set-cookie/content-length/cache-control/location/alt-svc/x-attested-relay-*, addsno-store+x-attested-relay-record/puzzle/evidence.
Upstream: v2_proxy.rs:139-200 connect_upstream/fetch -> net.rs:96-154 connect_tls (in-enclave resolve, cert vs host, ECH retry-once, public_destination bail) -> dns.rs:89-211 resolve/query (DoH by IP to 1.1.1.1/8.8.8.8 from v2_main.rs:157, TLS to IP, MAX_DNS_MESSAGE_BYTES 65535) -> relay.rs:85-110 Net::connect_ip (Upstream refuses direct when relay-required/down) -> transport.rs:135-141 connect_ip(CONNECT ip:port) only IPs to parent; parent main.rs:231-281 handle_tunnel/connect_upstream carries bytes; caller always TLS over it. Redirects re-validated, 5 max, 64K headers/trailers, max_response_bytes 10M (v2_proxy.rs:175,186,182).
Record plaintext (v2_proxy.rs:98-103 version 3 + exchanges with url/method/status/headers/trailers/response_body_b64/complete) -> timelock/src/lib.rs:73-91 record_context/record_key (HKDF from epoch_key, puzzle.id, AAD epoch/sequence/puzzle-id) -> 92-124 encrypt_record(XChaCha20Poly1305, 24B OsRng nonce) -> 125-156 decrypt_record (version/epoch/puzzle-id, commitment, AEAD).
Publication: v2_epoch.rs:57-74 publish (sha256(data).suffix, 32M bound, 3 tries, OK\n required) -> transport.rs:169-176 connect_records(vsock 8001) -> host/main.rs:307-350 records_server/handle_record -> host/artifacts.rs:67-123 store (content-address check, create_new, idempotent exact-match only, sync_all+dir sync) -> 188-233 verified_file/serve (hash re-verified on read, immutable cache).
Keys
- TLS:
enclave/tls.rs:68-111 generatefresh P-256 per boot,spki_derfor attestation;v2_main.rs:155,180generate +server_config; never persisted/leaves. Attested viav2_main.rs:102-114 attestation_for_epoch(attest_with_public_key(user_data=policy, nonce, spki)). - Signing:
v2_main.rs:169-172 Zeroizing[32]fromattest.rs:43-62 fill_random(NSM)->SigningKey::from_bytes;transportholds signer;timelock/lib.rs:388-393signscanonical_bytes. - Puzzle:
timelock/lib.rs:301-326 generate_with_rngfillsdataset_key+7 seeds+epoch_key+7 noncesbeforeDataset::new— no partial publish on entropy failure;327-3517 parallel RandomX chainsx_{n+1}=H(epoch||seg||iter||x_n)(238-244 input);352-387wrapsseed_{i+1}/epoch_keyunderHKDF(y_i, wrap_context)+ChaCha20Poly1305;seed_1/dataset_key/wrapped_keys/key_commitmentpublic, rest secret.solve:458-498serial segments +unwrap:262-284+ commitment check — must do7*iterationsserially. - Epoch liveness:
v2_epoch.rs:10-35 Epoch{manifest,key,activated_at_ms,expires_at_ms,activated_monotonic,sequence}+accepts(wall-clock window and monotonic);40-52 expiration_watchdog/expire_activereleases globalArcwithout NSM/generation;104-151 runholds futureGeneratedPuzzlein memory acrosspublication_not_beforewait, clearsactive=Nonebeforeactivate;76-102 activatepublishesattestation.jsonthen anchorsactivated_monotonic/activated_at_ms/expires_at_ms, publishespuzzle/bundle, checksaccepts(now)before*active=Some. - Time/RNG:
attest.rs:67-82 seed_os_rng(RNDADDENTROPY+RESEED, 2x, 20ms tick)before TLS/keys (v2_main.rs:150-155);86-98 trusted_time_ms_uncached(attest+parse)used for admission/activation;104-132 trusted_time_ms1s cache not used on v2 admission path.
Strongest code-grounded blocks (why easy attacks fail)
- Inner-TLS MITM by operator/front replaying чужой attestation:
verify.py:110-128 _bound_policyrequiresdoc.public_key==SPKI(peer_der)(client.py:60,161-163),protocol_version 2,randomx 2.0.1/aaafe71.../v2/7,iterations 1..1e12,graviton5_verified,ready(live), plus52-107 _authenticated_documentPCR0 pin (non-zero 48B), ES384 COSE, AWS-root641a03..., chain validity at signed time,±5minfreshness, 32B nonce match (131-138).client.py:143-165establishes new session/nonce perverify(), no app data before. Repro sketch (NOT EXECUTED): MITM proxy with valid stolen doc but different SPKI ->_bound_policyraisesattested TLS key differs. - Unrecorded upstream interaction on sequence exhaustion:
v2_proxy.rs:87,122-125reservesfetch_update checked_addbeforefetch;u64::MAXfails closed, never wraps/reuses AAD sequence. - Delayed ACK rebasing expired puzzle as fresh:
v2_epoch.rs:82-83anchors monotonic before puzzle disclosure;97-98accepts(now)after bundle;144timeout(60s, activate)+145publication_not_before=expiration+132-140wait. Host stall >60s ->runreturns,warming; stall <60s but past expiry ->ensurefails. Matchestests/test_v2_e2e.py:345-361(NOT EXECUTED). - Expired key lingering via global ref:
40-52watchdog monotonic-only clear;tests 178-210proveArcrelease except bounded request lease (v2_proxy.rs:82,89-107lease ≤ ~30s fetch +30s publish). Repro (NOT EXECUTED): expireactive, assertweak.upgrade().is_none()after dropping lease. - Silent weak-entropy fallback:
timelock/lib.rs:312-325+tests 580-602require all 16 fills (dataset_key+7 seeds+epoch_key+7 nonces);attest.rs:43-62,197-214zeroizes on failure, no OS fallback in prod;v2_main.rs:150-152abort ifseed_os_rngfails. - Forged puzzle/checkpoint/record:
timelock/lib.rs:174-236 canonical_bytes/id/verify(deny_unknown_fields, fixed-width signed bytes,verify_strict),400-456 Checkpoint::validate(manifest-id, bounds, digest,seed_1at 0,0),493-496commitment;archive.py:119-154 verify_bundlepins PCR0, binds puzzle-params to attested policy,manifest_id=sha256(canonical+key+sig),_strict_signaturesmall-order/noncanonical reject. Tamper tests inlib.rs:612-681(NOT EXECUTED). - Parent content read of records/puzzles: only
dataset_key/seed_1/wrapped_keys/commitment+EncryptedRecord{nonce,ciphertext}cross vsock/disk (framing.rs,artifacts.rs);record_keyHKDF + XChaCha AAD binds epoch/sequence/puzzle-id. Hoststore/servehash-checks. - Diagnostics/error oracle:
v2_diagnostics.rs:33-41static printable ≤160B, drop-on-full, 200ms IO timeout;v2_main.rs:123-131maps all run errors to static string;v2_proxy.rs:46-50maps dispatch failures to502 relay operation failed;transport.rs:16-39bounded parent lines.
Findings requiring conditional acceptance (not counted as content-recovery violations but must be explicit)
F1. Deliberate metadata enables candidate inference. Parent/front sees outbound IP, SNI unless ECH accepted (net.rs:94-95,150), DoH resolver sees questions, sizes/timing visible through inner-TLS + /relay chunking (transport.py:122-174, http_relay.rs). Threat model §6 accepts. Impact: hostname/size/timing inference, not full plaintext. Do not describe as hostname-hiding/anonymity.
F2. Publication-relative ~7d, not per-request 7d. config/relay-v2.toml:6-7 iterations 43768124/epoch 86400; 7 serial segments ≈7d on reference, generation 7-parallel ≈25h. Last record in 24h epoch has ~1d less remaining. Threat model §6 accepts. Impact: late-epoch records decrypt sooner after capture.
F3. Post-release forgery. threatmodel.md §5: record envelopes not service-signed; after solve anyone with epoch key can mint valid AEAD. Provenance needs prior trusted digest/receipt/publication, not implemented. Impact: provenance, not pre-delay confidentiality.
F4. Durability not proved by ACK. v2_epoch.rs:54-56 comment + threatmodel.md §5: malicious OK\n is ordering only; kill-after-upstream-before-seal loses interaction (GET side-effects possible); S3 without Object Lock, no enclave-authenticated retention check, Cloudflare/third-provider pending (§7). Impact: availability/retention/completeness.
F5. Live verifier as shipped does not enforce quantitative work policy. verify.py:121-124 checks 1<=iterations<=1e12, not ≈43M; no epoch_seconds check. Security relies on exact PCR0 pin covering relay-v2.toml + SEGMENTS/RANDOMX_* (verify.py:118-120, archive.py:143-147). If deployer accepts operator-supplied PCR0 or allow_dev, low-work image passes range check. Attacker: malicious operator + lax pin. Repro (NOT EXECUTED): build iterations=8 image, verify with its own PCR0 -> verified True; with prod PCR0 -> PCR0 mismatch. Must pin independently and gate ready+graviton5_verified.
F6. Generation > epoch causes fail-closed gaps. relay-v2.toml:3-4 ~25.14h vs 24h; v2_epoch.rs:126-128 return on generation failure. Impact: warming/503 gaps (v2_proxy.rs:82-83), no reuse of old key — safe but unavailable.
Unresolved / INSUFFICIENT EVIDENCE (need explicit assumption or more source)
U1. Side-channels/timing. No W^X/JIT note aside from randomx.rs:59 flags|128|16; RandomX data-dependent memory access, shared instance CPU, Instant/kernel clock, allocator copies of input/x, Dataset/VM buffers not zeroized (only app keys Zeroizing). Speculation alone is not a confirmed vuln — request: cache/timing analysis, Nitro memory-encryption/scheduling docs, disclosure of solver-hardware advantage measurements.
U2. FFI/composition/kernel. randomx.rs:48-82 unsafe Sync sharing, unchecked init_cache/init_dataset void returns, Vm::hash raw pointers; attest.rs:216-270 raw ioctl constants for pinned 4.14.256, reseed_kernel tick reasoning; build.rs hash-pins source but does not prove integration. Request: vendor/randomx/SHA256SUMS, kernel version evidence, graviton5-calibration raw logs, full-run generation/rollover/recovery logs.
U3. NSM time parsing trusts local device without signature check in-enclave (attest.rs:289-295 parses payload only). Justified only if /dev/nsm path is AWS-isolated from parent — accepted under “AWS trusted” but parent-host vs hypervisor boundary should be documented; replay of stale trusted_time_ms cache avoided on v2 path by using uncached, but confirm no legacy caller reintroduces cached time for admission.
U4. Host is_public weaker than enclave public_destination (host/main.rs:283-302 allows multicast/>=240/198.18/15/192.0, all-V6 filtered differently vs enclave/net.rs:240-251 deny-all-V6 + extra V4 denies). Currently defence-in-depth only because enclave filters before sending IP; confirm no direct host-tunnel caller bypasses enclave check. Request: host invocation args, Cloudflare worker, S3 uploader, solver fleet code if relied for delay/retention claims.
Would I rely for sensitive content?
Under explicit assumptions (AWS trusted, exact independent PCR0, correct live verifier use, tolerance of metadata + publication-relative ~6–7d + public-after-solve + no durability/provenance guarantee + fail-closed gaps), for low-to-moderate sensitivity GETs where hostname/size/timing leakage is acceptable: cautious conditional use. For highly sensitive content requiring strict per-request ≥7d, hostname hiding, guaranteed retention, or post-release authenticity: no — gaps F1–F6/U1–U4 preclude it.