Independent Pi/OpenRouter review: qwen/qwen3-coder-next
Source: a10323dede4413fbf295916b8ad12e3dbad7514e. Status: completed.
Security Review: Timelock Proxy v2 (RandomX Delayed-Release Design)
Executive Summary
This implementation delivers confidentiality with delayed disclosure of encrypted records using a novel construction of parallel RandomX puzzles combined with timestamp-bound epoch management. Under the explicit assumptions that:
- RandomX is computationally hard to shortcut,
- AWS/Nitro attestation is authentic,
- The public puzzle/time-bound are observed, and
- No practical side-channel/compile-time compromise exists,
the service successfully prevents intermediaries from accessing request/response contents until the puzzle is solved.
However, critical evidence gaps exist preventing a complete safety guarantee. The most severe issues relate to:
- Missing verification of puzzle publication deadlines relative to epoch activation,
- Unverified protection of active epoch keys from host compromise before publication,
- Absence of cryptographic binding between encrypted records and puzzle identities, and
- Unverified durability guarantees for ciphertext storage.
Below is a detailed breakdown of verified guarantees, critical gaps, and known risks.
A. Adversary Capability A: Can request/response contents be recovered early?
SUPPORTED CONDITIONALLY: Contents remain encrypted from intermediaries and host if puzzle isn't solved and epoch is still active.
Evidence in favor (supporting confidentiality):
-
Strong encryption primitives used correctly:
v2_proxy.rs:105usesXChaCha20Poly1305with a fresh 24-byte nonce for each recordrelay-timelock/src/lib.rs:92-124: Record key derivation usesHKDF-SHA256with unique context including puzzle ID- Records are sealed inside a hierarchical structure:
ageencryption (to operator's public key) wrapped insidetlock_age(locked to future drand round)
-
Inner TLS relays upstream GETs:
v2_proxy.rs:161-200: The enclave establishes TLS with upstream servers itself, never exposing the destination to the parentdns.rs:182-211: DNS-over-HTTPS resolution happens inside the enclavenet.rs:96-154: ECH (Encrypted Client Hello) is attempted when available, hiding SNI
-
Ciphertext never leaves the enclave unencrypted:
v2_proxy.rs:104-119: Plaintext record is canonicalized to CBOR inside the enclave, then encrypted before any network I/O- The host daemon (
crates/host/src/main.rs) only receives and stores encrypted ciphertext via the vsock interface
-
Configuration-bound retention:
crates/common/src/lib.rs:32: Minimum lock time is 7 days (604800 seconds)config/relay-v2.toml:7: Epoch duration is 86400 seconds (24 hours)
Critical gaps limiting confidence:
-
Missing puzzle publication deadline enforcement:
v2_epoch.rs:144: Theactivatefunction publishes the puzzle after generation completes, but there is no cryptographic proof that the puzzle was published before the epoch activation deadline- Attack scenario: A malicious parent/attacker with the ability to delay or suppress the
publishoperation could cause:- A puzzle that was already solvable at generation time to become a new epoch's puzzle after its activation deadline
- A situation where the puzzle was published with less than the required delay remaining
Root cause: While
publication_not_before(line 110) prevents publishing too early, there is no mechanism ensuring that puzzles are published before their activation deadline. The code structure assumes parent honesty about artifact persistence timing.Impact: This could allow an attacker to trivially "solve" future puzzles by replaying or reusing previously computed work.
-
Active key exposure window before puzzle publication:
v2_epoch.rs:171-177: Thesignerkey is generated at bootv2_epoch.rs:94-96: TheEpochstruct holds thekey(epoch key) inZeroizing<[u8;32]>- Missing protection: The epoch key exists in memory from generation until publication, during which time a host compromise could read it directly from process memory
The current model relies on:
Zeroizing(line 8) to clear the key when dropped, but this doesn't prevent:- Memory snapshots taken while the key is alive
- Side-channel extraction (DRAM remapping, cache attacks)
- Compiler/library bugs that copy the secret to non-zeroizing storage
Impact: If an attacker obtains the epoch key before puzzle publication, they can decrypt any record encrypted with that key, even before the RandomX puzzle is solved.
-
No cryptographic binding between records and puzzles:
relay-timelock/src/lib.rs:233-235:SignedManifest::id()only includes the puzzle + service key + signaturev2_proxy.rs:117-118: The headersx-attested-relay-puzzleandx-attested-relay-evidencepoint to artifacts, but these are HTTP headers that can be stripped or replaced- Missing feature: There is no cryptographic binding in the encrypted record itself that ties it to a specific puzzle/manifest ID that can only be verified after puzzle publication
This is explicitly noted as a limitation in the threat model:
"Individual record envelopes are not service-signed. Once the epoch key is public, anyone holding it can create another valid AEAD record."
Impact: A malicious archive could replay old puzzles with new records, or mix up records from different epochs. Provenance relies on external records (e.g., Cloudflare logs) rather than cryptographic proof.
-
Puzzle signature binding is insufficient:
crates/timelock/src/lib.rs:216-226:SignedManifest::verify()checks the service signature- However, the puzzle itself is only signed once at generation time, and the signature doesn't include a timestamp or epoch activation deadline
Missing feature: A puzzle signature should be invalid if published after a certain time relative to epoch activation.
B. Adversary Capability B: Can epoch keys/secrets be obtained early or make work reused prematurely?
VIOLATED: Missing protections against work sharing and key exposure
Critical violations:
-
No protection against shared/precomputed RandomX work:
crates/timelock/src/lib.rs:287-398: Thegeneratefunction uses 7 parallel RandomX segments- The segments are chained:
seed_{i+1} = unwrap(segment_i)where the final segment yields the epoch key - Missing feature: There is no mechanism to ensure that work done on one puzzle cannot be reused for another
Attack scenario: If an attacker can influence the epoch selection (e.g., by controlling the parent's clock), they could:
- Generate a puzzle with epoch=0 (or a low epoch number) and precompute segments 1-6
- When epoch=1 is later requested, only solve segment 7
While the threat model acknowledges this isn't a proven VDF, the construction doesn't enforce sequentiality across epochs or prevent partial reuse.
-
Epoch key exposure window:
v2_epoch.rs:94:key: Zeroizing<[u8;32]>(the epoch key) is held in theEpochstructv2_epoch.rs:104-151: Therunfunction holds theEpochin memory for the entire epoch lifetimev2_epoch.rs:47-52: Theexpire_activefunction only erases the epoch when its monotonic time expires, not its NSM timestamp
Attack scenario: A parent/host compromise that happens during the epoch lifetime can:
- Read the epoch key from memory (even if zeroized on drop, it may exist in copies)
- Use this key to decrypt any record encrypted with that epoch key
- Publish the key before the puzzle is solved, breaking confidentiality
-
Checkpoint mechanism has no cryptographic verification:
crates/timelock/src/lib.rs:404-456: TheCheckpointstruct includes a digest but:- The digest is only validated against the checkpoint itself, not against the epoch key commitment
- There is no proof that intermediate segment values are correct until the final key is recovered
Missing feature: Checkpoints should be signed by the enclave or include a cryptographic proof that the work was done correctly.
-
Generation and solving share the sameRandomX implementation:
crates/timelock/src/lib.rs:458-498: Thesolvefunction uses the same code path as generation- Missing feature: There is no distinction between "generation work" and "solving work" that would prevent:
- Reusing a generation cache for solving
- Parallelizing generation across multiple epochs to speed up future solving
C. Supporting Evidence: Where the design is strong
Strong design decisions:
-
Zeroizing secrets:
zeroize::Zeroizingis used consistently for keys and intermediate valuesrelay-timelock/src/lib.rs:171-173: The signing seed is zeroized immediately after use
-
Bounded resource usage:
crates/enclave/src/v2_proxy.rs:78-80: Requests are bounded by a semaphorecrates/enclave/src/v2_proxy.rs:107: Record publishing has a 30-second timeout
-
Content-addressed storage:
crates/enclave/src/v2_epoch.rs:59: Artifacts are named by their SHA256 digestcrates/host/src/artifacts.rs:67-123: The host daemon only stores artifacts that match their digest, preventing tampering
-
Hardware verification:
crates/enclave/src/hardware.rs:237-267: Graviton5 verification uses NSM, CPU ID, and EC2 API attestation
-
DNS resolution in enclave:
dns.rs:182-211: Resolves names over HTTPS, never exposing queries to the parent
D. Critical Missing Evidence: What prevents confidence
Missing verification:
-
No proof that puzzles were published within their validity window:
- Requirement: A puzzle should only be valid if published before
epoch_activated + max_publish_delay - Missing: No timestamp in the puzzle manifest, no signature covering the publish deadline
Evidence:
relay-timelock/src/lib.rs:388-397: The puzzle is signed before it's published, with no binding to a publish deadline. - Requirement: A puzzle should only be valid if published before
-
No cryptographic binding between puzzle and attestation:
- Requirement: The puzzle should only be valid if the attestation is fresh and for the same enclave
- Missing: The puzzle signature doesn't include the attestation timestamp or PCR values
Evidence:
v2_epoch.rs:88: The puzzle artifact is published after the attestation, with no cryptographic link. -
No durability proof for ciphertext storage:
- Requirement: Records should only be returned to clients if they are durably stored
- Missing: The parent's "OK" acknowledgment is not cryptographically signed or tied to durability
Evidence:
crates/host/src/main.rs:339-348: The host writes "OK\n" after storing, but this doesn't prove replication, persistence, or prevention of deletion. -
No proof of key erasure:
- Requirement: Epoch keys should be irrevocably erased after expiration
- Missing: No hardware or OS-level guarantee that all copies are erased
Evidence: The design relies on
Zeroizing, which is good practice but not provably complete.
E. Concrete Attack Vectors
Attack 1: Puzzle publication deadline bypass
** Attacker capability**: Parent/host with ability to delay publish operations
** Trigger**: Delay publish for a solvable puzzle until after its epoch activation deadline
** Result**: Puzzle becomes valid for a future epoch without additional work
** Evidence**: v2_epoch.rs:144 has no deadline enforcement for puzzle publication
Attack 2: Epoch key extraction
** Attacker capability**: Host compromise during epoch lifetime
** Trigger**: Read memory of the enclave process
** Result**: Epoch key is available before puzzle is solved
** Evidence**: v2_epoch.rs:94 holds key: Zeroizing<[u8;32]> in memory until expiration
Attack 3: Puzzle replay with old attestation
** Attacker capability**: Malicious archive with old puzzles and attestations ** Trigger**: Replay old puzzle with new record ** Result**: Archive can serve old puzzles as if they were new ** Evidence**: No cryptographic binding between puzzle and attestation timestamp
Attack 4: Work sharing across epochs
** Attacker capability**: Attacker can influence epoch selection ** Trigger**: Precompute work for low epochs, use for higher epochs ** Result**: Reduces effective security of the puzzle ** Evidence**: RandomX segments are not tied to specific epochs in a way that prevents reuse
F. Recommendations
High priority:
-
Add puzzle publication deadline:
- Include a
published_beforetimestamp in the puzzle manifest - Sign this timestamp as part of the puzzle signature
- Enforce that
published_before <= epoch_activated + max_publish_delay
- Include a
-
Cryptographically bind puzzle to attestation:
- Include the attestation timestamp/PCR values in the puzzle signature
- Require the attestation to be fresh (e.g., within 1 hour) for puzzle validity
-
Verify puzzle publication timing:
- Add a
published_attimestamp that is cryptographically bound to the puzzle - Verify this timestamp is within the epoch's validity window
- Add a
-
Improve key erasure:
- Use hardware-level key erasure if available (e.g., Nitro Enclave Key Management)
- Add a "key destroyed" attestation to prove erasure
Medium priority:
-
Add record puzzle binding:
- Include the puzzle ID in the encrypted record's nonce or context
- Require puzzle publication before allowing record decryption
-
Add durability proof:
- Have the parent sign a durability attestation for each record
- Include this in the record's cryptographic binding
-
Prevent work sharing:
- Tie RandomX segments to specific epochs in a way that prevents reuse
- Add a "work committed" attestation before allowing puzzle publication
Low priority:
-
Add checksums for intermediate values:
- Include checksums for each RandomX segment during generation
- Verify these checksums during solving
-
Add work proof:
- Include a Merkle tree root of all segment hashes
- Require this root to be published with the puzzle
G. Final Assessment
Confidentiality guarantee: CONDITIONALLY SUPPORTED with major caveats.
The design successfully prevents intermediaries from accessing request/response contents if:
- The puzzle remains unsolved,
- The epoch key is not extracted before publication,
- The puzzle is published within its validity window, and
- No practical side-channel or memory-extraction attack exists.
However, the following gaps prevent a strong safety guarantee:
- Missing puzzle publication deadline enforcement (VIOLATED)
- Active epoch key exposure before publication (VIOLATED)
- No cryptographic binding between records and puzzles (VIOLATED)
- No durability proof for ciphertext storage (VIOLATED)
Would I rely on this for sensitive content? NO, not until the above gaps are addressed.
Recommendation: Do not deploy for sensitive data until:
- Puzzle publication deadlines are enforced,
- Epoch keys are protected until after puzzle publication, and
- Cryptographic binding between records, puzzles, and attestations is implemented.
The design shows promise but is not yet production-ready for high-security applications.