LPsLux Proposals
Precompiles
LP-3521

Post-Quantum Cryptography Precompile Implementation Guide

Review

Comprehensive implementation reference for all NIST FIPS 203-205 post-quantum cryptography precompiles

Category
Core
Created
2025-12-24

Abstract

This LP provides the definitive implementation reference for all post-quantum cryptography (PQC) precompiles on Lux Network. It documents precompile addresses, gas costs for all algorithm modes, input/output formats, mode byte encodings, and implementation locations across all repositories (geth, evm, precompiles).

Motivation

Post-quantum cryptography is essential for Lux Network's long-term security. As quantum computers advance, traditional elliptic curve cryptography (ECC) will become vulnerable. This LP provides:

  1. Migration Path: Clear specification for developers transitioning from ECC to PQC
  2. Gas Cost Transparency: Predictable costs for smart contract budgeting
  3. Implementation Clarity: Exact addresses, mode bytes, and I/O formats for integration
  4. Test Coverage Visibility: Documented test cases with pass/fail status

Specification

This specification defines the interface and behavior for PQC precompiles:

  1. Precompile Addresses: Three dedicated addresses for ML-DSA, SLH-DSA, and unified PQC operations
  2. Mode Bytes: Algorithm/variant identification (0x00-0x87 range)
  3. Gas Costs: Fixed per-operation costs based on computational complexity
  4. Input/Output Format: Binary encoding for efficient EVM processing
  5. Function Selectors: Four-byte selectors for the unified precompile

Backwards Compatibility

PQC precompiles are additive additions that do not affect existing EVM functionality:

  • New Addresses: 0x0200000000000000000000000000000000000006 (ML-DSA), 0x0200000000000000000000000000000000000007 (SLH-DSA), 0x0200000000000000000000000000000000000010 (Unified) - outside existing range
  • No Behavioral Changes: Existing precompiles remain unchanged
  • Opt-in Migration: Contracts must explicitly call PQC precompiles
  • Gas Model: Separate gas calculation from standard EVM operations

Precompile Addresses

PrecompileAddressDescription
ML-DSA Verify0x0200000000000000000000000000000000000006Dedicated ML-DSA signature verification
SLH-DSA Verify0x0200000000000000000000000000000000000007Dedicated SLH-DSA signature verification
PQCrypto Unified0x0200000000000000000000000000000000000010Unified PQ crypto operations

Gas Costs Reference

ML-DSA Signature Verification (FIPS 204)

ModeSecurity LevelMode ByteGas CostPublic KeySignature
ML-DSA-44NIST Level 2 (128-bit)0x4475,0001,312 bytes2,420 bytes
ML-DSA-65NIST Level 3 (192-bit)0x65100,0001,952 bytes3,309 bytes
ML-DSA-87NIST Level 5 (256-bit)0x87150,0002,592 bytes4,627 bytes

Gas Formula:

gas = BASE_COST[mode]

ML-KEM Key Encapsulation (FIPS 203)

Encapsulation

ModeSecurity LevelMode ByteGas CostPublic KeyCiphertextShared Secret
ML-KEM-512NIST Level 1 (128-bit)0x006,000800 bytes768 bytes32 bytes
ML-KEM-768NIST Level 3 (192-bit)0x018,0001,184 bytes1,088 bytes32 bytes
ML-KEM-1024NIST Level 5 (256-bit)0x0210,0001,568 bytes1,568 bytes32 bytes

Decapsulation

ModeSecurity LevelMode ByteGas CostPrivate Key
ML-KEM-512NIST Level 1 (128-bit)0x006,0001,632 bytes
ML-KEM-768NIST Level 3 (192-bit)0x018,0002,400 bytes
ML-KEM-1024NIST Level 5 (256-bit)0x0210,0003,168 bytes

SLH-DSA Signature Verification (FIPS 205)

ModeHash FunctionSecurityMode ByteGas CostPublic KeySignature
SLH-DSA-SHA2-128sSHA-256Level 10x0050,00032 bytes7,856 bytes
SLH-DSA-SHA2-128fSHA-256Level 10x0175,00032 bytes17,088 bytes
SLH-DSA-SHA2-192sSHA-256Level 30x02100,00048 bytes16,224 bytes
SLH-DSA-SHA2-192fSHA-256Level 30x03150,00048 bytes35,664 bytes
SLH-DSA-SHA2-256sSHA-256Level 50x04175,00064 bytes29,792 bytes
SLH-DSA-SHA2-256fSHA-256Level 50x05250,00064 bytes49,856 bytes
SLH-DSA-SHAKE-128sSHAKE256Level 10x1050,00032 bytes7,856 bytes
SLH-DSA-SHAKE-128fSHAKE256Level 10x1175,00032 bytes17,088 bytes
SLH-DSA-SHAKE-192sSHAKE256Level 30x12100,00048 bytes16,224 bytes
SLH-DSA-SHAKE-192fSHAKE256Level 30x13150,00048 bytes35,664 bytes
SLH-DSA-SHAKE-256sSHAKE256Level 50x14175,00064 bytes29,792 bytes
SLH-DSA-SHAKE-256fSHAKE256Level 50x15250,00064 bytes49,856 bytes

Gas Cost Summary Table

╔════════════════════════════════════════════════════════════════════════════╗
║                    POST-QUANTUM CRYPTOGRAPHY GAS COSTS                     ║
╠════════════════════════════════════════════════════════════════════════════╣
║ ALGORITHM      │ MODE/VARIANT           │ MODE BYTE │ GAS COST            ║
╠════════════════════════════════════════════════════════════════════════════╣
║ ML-DSA         │ ML-DSA-44 (Level 2)    │   0x44    │      75,000         ║
║                │ ML-DSA-65 (Level 3)    │   0x65    │     100,000         ║
║                │ ML-DSA-87 (Level 5)    │   0x87    │     150,000         ║
╠════════════════════════════════════════════════════════════════════════════╣
║ ML-KEM Encap   │ ML-KEM-512 (Level 1)   │   0x00    │       6,000         ║
║                │ ML-KEM-768 (Level 3)   │   0x01    │       8,000         ║
║                │ ML-KEM-1024 (Level 5)  │   0x02    │      10,000         ║
╠════════════════════════════════════════════════════════════════════════════╣
║ ML-KEM Decap   │ ML-KEM-512 (Level 1)   │   0x00    │       6,000         ║
║                │ ML-KEM-768 (Level 3)   │   0x01    │       8,000         ║
║                │ ML-KEM-1024 (Level 5)  │   0x02    │      10,000         ║
╠════════════════════════════════════════════════════════════════════════════╣
║ SLH-DSA        │ SHA2/SHAKE-128s        │ 0x00/0x10 │      50,000         ║
║                │ SHA2/SHAKE-128f        │ 0x01/0x11 │      75,000         ║
║                │ SHA2/SHAKE-192s        │ 0x02/0x12 │     100,000         ║
║                │ SHA2/SHAKE-192f        │ 0x03/0x13 │     150,000         ║
║                │ SHA2/SHAKE-256s        │ 0x04/0x14 │     175,000         ║
║                │ SHA2/SHAKE-256f        │ 0x05/0x15 │     250,000         ║
╚════════════════════════════════════════════════════════════════════════════╝

Mode Byte Encoding

ML-DSA Mode Bytes

const (
    ModeMLDSA44 uint8 = 0x44  // Maps to mldsa.MLDSA44
    ModeMLDSA65 uint8 = 0x65  // Maps to mldsa.MLDSA65
    ModeMLDSA87 uint8 = 0x87  // Maps to mldsa.MLDSA87
)

Important: The precompile mode bytes (0x44, 0x65, 0x87) differ from the library's internal mode values (0, 1, 2). The precompile implementation converts between these formats.

ML-KEM Mode Bytes

const (
    MLKEMMode512  uint8 = 0x00  // Maps to mlkem.MLKEM512
    MLKEMMode768  uint8 = 0x01  // Maps to mlkem.MLKEM768
    MLKEMMode1024 uint8 = 0x02  // Maps to mlkem.MLKEM1024
)

SLH-DSA Mode Bytes

const (
    // SHA-256 variants
    SLHDSAModeSHA2_128s  uint8 = 0x00
    SLHDSAModeSHA2_128f  uint8 = 0x01
    SLHDSAModeSHA2_192s  uint8 = 0x02
    SLHDSAModeSHA2_192f  uint8 = 0x03
    SLHDSAModeSHA2_256s  uint8 = 0x04
    SLHDSAModeSHA2_256f  uint8 = 0x05

    // SHAKE-256 variants
    SLHDSAModeSHAKE_128s uint8 = 0x10
    SLHDSAModeSHAKE_128f uint8 = 0x11
    SLHDSAModeSHAKE_192s uint8 = 0x12
    SLHDSAModeSHAKE_192f uint8 = 0x13
    SLHDSAModeSHAKE_256s uint8 = 0x14
    SLHDSAModeSHAKE_256f uint8 = 0x15
)

Input Formats

ML-DSA Verify (Dedicated Precompile)

Offset  Length   Field         Description
─────────────────────────────────────────────────────────────
0       1        mode          Mode byte (0x44, 0x65, 0x87)
1       var      publicKey     Public key (1312/1952/2592 bytes)
1+pk    32       messageLen    Message length as big-endian uint256
1+pk+32 var      signature     Signature (2420/3309/4627 bytes)
min     var      message       Message to verify

PQCrypto Unified Precompile

Function Selector (first 4 bytes):

SelectorOperation
"mlds"ML-DSA Verify
"encp"ML-KEM Encapsulate
"decp"ML-KEM Decapsulate
"slhs"SLH-DSA Verify

ML-DSA Verify (via PQCrypto):

Offset  Length   Field         Description
─────────────────────────────────────────────────────────────
0       4        selector      "mlds" (0x6d6c6473)
4       1        mode          Mode byte (0x44, 0x65, 0x87)
5       2        pubKeyLen     Public key length (big-endian)
7       var      publicKey     Public key bytes
7+pk    2        messageLen    Message length (big-endian)
9+pk    var      message       Message bytes
9+pk+m  var      signature     Remaining bytes are signature

ML-KEM Encapsulate:

Offset  Length   Field         Description
─────────────────────────────────────────────────────────────
0       4        selector      "encp" (0x656e6370)
4       1        mode          Mode byte (0x00, 0x01, 0x02)
5       var      publicKey     Public key (800/1184/1568 bytes)

ML-KEM Decapsulate:

Offset  Length   Field         Description
─────────────────────────────────────────────────────────────
0       4        selector      "decp" (0x64656370)
4       1        mode          Mode byte (0x00, 0x01, 0x02)
5       2        privKeyLen    Private key length (big-endian)
7       var      privateKey    Private key bytes
7+sk    var      ciphertext    Remaining bytes are ciphertext

SLH-DSA Verify:

Offset  Length   Field         Description
─────────────────────────────────────────────────────────────
0       4        selector      "slhs" (0x736c6873)
4       1        mode          Mode byte (0x00-0x15)
5       2        pubKeyLen     Public key length (big-endian)
7       var      publicKey     Public key bytes (32/48/64)
7+pk    2        messageLen    Message length (big-endian)
9+pk    var      message       Message bytes
9+pk+m  var      signature     Remaining bytes are signature

Output Formats

Verification Results (ML-DSA, SLH-DSA)

Returns a 32-byte word:

  • 0x0000...0001 - Signature valid
  • 0x0000...0000 - Signature invalid

ML-KEM Encapsulate Output

Returns concatenated ciphertext || sharedSecret:

  • Ciphertext: 768/1088/1568 bytes (mode-dependent)
  • Shared Secret: 32 bytes

ML-KEM Decapsulate Output

Returns:

  • Shared Secret: 32 bytes

Implementation Locations

Core Cryptography Library

github.com/luxfi/crypto/
├── mldsa/
│   ├── mldsa.go          # ML-DSA implementation
│   ├── mldsa_test.go     # Tests
│   └── keygen.go         # Key generation
├── mlkem/
│   ├── mlkem.go          # ML-KEM implementation
│   ├── mlkem_test.go     # Tests
│   └── kem.go            # KEM operations
└── slhdsa/
    ├── slhdsa.go         # SLH-DSA implementation
    ├── slhdsa_test.go    # Tests
    └── params.go         # Algorithm parameters

EVM Precompile Implementation

github.com/luxfi/evm/precompile/contracts/
├── mldsa/
│   ├── contract.go       # ML-DSA precompile (dedicated)
│   ├── contract_test.go  # 334 lines of tests
│   └── module.go         # Registration
└── pqcrypto/
    ├── contract.go       # Unified PQ precompile
    ├── contract_test.go  # 234 lines of tests
    ├── module.go         # Registration
    └── config.go         # Configuration

Geth Integration

github.com/luxfi/geth/core/vm/
├── contracts.go          # Precompile registry
└── pq_contracts.go       # PQ crypto precompile bindings

Solidity Interface

ML-DSA Verify

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IMLDSA {
    function verify(
        bytes calldata publicKey,
        bytes calldata message,
        bytes calldata signature
    ) external view returns (bool valid);
}

library MLDSALib {
    address constant MLDSA_PRECOMPILE = 0x0200000000000000000000000000000000000006;

    uint8 constant MODE_MLDSA_44 = 0x44;
    uint8 constant MODE_MLDSA_65 = 0x65;
    uint8 constant MODE_MLDSA_87 = 0x87;

    uint256 constant MLDSA44_GAS = 75000;
    uint256 constant MLDSA65_GAS = 100000;
    uint256 constant MLDSA87_GAS = 150000;

    function verify65(
        bytes calldata publicKey,
        bytes calldata message,
        bytes calldata signature
    ) internal view returns (bool) {
        bytes memory input = abi.encodePacked(
            MODE_MLDSA_65,
            publicKey,
            uint256(message.length),
            signature,
            message
        );
        (bool success, bytes memory result) = MLDSA_PRECOMPILE.staticcall{gas: MLDSA65_GAS}(input);
        return success && result.length == 32 && result[31] == 0x01;
    }
}

PQCrypto Unified

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IPQCrypto {
    function mldsaVerify(
        uint8 mode,
        bytes calldata publicKey,
        bytes calldata message,
        bytes calldata signature
    ) external view returns (bool valid);

    function mlkemEncapsulate(
        uint8 mode,
        bytes calldata publicKey
    ) external view returns (bytes memory ciphertext, bytes32 sharedSecret);

    function mlkemDecapsulate(
        uint8 mode,
        bytes calldata privateKey,
        bytes calldata ciphertext
    ) external view returns (bytes32 sharedSecret);

    function slhdsaVerify(
        uint8 mode,
        bytes calldata publicKey,
        bytes calldata message,
        bytes calldata signature
    ) external view returns (bool valid);
}

library PQCryptoLib {
    address constant PQCRYPTO_PRECOMPILE = 0x0200000000000000000000000000000000000010;

    // Function selectors
    bytes4 constant MLDSA_SELECTOR = 0x6d6c6473;   // "mlds"
    bytes4 constant ENCAP_SELECTOR = 0x656e6370;   // "encp"
    bytes4 constant DECAP_SELECTOR = 0x64656370;   // "decp"
    bytes4 constant SLHDSA_SELECTOR = 0x736c6873;  // "slhs"
}

Test Coverage

ML-DSA Tests (contract_test.go)

Test NameDescriptionStatus
TestMLDSAVerify_ValidSignatureValid ML-DSA-65 signature✅ PASS
TestMLDSAVerify_InvalidSignatureCorrupted signature✅ PASS
TestMLDSAVerify_WrongMessageSignature for different message✅ PASS
TestMLDSAVerify_InputTooShortInput validation✅ PASS
TestMLDSAVerify_EmptyMessageEmpty message signing✅ PASS
TestMLDSAVerify_LargeMessage10KB message✅ PASS
TestMLDSAVerify_GasCostPer-mode gas calculation✅ PASS
TestMLDSAPrecompile_AddressAddress verification✅ PASS
BenchmarkMLDSAVerify_SmallMessagePerformance benchmark✅ PASS
BenchmarkMLDSAVerify_LargeMessageLarge message benchmark✅ PASS

PQCrypto Tests (contract_test.go)

Test NameDescriptionStatus
TestPQCryptoPrecompileBasic setup✅ PASS
TestMLDSAVerifyML-DSA-44 verification✅ PASS
TestMLKEMEncapsulateDecapsulateFull KEM round-trip✅ PASS
TestSLHDSAVerifySLH-DSA-SHA2-128s verification✅ PASS
TestGasCalculationAll 15 per-mode gas costs✅ PASS
BenchmarkPQPrecompile/ML-DSA-VerifyML-DSA performance✅ PASS
BenchmarkPQPrecompile/ML-KEM-EncapsulateML-KEM performance✅ PASS

Rationale

ML-DSA Gas Derivation

Gas costs are based on computational complexity relative to ecrecover:

OperationTime (μs)Relative to ecrecoverGas
ecrecover~501x3,000
ML-DSA-44 verify~70~1.4x75,000
ML-DSA-65 verify~108~2.2x100,000
ML-DSA-87 verify~180~3.6x150,000

Quantum Premium: 25x multiplier applied to account for:

  1. Lattice-based computation complexity
  2. Large key/signature handling overhead
  3. Future-proofing against algorithm optimizations

ML-KEM Gas Derivation

Key encapsulation is computationally simpler than signatures:

OperationTime (μs)Gas
ML-KEM-512 encap/decap~206,000
ML-KEM-768 encap/decap~258,000
ML-KEM-1024 encap/decap~3510,000

SLH-DSA Gas Derivation

Hash-based signatures have wide variance between "small" and "fast" variants:

VariantTime (ms)Gas
128s (small sigs)~250,000
128f (fast verify)~375,000
192s~5100,000
192f~8150,000
256s~12175,000
256f~20250,000

Security Considerations

Algorithm Security Levels

AlgorithmVariantNIST LevelClassicalQuantum
ML-DSA44Level 2128-bit~128-bit
ML-DSA65Level 3192-bit~192-bit
ML-DSA87Level 5256-bit~256-bit
ML-KEM512Level 1128-bit~128-bit
ML-KEM768Level 3192-bit~192-bit
ML-KEM1024Level 5256-bit~256-bit
SLH-DSA128s/fLevel 1128-bit~128-bit
SLH-DSA192s/fLevel 3192-bit~192-bit
SLH-DSA256s/fLevel 5256-bit~256-bit

Implementation Security

  1. Constant-time operations: All implementations use constant-time code paths
  2. Input validation: All input lengths validated before processing
  3. No secret leakage: No branching on secret values
  4. Audited library: Uses Cloudflare CIRCL (audited implementation)

Mode Byte Validation

Precompiles reject unknown mode bytes with clear error messages:

  • ML-DSA: Only accepts 0x44, 0x65, 0x87
  • ML-KEM: Only accepts 0x00, 0x01, 0x02
  • SLH-DSA: Only accepts 0x00-0x05, 0x10-0x15

Standards Compliance

StandardDescriptionImplementation
FIPS 203ML-KEMFull compliance
FIPS 204ML-DSAFull compliance
FIPS 205SLH-DSAFull compliance
NIST SP 800-208PQ RecommendationsAligned
LPTitleRelationship
LP-4200Post-Quantum Cryptography SuiteParent specification
LP-3500ML-DSA PrecompileDedicated ML-DSA spec
LP-4316ML-DSA Digital SignaturesAlgorithm specification
LP-4317SLH-DSA Digital SignaturesAlgorithm specification
LP-4318ML-KEM Key EncapsulationAlgorithm specification

References

Copyright and related rights waived via CC0.