Five-layer obfuscation with a planted LWE puzzle (one-time floor) and a configurable hash-PoW layer.
TLOS is a practical circuit obfuscation framework for EVM. It uses standard LWE with Gaussian noise (σ=25, n=384) for control function hiding, full-rank linear hashing for wire binding, and a planted LWE puzzle that adds a one-time q^128 brute-force floor for recovering the planted solution (lattice estimator ~2^58 for current params). Security is based on standard LWE hardness (~2^112 PQ).
TLOS is an active research prototype exploring layered obfuscation for smart contracts. It is not a production security system and should not be used to protect real value without independent review. The repo remains open to issues and discussion, and we intend to publish iterative updates as we refine the threat model, benchmarks, and parameter choices.
Security note: Low-entropy secrets are dictionary-bound even with the planted puzzle and PoW. The puzzle's "q^128 floor" applies only to recovering the planted solution from (A, b) without guessing; it does not raise offline enumeration cost for weak inputs. PoW is an online throttle unless the success predicate is fully masked with no public side channel.
TLOS provides five-layer security for on-chain circuit obfuscation, with configurable PoW throttling:
- Topology layer: Structural mixing defeats structural/statistical attacks (heuristic)
- LWE layer: Standard LWE with Gaussian noise (σ=25, n=384) hides control functions (~2^112 PQ)
- Wire binding layer: Full-rank 64x64 public linear map binds wire values across gates (algebraic binding)
- Planted LWE puzzle: One-time q^128 brute-force floor for recovering the planted solution (8.62M gas, 14% of 60M block)
- Hash-PoW (Layer 5): Hashcash-style work bound to commit-time randomness; throttles on-chain guessing and binds each guess to its commit (set difficulty to 0 to disable)
PoW-masked accept/reject: Success signal keyed by a PoW-derived mask (e.g., M = H(randao || input || solutionHash || nonce)), so guesses cannot be tested without PoW. This only helps if success cannot be computed from public data without M; any unmasked accept/reject side-channel defeats it. Optional variant: mask the input with M, which makes the effective target per-commit (acceptable for recovery flows). Default keeps the LWE instance fixed.
Measured throughput and derived solve times:
- Throughput: 1.768782e9 hashes/sec (~1.77 GH/s)
- D=32: 2.428s per solve (1 GPU)
- D=40: 621.6s per solve (1 GPU)
- 1M dictionary @ D=32: ~28.1 days (1 GPU)
Cost per solve (D=40) ≈ $0.7839 at $0.001261/sec.
| Dictionary size N | eps = 1% | eps = 5% | eps = 10% |
|---|---|---|---|
| 1M | $7,839 | $39,193 | $78,386 |
| 10M | $78,386 | $391,932 | $783,864 |
| 100M | $783,864 | $3,919,319 | $7,838,639 |
| 1B | $7,838,639 | $39,193,194 | $78,386,389 |
Expected cost for a uniform secret is ~N/2 solves (~50x the 1% line). Delegated solvers are optional for rare recovery flows; they add latency and a trust trade-off, so they are not a default for frequent authentication.
The wire binding construction is inspired by Ma-Dai-Shi 2025 but is not subspace-evasive in the formal sense - it is a public bijective linear map providing algebraic binding, not cryptographic hiding.
docs/ARCHITECTURE.mddocs/DEPLOYMENT.mddocs/FEATURE_FLAGS.mddocs/API_ENDPOINTS.mddocs/IMPLEMENTATION.mddocs/SECURITY.md
| Layer | Purpose | Security |
|---|---|---|
| Topology | Anti-attack wire patterns (non-pow2 distances, uniform usage) | Heuristic (empirical) |
| Lattice (LWE) | Control function hiding via LWE with Gaussian noise (σ=25) | ~2^112 PQ (n=384) |
| Obfuscation | Circuit representation hiding | Heuristic |
| Wire Binding | Inter-gate wire consistency via full-rank linear hash | Algebraic binding |
| Puzzle | One-time floor for planted-solution recovery (not per-guess) | Computational (q^128 brute-force) |
| PoW (configurable) | On-chain throttling bound to commit-time randomness (per-commit guesses) | Protocol-level |
+--------------------------------------------------------------+
| Input x + puzzle solution s |
+--------------------------------------------------------------+
|
v
+--------------------------------------------------------------+
| Layer 4: Verify puzzle ||A s - b||^2 < threshold |
+--------------------------------------------------------------+
|
v
+--------------------------------------------------------------+
| Wire binding init: acc0 = H(seed || x || H(s)) |
+--------------------------------------------------------------+
|
v
+--------------------------------------------------------------+
| Gates 0..N: LWE C&C + binding update |
+--------------------------------------------------------------+
|
v
+--------------------------------------------------------------+
| Verify: accN == expected AND output == expectedOutput |
+--------------------------------------------------------------+
Total gas below is checkWithPuzzle() (includes the puzzle verification).
| Config (n=384) | Gates | Total Gas | % of 60M Block |
|---|---|---|---|
| Conservative | 64 | 4,734,943 | 7% |
| Balanced | 128 | 5,917,147 | 9% |
| Standard | 256 | 8,981,516 | 14% |
| Full | 640 | 18,184,574 | 30% |
Optimizations applied:
- Seed-derived
avectors: 99.6% storage reduction (11 bytes/gate vs 3083 bytes) - Wire binding PRG: 16 coefficients per Keccak256 (320 calls vs 4096)
- Single mod at end of inner product (vs per-term mod)
- Batch size 128 (binding updates every 128 gates)
- n=384 LWE dimension with Gaussian noise (σ=25) for ~2^112 PQ security
- Layer 4 puzzle: n=128, m=192, q=2039 for a one-time q^128 brute-force floor
TLOS uses seed-derived a vectors - the public LWE vectors are regenerated on-chain from a circuit seed instead of being stored.
| Config (n=384) | Gates | Storage | Old Format | Savings |
|---|---|---|---|---|
| Conservative | 64 | 704 bytes | 394 KB | 99.6% |
| Balanced | 128 | 1.4 KB | 788 KB | 99.6% |
| Full security | 256 | 2.8 KB | 1.58 MB | 99.6% |
Deployment scheme (Tenderly, receipts):
- Deploy circuit data via SSTORE2: 662,099 gas (256 gates) / 1,575,259 gas (640 gates)
- Deploy puzzle
bvia SSTORE2: fixed per-m(384 bytes for n=128, m=192) - Deploy TLOS instance (no data duplication): 1,707,897 gas (256) / 1,707,885 gas (640)
- Total deploy: 2,454,436 gas (256) / 3,367,584 gas (640)
View vs Transaction costs:
check(input): Free (view function, local simulation)reveal(input, puzzleSolution, nonce)/mint(): 4,734,943-18,184,574 gas (state-changing, executes_evaluate()+ puzzle)- If PoW is disabled,
noncecan be 0
- If PoW is disabled,
Why use TLOS instead of keccak256(secret)? For random 256-bit secrets, Keccak256 is simpler and sufficient. However, TLOS provides significant advantage for low-entropy secrets and multi-bit payloads.
| Secret Type | Keccak256 Attack (est.) | TLOS Attack (GPU) |
|---|---|---|
| Random 256-bit | ~2^256 hashes | min(2^256, ~2^112) |
| Human password (10^6) | Milliseconds | ~181 ms |
| Range 0-100K | ~0.1 seconds | ~17 ms |
| 4-word phrase | Seconds | Seconds |
Critical limitation: Layer 4 provides only ~0.17 µs per-guess overhead on GPU (5.8M guesses/sec on A100). For low-entropy secrets, dictionary attacks complete in milliseconds to seconds. The "q^128 floor" only applies to recovering the planted solution from (A,b) without guessing the secret.
Attack cost formula:
AttackCost = |Dictionary| × 0.17µs (GPU)
AttackCost = |Dictionary| × 75µs (CPU)
PoW throttles on-chain attempts only; it does not affect offline enumeration. High-entropy secrets are required for security against offline adversaries.
- Password-gated vaults: Human phrase unlocks funds
- On-chain treasure hunts: Riddle answer reveals GPS coordinates or URL
- Number guessing games: Hide value in 0-100K range without enumeration
- Multi-code access: OR of N event codes (any code unlocks)
- Hidden game parameters: Tournament seeds revealed at game start
For random 256-bit secrets with no payload beyond TRUE/FALSE, simple Keccak256 commitments are better (cheaper, simpler, no security loss).
Security is based on standard LWE hardness with Gaussian noise (σ=25). The lattice estimator confirms ~2^112 PQ security for n=384, q=65521, m=2560. See paper/tlos-paper.pdf for full analysis.
| Parameter | Value |
|---|---|
| Secret dimension n | 128 |
| Samples m | 192 |
| Modulus q | 2039 |
| Secret distribution | Uniform mod q |
| Error distribution | Uniform {-2,-1,0,1,2} |
| Threshold | 800 |
| Search space | q^128 brute-force (lattice estimator ~2^58) |
| Verification gas | 8.62M (Tenderly, 14% of 60M block) |
Puzzle recovery cost (one-time): Brute-force search over q^128 is computationally infeasible. The lattice estimator suggests ~2^58 security for current parameters. This does not change low-entropy dictionary asymptotics.
- Mix-and-match prevention: Gates cannot be evaluated with inconsistent inputs
- Execution trace binding: Full evaluation history is committed
- Algebraic binding: Full-rank 64x64 matrix provides unique preimage (bijective map)
- Cryptographic hiding: The linear map is public and invertible
- Collision resistance: Trivial to find x given Ax = y
- Key extraction resistance: Still relies on LWE layer for CF hiding
- iO security: Obfuscations of equivalent circuits are not indistinguishable
- VBB security: Virtual black-box is impossible in general
- Long-term secret protection: Not recommended for secrets requiring decades of protection
# Clone
git clone https://github.com/igor53627/tlos.git
cd tlos
# Build contracts
forge build
# Generate circuit data
cargo run --bin generate_tlos -- --secret 0x... --seed 42
# Run benchmarks on Tenderly
source ~/.zsh_secrets
forge script scripts/BenchmarkTLOS.s.sol --rpc-url "$TENDERLY_RPC" --broadcast --unlocked -vvvtlos/
├── contracts/
│ ├── TLOSWithPuzzleV5.sol # Production: 5-layer TLOS (PoW default; difficulty configurable)
│ ├── WeakLWEPuzzleV7.sol # Production puzzle (n=128, m=192, q^128 brute-force floor)
│ └── interfaces/
│ └── IHoneypot.sol # Commit-reveal interface
├── src/ # Rust implementation
│ ├── circuit.rs # Circuit/gate structures (Layer 1)
│ ├── lwe.rs # LWE encoding (Layer 2, Gaussian noise, σ=25)
│ ├── wire_binding.rs # Wire binding implementation (Layer 3)
│ ├── generator.rs # Deployment generator
│ ├── security/ # Security estimation
│ │ └── lattice_estimator.rs # lattice-estimator CLI wrapper
│ └── bin/
│ └── generate_tlos.rs # CLI binary
├── test/ # Foundry tests (168 tests)
│ ├── TLOSWithPuzzleV5.t.sol # Production contract tests (61 tests)
│ ├── TLOSWithPuzzleV5Harness.sol # Test harness for isolated testing
│ ├── PuzzleVariants.t.sol # Puzzle V7 tests (7 tests)
│ └── *.t.sol # Example contract tests
├── scripts/
│ ├── BenchmarkTLOS.s.sol # Tenderly benchmark
│ └── attacks/ # Attack scripts organized by layer
│ ├── layer1-topology/ # SAT/oracle-guided attacks (Rust)
│ ├── layer2-lwe/ # Lattice attacks (Python)
│ ├── layer3-binding/ # Mix-and-match attacks (Python)
│ ├── layer4-puzzle/ # Brute-force attacks (Python/GPU)
│ └── estimators/ # Security estimation tools
├── docs/
│ ├── layers/ # Per-layer technical documentation
│ │ ├── layer1-topology/ # Circuit mixing (heuristic)
│ │ ├── layer2-lwe/ # LWE encryption (~2^112 PQ)
│ │ ├── layer3-binding/ # Wire binding (algebraic)
│ │ └── layer4-puzzle/ # Planted LWE puzzle (q^128 brute-force)
│ ├── SECURITY.md # Security model
│ └── wire-binding.md # Wire binding details
├── paper/
│ ├── tlos-paper.pdf # Full paper (source of truth)
│ └── tlos.pdf # Short paper
└── examples/
├── TLOSDeadManSwitch.sol # Inheritance: Heartbeat + hidden heir codes
├── TLOSRecovery.sol # Wallet: Phrase-based recovery with puzzle
├── TLOSSealedAuction.sol # Gaming: Sealed-bid auction with puzzle
├── TLOSStopLoss.sol # DeFi: Hidden stop-loss triggers
└── TLOSTreasureHunt.sol # Honeypot: Commit-reveal + puzzle (educational)
The examples/ directory contains demonstration contracts showing TLOS integration patterns for various use cases. These are for education only - see warnings in each file.
| Example | Use Case | Layers Used | Puzzle | Production Ready |
|---|---|---|---|---|
| TLOSWithPuzzleV5 | Production | 1-5 (all) | Yes | [OK] |
| TLOSDeadManSwitch | Inheritance | 4 (puzzle) | Yes | [X] Demo only |
| TLOSRecovery | Wallet recovery | 4 (puzzle) | Yes | [X] Needs phrase entropy |
| TLOSSealedAuction | Sealed-bid auction | 4 (puzzle) | Yes | [X] Demo only |
| TLOSStopLoss | Stop-loss trigger | 2 (circuit) | No | [X] Demo only |
| TLOSTreasureHunt | Honeypot | 4 (puzzle) | Yes | [X] Educational |
Layer key:
- Layer 1: Topology mixing (structural)
- Layer 2: LWE control function hiding (n=384, σ=25 for production)
- Layer 3: Wire binding (algebraic)
- Layer 4: Planted LWE puzzle (one-time q^128 brute-force floor for planted-solution recovery)
- Layer 5: Hash-based PoW (commit-time randomness binding; difficulty configurable)
TLOS has comprehensive test coverage with 168 tests across all layers:
# Run all tests
forge test
# Run with gas reporting
forge test --gas-report
# Run specific test file
forge test --match-path test/TLOSWithPuzzleV5.t.solKey test files:
test/TLOSWithPuzzleV5.t.sol- 61 tests for the production contract (deployment, puzzle, wire binding, cross-layer, commit-reveal, gas benchmarks)test/PuzzleVariants.t.sol- 7 tests for the production puzzle V7 (n=128, q^128 brute-force)test/TLOSWithPuzzleV5Harness.sol- Exposes internal functions for isolated layer testing
TLOS security is based on the standard LWE problem with Gaussian noise and the planted LWE puzzle.
- The ~2^112 PQ estimate (for n=384, σ=25) is confirmed by the lattice estimator
- Layer 4 puzzle adds a one-time q^128 brute-force floor for recovering the planted solution; lattice estimator ~2^58 for current params
- Brute-force search over q^128 is computationally infeasible
- We encourage independent cryptanalysis
- Attack scripts organized by layer in
scripts/attacks/- seescripts/attacks/README.md - Do not use for high-value, long-lived secrets until further analysis is available
For programmatic security estimation, set up the lattice-estimator:
# Clone the estimator (requires SageMath)
git clone https://github.com/malb/lattice-estimator estimator
export PYTHONPATH="$PYTHONPATH:$(pwd)/estimator"
# Add the CLI to PATH
export PATH="$PATH:$(pwd)/scripts"
# Verify installation
lattice-estimator-cli 384 65521 \
--s-dist '{"distribution":"uniform_mod"}' \
--e-dist '{"distribution":"discrete_gaussian","stddev":25.0}' \
--m 2560Then run the ignored tests to validate security parameters:
cargo test -- --ignored- Ma-Dai-Shi 2025 - Quasi-Linear iO (wire binding inspiration)
- TLO - Base construction (archived)
- LWE Estimator - Security estimates
MIT