---
last_verified: 2026-05-30
verified_version: 0.1.48
owner: backend
freshness_days: 30
---

# Core / Crypto Patterns

Critical patterns for the Rust core. See `.claude/rules/coding-standards.md` and `THREAT_MODEL.md`.

---

## #1 Use only audited crypto crates

**Severity**: CRITICAL

### Rule
Never implement a cryptographic primitive or protocol by hand. Use the agreed audited crates. A change requires an ADR.

### Correct
```rust
use x25519_dalek::{EphemeralSecret, PublicKey};
use ml_kem::MlKem768;            // pinned, audited
```
### Wrong
```rust
fn my_xor_cipher(data: &[u8], key: &[u8]) -> Vec<u8> { /* homerolled — FORBIDDEN */ }
```
### Verification
```bash
cargo deny check && cargo audit
```

---

## #2 Zeroize and never log secrets

**Severity**: CRITICAL

### Rule
Wrap key material in `Zeroizing`/`Secret`; never put plaintext, keys, tokens, IPs, or onion addresses in logs, `Debug`,
panics, or errors.

### Correct
```rust
use zeroize::Zeroizing;
let shared = Zeroizing::new(kdf(&dh, &kem));   // wiped on drop
tracing::debug!(peer = %contact_id, "message decrypted");   // no content/keys
```
### Wrong
```rust
println!("session key = {:?}", session_key);   // FORBIDDEN
```
### Verification
Grep for `println!`/`dbg!`/`{:?}` near key types; `enforce_coding_standards.py` flags these.

---

## #3 Fail closed; no panic on attacker input

**Severity**: CRITICAL / HIGH

### Rule
If transport is unavailable, do NOT fall back to a direct/clearnet path. Never `unwrap()`/`expect()`/`panic!` on
attacker-controllable input in the core — return a typed error.

### Correct
```rust
let circuit = tor.connect(&token).await?;       // propagate error, do not fall back
let msg = Envelope::decode(bytes).map_err(Error::Malformed)?;
```
### Wrong
```rust
let msg = Envelope::decode(bytes).unwrap();      // panics on hostile input
if tor_failed { connect_directly(); }            // FORBIDDEN
```

---

## #4 Constant-time secret comparison

**Severity**: CRITICAL

### Rule
Compare secrets/MACs/tokens with `subtle::ConstantTimeEq`, never `==`.

### Correct
```rust
use subtle::ConstantTimeEq;
if mac_a.ct_eq(&mac_b).into() { /* ok */ }
```
### Wrong
```rust
if mac_a == mac_b { /* timing side-channel */ }
```
