reviews/current/objective-20260909T223027Z/mistralai__mistral-medium-3-5.md
On this page

Independent Pi/OpenRouter review: mistralai/mistral-medium-3-5

Source: a10323dede4413fbf295916b8ad12e3dbad7514e. Status: completed.

To assess whether the timelock-proxy implementation meets its confidentiality and delayed-disclosure objectives, we must evaluate two core properties:



❓ A. Can an in-scope adversary recover request/response contents before the intended ~1-week delay?

πŸ” Key Findings

1. Puzzle Calibration and Solving Time

2. Epoch Lifetime and Publication Timing

3. Record Encryption and Key Derivation

4. Practical Implications


βœ… Verdict for A: VIOLATED



❓ B. Can an adversary obtain epoch keys, private seeds/intermediates, or equivalent decryption capability early?

πŸ” Key Findings

1. Epoch Key Generation and Protection

2. Puzzle Structure and Seed Chaining

3. Early Key Recovery via Parallelism

4. No Pre-Solved Puzzle Reuse

5. Checkpoints and Resume Attacks


βœ… Verdict for B: VIOLATED



πŸ“Œ Summary of Critical Issues

Property Verdict Reason Evidence
A: Confidentiality until ~1 week VIOLATED Adversary with 7+ CPUs can solve the puzzle in ≀24h, recovering late-epoch requests with 0h confidentiality. config/relay-v2.toml (7 segments Γ— 24h/segment = 168 CPU-hours), v2_epoch.rs:activate() (puzzle published at epoch start), lib.rs:generate_with_rng (sequential unwrapping).
B: No early epoch key recovery VIOLATED Same as A: Adversary can solve the puzzle early, obtaining the epoch key without breaking RandomX. v2_epoch.rs:run() (generation before activation), lib.rs:solve() (sequential solving required).

πŸ”¬ Root Cause Analysis

1. Work-Based Delay vs. Wall-Clock Guarantee

2. Epoch Lifetime Shorter Than Solving Time on Reference CPU

3. No Mitigation for Parallelism


🎯 Code-Grounded Evidence

1. Puzzle Publication Before Activation

File: crates/enclave/src/v2_epoch.rs

// Line 76-102: `activate()` function
let document = state.attestation_for_epoch(&rand::random::<[u8;32]>()).await?;
let evidence_artifact = publish(state, "attestation.json", &serde_json::to_vec(&document)?).await?;
// ...
let puzzle_artifact = publish(state, "puzzle.json", &serde_json::to_vec(&generated.manifest)?).await?; // Puzzle published FIRST
// ...
let active = Arc::new(Epoch { ... });
*state.active.write().await = Some(active); // Epoch activated AFTER puzzle is public

➑ Puzzle is public before epoch is active, allowing adversaries to start solving immediately.

2. Sequential Unwrapping Requires Full Work

File: crates/timelock/src/lib.rs

// Line 262-284: `unwrap()` function
fn unwrap(puzzle: &Manifest, segment: usize, y: &[u8; 32]) -> Result<Zeroizing<[u8; 32]>> {
    let context = wrap_context(puzzle, segment)?;
    let key = wrap_key(y, &context)?; // Key derived from segment output
    let wrapped = bytes::<60>(&puzzle.wrapped_keys[segment])?;
    let plaintext = ChaCha20Poly1305::new_from_slice(key.as_ref())
        .unwrap()
        .decrypt(Nonce::from_slice(&wrapped[..12]), Payload { msg: &wrapped[12..], aad: &context })
        .map_err(|_| anyhow::anyhow!("segment {} authentication failed", segment + 1))?;
    Ok(Zeroizing::new(plaintext.as_slice().try_into()?))
}

➑ No shortcut: Each wrapped_key can only be decrypted with the output of the previous segment’s hash chain.

3. Record Encryption Tied to Epoch Key

File: crates/timelock/src/lib.rs

// Line 92-124: `encrypt_record()`
pub fn encrypt_record(
    manifest: &SignedManifest,
    epoch_key: &[u8; 32],
    sequence: u64,
    plaintext: &[u8],
) -> Result<EncryptedRecord> {
    let key = record_key(manifest, epoch_key)?; // Derived via HKDF from epoch_key
    let nonce = [0; 24];
    OsRng.fill_bytes(&mut nonce);
    let context = record_context(manifest, sequence)?;
    let ciphertext = XChaCha20Poly1305::new_from_slice(key.as_ref())
        .unwrap()
        .encrypt(XNonce::from_slice(&nonce), Payload { msg: plaintext, aad: &context })?;
    // ...
}

➑ All records for an epoch are decryptable if the epoch key is known.

4. No Early Publication of Next Puzzle

File: crates/enclave/src/v2_epoch.rs

// Line 104-150: `run()` loop
loop {
    // ...
    let mut publication_not_before: Option<u64> = None;
    // ...
    loop {
        let Some(expiration) = publication_not_before else { break };
        let now = match state.attester.trusted_time_ms_uncached() {
            Ok(now) => now,
            Err(_) => { tokio::time::sleep(Duration::from_secs(1)).await; continue }
        };
        if now >= expiration { break }
        tokio::time::sleep(Duration::from_millis((expiration-now).min(1000))).await;
    }
    // ...
    *state.active.write().await = None;
    match tokio::time::timeout(Duration::from_secs(60), activate(&state, generated)).await {
        Ok(Ok(expiration)) => { activated_once = true; publication_not_before = Some(expiration); },
        _ => { crate::log("puzzle publication failed; service remains closed"); return }
    }
}

➑ Next puzzle cannot be published until the previous epoch’s expiration time (prevents early publication).


πŸ›‘οΈ Strongest Code-Grounded Protections

Protection Mechanism File:Line
Epoch key never leaves enclave Stored as Zeroizing<[u8; 32]> v2_epoch.rs:12
Puzzle requires full work Sequential RandomX chains + wrapped seeds lib.rs:262-284
No early puzzle publication publication_not_before enforced via NSM time v2_epoch.rs:132-140
No pre-solved puzzle reuse Epoch numbers increment sequentially v2_epoch.rs:148-149
Record encryption per-request HKDF-derived per-record keys lib.rs:92-124
Parent cannot read enclave memory Nitro isolation (trusted AWS) Assumption

❓ Missing Evidence / Unresolved Questions

  1. Hardware Side Channels:

    • The threat model explicitly excludes hardware-independent side channel resistance.
    • Unresolved: Could a malicious parent exploit cache timing, memory access patterns, or other microarchitectural leaks to extract the epoch key or RandomX state?
    • Impact: If such leaks exist, they could bypass the RandomX work requirement entirely.
    • Mitigation: None in code (reliant on AWS/Nitro hardware isolation).
  2. RandomX FFI Safety:

    • The RandomX binding (crates/timelock/src/randomx.rs) uses raw FFI to the vendored C library.
    • Unresolved: Are there memory corruption, use-after-free, or buffer overflow vulnerabilities in:
      • The RandomX C library (vendored in vendor/randomx)?
      • The FFI boundary (e.g., incorrect pointer handling, lifetime mismatches)?
    • Impact: A memory corruption bug could leak secrets or crash the enclave.
    • Mitigation: The build script (crates/timelock/build.rs) verifies SHA256 of the vendored source, but does not audit the C code.
  3. Kernel RNG Seeding:

    • The enclave reseeds the kernel RNG from NSM (attest.rs:67-82).
    • Unresolved: Does this fully replace the initial entropy pool? Could a malicious parent influence the kernel RNG state before seeding?
    • Impact: If the kernel RNG retains some parent-influenced state, TLS keys or epoch keys could be predictable.
    • Mitigation: The code uses two reseed operations with a jiffies-based delay to ensure the first reseed is not the only one.
  4. Checkpoint Tampering in CLI Solver:

    • The CLI solver (crates/timelock/src/main.rs) saves checkpoints to disk.
    • Unresolved: Could an adversary modify a checkpoint to skip work?
    • Impact: The solve() function validates checkpoints (Checkpoint::validate() in lib.rs:438-455), so tampered checkpoints are rejected.
    • Mitigation: Checkpoints are signed by their digest and validated against the manifest.
  5. Monotonic vs. NSM Time Mismatch:

    • The epoch uses both NSM time (activated_at_ms) and monotonic time (activated_monotonic) for expiration.
    • Unresolved: Could a clock rollback attack (e.g., parent manipulates NSM time) cause an expired epoch to be reused?
    • Impact: The accepts_at() function (line 26-34) requires both NSM time and monotonic time to be valid, preventing rollback attacks.
    • Mitigation: Monotonic time is independent of NSM time, so even if NSM time is manipulated, the epoch will expire after its monotonic lifetime.

🎯 Final Assessment

Property Verdict Reason
A: Confidentiality until ~1 week VIOLATED Adversaries with 7+ CPUs can solve the puzzle in ≀24h, recovering late-epoch requests with near-zero confidentiality. The work-based delay (7 CPU-days) does not guarantee 7 days of wall-clock confidentiality.
B: No early epoch key recovery VIOLATED Same as A: Adversaries can obtain the epoch key early by solving the puzzle with sufficient compute, without breaking RandomX.

πŸ”₯ Critical Flaw

The fundamental issue is that the objective demands a wall-clock delay (~1 week), but the implementation provides a work-based delay (7 CPU-days). These are not equivalent when adversaries have more compute power than a reference CPU.

πŸ“Œ Would I Rely on This for Sensitive Content?

❌ NO – Under the explicit assumptions:

πŸ› οΈ Recommendations to Fix

  1. Increase the epoch length to 7 days (matching the solo solving time).

    • This ensures that even with 1 CPU, the puzzle cannot be solved within the epoch.
    • Trade-off: Longer gaps if generation takes >7 days.
  2. Use a Verifiable Delay Function (VDF) instead of RandomX.

    • VDFs provide sequentiality guarantees (no parallel speedup).
    • Trade-off: VDFs are less mature and may have higher overhead.
  3. Add a "warm-up" period where the puzzle is not published until partway through the epoch.

    • Example: Publish the puzzle 3 days into a 7-day epoch, ensuring 4 days of minimum confidentiality for all requests.
    • Trade-off: Reduces the audit window (records are only decryptable after the puzzle is public).
  4. Implement per-record delays (e.g., each record has its own puzzle with a unique delay).

    • Trade-off: Higher storage/compute overhead for clients.


πŸ“„ Formal Answer

A. Can an in-scope adversary recover or infer request/response contents before the intended delay?

Verdict: VIOLATED


B. Can an adversary obtain epoch keys, private seeds/intermediates, or equivalent decryption capability early?

Verdict: VIOLATED



πŸ” Strongest Code-Grounded Reasons Attacks Are Blocked

Attack Blocked By File:Line
Parent reads epoch key Zeroizing<[u8; 32]> + Nitro isolation v2_epoch.rs:12 (enclave memory)
Parent modifies puzzle Puzzle signed by enclave key lib.rs:388-393 (manifest signature)
Early puzzle publication publication_not_before + NSM time v2_epoch.rs:132-140
Reuse of solved puzzle Epoch numbers increment sequentially v2_epoch.rs:148-149
Bypass RandomX work Sequential unwrapping required lib.rs:262-284
Decrypt records without epoch key Per-record keys derived from epoch key lib.rs:81-90


❓ Unresolved Questions

  1. Hardware Side Channels:

    • Question: Can a malicious parent exploit cache timing, power analysis, or memory access patterns to extract the epoch key or RandomX state?
    • Impact: If yes, RandomX work can be bypassed entirely.
    • Evidence Needed: Audit of Nitro enclave side-channel resistance (e.g., cache isolation, constant-time operations in RandomX).
  2. RandomX C Library Vulnerabilities:

    • Question: Does the vendored RandomX C library (vendor/randomx) contain memory corruption bugs (e.g., buffer overflows, use-after-free)?
    • Impact: If yes, epoch key or RandomX state could leak.
    • Evidence Needed: Fuzzing/audit of the RandomX C code (especially FFI boundaries in randomx.rs).
  3. Kernel RNG Seeding Effectiveness:

    • Question: Does the two-step kernel RNG reseed (attest.rs:67-82) fully replace the initial entropy pool?
    • Impact: If no, TLS/epoch keys could be predictable.
    • Evidence Needed: Linux kernel source audit (RNG reseed behavior in 4.14.256).
  4. Checkpoint Validation Bypass:

    • Question: Are there edge cases in Checkpoint::validate() (lib.rs:438-455) that allow malicious checkpoints to be accepted?
    • Impact: If yes, solver could skip work.
    • Evidence Needed: Formal verification of checkpoint validation logic.


πŸ“Œ Conclusion

The implementation fails to meet its confidentiality and delayed-disclosure objectives because:

  1. Wall-clock confidentiality degrades with adversary compute power (VIOLATED for A).
  2. Epoch keys can be obtained early via parallel solving (VIOLATED for B).

Would I rely on this for sensitive content? ❌ NO – The work-based delay does not guarantee a ~1-week wall-clock delay against well-resourced adversaries, and late-epoch requests may have near-zero confidentiality.

Strongest protections:

Critical missing evidence: