Crypto-Agility Specification
Crypto-Agility Specification
Galileo Luxury Standard - Post-Quantum Cryptography Readiness
Version: 1.0.0 Status: Draft Last Updated: 2026-01-30
1. Overview
1.1 Purpose
This specification defines the crypto-agile architecture for the Galileo Luxury Standard, enabling:
- Signature scheme migration without breaking deployed contracts
- Post-quantum cryptography readiness (NIST timeline: 2030 recommended full transition)
- Hybrid signatures during transition period (2027-2030)
- 10+ year product lifecycle support - luxury products require crypto-agility from day one
1.2 Standards Referenced
| Standard | Name | Date | |----------|------|------| | NIST FIPS 203 | ML-KEM (Module-Lattice-Based Key Encapsulation Mechanism) | August 2024 | | NIST FIPS 204 | ML-DSA (Module-Lattice-Based Digital Signature Algorithm) | August 2024 | | NIST FIPS 205 | SLH-DSA (Stateless Hash-Based Digital Signature Algorithm) | August 2024 | | ERC-4337 | Account Abstraction | Ethereum |
1.3 Design Principles
| Principle | Description | |-----------|-------------| | Never hardcode algorithms | All crypto operations go through interfaces | | Configuration over code | Algorithm selection via registry, not conditionals | | Graceful degradation | If PQC unavailable, fall back to classical | | Fail-secure | Unknown algorithm ID rejects signature, does not accept |
2. Algorithm Abstraction Interfaces
2.1 Signature Verification Interface
/**
* Abstract interface for signature verification.
* Implementations exist for each supported algorithm family.
*/
interface ISignatureVerifier {
/**
* Verify a signature against a message hash.
* @param hash - The message hash (always 32 bytes for interop)
* @param signature - The signature bytes (format varies by algorithm)
* @param publicKey - The public key bytes (format varies by algorithm)
* @returns true if signature is valid, false otherwise
*/
verify(hash: bytes32, signature: bytes, publicKey: bytes): boolean;
/**
* Return the algorithm identifier for this verifier.
* Must match registry key exactly.
*/
algorithmId(): string;
/**
* Return expected signature length in bytes.
* Used for format validation before verification.
*/
signatureLength(): uint256;
/**
* Return expected public key length in bytes.
*/
publicKeyLength(): uint256;
}
2.2 Key Encapsulation Interface
/**
* Abstract interface for key encapsulation (key exchange).
* Used for encrypted off-chain communication.
*/
interface IKeyEncapsulation {
/**
* Encapsulate a shared secret using recipient's public key.
* @param publicKey - Recipient's public key
* @returns ciphertext (to send) and sharedSecret (to use for encryption)
*/
encapsulate(publicKey: bytes): { ciphertext: bytes, sharedSecret: bytes32 };
/**
* Decapsulate to recover shared secret using private key.
* @param ciphertext - The encapsulated ciphertext
* @param privateKey - Recipient's private key
* @returns The shared secret (32 bytes)
*/
decapsulate(ciphertext: bytes, privateKey: bytes): bytes32;
/**
* Return the algorithm identifier.
*/
algorithmId(): string;
/**
* Return expected ciphertext length.
*/
ciphertextLength(): uint256;
}
2.3 Crypto Registry Interface
/**
* Central registry for algorithm implementations.
* Enables runtime algorithm selection without code changes.
*/
interface ICryptoRegistry {
/**
* Get verifier for a given algorithm ID.
* @throws if algorithm not supported
*/
getVerifier(algorithmId: string): ISignatureVerifier;
/**
* Get KEM for a given algorithm ID.
* @throws if algorithm not supported
*/
getKEM(algorithmId: string): IKeyEncapsulation;
/**
* Check if algorithm is supported.
*/
isSupported(algorithmId: string): boolean;
/**
* List all supported algorithm IDs.
*/
supportedAlgorithms(): string[];
/**
* Get the current default algorithm for new signatures.
* Changes over time as migration progresses.
*/
defaultSignatureAlgorithm(): string;
/**
* Get the current default algorithm for new key encapsulation.
*/
defaultKEMAlgorithm(): string;
}
3. Algorithm Identifiers
3.1 Signature Algorithms
| Algorithm ID | Standard | Status | Key Size | Sig Size | Notes |
|--------------|----------|--------|----------|----------|-------|
| ECDSA-secp256k1 | SEC 2 | Current default | 33 bytes | 65 bytes | Native EVM support |
| ECDSA-P256 | NIST P-256 | Supported | 33 bytes | 64 bytes | Apple/Android hardware |
| Ed25519 | RFC 8032 | Supported | 32 bytes | 64 bytes | High performance |
| ML-DSA-44 | FIPS 204 | Future (2027+) | 1312 bytes | 2420 bytes | NIST Level 2 |
| ML-DSA-65 | FIPS 204 | Future (2027+) | 1952 bytes | 3309 bytes | NIST Level 3 |
| ML-DSA-87 | FIPS 204 | Future (2027+) | 2592 bytes | 4627 bytes | NIST Level 5 |
| SLH-DSA-128s | FIPS 205 | Backup option | 32 bytes | 7856 bytes | Hash-based (conservative) |
3.2 Key Encapsulation Algorithms
| Algorithm ID | Standard | Status | Ciphertext | Shared Secret | Notes |
|--------------|----------|--------|------------|---------------|-------|
| ECDH-X25519 | RFC 7748 | Current default | 32 bytes | 32 bytes | High performance |
| ECDH-P256 | NIST P-256 | Supported | 65 bytes | 32 bytes | Hardware support |
| ML-KEM-512 | FIPS 203 | Future (2027+) | 768 bytes | 32 bytes | NIST Level 1 |
| ML-KEM-768 | FIPS 203 | Future (2027+) | 1088 bytes | 32 bytes | NIST Level 3 |
| ML-KEM-1024 | FIPS 203 | Future (2027+) | 1568 bytes | 32 bytes | NIST Level 5 |
3.3 Recommended Security Levels
| Use Case | Current (2026) | Transition (2027-2029) | Target (2030+) | |----------|----------------|------------------------|----------------| | Standard products | ECDSA-secp256k1 | Hybrid (ECDSA + ML-DSA-65) | ML-DSA-65 | | High-value items | Ed25519 | Hybrid (Ed25519 + ML-DSA-87) | ML-DSA-87 | | Off-chain encryption | ECDH-X25519 | Hybrid (X25519 + ML-KEM-768) | ML-KEM-768 |
4. Migration Roadmap
4.1 Timeline Overview
2026 2027 2028 2029 2030+
| | | | |
Phase 1: [=========]
Classical Only ECDSA-secp256k1 or Ed25519 for all operations
Phase 2: [=======================]
Hybrid Signatures ECDSA + ML-DSA dual-signature required
All new signatures use hybrid format
Phase 3: [=========>
PQC Primary ML-DSA primary
Classical optional
4.2 Phase 1: Classical Only (Current - 2026)
Configuration:
{
"signaturePolicy": {
"required": ["ECDSA-secp256k1"],
"optional": [],
"default": "ECDSA-secp256k1"
},
"kemPolicy": {
"required": ["ECDH-X25519"],
"optional": [],
"default": "ECDH-X25519"
}
}
Verification Logic:
- Accept: Single valid ECDSA signature
- Reject: Any other format
4.3 Phase 2: Hybrid Signatures (2027-2029)
Rationale:
- Hedge against both classical break AND PQC implementation bugs
- "Belt and suspenders" - signature valid only if BOTH algorithms verify
- Industry standard transition approach (NIST recommendation)
Configuration:
{
"signaturePolicy": {
"required": ["ECDSA-secp256k1", "ML-DSA-65"],
"optional": [],
"default": "hybrid"
},
"kemPolicy": {
"required": ["ECDH-X25519", "ML-KEM-768"],
"optional": [],
"default": "hybrid"
}
}
Verification Logic:
- Accept: Both ECDSA AND ML-DSA signatures present and valid
- Reject: Only one signature present OR either signature invalid
4.4 Phase 3: PQC Primary (2030+)
Configuration:
{
"signaturePolicy": {
"required": ["ML-DSA-65"],
"optional": ["ECDSA-secp256k1"],
"default": "ML-DSA-65"
},
"kemPolicy": {
"required": ["ML-KEM-768"],
"optional": ["ECDH-X25519"],
"default": "ML-KEM-768"
}
}
Verification Logic:
- Accept: Valid ML-DSA signature (with or without ECDSA)
- Reject: Only ECDSA present (classical-only not accepted)
5. Hybrid Signature Format
5.1 Signature Envelope
/**
* Hybrid signature envelope containing multiple algorithm signatures.
* Each signature signs the SAME message hash.
*/
interface HybridSignature {
/**
* Version of the envelope format.
* Enables future format evolution.
*/
version: uint8; // Current: 1
/**
* Array of algorithm-signature pairs.
* Order: classical first, then PQC.
*/
signatures: SignatureEntry[];
}
interface SignatureEntry {
/**
* Algorithm identifier from registry.
*/
algorithmId: string;
/**
* Signature bytes for this algorithm.
*/
signature: bytes;
/**
* Public key bytes (included for verification).
*/
publicKey: bytes;
}
5.2 Wire Encoding Format
Hybrid Signature Wire Format:
+--------+--------+--------+--------+--------+--------+
| Version| Count | Entry 1 ... | Entry 2 ... |
| 1 byte | 1 byte | variable | variable |
+--------+--------+--------+--------+--------+--------+
Entry Format:
+--------+--------+--------+--------+--------+--------+
| AlgLen | AlgID | SigLen | Signature | PkLen | PublicKey |
| 1 byte | var | 2 bytes| variable | 2 bytes| variable |
+--------+--------+--------+--------+--------+--------+
Example dual-signature envelope (Phase 2):
Version: 0x01
Count: 0x02
Entry 1 (Classical):
AlgLen: 0x10 (16 bytes)
AlgID: "ECDSA-secp256k1"
SigLen: 0x0041 (65 bytes)
Signature: [65 bytes]
PkLen: 0x0021 (33 bytes)
PublicKey: [33 bytes]
Entry 2 (PQC):
AlgLen: 0x09 (9 bytes)
AlgID: "ML-DSA-65"
SigLen: 0x0CED (3309 bytes)
Signature: [3309 bytes]
PkLen: 0x07A0 (1952 bytes)
PublicKey: [1952 bytes]
Total: ~5360 bytes
5.3 Verification Procedure
function verifyHybridSignature(
hash: bytes32,
envelope: HybridSignature,
registry: ICryptoRegistry,
policy: SignaturePolicy
): boolean {
// Check required algorithms are present
const presentAlgorithms = new Set(envelope.signatures.map(s => s.algorithmId));
for (const required of policy.required) {
if (!presentAlgorithms.has(required)) {
return false; // Missing required algorithm
}
}
// Verify each signature
for (const entry of envelope.signatures) {
if (!registry.isSupported(entry.algorithmId)) {
// Unknown algorithm - skip if optional, fail if required
if (policy.required.includes(entry.algorithmId)) {
return false;
}
continue;
}
const verifier = registry.getVerifier(entry.algorithmId);
if (!verifier.verify(hash, entry.signature, entry.publicKey)) {
return false; // Any invalid signature fails entire verification
}
}
return true;
}
6. ERC-4337 Integration
6.1 Account Abstraction for Custom Signatures
ERC-4337 Account Abstraction enables custom signature schemes on Ethereum, which is critical for post-quantum cryptography support. Standard EOAs (Externally Owned Accounts) are locked to secp256k1 ECDSA.
Smart Account Validation:
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.24;
import {'IAccount'} from "@account-abstraction/contracts/interfaces/IAccount.sol";
/**
* @title GalileoSmartAccount
* @notice ERC-4337 compatible account with crypto-agile signature validation
*/
contract GalileoSmartAccount is IAccount {
ICryptoRegistry public immutable cryptoRegistry;
function validateUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external override returns (uint256 validationData) {
// Decode hybrid signature envelope from userOp.signature
HybridSignature memory envelope = abi.decode(
userOp.signature,
(HybridSignature)
);
// Get current signature policy
SignaturePolicy memory policy = getCurrentPolicy();
// Verify using crypto-agile pattern
bool valid = verifyHybridSignature(
userOpHash,
envelope,
cryptoRegistry,
policy
);
return valid ? 0 : SIG_VALIDATION_FAILED;
}
}
6.2 Bundler Compatibility
Bundlers validate UserOperations before including them in bundles. Key considerations for PQC signatures:
| Aspect | Impact | Mitigation | |--------|--------|------------| | Signature size | ML-DSA-65: 3309 bytes vs ECDSA: 65 bytes | Bundlers must accept larger signatures | | Gas estimation | PQC verification costs more gas | Update gas estimation algorithms | | Mempool storage | Larger UserOperations | Increase mempool size limits | | Bandwidth | More data per operation | Consider compression |
6.3 Paymaster Support
Paymasters that sponsor transactions must also update for hybrid signatures:
function validatePaymasterUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) external returns (bytes memory context, uint256 validationData) {
// Paymaster may have its own signature in paymasterAndData
// Must also support hybrid signatures for paymaster approval
bytes memory paymasterSig = extractPaymasterSignature(userOp.paymasterAndData);
// Validate using same crypto-agile pattern
bool valid = verifyHybridSignature(
keccak256(abi.encode(userOpHash, maxCost)),
decodeHybridSignature(paymasterSig),
cryptoRegistry,
getCurrentPolicy()
);
return (context, valid ? 0 : SIG_VALIDATION_FAILED);
}
7. Key Rotation Procedures
7.1 Product Key Rotation
Products may need key rotation without losing provenance chain. This is critical for luxury items with 10+ year lifecycles where:
- Original signing keys may become compromised
- Algorithm upgrades require new key types
- Ownership transfer may require key handover
Key Rotation Protocol:
Key Rotation Flow:
1. ANNOUNCE ROTATION
- Emit KeyRotationInitiated event on-chain
- Include old key hash, new key commitment
- Grace period starts (7 days recommended)
event KeyRotationInitiated(
bytes32 indexed tokenId,
bytes32 oldKeyHash,
bytes32 newKeyCommitment,
uint256 gracePeriodEnd
);
2. DUAL-VALIDITY PERIOD
- Both old and new keys accepted for verification
- New attestations use new key
- Old attestations remain valid
- Duration: 7-30 days depending on product category
3. COMPLETE ROTATION
- Emit KeyRotationCompleted event
- Old key deactivated for NEW signatures only
- Historical signatures with old key remain valid forever
event KeyRotationCompleted(
bytes32 indexed tokenId,
bytes32 oldKeyHash,
bytes32 newKeyHash,
uint256 timestamp
);
4. ARCHIVE OLD KEY
- Store old public key in rotation history
- Enable verification of historical signatures
- NEVER delete - needed for provenance verification
7.2 Algorithm Upgrade Path
When upgrading from classical (ECDSA) to hybrid (ECDSA + ML-DSA):
| Step | Action | On-Chain Event |
|------|--------|----------------|
| 1 | Generate new PQC keypair | - |
| 2 | Register PQC key alongside ECDSA | PQCKeyRegistered(tokenId, algorithmId, publicKey) |
| 3 | Issue dual-key attestation | Links old and new keys cryptographically |
| 4 | Begin signing with hybrid format | Both signatures required |
| 5 | Historical signatures remain valid | No re-signing needed |
Critical Rule: NEVER invalidate old signatures. Products have 10+ year lifecycle and provenance chain must remain verifiable.
7.3 Emergency Key Revocation
In case of key compromise:
event KeyRevoked(
bytes32 indexed tokenId,
bytes32 keyHash,
string reason,
uint256 timestamp
);
Revocation does NOT invalidate historical signatures - it only prevents new signatures from being accepted with that key.
8. Implementation Notes
8.1 Library Recommendations
| Language | Classical Crypto | Post-Quantum Crypto | |----------|------------------|---------------------| | TypeScript | @noble/curves, @noble/secp256k1 | @noble/post-quantum (when stable) | | Rust | ring, ed25519-dalek, secp256k1 | pqcrypto, oqs-rs, ml-dsa | | Solidity | ecrecover (native), OpenZeppelin | Precompile (future EIP), custom verifier | | Go | crypto/ecdsa, crypto/ed25519 | cloudflare/circl, go-pqcrypto | | Python | cryptography, ecdsa | pqcrypto, liboqs-python |
8.2 Performance Considerations
| Algorithm | Sign Time | Verify Time | Gas (EVM) | Notes | |-----------|-----------|-------------|-----------|-------| | ECDSA-secp256k1 | ~50 us | ~100 us | 3000 | Native ecrecover | | Ed25519 | ~40 us | ~80 us | ~10000 | No native support | | ML-DSA-44 | ~150 us | ~80 us | TBD | Awaiting precompile | | ML-DSA-65 | ~200 us | ~100 us | TBD | Comparable verify time | | ML-DSA-87 | ~300 us | ~150 us | TBD | Higher security | | SLH-DSA-128s | ~5 ms | ~500 us | TBD | Slower, conservative |
Hybrid overhead: Expect ~2x verify time during transition (both algorithms checked).
8.3 Storage Impact
Plan for larger key and signature storage in off-chain systems:
| Data Type | Classical (ECDSA) | PQC (ML-DSA-65) | Hybrid | |-----------|-------------------|-----------------|--------| | Public Key | 33 bytes | 1952 bytes | ~2000 bytes | | Signature | 65 bytes | 3309 bytes | ~3400 bytes | | Private Key | 32 bytes | 4032 bytes | ~4100 bytes |
Storage recommendations:
- Off-chain databases must accommodate larger blobs
- IPFS CIDs remain constant size (hash-based addressing)
- On-chain storage should minimize PQC material (store commitments, not full keys)
8.4 Testing Strategy
- Unit tests: Each algorithm verifier independently
- Integration tests: Hybrid verification with mock policies
- Interoperability tests: Verify against NIST test vectors
- Fuzz testing: Invalid signature/key combinations
- Performance benchmarks: Track regression as algorithms change
Appendix A: NIST PQC Timeline Reference
| Date | Milestone | |------|-----------| | August 2024 | FIPS 203, 204, 205 published | | 2024-2026 | Early adopter implementations | | 2027 | Expected widespread library support | | 2030 | NIST recommended full transition | | 2030+ | Classical algorithms deprecated |
Appendix B: Related Specifications
- IDENTITY - Uses crypto-agile signatures for DID authentication
- TOKEN - Token attestations use hybrid signatures
- RESOLVER - Resolver responses signed with crypto-agile scheme
This specification addresses FOUND-06: Crypto-agile specification for post-quantum readiness.