UNPKG

@btc-vision/btc-runtime

Version:

Bitcoin L1 Smart Contract Runtime for OP_NET. Build decentralized applications on Bitcoin using AssemblyScript and WebAssembly. Fully audited.

663 lines (525 loc) 22.8 kB
# Quantum Resistance OP_NET includes built-in quantum-resistant cryptography through ML-DSA (Module-Lattice Digital Signature Algorithm). The `Address` class provides automatic access to ML-DSA public keys without requiring any custom storage. ## Overview Quantum computers pose a threat to traditional cryptographic schemes: | Algorithm | Quantum Threat | OP_NET Status | |-----------|---------------|--------------| | ECDSA | Vulnerable (Shor's algorithm) | Supported (deprecated), transition to ML-DSA | | Schnorr | Vulnerable (Shor's algorithm) | Supported, with transition plan | | ML-DSA | Quantum-resistant | **Fully supported** | ## Quick Start ```typescript import { Blockchain, sha256 } from '@btc-vision/btc-runtime/runtime'; // Verify a signature with quantum resistance const message = new BytesWriter(32); message.writeString('Sign this message'); const messageHash = sha256(message.getBuffer()); // SignaturesMethods.MLDSA ensures quantum-resistant verification const isValid = Blockchain.verifySignature( Blockchain.tx.origin, // Signer's address signature, // Signature bytes messageHash, // Message hash SignaturesMethods.MLDSA // Force ML-DSA (quantum-resistant) ); ``` ## Why Quantum Resistance? ### The Threat - **Public keys are exposed** when addresses send transactions - Quantum computers with ~4000 qubits could derive private keys - Existing signatures remain safe (retroactive attack impossible) - Future transactions from exposed addresses are at risk ### OP_NET's Approach 1. **Dual signature support** - Both Schnorr and ML-DSA signatures 2. **Extended addresses** - Store both Schnorr (taproot) and ML-DSA key references 3. **Built-in key access** - `Address.mldsaPublicKey` property loads keys automatically 4. **Consensus-managed transition** - Automatic migration from Schnorr to ML-DSA ## ML-DSA Security Levels OP_NET supports three ML-DSA security levels, from classical Schnorr to quantum-resistant ML-DSA: ```mermaid --- config: theme: dark --- flowchart LR subgraph OP_NET["OP_NET Security Architecture"] subgraph Classical["Classical Security"] C1["Schnorr<br/>256-bit<br/>Classical: Strong<br/>Quantum: Broken"] end subgraph PostQuantum["Post-Quantum Security Levels"] Q1["ML-DSA-44<br/>Level2<br/>~AES-128<br/>Quantum: Strong"] Q2["ML-DSA-65<br/>Level3<br/>~AES-192<br/>Quantum: Stronger"] Q3["ML-DSA-87<br/>Level5<br/>~AES-256<br/>Quantum: Strongest"] end QC["Quantum Computer<br/>4000+ qubits"] QC -.->|"Breaks in hours"| C1 QC -.->|"Cannot break"| Q1 QC -.->|"Cannot break"| Q2 QC -.->|"Cannot break"| Q3 end ``` | Level | Name | Public Key | Signature | Private Key | NIST Category | |-------|------|------------|-----------|-------------|---------------| | Level2 | ML-DSA-44 | 1,312 bytes | 2,420 bytes | 2,560 bytes | Category 2 (~AES-128) | | Level3 | ML-DSA-65 | 1,952 bytes | 3,309 bytes | 4,032 bytes | Category 3 (~AES-192) | | Level5 | ML-DSA-87 | 2,592 bytes | 4,627 bytes | 4,896 bytes | Category 5 (~AES-256) | **OP_NET uses ML-DSA-44 (Level2) by default**, balancing security and performance. ### Level Constants ```typescript import { MLDSASecurityLevel, MLDSA44_PUBLIC_KEY_LEN, // 1312 MLDSA44_SIGNATURE_LEN, // 2420 MLDSA65_PUBLIC_KEY_LEN, // 1952 MLDSA65_SIGNATURE_LEN, // 3309 MLDSA87_PUBLIC_KEY_LEN, // 2592 MLDSA87_SIGNATURE_LEN // 4627 } from '@btc-vision/btc-runtime/runtime'; ``` ## ML-DSA Signature Structure The ML-DSA signature format follows FIPS 204: ```mermaid --- config: theme: dark --- flowchart LR subgraph OP_NET["OP_NET Quantum-Resistant Signatures"] subgraph MLDSA["ML-DSA-44 - Level2 - Default"] PK["Public Key<br/>1,312 bytes"] SIG["Signature<br/>2,420 bytes"] SK["Private Key<br/>2,560 bytes"] end subgraph PKComp["Public Key Components"] PK1["rho: seed"] PK2["t1: compressed hint"] end subgraph SigComp["Signature Components"] SIG1["c_tilde: commitment hash"] SIG2["z: response vector"] SIG3["h: hint"] end end ``` ## The Address Class ### Automatic ML-DSA Key Access Every `Address` in OP_NET stores the SHA256 hash of an ML-DSA public key. The full public key is automatically loaded on demand: ```typescript import { Address, Blockchain } from '@btc-vision/btc-runtime/runtime'; // Any address can access its ML-DSA public key const sender: Address = Blockchain.tx.sender; // Get the ML-DSA public key (loaded automatically from the blockchain) const mldsaKey: Uint8Array = sender.mldsaPublicKey; // The key is cached after first access for efficiency const sameKey: Uint8Array = sender.mldsaPublicKey; // Returns cached key ``` **Key points:** - No custom storage needed - the runtime handles key storage - Keys are lazily loaded on first access and cached - The address itself is the SHA256 hash of the ML-DSA public key ### How It Works Internally ```typescript // Address stores 32-byte SHA256 hash of ML-DSA public key class Address extends Uint8Array { protected _mldsaPublicKey: Uint8Array | null = null; // Getter loads key from blockchain on demand public get mldsaPublicKey(): Uint8Array { if (!this._mldsaPublicKey) { this._mldsaPublicKey = loadMLDSAPublicKey(this, MLDSASecurityLevel.Level2); } return this._mldsaPublicKey; } } ``` ## Extended Address `ExtendedAddress` supports dual-key addresses for the quantum transition: ```typescript import { ExtendedAddress } from '@btc-vision/btc-runtime/runtime'; // Create from both key components const extAddr = ExtendedAddress.fromStringPair( '0x' + 'aa'.repeat(32), // Tweaked Schnorr key (taproot) '0x' + 'bb'.repeat(32) // ML-DSA key hash ); // Access the tweaked Schnorr key (for taproot/P2TR) const schnorrKey: Uint8Array = extAddr.tweakedPublicKey; // 32 bytes // Access the ML-DSA public key (inherited from Address) const mldsaKey: Uint8Array = extAddr.mldsaPublicKey; // 1312 bytes (ML-DSA-44) // Generate Bitcoin addresses const p2trAddress: string = extAddr.p2tr(); // "bc1p..." or "tb1p..." ``` ### Dual Key Structure ``` ExtendedAddress (64 bytes reference) +-- tweakedPublicKey: Uint8Array[32] --> Schnorr (taproot) +-- Address bytes: Uint8Array[32] --> SHA256(ML-DSA public key) +-> .mldsaPublicKey loads full key ``` ## ML-DSA Key Generation and Verification Flow The following diagram shows the complete lifecycle from key generation to signature verification: ```mermaid --- config: theme: dark --- sequenceDiagram participant User as 👤 User participant Wallet as Wallet participant Blockchain as OP_NET Runtime participant Contract as Contract Note over User,Blockchain: Key Generation (Off-chain) User->>Wallet: Generate ML-DSA keypair Wallet->>Wallet: Generate random seed Wallet->>Wallet: Expand seed to matrix A Wallet->>Wallet: Sample secret vectors s1, s2 Wallet->>Wallet: Compute t = As1 + s2 Wallet->>Wallet: Public key = (rho, t1) Wallet->>Wallet: Private key = (rho, K, tr, s1, s2, t0) Note over User,Blockchain: Address Creation Wallet->>Wallet: Hash ML-DSA public key (SHA256) Wallet->>Wallet: Store 32-byte hash as Address Wallet->>Blockchain: Register address with full public key Note over User,Contract: Signature Verification User->>Contract: call method(signature, message) Contract->>Contract: Get sender address (32-byte hash) Contract->>Blockchain: verifySignature(address, sig, hash, true) Blockchain->>Blockchain: Load cached ML-DSA public key Note over Blockchain: If not cached, load from storage Blockchain->>Blockchain: Verify ML-DSA signature Blockchain->>Blockchain: Decode signature (c_tilde, z, h) Blockchain->>Blockchain: Reconstruct commitment w' Blockchain->>Blockchain: Verify ||z|| < gamma1 - beta Blockchain->>Blockchain: Verify high-order bits match Blockchain-->>Contract: valid: bool ``` ## Signature Verification ### The Simple Way (Recommended) Just use `Blockchain.verifySignature()` - it handles everything: ```typescript import { Blockchain, sha256 } from '@btc-vision/btc-runtime/runtime'; @method(ABIDataTypes.BYTES) @returns({ name: 'valid', type: ABIDataTypes.BOOL }) public verifySignature(calldata: Calldata): BytesWriter { const signature = calldata.readBytesWithLength(); // Create message hash const message = new BytesWriter(55); message.writeString('Hello, world! This is a test message for MLDSA signing.'); const messageHash = sha256(message.getBuffer()); // Verify with quantum resistance (MLDSA) const isValid = Blockchain.verifySignature( Blockchain.tx.origin, signature, messageHash, SignaturesMethods.MLDSA // Force ML-DSA ); const writer = new BytesWriter(1); writer.writeBoolean(isValid); return writer; } ``` ### Consensus-Aware Verification Let the consensus decide which algorithm to use: ```typescript // During transition: uses Schnorr if allowed, ML-DSA otherwise // After transition: always uses ML-DSA const isValid = Blockchain.verifySignature( signer, signature, messageHash, SignaturesMethods.Schnorr // Let consensus decide (falls back to ML-DSA if Schnorr not allowed) ); ``` ### Direct ML-DSA Verification For cases where you need explicit control: ```typescript import { MLDSASecurityLevel } from '@btc-vision/btc-runtime/runtime'; const isValid = Blockchain.verifyMLDSASignature( MLDSASecurityLevel.Level2, // Security level signer.mldsaPublicKey, // Auto-loaded from address signature, // 2420-byte signature messageHash // 32-byte message hash ); ``` ## Migration Path: Schnorr to ML-DSA OP_NET manages a phased transition from classical to quantum-resistant signatures: ```mermaid --- config: theme: dark --- sequenceDiagram participant User as User/Wallet participant Network as OP_NET Network participant Consensus as Consensus Rules Note over User,Consensus: Phase 1: Transition Period (Current) User->>Network: Submit Schnorr signature Network->>Consensus: Check UNSAFE_QUANTUM_SIGNATURES_ALLOWED Consensus-->>Network: Flag = true Network-->>User: Signature accepted User->>Network: Submit ML-DSA signature Network-->>User: Signature accepted Note over Network: Both signature types valid Note over User,Consensus: Phase 2: Warning Period User->>Network: Submit Schnorr signature Network->>Consensus: Check signature type Consensus-->>Network: Log deprecation warning Network-->>User: Signature accepted (with warning) Note over User: Encouraged to migrate to ML-DSA Note over User,Consensus: Phase 3: Quantum-Safe Only User->>Network: Submit Schnorr signature Network->>Consensus: Check UNSAFE_QUANTUM_SIGNATURES_ALLOWED Consensus-->>Network: Flag = false Network-->>User: Signature REJECTED User->>Network: Submit ML-DSA signature Network-->>User: Signature accepted Note over Network: Only ML-DSA signatures valid ``` The `UNSAFE_QUANTUM_SIGNATURES_ALLOWED` consensus flag controls whether Schnorr signatures are still permitted. When disabled, only ML-DSA signatures will be valid. ## Complete Contract Example ```typescript import { OP_NET, Blockchain, Calldata, BytesWriter, Revert, sha256, ABIDataTypes } from '@btc-vision/btc-runtime/runtime'; @final class QuantumSecureContract extends OP_NET { @method( { name: 'message', type: ABIDataTypes.BYTES }, { name: 'signature', type: ABIDataTypes.BYTES }, ) @returns({ name: 'valid', type: ABIDataTypes.BOOL }) public verifyQuantum(calldata: Calldata): BytesWriter { const message = calldata.readBytesWithLength(); const signature = calldata.readBytesWithLength(); const messageHash = sha256(message); // Always use quantum-resistant verification const isValid = Blockchain.verifySignature( Blockchain.tx.origin, signature, messageHash, SignaturesMethods.MLDSA // Force ML-DSA ); const writer = new BytesWriter(1); writer.writeBoolean(isValid); return writer; } @method( { name: 'signer', type: ABIDataTypes.ADDRESS }, { name: 'message', type: ABIDataTypes.BYTES }, { name: 'signature', type: ABIDataTypes.BYTES }, ) @returns({ name: 'valid', type: ABIDataTypes.BOOL }) public verifyForAddress(calldata: Calldata): BytesWriter { const signer = calldata.readAddress(); const message = calldata.readBytesWithLength(); const signature = calldata.readBytesWithLength(); const messageHash = sha256(message); // Verify for a specific address const isValid = Blockchain.verifySignature( signer, signature, messageHash, SignaturesMethods.MLDSA ); const writer = new BytesWriter(1); writer.writeBoolean(isValid); return writer; } @method({ name: 'owner', type: ABIDataTypes.ADDRESS }) @returns({ name: 'keyLength', type: ABIDataTypes.UINT32 }) public getMLDSAKeyLength(calldata: Calldata): BytesWriter { const owner = calldata.readAddress(); // Access ML-DSA public key - automatically loaded const mldsaKey = owner.mldsaPublicKey; const writer = new BytesWriter(4); writer.writeU32(mldsaKey.length); // 1312 for Level2 return writer; } } ``` ## Solidity vs OP_NET: Quantum Resistance Comparison OP_NET is the first smart contract platform with built-in quantum-resistant cryptography. Solidity and the EVM have no quantum resistance capabilities. ### Feature Comparison Table | Feature | Solidity/EVM | OP_NET | OP_NET Advantage | |---------|--------------|-------|-----------------| | **Quantum-Safe Signatures** | Not supported | ML-DSA (FIPS 204) | Future-proof security | | **Post-Quantum Algorithm** | None | ML-DSA-44/65/87 | NIST standardized | | **Dual-Key Architecture** | Not available | Schnorr + ML-DSA | Smooth transition | | **Key Loading** | Manual storage/derivation | Automatic via `Address` | Zero setup required | | **Consensus Migration** | Not applicable | Built-in transition plan | Network-coordinated | | **Security Levels** | ECDSA only | 3 ML-DSA levels | Flexible security | | **Algorithm Selection** | Fixed (ECDSA) | Consensus-aware | Automatic switching | ### Quantum Threat Analysis | Algorithm | Shor's Algorithm Impact | Grover's Algorithm Impact | Status in OP_NET | |-----------|------------------------|---------------------------|-----------------| | ECDSA (Solidity) | **Broken** (polynomial time) | Weakened | N/A | | ECDSA (OP_NET) | **Broken** (polynomial time) | Weakened | Deprecated | | Schnorr (OP_NET) | **Broken** (polynomial time) | Weakened | Transition only | | ML-DSA (OP_NET) | **Secure** | Minimal impact | **Recommended** | ### Security Level Comparison | Security Level | Solidity | OP_NET ML-DSA | NIST Category | Quantum Security | |----------------|----------|--------------|---------------|------------------| | ~AES-128 equivalent | ECDSA (broken by quantum) | ML-DSA-44 (Level2) | Category 2 | **Secure** | | ~AES-192 equivalent | Not available | ML-DSA-65 (Level3) | Category 3 | **Secure** | | ~AES-256 equivalent | Not available | ML-DSA-87 (Level5) | Category 5 | **Secure** | ### Key and Signature Size Comparison | Metric | Solidity (ECDSA) | OP_NET (Schnorr) | OP_NET (ML-DSA-44) | OP_NET (ML-DSA-87) | |--------|------------------|-----------------|-------------------|-------------------| | Public Key Size | 33/65 bytes | 32 bytes | 1,312 bytes | 2,592 bytes | | Signature Size | 65 bytes | 64 bytes | 2,420 bytes | 4,627 bytes | | Private Key Size | 32 bytes | 32 bytes | 2,560 bytes | 4,896 bytes | | Quantum Safe | **No** | **No** | **Yes** | **Yes** | ### Capability Matrix | Capability | Solidity | OP_NET | |------------|:--------:|:-----:| | ECDSA verification (Ethereum) | Yes | Yes (deprecated) | | ECDSA verification (Bitcoin) | No | Yes (deprecated) | | Schnorr verification | No | Yes | | ML-DSA-44 verification | No | Yes | | ML-DSA-65 verification | No | Yes | | ML-DSA-87 verification | No | Yes | | Automatic public key loading | No | Yes | | Quantum-resistant addresses | No | Yes | | Dual-key addresses | No | Yes | | Consensus-aware algorithm | No | Yes | | Phased migration support | No | Yes | ### Timeline: Quantum Threat vs Platform Readiness | Timeframe | Quantum Computer Status | Solidity Status | OP_NET Status | |-----------|------------------------|-----------------|--------------| | **2024-2025** | Early NISQ era (~1000 qubits) | Vulnerable (no plan) | ML-DSA ready | | **2026-2030** | Scaling (~4000+ qubits possible) | **Critical risk** | Dual-key transition | | **2030+** | Cryptographically relevant | **Funds at risk** | ML-DSA only (secure) | ### Migration Path Comparison ```mermaid --- config: theme: dark --- flowchart LR subgraph Solidity["Solidity/EVM - No Migration Path"] S1["ECDSA Only"] --> S2["No quantum option"] S2 --> S3["Fork required?"] S3 --> S4["Funds at risk"] end subgraph OP_NET["OP_NET - Built-in Migration"] O1["Schnorr (current)"] --> O2["Dual-key period"] O2 --> O3["ML-DSA only"] O3 --> O4["Quantum secure"] end ``` ### Signature Verification Comparison #### Solidity: No Quantum Protection ```solidity // Solidity - Vulnerable to quantum attacks // There is NO way to add quantum resistance to Solidity/EVM function verify(bytes32 hash, uint8 v, bytes32 r, bytes32 s) external view returns (bool) { address recovered = ecrecover(hash, v, r, s); return recovered == expectedSigner; // CRITICAL VULNERABILITIES: // - ECDSA is broken by Shor's algorithm // - No update path to quantum-safe algorithms // - All funds signed with exposed public keys at risk // - No way to add ML-DSA or other PQC algorithms } // Even with precompiles, no quantum-safe option exists // EIP proposals for PQC have not been implemented ``` #### OP_NET: Built-in Quantum Resistance ```typescript // OP_NET - Quantum-resistant @method( { name: 'hash', type: ABIDataTypes.BYTES32 }, { name: 'signature', type: ABIDataTypes.BYTES }, ) @returns({ name: 'valid', type: ABIDataTypes.BOOL }) public verify(calldata: Calldata): BytesWriter { const hash = calldata.readBytes(32); const signature = calldata.readBytesWithLength(); // ML-DSA provides quantum resistance // FIPS 204 standardized algorithm // Public key loaded automatically from address const valid = Blockchain.verifySignature( this.expectedSigner.value, signature, hash, SignaturesMethods.MLDSA // Force quantum-resistant ML-DSA ); const writer = new BytesWriter(1); writer.writeBoolean(valid); return writer; } // Advantages: // - ML-DSA is quantum-resistant (lattice-based) // - NIST standardized (FIPS 204) // - Automatic key loading from address // - Consensus-managed transition // - No code changes needed for migration ``` ### Public Key Access Comparison ```solidity // Solidity - Must store or derive public key contract SolidityContract { // Must manually store public keys mapping(address => bytes) public publicKeys; function registerKey(bytes calldata pubkey) external { // Verify key matches address require(address(uint160(uint256(keccak256(pubkey)))) == msg.sender); publicKeys[msg.sender] = pubkey; } // No way to store/use ML-DSA keys (1,312+ bytes each!) // Storage costs would be prohibitive } ``` ```typescript // OP_NET - Automatic key access @final class OPNetContract extends OP_NET { public getPublicKey(calldata: Calldata): BytesWriter { const user = calldata.readAddress(); // ML-DSA public key loaded automatically! // No storage needed - runtime handles it const mldsaKey = user.mldsaPublicKey; // 1,312 bytes // Key is cached after first access const writer = new BytesWriter(4); writer.writeU32(mldsaKey.length); return writer; } } ``` ### Why OP_NET for Quantum Security? | Solidity Limitation | OP_NET Solution | |---------------------|----------------| | ECDSA only (quantum vulnerable) | ML-DSA (quantum resistant) | | No update path | Built-in consensus migration | | Must store large keys manually | Automatic key loading | | No NIST PQC algorithms | FIPS 204 ML-DSA | | Single key per address | Dual-key architecture | | Fixed algorithm forever | Consensus-aware selection | | Fork required for PQC | Smooth transition built-in | | All funds at risk | Protected from day one | ### Cost Comparison | Operation | Solidity | OP_NET Cost | Notes | |-----------|----------|------------|-------| | Store ML-DSA public key | Not practical (1,312 bytes) | 0 | OP_NET loads automatically | | Store ML-DSA signature | Not practical (2,420 bytes) | N/A | Not stored, verified | | Quantum-safe verification | Not possible | Standard | No additional cost | | Key migration | Contract redeploy | Consensus-managed | No user action needed | ## Best Practices 1. **Use `Address.mldsaPublicKey`** - Don't store ML-DSA keys manually; the Address class handles key loading and caching automatically (see [The Address Class](#the-address-class)) 2. **Force ML-DSA for high-security operations** - Pass `SignaturesMethods.MLDSA` to `Blockchain.verifySignature()` for critical operations (see [Signature Verification](#signature-verification)) 3. **Use consensus-aware verification for general use** - Pass `SignaturesMethods.Schnorr` (the default) to let the network decide which algorithm to use during the transition period. When Schnorr is no longer allowed by consensus, it automatically falls back to ML-DSA 4. **Document security level** - When using quantum-resistant signatures, document the ML-DSA level in your contract comments: ```typescript /** * Security Note: This contract uses ML-DSA-44 for quantum resistance. * ML-DSA-44 provides NIST Category 2 security (~AES-128 equivalent). * For higher security requirements, ML-DSA-65 or ML-DSA-87 may be used. */ ``` --- **Navigation:** - Previous: [Signature Verification](./signature-verification.md) - Next: [Bitcoin Scripts](./bitcoin-scripts.md)