Sync with geth

This commit is contained in:
Zach Kelling
2025-12-12 19:49:01 -08:00
parent d3c8fb2fe2
commit 78d37550c9
53 changed files with 367 additions and 8896 deletions
-62
View File
@@ -1,62 +0,0 @@
# Crypto Consolidation Complete
## Summary
Successfully consolidated all crypto implementations from multiple packages into the centralized `/Users/z/work/lux/crypto` package.
## What Was Done
### 1. Moved Crypto Implementations
- **From node/utils/crypto**: BLS and SECP256K1 implementations
- **From threshold**: Blake3 hash (common crypto extracted, threshold-specific logic preserved)
- **From consensus/bls**: Removed duplicate BLS (uses crypto/bls now)
- **From geth/crypto**: Already using centralized crypto
- **From evm**: Already using centralized crypto
- **From mpc**: Already using centralized crypto
### 2. Package Independence
- **crypto package**: Now completely independent of node package
- **ledger-lux-go package**: Created to hold keychain and ledger implementations (which depend on node)
### 3. Import Path Updates
All import paths have been updated:
- `github.com/luxfi/node/utils/crypto/bls``github.com/luxfi/crypto/bls`
- `github.com/luxfi/node/utils/crypto/secp256k1``github.com/luxfi/crypto/secp256k1`
- `github.com/luxfi/node/utils/crypto/keychain``github.com/luxfi/ledger-lux-go/keychain`
- `github.com/luxfi/node/utils/crypto/ledger``github.com/luxfi/ledger-lux-go/ledger`
### 4. Test Status
- BLS tests: ✅ Passing
- SECP256K1 tests: ✅ Passing
- Blake3 hash: ✅ Integrated
- Import paths: ✅ Updated across all packages
## Package Structure
```
/Users/z/work/lux/
├── crypto/ # Centralized crypto package (independent)
│ ├── bls/ # BLS signatures
│ ├── secp256k1/ # SECP256K1 signatures
│ ├── hashing/
│ │ └── blake3/ # Blake3 hash
│ └── ... # Other crypto implementations
├── ledger-lux-go/ # Ledger/keychain package (depends on node)
│ ├── keychain/ # Key management
│ └── ledger/ # Hardware wallet support
└── node/ # Node package (crypto removed)
└── utils/crypto/ # Now uses imports from crypto package
```
## Next Steps
1. Tag crypto package: `git tag -a v1.0.0 -m "Consolidated crypto package"`
2. Tag ledger package: `cd /Users/z/work/lux/ledger-lux-go && git tag -a v1.0.0 -m "Ledger and keychain package"`
3. Update go.mod files to use tagged versions
## Benefits
- Single source of truth for all crypto implementations
- No more duplicate code across packages
- Clear separation of concerns (crypto vs ledger/keychain)
- Easier maintenance and updates
- Better test coverage and consistency
+39 -14
View File
@@ -1,41 +1,66 @@
# AI Assistant Knowledge Base
**Last Updated**: $(date +%Y-%m-%d)
**Project**: $(basename "$REPO_PATH")
**Organization**: $(basename "$(dirname "$REPO_PATH")")
**Last Updated**: 2025-08-15
**Project**: crypto
**Organization**: lux
## Project Overview
This repository is part of the $(basename "$(dirname "$REPO_PATH")") organization.
This repository contains the Lux post-quantum cryptography implementation.
## Essential Commands
### Development
```bash
# Add common commands here
# Run tests
make test
# Run with coverage
make test-coverage
# Run benchmarks
make bench
# Full CI suite
make ci
```
## Architecture
The Lux crypto library provides comprehensive post-quantum cryptography support including:
- ML-KEM (Module-Lattice-based Key Encapsulation Mechanism)
- ML-DSA (Module-Lattice-Based Digital Signature Algorithm)
- SLH-DSA (Stateless Hash-Based Digital Signature Algorithm)
- BLS signatures
- Corona ring signatures
- SHAKE hash functions
## Key Technologies
- Go 1.21+
- Cloudflare CIRCL library for post-quantum cryptography
- CGO optimizations for performance
- Comprehensive test coverage
## Development Workflow
1. Implement cryptographic algorithms using Cloudflare CIRCL
2. Add comprehensive unit tests
3. Implement precompiled contracts for EVM integration
4. Run full test suite and benchmarks
5. Update documentation
## Context for All AI Assistants
This file (`LLM.md`) is symlinked as:
- `.AGENTS.md`
- `CLAUDE.md`
- `QWEN.md`
- `GEMINI.md`
All files reference the same knowledge base. Updates here propagate to all AI systems.
This file (`LLM.md`) serves as the central knowledge base for AI assistants working on this project.
## Rules for AI Assistants
1. **ALWAYS** update LLM.md with significant discoveries
2. **NEVER** commit symlinked files (.AGENTS.md, CLAUDE.md, etc.) - they're in .gitignore
3. **NEVER** create random summary files - update THIS file
2. **NEVER** commit AI-generated summary files
3. **NEVER** create redundant documentation - update existing files
4. Follow Go coding standards and best practices
5. Ensure all implementations are properly tested
---
-57
View File
@@ -1,57 +0,0 @@
# Geth Dependency Removal Notes
## Summary
Successfully removed all dependencies on `github.com/luxfi/geth` from the crypto package by implementing the necessary types and utilities locally.
## Changes Made
### 1. Created Common Types (`common/types.go`)
- Implemented `Hash` type (32-byte array) with all necessary methods
- Implemented `Address` type (20-byte array) with EIP-55 compliant checksumming
- Added common big integer constants (Big0, Big1, etc.)
- Added helper functions for hex encoding/decoding
### 2. Created Hex Utilities (`common/hexutil/`)
- Implemented hex encoding/decoding with 0x prefix support
- Added types for JSON marshaling (Big, Uint64, Uint, Bytes)
- Supports all the hex utility functions previously imported from geth
### 3. Created Math Utilities (`common/math/`)
- Implemented big integer math functions (BigPow, BigMax, BigMin)
- Added PaddedBigBytes for encoding big integers with padding
- Implemented safe arithmetic operations (SafeAdd, SafeSub, SafeMul, SafeDiv)
- Added other utility functions like ReadBits, U256, S256
### 4. Created RLP Encoding (`rlp/encode.go`)
- Minimal RLP encoder implementation supporting:
- Basic types: []byte, string, uint64, *big.Int
- Common types: common.Address, common.Hash
- Lists: []interface{}
- Sufficient for crypto package needs (primarily CreateAddress function)
### 5. Updated All Imports
- Changed all imports from `github.com/luxfi/geth/*` to `github.com/luxfi/crypto/*`
- Updated files:
- crypto.go
- crypto_test.go
- signature_test.go
- signature_cgo.go
- secp256k1/ethereum.go
- kzg4844/kzg4844.go
- kzg4844/kzg4844_ckzg_cgo.go
## Testing
- All existing tests pass
- No functionality changes, only dependency removal
- The CreateAddress function works correctly with the new RLP encoder
## Benefits
1. No external dependency on geth
2. Reduced binary size (only includes necessary code)
3. Better control over the implementation
4. Easier to maintain and update
## Notes
- The implementations are minimal but complete for crypto package needs
- If more RLP functionality is needed in the future, the encoder can be extended
- The common types match the geth interface exactly for compatibility
-117
View File
@@ -1,117 +0,0 @@
# Crypto Consolidation Migration Plan
## Overview
Consolidate all cryptographic implementations from various packages into `/Users/z/work/lux/crypto` to eliminate duplication and ensure consistency.
## Current Situation
### Duplicate Implementations Found
1. **BLS Signatures**
- `/Users/z/work/lux/node/utils/crypto/bls/` - Uses `supranational/blst` (C library, high performance)
- `/Users/z/work/lux/crypto/bls/` - Uses `cloudflare/circl` (Pure Go)
- **Decision**: Keep BLST implementation for performance, move to crypto package
2. **SECP256K1**
- `/Users/z/work/lux/node/utils/crypto/secp256k1/` - Full implementation
- `/Users/z/work/lux/crypto/secp256k1/` - Existing implementation
- **Decision**: Merge best features from both
3. **Keychain & Ledger**
- `/Users/z/work/lux/node/utils/crypto/keychain/` - Key management
- `/Users/z/work/lux/node/utils/crypto/ledger/` - Hardware wallet support
- **Decision**: Move to crypto package as-is
## Migration Steps
### Phase 1: BLS Consolidation
1. **Create new BLS structure in crypto**
```
/Users/z/work/lux/crypto/bls/
├── bls.go (BLST-based implementation from node)
├── bls_circl.go (CIRCL-based for compatibility)
├── interface.go (Common interface)
└── bls_test.go (Unified tests)
```
2. **Merge implementations**
- Primary: BLST for performance
- Fallback: CIRCL for pure Go environments
- Build tags to select implementation
### Phase 2: SECP256K1 Consolidation
1. **Merge node SECP256K1 into crypto**
- Keep best test coverage
- Maintain API compatibility
- Add RFC6979 deterministic nonce support
### Phase 3: Move Supporting Infrastructure
1. **Keychain**: `/Users/z/work/lux/crypto/keychain/`
2. **Ledger**: `/Users/z/work/lux/crypto/ledger/`
3. **Common utilities**: Extract and consolidate
### Phase 4: Extract Common Crypto from Other Packages
1. **From threshold package**:
- Blake3 hash → `/Users/z/work/lux/crypto/hashing/blake3/`
- Keep threshold-specific logic in place
2. **From MPC package**:
- Age encryption utilities → `/Users/z/work/lux/crypto/encryption/age/`
- Ed25519 wrappers → `/Users/z/work/lux/crypto/ed25519/`
### Phase 5: Update Import Paths
All imports need to be updated from:
```go
"github.com/luxfi/node/utils/crypto/bls"
"github.com/luxfi/node/utils/crypto/secp256k1"
```
To:
```go
"github.com/luxfi/crypto/bls"
"github.com/luxfi/crypto/secp256k1"
```
## Files Requiring Import Updates
### Consensus Package
- `/Users/z/work/lux/consensus/snowman/validator.go`
- `/Users/z/work/lux/consensus/ctx.go`
### Node Package
- `/Users/z/work/lux/node/vms/platformvm/signer/*.go`
- `/Users/z/work/lux/node/wallet/subnet/primary/*.go`
- `/Users/z/work/lux/node/staking/*.go`
## Implementation Plan
### Step 1: Create Compatibility Layer
Create interfaces that both implementations satisfy to ensure smooth migration.
### Step 2: Move and Test
1. Copy node crypto to crypto package
2. Update imports in crypto package
3. Run tests to ensure functionality
4. Update external imports one package at a time
### Step 3: Remove Duplicates
Once all imports are updated and tests pass, remove the original implementations from node.
## Testing Strategy
1. **Unit Tests**: Ensure all existing tests pass
2. **Integration Tests**: Test with consensus and node packages
3. **Performance Tests**: Verify no performance regression
4. **Compatibility Tests**: Ensure API compatibility
## Risk Mitigation
1. **Backup**: Keep original implementations until migration is complete
2. **Gradual Migration**: Update one package at a time
3. **Feature Flags**: Use build tags to switch implementations if needed
4. **Rollback Plan**: Git tags at each migration phase
-82
View File
@@ -1,82 +0,0 @@
# Crypto Package Test Status
## Overall Status
**Core crypto packages are working with consolidated implementations**
## Test Results Summary
### ✅ Passing (Core Packages)
- `crypto`: Main package tests passing
- `blake2b`: 90.4% coverage
- `bls`: Tests passing (using BLST implementation)
- `secp256k1`: All tests passing
- `mldsa`: 91.8% coverage
- `mlkem`: 42.4% coverage
- `slhdsa`: 43.0% coverage
- `ecies`: 81.6% coverage
- `signify`: 83.8% coverage
- `ipa/*`: All IPA packages passing
### ⚠️ Need Dependency Resolution
- `keychain`: Requires node/utils/set and node/version
- `ledger`: Requires node/version
- `hashing/blake3`: Needs go.mod update
### 📊 Coverage Statistics
- **Overall**: >40% coverage across crypto package
- **High Coverage (>80%)**: 9 packages
- **Medium Coverage (40-80%)**: 13 packages
## Git Tags Created
### CLI Package
- **Tag**: `cli-v2.0.0` ✅ Pushed
- **Changes**: Updated to use consolidated crypto imports
- **Breaking Change**: Import paths changed from node/utils/crypto to crypto
### Crypto Package
- **Ready for tagging once tests fully pass**
- **Version**: Will be `crypto-v1.0.0`
- **Features**:
- Consolidated implementations
- Post-quantum crypto support
- Blake3 hashing
- Precompile support
## Next Steps for 100% Tests
1. **Fix keychain/ledger dependencies**:
```bash
# Option 1: Copy needed utilities from node
cp -r /Users/z/work/lux/node/utils/set /Users/z/work/lux/crypto/utils/
# Option 2: Update imports to use minimal dependencies
```
2. **Update go.mod**:
```bash
cd /Users/z/work/lux/crypto
go mod tidy
go test ./...
```
3. **Create and push crypto tag**:
```bash
cd /Users/z/work/lux/crypto
git tag -a v1.0.0 -m "Initial consolidated crypto package"
git push origin v1.0.0
```
## Migration Impact
### Packages Using New Crypto
- ✅ CLI: Import paths updated
- ✅ SDK: Import paths updated
- ✅ VMSDK: Import paths updated
- ⚠️ Node: Needs broader dependency resolution
### Breaking Changes
All packages importing from `github.com/luxfi/node/utils/crypto/*` must update to `github.com/luxfi/crypto/*`
## Conclusion
The crypto consolidation is functionally complete with core packages working. The keychain and ledger packages need minor dependency resolution to achieve 100% test passing, but all cryptographic algorithms and core functionality are operational.
-372
View File
@@ -1,372 +0,0 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// Comprehensive test suite for all post-quantum cryptography implementations
package crypto
import (
"crypto/rand"
"testing"
"time"
"github.com/luxfi/crypto/lamport"
"github.com/luxfi/crypto/mldsa"
"github.com/luxfi/crypto/mlkem"
"github.com/luxfi/crypto/precompile"
"github.com/luxfi/crypto/slhdsa"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestAllCryptoImplementations tests all crypto standards
func TestAllCryptoImplementations(t *testing.T) {
t.Run("ML-KEM", testMLKEM)
t.Run("ML-DSA", testMLDSA)
t.Run("SLH-DSA", testSLHDSA)
// Lamport tests are covered in the lamport package
// t.Run("Lamport", testLamport)
t.Run("Precompiles", testPrecompiles)
t.Run("CGO Performance", testCGOPerformance)
}
func testMLKEM(t *testing.T) {
modes := []mlkem.Mode{mlkem.MLKEM512, mlkem.MLKEM768, mlkem.MLKEM1024}
names := []string{"ML-KEM-512", "ML-KEM-768", "ML-KEM-1024"}
for i, mode := range modes {
t.Run(names[i], func(t *testing.T) {
// Generate key pair
priv, err := mlkem.GenerateKeyPair(rand.Reader, mode)
require.NoError(t, err)
// Encapsulate
result, err := priv.PublicKey.Encapsulate(rand.Reader)
require.NoError(t, err)
// Decapsulate
sharedSecret, err := priv.Decapsulate(result.Ciphertext)
require.NoError(t, err)
// Verify shared secrets match
assert.Equal(t, result.SharedSecret, sharedSecret)
// Test wrong ciphertext
wrongCT := make([]byte, len(result.Ciphertext))
copy(wrongCT, result.Ciphertext)
wrongCT[0] ^= 0xFF
wrongSecret, err := priv.Decapsulate(wrongCT)
assert.NoError(t, err) // ML-KEM has implicit rejection
assert.NotEqual(t, sharedSecret, wrongSecret)
// Test serialization
pubBytes := priv.PublicKey.Bytes()
privBytes := priv.Bytes()
pub2, err := mlkem.PublicKeyFromBytes(pubBytes, mode)
require.NoError(t, err)
priv2, err := mlkem.PrivateKeyFromBytes(privBytes, mode)
require.NoError(t, err)
// Test with deserialized keys
result2, err := pub2.Encapsulate(rand.Reader)
require.NoError(t, err)
secret2, err := priv2.Decapsulate(result2.Ciphertext)
require.NoError(t, err)
assert.Equal(t, result2.SharedSecret, secret2)
})
}
}
func testMLDSA(t *testing.T) {
modes := []mldsa.Mode{mldsa.MLDSA44, mldsa.MLDSA65, mldsa.MLDSA87}
names := []string{"ML-DSA-44", "ML-DSA-65", "ML-DSA-87"}
message := []byte("Test message for ML-DSA signature")
for i, mode := range modes {
t.Run(names[i], func(t *testing.T) {
// Generate key pair
priv, err := mldsa.GenerateKey(rand.Reader, mode)
require.NoError(t, err)
// Sign message
signature, err := priv.Sign(rand.Reader, message, nil)
require.NoError(t, err)
// Verify signature
valid := priv.PublicKey.Verify(message, signature, nil)
assert.True(t, valid)
// Test wrong message
wrongMsg := []byte("Wrong message")
assert.False(t, priv.PublicKey.Verify(wrongMsg, signature, nil))
// Test corrupted signature
corruptedSig := make([]byte, len(signature))
copy(corruptedSig, signature)
corruptedSig[0] ^= 0xFF
assert.False(t, priv.PublicKey.Verify(message, corruptedSig, nil))
// Test serialization
pubBytes := priv.PublicKey.Bytes()
privBytes := priv.Bytes()
pub2, err := mldsa.PublicKeyFromBytes(pubBytes, mode)
require.NoError(t, err)
priv2, err := mldsa.PrivateKeyFromBytes(privBytes, mode)
require.NoError(t, err)
// Sign with deserialized key
sig2, err := priv2.Sign(rand.Reader, message, nil)
require.NoError(t, err)
assert.True(t, pub2.Verify(message, sig2, nil))
})
}
}
func testSLHDSA(t *testing.T) {
// Test only fast variants for speed
modes := []slhdsa.Mode{slhdsa.SLHDSA128f, slhdsa.SLHDSA192f}
names := []string{"SLH-DSA-128f", "SLH-DSA-192f"}
message := []byte("Test message for SLH-DSA")
for i, mode := range modes {
t.Run(names[i], func(t *testing.T) {
// Generate key pair
priv, err := slhdsa.GenerateKey(rand.Reader, mode)
require.NoError(t, err)
// Sign message
signature, err := priv.Sign(rand.Reader, message, nil)
require.NoError(t, err)
// Verify signature
valid := priv.PublicKey.Verify(message, signature, nil)
assert.True(t, valid)
// Test stateless property - same signature for same message
signature2, err := priv.Sign(rand.Reader, message, nil)
require.NoError(t, err)
assert.Equal(t, signature, signature2, "SLH-DSA should be deterministic")
// Test wrong message
wrongMsg := []byte("Wrong message")
assert.False(t, priv.PublicKey.Verify(wrongMsg, signature, nil))
// Test serialization
pubBytes := priv.PublicKey.Bytes()
pub2, err := slhdsa.PublicKeyFromBytes(pubBytes, mode)
require.NoError(t, err)
assert.True(t, pub2.Verify(message, signature, nil))
})
}
}
func testLamport(t *testing.T) {
message := []byte("Test message for Lamport signature")
t.Run("SHA256", func(t *testing.T) {
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
require.NoError(t, err)
pub := priv.Public()
// Sign message
sig, err := priv.Sign(message)
require.NoError(t, err)
// Verify signature
assert.True(t, pub.Verify(message, sig))
// Test wrong message
wrongMsg := []byte("Wrong message")
assert.False(t, pub.Verify(wrongMsg, sig))
// Test serialization
pubBytes := pub.Bytes()
sigBytes := sig.Bytes()
pub2, err := lamport.PublicKeyFromBytes(pubBytes)
require.NoError(t, err)
sig2, err := lamport.SignatureFromBytes(sigBytes)
require.NoError(t, err)
assert.True(t, pub2.Verify(message, sig2))
})
t.Run("OneTimeUse", func(t *testing.T) {
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
require.NoError(t, err)
pub := priv.Public()
// First signature should work
sig1, err := priv.Sign(message)
require.NoError(t, err)
assert.True(t, pub.Verify(message, sig1))
// Second signature should fail (key was zeroed)
sig2, err := priv.Sign([]byte("Second message"))
require.NoError(t, err)
// Verify that second signature doesn't work (since key was zeroed)
// This is a one-time signature scheme
assert.NotNil(t, sig2)
})
}
func testPrecompiles(t *testing.T) {
// Test SHAKE precompiles
t.Run("SHAKE", func(t *testing.T) {
shake256 := &precompile.SHAKE256{}
// Create input: [4 bytes output_len][data]
input := make([]byte, 4+32)
input[0] = 0x00
input[1] = 0x00
input[2] = 0x00
input[3] = 0x20 // 32 bytes output
copy(input[4:], []byte("test data for SHAKE256"))
gas := shake256.RequiredGas(input)
assert.Greater(t, gas, uint64(0))
output, err := shake256.Run(input)
require.NoError(t, err)
assert.Len(t, output, 32)
})
// Lamport precompile tests are covered in the precompile package
// t.Run("Lamport", func(t *testing.T) { ... })
// Test BLS precompile
t.Run("BLS", func(t *testing.T) {
blsVerify := &precompile.BLSVerify{}
// Create dummy input (96 bytes sig + 48 bytes pubkey + message)
input := make([]byte, 96+48+32)
rand.Read(input)
gas := blsVerify.RequiredGas(input)
assert.Equal(t, uint64(150000), gas)
// Run will return placeholder result
result, err := blsVerify.Run(input)
require.NoError(t, err)
assert.Len(t, result, 32)
})
}
func testCGOPerformance(t *testing.T) {
// This test is for comparing performance when CGO optimizations are available
// CGO implementations are opt-in only with CGO=1
t.Run("ML-KEM Performance", func(t *testing.T) {
message := make([]byte, 32)
rand.Read(message)
// Benchmark pure Go implementation
priv, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
start := time.Now()
for i := 0; i < 100; i++ {
priv.PublicKey.Encapsulate(rand.Reader)
}
duration := time.Since(start)
t.Logf("ML-KEM-768 Encapsulate (100 ops): %v", duration)
})
t.Run("ML-DSA Performance", func(t *testing.T) {
message := make([]byte, 32)
rand.Read(message)
// Benchmark pure Go implementation
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
start := time.Now()
for i := 0; i < 100; i++ {
priv.Sign(rand.Reader, message, nil)
}
duration := time.Since(start)
t.Logf("ML-DSA-65 Sign (100 ops): %v", duration)
})
t.Run("SLH-DSA Performance", func(t *testing.T) {
message := make([]byte, 32)
rand.Read(message)
// Benchmark pure Go implementation (fast variant)
priv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128f)
start := time.Now()
for i := 0; i < 10; i++ { // Fewer iterations due to larger signatures
priv.Sign(rand.Reader, message, nil)
}
duration := time.Since(start)
t.Logf("SLH-DSA-128f Sign (10 ops): %v", duration)
})
}
// BenchmarkCrypto benchmarks all crypto implementations
func BenchmarkCrypto(b *testing.B) {
b.Run("ML-KEM-768", func(b *testing.B) {
priv, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
b.Run("Encapsulate", func(b *testing.B) {
for i := 0; i < b.N; i++ {
priv.PublicKey.Encapsulate(rand.Reader)
}
})
result, _ := priv.PublicKey.Encapsulate(rand.Reader)
b.Run("Decapsulate", func(b *testing.B) {
for i := 0; i < b.N; i++ {
priv.Decapsulate(result.Ciphertext)
}
})
})
b.Run("ML-DSA-65", func(b *testing.B) {
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
message := make([]byte, 32)
b.Run("Sign", func(b *testing.B) {
for i := 0; i < b.N; i++ {
priv.Sign(rand.Reader, message, nil)
}
})
sig, _ := priv.Sign(rand.Reader, message, nil)
b.Run("Verify", func(b *testing.B) {
for i := 0; i < b.N; i++ {
priv.PublicKey.Verify(message, sig, nil)
}
})
})
b.Run("Lamport-SHA256", func(b *testing.B) {
message := make([]byte, 32)
b.Run("Generate", func(b *testing.B) {
for i := 0; i < b.N; i++ {
lamport.GenerateKey(rand.Reader, lamport.SHA256)
}
})
priv, _ := lamport.GenerateKey(rand.Reader, lamport.SHA256)
pub := priv.Public()
sig, _ := priv.Sign(message)
b.Run("Verify", func(b *testing.B) {
for i := 0; i < b.N; i++ {
pub.Verify(message, sig)
}
})
})
}
-253
View File
@@ -1,253 +0,0 @@
package crypto
import (
"crypto/rand"
"fmt"
"testing"
"github.com/luxfi/crypto/mldsa"
"github.com/luxfi/crypto/mlkem"
"github.com/luxfi/crypto/slhdsa"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestMLKEMEdgeCases tests edge cases and potential bugs
func TestMLKEMEdgeCases(t *testing.T) {
t.Run("Invalid Mode", func(t *testing.T) {
_, err := mlkem.GenerateKeyPair(rand.Reader, mlkem.Mode(99))
assert.Error(t, err)
})
t.Run("Nil Random Source", func(t *testing.T) {
_, err := mlkem.GenerateKeyPair(nil, mlkem.MLKEM768)
assert.Error(t, err)
})
t.Run("Empty Ciphertext", func(t *testing.T) {
priv, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
_, err := priv.Decapsulate([]byte{})
assert.Error(t, err)
})
t.Run("Wrong Size Ciphertext", func(t *testing.T) {
priv, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
wrongCT := make([]byte, 100) // Wrong size
_, err := priv.Decapsulate(wrongCT)
assert.Error(t, err)
})
t.Run("Serialization Round Trip", func(t *testing.T) {
modes := []mlkem.Mode{mlkem.MLKEM512, mlkem.MLKEM768, mlkem.MLKEM1024}
for _, mode := range modes {
priv1, _ := mlkem.GenerateKeyPair(rand.Reader, mode)
// Serialize
privBytes := priv1.Bytes()
pubBytes := priv1.PublicKey.Bytes()
// Deserialize
priv2, err := mlkem.PrivateKeyFromBytes(privBytes, mode)
require.NoError(t, err)
pub2, err := mlkem.PublicKeyFromBytes(pubBytes, mode)
require.NoError(t, err)
// Verify they work the same
result1, _ := priv1.PublicKey.Encapsulate(rand.Reader)
secret1, _ := priv1.Decapsulate(result1.Ciphertext)
result2, _ := pub2.Encapsulate(rand.Reader)
secret2, _ := priv2.Decapsulate(result2.Ciphertext)
// Both should produce valid shared secrets
assert.Len(t, secret1, 32)
assert.Len(t, secret2, 32)
}
})
t.Run("Deterministic Public Key", func(t *testing.T) {
// Same private key seed should generate same public key
privBytes := make([]byte, mlkem.MLKEM768PrivateKeySize)
copy(privBytes, []byte("deterministic seed for testing"))
priv1, _ := mlkem.PrivateKeyFromBytes(privBytes, mlkem.MLKEM768)
priv2, _ := mlkem.PrivateKeyFromBytes(privBytes, mlkem.MLKEM768)
assert.Equal(t, priv1.PublicKey.Bytes(), priv2.PublicKey.Bytes())
})
}
// TestMLDSAEdgeCases tests ML-DSA edge cases
func TestMLDSAEdgeCases(t *testing.T) {
t.Run("Invalid Mode", func(t *testing.T) {
_, err := mldsa.GenerateKey(rand.Reader, mldsa.Mode(99))
assert.Error(t, err)
})
t.Run("Empty Message", func(t *testing.T) {
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
sig, err := priv.Sign(rand.Reader, []byte{}, nil)
require.NoError(t, err)
assert.True(t, priv.PublicKey.Verify([]byte{}, sig, nil))
})
t.Run("Large Message", func(t *testing.T) {
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
largeMsg := make([]byte, 10000)
rand.Read(largeMsg)
sig, err := priv.Sign(rand.Reader, largeMsg, nil)
require.NoError(t, err)
assert.True(t, priv.PublicKey.Verify(largeMsg, sig, nil))
})
t.Run("Signature Malleability", func(t *testing.T) {
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
msg := []byte("test message")
sig1, _ := priv.Sign(rand.Reader, msg, nil)
sig2, _ := priv.Sign(rand.Reader, msg, nil)
// Signatures should be deterministic in our implementation
assert.Equal(t, sig1, sig2)
})
t.Run("Wrong Signature Size", func(t *testing.T) {
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
msg := []byte("test")
wrongSig := make([]byte, 100) // Wrong size
assert.False(t, priv.PublicKey.Verify(msg, wrongSig, nil))
})
t.Run("Cross Mode Verification", func(t *testing.T) {
priv44, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
priv65, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
msg := []byte("test")
sig44, _ := priv44.Sign(rand.Reader, msg, nil)
// ML-DSA65 key shouldn't verify ML-DSA44 signature
assert.False(t, priv65.PublicKey.Verify(msg, sig44, nil))
})
}
// TestSLHDSAEdgeCases tests SLH-DSA edge cases
func TestSLHDSAEdgeCases(t *testing.T) {
t.Run("Deterministic Signatures", func(t *testing.T) {
priv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128f)
msg := []byte("deterministic test")
sig1, _ := priv.Sign(rand.Reader, msg, nil)
sig2, _ := priv.Sign(rand.Reader, msg, nil)
// SLH-DSA is deterministic - same message should produce same signature
assert.Equal(t, sig1, sig2)
})
t.Run("Large Signature Sizes", func(t *testing.T) {
modes := []struct {
mode slhdsa.Mode
name string
size int
}{
{slhdsa.SLHDSA128f, "128f", slhdsa.SLHDSA128fSignatureSize},
{slhdsa.SLHDSA192f, "192f", slhdsa.SLHDSA192fSignatureSize},
}
for _, m := range modes {
t.Run(m.name, func(t *testing.T) {
priv, _ := slhdsa.GenerateKey(rand.Reader, m.mode)
msg := []byte("test")
sig, _ := priv.Sign(rand.Reader, msg, nil)
assert.Len(t, sig, m.size)
})
}
})
}
// TestConcurrency tests thread safety
func TestConcurrency(t *testing.T) {
t.Run("ML-KEM Concurrent Operations", func(t *testing.T) {
priv, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
// Run concurrent encapsulations
done := make(chan bool, 10)
for i := 0; i < 10; i++ {
go func() {
result, err := priv.PublicKey.Encapsulate(rand.Reader)
assert.NoError(t, err)
secret, err := priv.Decapsulate(result.Ciphertext)
assert.NoError(t, err)
assert.Equal(t, result.SharedSecret, secret)
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
})
t.Run("ML-DSA Concurrent Signing", func(t *testing.T) {
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
done := make(chan bool, 10)
for i := 0; i < 10; i++ {
go func(id int) {
msg := []byte(fmt.Sprintf("message %d", id))
sig, err := priv.Sign(rand.Reader, msg, nil)
assert.NoError(t, err)
assert.True(t, priv.PublicKey.Verify(msg, sig, nil))
done <- true
}(i)
}
for i := 0; i < 10; i++ {
<-done
}
})
}
// TestMemoryLeaks checks for potential memory issues
func TestMemoryLeaks(t *testing.T) {
t.Run("ML-KEM No Leak", func(t *testing.T) {
// This would need proper memory profiling
// For now, just ensure no panics on repeated operations
for i := 0; i < 100; i++ {
priv, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
result, _ := priv.PublicKey.Encapsulate(rand.Reader)
priv.Decapsulate(result.Ciphertext)
}
})
}
// TestParameterValidation ensures all parameters match NIST specs
func TestParameterValidation(t *testing.T) {
// ML-KEM parameters from FIPS 203
assert.Equal(t, 800, mlkem.MLKEM512PublicKeySize)
assert.Equal(t, 1632, mlkem.MLKEM512PrivateKeySize)
assert.Equal(t, 768, mlkem.MLKEM512CiphertextSize)
assert.Equal(t, 1184, mlkem.MLKEM768PublicKeySize)
assert.Equal(t, 2400, mlkem.MLKEM768PrivateKeySize)
assert.Equal(t, 1088, mlkem.MLKEM768CiphertextSize)
assert.Equal(t, 1568, mlkem.MLKEM1024PublicKeySize)
assert.Equal(t, 3168, mlkem.MLKEM1024PrivateKeySize)
assert.Equal(t, 1568, mlkem.MLKEM1024CiphertextSize)
// ML-DSA parameters from FIPS 204
assert.Equal(t, 1312, mldsa.MLDSA44PublicKeySize)
assert.Equal(t, 2528, mldsa.MLDSA44PrivateKeySize)
assert.Equal(t, 2420, mldsa.MLDSA44SignatureSize)
assert.Equal(t, 1952, mldsa.MLDSA65PublicKeySize)
assert.Equal(t, 4000, mldsa.MLDSA65PrivateKeySize)
assert.Equal(t, 3293, mldsa.MLDSA65SignatureSize)
assert.Equal(t, 2592, mldsa.MLDSA87PublicKeySize)
assert.Equal(t, 4864, mldsa.MLDSA87PrivateKeySize)
assert.Equal(t, 4595, mldsa.MLDSA87SignatureSize)
}
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2025 The luxfi Authors
// This file is part of the luxfi library.
//
// The luxfi library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The luxfi library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the luxfi library. If not, see <http://www.gnu.org/licenses/>.
package bitutil
// TestBytes tests if all bytes in the slice are zero
func TestBytes(buf []byte) bool {
for _, b := range buf {
if b != 0 {
return false
}
}
return true
}
+2 -2
View File
@@ -302,7 +302,7 @@ func appendUint64(b []byte, x uint64) []byte {
return append(b, a[:]...)
}
//nolint:unused,deadcode
//nolint:unused
func appendUint32(b []byte, x uint32) []byte {
var a [4]byte
binary.BigEndian.PutUint32(a[:], x)
@@ -314,7 +314,7 @@ func consumeUint64(b []byte) ([]byte, uint64) {
return b[8:], x
}
//nolint:unused,deadcode
//nolint:unused
func consumeUint32(b []byte) ([]byte, uint32) {
x := binary.BigEndian.Uint32(b)
return b[4:], x
+1 -1
View File
@@ -25,7 +25,7 @@ var precomputed = [10][16]byte{
{10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0},
}
// nolint:unused,deadcode
// nolint:unused
func hashBlocksGeneric(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
var m [16]uint64
c0, c1 := c[0], c[1]
+2 -4
View File
@@ -303,8 +303,7 @@ func benchmarkSum(b *testing.B, size int, sse4, avx, avx2 bool) {
data := make([]byte, size)
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
Sum512(data)
}
}
@@ -319,8 +318,7 @@ func benchmarkWrite(b *testing.B, size int, sse4, avx, avx2 bool) {
data := make([]byte, size)
h, _ := New512(nil)
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
h.Write(data)
}
}
+4 -4
View File
@@ -9,18 +9,18 @@
package bn256
import (
bn256cf "github.com/luxfi/crypto/bn256/cloudflare"
gnark "github.com/luxfi/crypto/bn256/gnark"
)
// G1 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type G1 = bn256cf.G1
type G1 = gnark.G1
// G2 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type G2 = bn256cf.G2
type G2 = gnark.G2
// PairingCheck calculates the Optimal Ate pairing for a set of points.
func PairingCheck(a []*G1, b []*G2) bool {
return bn256cf.PairingCheck(a, b)
return gnark.PairingCheck(a, b)
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"golang.org/x/sys/cpu"
)
//nolint:varcheck,unused,deadcode
//nolint:unused
var hasBMI2 = cpu.X86.HasBMI2
//go:noescape
+2 -10
View File
@@ -5,6 +5,7 @@ import (
"math/big"
"github.com/consensys/gnark-crypto/ecc/bn254"
"github.com/luxfi/bitutil"
)
// G1 is the affine representation of a G1 group element.
@@ -43,7 +44,7 @@ func (g *G1) Unmarshal(buf []byte) (int, error) {
return 0, errors.New("invalid G1 point size")
}
if allZeroes(buf[:64]) {
if !bitutil.TestBytes(buf[:64]) {
// point at infinity
g.inner.X.SetZero()
g.inner.Y.SetZero()
@@ -82,12 +83,3 @@ func (p *G1) Marshal() []byte {
return output
}
func allZeroes(buf []byte) bool {
for i := range buf {
if buf[i] != 0 {
return false
}
}
return true
}
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"errors"
"github.com/consensys/gnark-crypto/ecc/bn254"
"github.com/luxfi/bitutil"
)
// G2 is the affine representation of a G2 group element.
@@ -31,7 +32,7 @@ func (g *G2) Unmarshal(buf []byte) (int, error) {
return 0, errors.New("invalid G2 point size")
}
if allZeroes(buf[:128]) {
if !bitutil.TestBytes(buf[:128]) {
// point at infinity
g.inner.X.A0.SetZero()
g.inner.X.A1.SetZero()
-3808
View File
File diff suppressed because it is too large Load Diff
+15 -62
View File
@@ -28,10 +28,8 @@ import (
"io"
"math/big"
"os"
"sync"
"github.com/luxfi/crypto/rlp"
"golang.org/x/crypto/sha3"
)
// SignatureLength indicates the byte length required to carry a signature with recovery id.
@@ -40,15 +38,15 @@ const SignatureLength = 64 + 1 // 64 bytes ECDSA signature + 1 byte recovery id
// RecoveryIDOffset points to the byte offset within the signature that contains the recovery id.
const RecoveryIDOffset = 64
// DigestLength sets the signature digest exact length
const DigestLength = 32
// HashLength is the expected length of the hash
const HashLength = 32
// AddressLength is the expected length of the address
const AddressLength = 20
// DigestLength sets the signature digest exact length
const DigestLength = 32
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
type Hash [HashLength]byte
@@ -145,59 +143,7 @@ type KeccakState interface {
Read([]byte) (int, error)
}
// NewKeccakState creates a new KeccakState
func NewKeccakState() KeccakState {
return sha3.NewLegacyKeccak256().(KeccakState)
}
var hasherPool = sync.Pool{
New: func() any {
return sha3.NewLegacyKeccak256().(KeccakState)
},
}
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
func HashData(kh KeccakState, data []byte) (h Hash) {
kh.Reset()
kh.Write(data)
kh.Read(h[:])
return h
}
// Keccak256 calculates and returns the Keccak256 hash of the input data.
func Keccak256(data ...[]byte) []byte {
b := make([]byte, 32)
d := hasherPool.Get().(KeccakState)
d.Reset()
for _, b := range data {
d.Write(b)
}
d.Read(b)
hasherPool.Put(d)
return b
}
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
// converting it to an internal Hash data structure.
func Keccak256Hash(data ...[]byte) (h Hash) {
d := hasherPool.Get().(KeccakState)
d.Reset()
for _, b := range data {
d.Write(b)
}
d.Read(h[:])
hasherPool.Put(d)
return h
}
// Keccak512 calculates and returns the Keccak512 hash of the input data.
func Keccak512(data ...[]byte) []byte {
d := sha3.NewLegacyKeccak512()
for _, b := range data {
d.Write(b)
}
return d.Sum(nil)
}
// HexToAddress returns Address with byte values of s.
func HexToAddress(s string) Address {
@@ -273,15 +219,14 @@ func FromECDSA(priv *ecdsa.PrivateKey) []byte {
// UnmarshalPubkey converts bytes to a secp256k1 public key.
func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
curve := S256().(EllipticCurve)
x, y := curve.Unmarshal(pub)
x, y := S256().Unmarshal(pub)
if x == nil {
return nil, errInvalidPubkey
}
if !curve.IsOnCurve(x, y) {
if !S256().IsOnCurve(x, y) {
return nil, errInvalidPubkey
}
return &ecdsa.PublicKey{Curve: curve, X: x, Y: y}, nil
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
}
// FromECDSAPub converts a secp256k1 public key to bytes.
@@ -316,7 +261,7 @@ func LoadECDSA(file string) (*ecdsa.PrivateKey, error) {
r := bufio.NewReader(fd)
buf := make([]byte, 64)
n, err := readASCII(buf, r)
if err != nil && err != io.EOF {
if err != nil {
return nil, err
} else if n != len(buf) {
return nil, errors.New("key file too short, want 64 hex characters")
@@ -419,6 +364,14 @@ func ReadBits(bigint *big.Int, buf []byte) {
// wordBytes is the number of bytes in a big.Word
const wordBytes = int(32 << (uint64(^big.Word(0)) >> 63))
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
func HashData(kh KeccakState, data []byte) (h Hash) {
kh.Reset()
kh.Write(data)
kh.Read(h[:])
return h
}
func zeroBytes(bytes []byte) {
clear(bytes)
}
+20 -9
View File
@@ -26,7 +26,6 @@ import (
"reflect"
"testing"
"github.com/luxfi/crypto/common"
"github.com/luxfi/crypto/common/hexutil"
)
@@ -60,7 +59,7 @@ func TestToECDSAErrors(t *testing.T) {
func BenchmarkSha3(b *testing.B) {
a := []byte("hello world")
for i := 0; i < b.N; i++ {
for b.Loop() {
Keccak256(a)
}
}
@@ -231,9 +230,9 @@ func TestValidateSignatureValues(t *testing.T) {
}
}
minusOne := big.NewInt(-1)
one := common.Big1
zero := common.Big0
secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, common.Big1)
one := Big1
zero := Big0
secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, Big1)
// correct v,r,s
check(true, 0, one, one)
@@ -279,10 +278,22 @@ func checkhash(t *testing.T, name string, f func([]byte) []byte, msg, exp []byte
func checkAddr(t *testing.T, addr0, addr1 Address) {
if addr0 != addr1 {
t.Fatalf("address mismatch: want: %s have: %s", addr0.Hex(), addr1.Hex())
t.Fatalf("address mismatch: want: %x have: %x", addr0, addr1)
}
}
// FromHex converts a hex string to bytes
func FromHex(s string) []byte {
if len(s) >= 2 && (s[0:2] == "0x" || s[0:2] == "0X") {
s = s[2:]
}
if len(s)%2 == 1 {
s = "0" + s
}
b, _ := hex.DecodeString(s)
return b
}
// test to help Python team with integration of libsecp256k1
// skip but keep it after they are done
func TestPythonIntegration(t *testing.T) {
@@ -292,7 +303,7 @@ func TestPythonIntegration(t *testing.T) {
msg0 := Keccak256([]byte("foo"))
sig0, _ := Sign(msg0, k0)
msg1 := common.FromHex("00000000000000000000000000000000")
msg1 := FromHex("00000000000000000000000000000000")
sig1, _ := Sign(msg0, k0)
t.Logf("msg: %x, privkey: %s sig: %x\n", msg0, kh, sig0)
@@ -310,7 +321,7 @@ func BenchmarkKeccak256Hash(b *testing.B) {
rand.Read(input[:])
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
Keccak256Hash(input[:])
}
}
@@ -329,7 +340,7 @@ func BenchmarkHashData(b *testing.B) {
rand.Read(input[:])
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
HashData(buffer, input[:])
}
}
+3 -5
View File
@@ -164,7 +164,7 @@ func TestTooBigSharedKey(t *testing.T) {
// Benchmark the generation of P256 keys.
func BenchmarkGenerateKeyP256(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
if _, err := GenerateKey(rand.Reader, elliptic.P256(), nil); err != nil {
b.Fatal(err)
}
@@ -177,8 +177,7 @@ func BenchmarkGenSharedKeyP256(b *testing.B) {
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_, err := prv.GenerateShared(&prv.PublicKey, 16, 16)
if err != nil {
b.Fatal(err)
@@ -192,8 +191,7 @@ func BenchmarkGenSharedKeyS256(b *testing.B) {
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_, err := prv.GenerateShared(&prv.PublicKey, 16, 16)
if err != nil {
b.Fatal(err)
+5
View File
@@ -0,0 +1,5 @@
go 1.25.5
use (
.
)
+5
View File
@@ -0,0 +1,5 @@
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
+62
View File
@@ -0,0 +1,62 @@
// Copyright 2025 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//go:build !ziren
package crypto
import (
"sync"
"golang.org/x/crypto/sha3"
)
// NewKeccakState creates a new KeccakState
func NewKeccakState() KeccakState {
return sha3.NewLegacyKeccak256().(KeccakState)
}
var hasherPool = sync.Pool{
New: func() any {
return sha3.NewLegacyKeccak256().(KeccakState)
},
}
// Keccak256 calculates and returns the Keccak256 hash of the input data.
func Keccak256(data ...[]byte) []byte {
b := make([]byte, 32)
d := hasherPool.Get().(KeccakState)
d.Reset()
for _, b := range data {
d.Write(b)
}
d.Read(b)
hasherPool.Put(d)
return b
}
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
// converting it to an internal Hash data structure.
func Keccak256Hash(data ...[]byte) (h Hash) {
d := hasherPool.Get().(KeccakState)
d.Reset()
for _, b := range data {
d.Write(b)
}
d.Read(h[:])
hasherPool.Put(d)
return h
}
+122
View File
@@ -0,0 +1,122 @@
// Copyright 2025 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//go:build ziren
package crypto
import (
"github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime"
"github.com/luxfi/crypto"
)
// zirenKeccakState implements the KeccakState interface using the Ziren zkvm_runtime.
// It accumulates data written to it and uses the zkvm's Keccak256 system call for hashing.
type zirenKeccakState struct {
buf []byte // accumulated data
result []byte // cached result
dirty bool // whether new data has been written since last hash
}
func newZirenKeccakState() KeccakState {
return &zirenKeccakState{
buf: make([]byte, 0, 512), // pre-allocate reasonable capacity
}
}
func (s *zirenKeccakState) Write(p []byte) (n int, err error) {
s.buf = append(s.buf, p...)
s.dirty = true
return len(p), nil
}
func (s *zirenKeccakState) Sum(b []byte) []byte {
s.computeHashIfNeeded()
return append(b, s.result...)
}
func (s *zirenKeccakState) Reset() {
s.buf = s.buf[:0]
s.result = nil
s.dirty = false
}
func (s *zirenKeccakState) Size() int {
return 32
}
func (s *zirenKeccakState) BlockSize() int {
return 136 // Keccak256 rate
}
func (s *zirenKeccakState) Read(p []byte) (n int, err error) {
s.computeHashIfNeeded()
if len(p) == 0 {
return 0, nil
}
// After computeHashIfNeeded(), s.result is always a 32-byte slice
n = copy(p, s.result)
return n, nil
}
func (s *zirenKeccakState) computeHashIfNeeded() {
if s.dirty || s.result == nil {
// Use the zkvm_runtime Keccak256 which uses SyscallKeccakSponge
hashArray := zkvm_runtime.Keccak256(s.buf)
s.result = hashArray[:]
s.dirty = false
}
}
// NewKeccakState creates a new KeccakState
// This uses a Ziren-optimized implementation that leverages the zkvm_runtime.Keccak256 system call.
func NewKeccakState() KeccakState {
return newZirenKeccakState()
}
// Keccak256 calculates and returns the Keccak256 hash using the Ziren zkvm_runtime implementation.
func Keccak256(data ...[]byte) []byte {
// For multiple data chunks, concatenate them
if len(data) == 0 {
result := zkvm_runtime.Keccak256(nil)
return result[:]
}
if len(data) == 1 {
result := zkvm_runtime.Keccak256(data[0])
return result[:]
}
// Concatenate multiple data chunks
var totalLen int
for _, d := range data {
totalLen += len(d)
}
combined := make([]byte, 0, totalLen)
for _, d := range data {
combined = append(combined, d...)
}
result := zkvm_runtime.Keccak256(combined)
return result[:]
}
// Keccak256Hash calculates and returns the Keccak256 hash as a Hash using the Ziren zkvm_runtime implementation.
func Keccak256Hash(data ...[]byte) common.Hash {
return common.Hash(Keccak256(data...))
}
-1
View File
@@ -1 +0,0 @@
mode: set
-150
View File
@@ -1,150 +0,0 @@
mode: atomic
github.com/luxfi/crypto/mldsa/mldsa.go:54.66,57.14 2 43
github.com/luxfi/crypto/mldsa/mldsa.go:58.15,60.38 2 16
github.com/luxfi/crypto/mldsa/mldsa.go:61.15,63.38 2 13
github.com/luxfi/crypto/mldsa/mldsa.go:64.15,66.38 2 13
github.com/luxfi/crypto/mldsa/mldsa.go:67.10,68.48 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:72.2,72.17 1 42
github.com/luxfi/crypto/mldsa/mldsa.go:72.17,74.3 1 3
github.com/luxfi/crypto/mldsa/mldsa.go:77.2,78.56 2 39
github.com/luxfi/crypto/mldsa/mldsa.go:78.56,80.3 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:84.2,91.38 6 39
github.com/luxfi/crypto/mldsa/mldsa.go:91.38,97.23 6 2319
github.com/luxfi/crypto/mldsa/mldsa.go:97.23,99.4 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:100.3,100.30 1 2319
github.com/luxfi/crypto/mldsa/mldsa.go:103.2,109.8 1 39
github.com/luxfi/crypto/mldsa/mldsa.go:113.102,114.42 1 51
github.com/luxfi/crypto/mldsa/mldsa.go:114.42,116.3 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:118.2,120.29 2 50
github.com/luxfi/crypto/mldsa/mldsa.go:121.15,122.33 1 24
github.com/luxfi/crypto/mldsa/mldsa.go:123.15,124.33 1 13
github.com/luxfi/crypto/mldsa/mldsa.go:125.15,126.33 1 13
github.com/luxfi/crypto/mldsa/mldsa.go:127.10,128.48 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:133.2,148.47 11 50
github.com/luxfi/crypto/mldsa/mldsa.go:148.47,150.20 2 4985
github.com/luxfi/crypto/mldsa/mldsa.go:150.20,152.4 1 50
github.com/luxfi/crypto/mldsa/mldsa.go:153.3,155.24 3 4985
github.com/luxfi/crypto/mldsa/mldsa.go:158.2,158.23 1 50
github.com/luxfi/crypto/mldsa/mldsa.go:162.86,163.16 1 95
github.com/luxfi/crypto/mldsa/mldsa.go:163.16,165.3 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:167.2,169.18 2 94
github.com/luxfi/crypto/mldsa/mldsa.go:170.15,171.41 1 46
github.com/luxfi/crypto/mldsa/mldsa.go:172.15,173.41 1 24
github.com/luxfi/crypto/mldsa/mldsa.go:174.15,175.41 1 24
github.com/luxfi/crypto/mldsa/mldsa.go:176.10,177.15 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:181.2,181.39 1 94
github.com/luxfi/crypto/mldsa/mldsa.go:181.39,183.3 1 2
github.com/luxfi/crypto/mldsa/mldsa.go:187.2,194.25 5 92
github.com/luxfi/crypto/mldsa/mldsa.go:194.25,196.3 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:199.2,199.26 1 92
github.com/luxfi/crypto/mldsa/mldsa.go:199.26,200.42 1 2479
github.com/luxfi/crypto/mldsa/mldsa.go:200.42,202.4 1 15
github.com/luxfi/crypto/mldsa/mldsa.go:205.2,205.13 1 77
github.com/luxfi/crypto/mldsa/mldsa.go:209.31,210.11 1 24
github.com/luxfi/crypto/mldsa/mldsa.go:211.15,212.21 1 8
github.com/luxfi/crypto/mldsa/mldsa.go:213.15,214.21 1 8
github.com/luxfi/crypto/mldsa/mldsa.go:215.15,216.21 1 8
github.com/luxfi/crypto/mldsa/mldsa.go:217.10,218.19 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:223.43,228.2 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:231.41,236.2 1 2
github.com/luxfi/crypto/mldsa/mldsa.go:239.45,240.49 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:240.49,242.3 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:243.2,243.21 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:247.44,248.49 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:248.49,250.3 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:251.2,251.21 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:255.46,259.2 1 3
github.com/luxfi/crypto/mldsa/mldsa.go:261.39,262.14 1 5
github.com/luxfi/crypto/mldsa/mldsa.go:263.15,264.31 1 2
github.com/luxfi/crypto/mldsa/mldsa.go:265.15,266.31 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:267.15,268.31 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:269.10,270.11 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:274.38,275.14 1 6
github.com/luxfi/crypto/mldsa/mldsa.go:276.15,277.30 1 3
github.com/luxfi/crypto/mldsa/mldsa.go:278.15,279.30 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:280.15,281.30 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:282.10,283.11 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:288.38,290.2 1 16
github.com/luxfi/crypto/mldsa/mldsa.go:293.40,295.2 1 16
github.com/luxfi/crypto/mldsa/mldsa.go:298.69,301.14 2 3
github.com/luxfi/crypto/mldsa/mldsa.go:302.15,303.38 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:304.15,305.38 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:306.15,307.38 1 1
github.com/luxfi/crypto/mldsa/mldsa.go:308.10,309.48 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:312.2,312.31 1 3
github.com/luxfi/crypto/mldsa/mldsa.go:312.31,314.3 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:317.2,323.8 3 3
github.com/luxfi/crypto/mldsa/mldsa.go:327.71,330.14 2 3
github.com/luxfi/crypto/mldsa/mldsa.go:331.15,333.41 2 1
github.com/luxfi/crypto/mldsa/mldsa.go:334.15,336.41 2 1
github.com/luxfi/crypto/mldsa/mldsa.go:337.15,339.41 2 1
github.com/luxfi/crypto/mldsa/mldsa.go:340.10,341.48 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:344.2,344.35 1 3
github.com/luxfi/crypto/mldsa/mldsa.go:344.35,346.3 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:349.2,360.43 8 3
github.com/luxfi/crypto/mldsa/mldsa.go:360.43,366.28 6 183
github.com/luxfi/crypto/mldsa/mldsa.go:366.28,368.4 1 0
github.com/luxfi/crypto/mldsa/mldsa.go:369.3,369.29 1 183
github.com/luxfi/crypto/mldsa/mldsa.go:372.2,378.8 1 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:14.26,16.3 1 2
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:21.26,23.3 1 1
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:27.42,29.21 2 6
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:29.21,31.3 1 1
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:32.2,32.19 1 5
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:36.37,37.38 1 6
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:37.38,39.3 1 5
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:43.75,46.14 2 7
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:47.15,49.38 2 2
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:50.15,52.38 2 2
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:53.15,55.38 2 2
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:56.10,57.48 1 1
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:61.2,61.17 1 6
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:61.17,63.3 1 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:66.2,70.56 4 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:70.56,72.3 1 0
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:75.2,84.8 3 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:88.59,96.29 6 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:96.29,98.22 2 183
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:98.22,101.4 2 183
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:101.9,103.9 2 0
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:106.3,106.20 1 183
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:106.20,111.4 4 180
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:116.111,119.29 2 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:120.15,121.33 1 1
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:122.15,123.33 1 1
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:124.15,125.33 1 1
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:126.10,127.48 1 0
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:131.2,152.36 14 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:152.36,154.20 2 320
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:154.20,156.4 1 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:157.3,158.20 2 320
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:158.20,163.4 4 317
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:167.2,169.20 3 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:180.61,182.22 2 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:182.22,184.17 2 15
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:184.17,186.4 1 0
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:187.3,187.16 1 15
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:190.2,193.8 1 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:197.67,198.34 1 6
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:198.34,200.3 1 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:202.2,207.26 4 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:207.26,209.20 2 15
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:209.20,212.18 3 15
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:212.18,215.5 2 0
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:216.4,216.25 1 15
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:220.2,223.29 2 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:223.29,224.17 1 15
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:224.17,226.4 1 0
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:229.2,229.24 1 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:233.88,234.70 1 9
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:234.70,236.3 1 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:238.2,242.26 3 6
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:242.26,244.20 2 30
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:244.20,247.4 2 30
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:250.2,252.21 2 6
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:264.65,270.2 1 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:273.71,281.42 5 9
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:281.42,284.3 2 3
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:285.2,289.16 3 6
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:289.16,291.3 1 0
github.com/luxfi/crypto/mldsa/mldsa_optimized.go:293.2,297.17 4 6
-190
View File
@@ -1,190 +0,0 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// +build cgo
// Package mldsa provides ML-DSA (FIPS 204) post-quantum signatures
// CGO implementation using pq-crystals/dilithium reference code
package mldsa
/*
#cgo CFLAGS: -I${SRCDIR}/c -I${SRCDIR}/c/ref -DDILITHIUM_MODE=3 -O3
#cgo LDFLAGS: -L${SRCDIR}/c -lmldsa
#include <stdlib.h>
#include <string.h>
#include "api.h"
#include "sign.h"
#include "params.h"
// Wrapper functions for cleaner Go interface
int mldsa_keypair(unsigned char *pk, unsigned char *sk) {
return crypto_sign_keypair(pk, sk);
}
int mldsa_sign(unsigned char *sig, size_t *siglen,
const unsigned char *m, size_t mlen,
const unsigned char *sk) {
return crypto_sign_signature(sig, siglen, m, mlen, sk);
}
int mldsa_verify(const unsigned char *sig, size_t siglen,
const unsigned char *m, size_t mlen,
const unsigned char *pk) {
return crypto_sign_verify(sig, siglen, m, mlen, pk);
}
// Get sizes for different security levels
int mldsa_publickey_bytes(int mode) {
switch(mode) {
case 2: return 1312; // ML-DSA-44 (Dilithium2)
case 3: return 1952; // ML-DSA-65 (Dilithium3)
case 5: return 2592; // ML-DSA-87 (Dilithium5)
default: return 0;
}
}
int mldsa_secretkey_bytes(int mode) {
switch(mode) {
case 2: return 2560; // ML-DSA-44
case 3: return 4032; // ML-DSA-65
case 5: return 4896; // ML-DSA-87
default: return 0;
}
}
int mldsa_signature_bytes(int mode) {
switch(mode) {
case 2: return 2420; // ML-DSA-44
case 3: return 3309; // ML-DSA-65
case 5: return 4627; // ML-DSA-87
default: return 0;
}
}
*/
import "C"
import (
"crypto"
"errors"
"io"
"unsafe"
)
// CGO-based implementation of ML-DSA
type cgoMLDSA struct {
mode Mode
}
// GenerateKeyCGO generates a new ML-DSA key pair using C implementation
func GenerateKeyCGO(rand io.Reader, mode Mode) (*PrivateKey, error) {
// Get sizes for the chosen mode
var cMode C.int
switch mode {
case MLDSA44:
cMode = 2
case MLDSA65:
cMode = 3
case MLDSA87:
cMode = 5
default:
return nil, errors.New("invalid ML-DSA mode")
}
pkSize := int(C.mldsa_publickey_bytes(cMode))
skSize := int(C.mldsa_secretkey_bytes(cMode))
if pkSize == 0 || skSize == 0 {
return nil, errors.New("invalid ML-DSA mode parameters")
}
// Allocate memory for keys
pk := make([]byte, pkSize)
sk := make([]byte, skSize)
// Generate key pair
ret := C.mldsa_keypair(
(*C.uchar)(unsafe.Pointer(&pk[0])),
(*C.uchar)(unsafe.Pointer(&sk[0])),
)
if ret != 0 {
return nil, errors.New("key generation failed")
}
return &PrivateKey{
PublicKey: PublicKey{
mode: mode,
data: pk,
},
data: sk,
}, nil
}
// SignCGO signs a message using the C implementation
func SignCGO(priv *PrivateKey, rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
var cMode C.int
switch priv.mode {
case MLDSA44:
cMode = 2
case MLDSA65:
cMode = 3
case MLDSA87:
cMode = 5
default:
return nil, errors.New("invalid ML-DSA mode")
}
sigSize := int(C.mldsa_signature_bytes(cMode))
if sigSize == 0 {
return nil, errors.New("invalid signature size")
}
// Allocate memory for signature
sig := make([]byte, sigSize)
var sigLen C.size_t
// Sign the message
ret := C.mldsa_sign(
(*C.uchar)(unsafe.Pointer(&sig[0])),
&sigLen,
(*C.uchar)(unsafe.Pointer(&message[0])),
C.size_t(len(message)),
(*C.uchar)(unsafe.Pointer(&priv.data[0])),
)
if ret != 0 {
return nil, errors.New("signing failed")
}
return sig[:sigLen], nil
}
// VerifyCGO verifies a signature using the C implementation
func VerifyCGO(pub *PublicKey, message, signature []byte) bool {
var cMode C.int
switch pub.mode {
case MLDSA44:
cMode = 2
case MLDSA65:
cMode = 3
case MLDSA87:
cMode = 5
default:
return false
}
// Verify the signature
ret := C.mldsa_verify(
(*C.uchar)(unsafe.Pointer(&signature[0])),
C.size_t(len(signature)),
(*C.uchar)(unsafe.Pointer(&message[0])),
C.size_t(len(message)),
(*C.uchar)(unsafe.Pointer(&pub.data[0])),
)
return ret == 0
}
// UseCGO returns true if CGO implementation is available
func UseCGO() bool {
return true
}
-379
View File
@@ -1,379 +0,0 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// Package mldsa provides ML-DSA (FIPS 204) digital signature algorithm
// This is a placeholder implementation for CI testing
package mldsa
import (
"crypto"
"crypto/sha256"
"errors"
"io"
)
// Security parameters for ML-DSA (Module Lattice Digital Signature Algorithm)
const (
// ML-DSA-44 (Level 2 security)
MLDSA44PublicKeySize = 1312
MLDSA44PrivateKeySize = 2528
MLDSA44SignatureSize = 2420
// ML-DSA-65 (Level 3 security)
MLDSA65PublicKeySize = 1952
MLDSA65PrivateKeySize = 4000
MLDSA65SignatureSize = 3293
// ML-DSA-87 (Level 5 security)
MLDSA87PublicKeySize = 2592
MLDSA87PrivateKeySize = 4864
MLDSA87SignatureSize = 4595
)
// Mode represents the ML-DSA parameter set
type Mode int
const (
MLDSA44 Mode = 2 // Level 2
MLDSA65 Mode = 3 // Level 3
MLDSA87 Mode = 5 // Level 5
)
// PublicKey represents an ML-DSA public key
type PublicKey struct {
mode Mode
data []byte
}
// PrivateKey represents an ML-DSA private key
type PrivateKey struct {
PublicKey *PublicKey
data []byte
}
// GenerateKey generates a new ML-DSA key pair
func GenerateKey(rand io.Reader, mode Mode) (*PrivateKey, error) {
var pubKeySize, privKeySize int
switch mode {
case MLDSA44:
pubKeySize = MLDSA44PublicKeySize
privKeySize = MLDSA44PrivateKeySize
case MLDSA65:
pubKeySize = MLDSA65PublicKeySize
privKeySize = MLDSA65PrivateKeySize
case MLDSA87:
pubKeySize = MLDSA87PublicKeySize
privKeySize = MLDSA87PrivateKeySize
default:
return nil, errors.New("invalid ML-DSA mode")
}
// Check for nil random source
if rand == nil {
return nil, errors.New("random source is nil")
}
// Placeholder implementation - generate random private key
privBytes := make([]byte, privKeySize)
if _, err := io.ReadFull(rand, privBytes); err != nil {
return nil, err
}
// Derive public key from private key for consistency
// In real ML-DSA, public key is derived from private key seed
h := sha256.New()
h.Write(privBytes[:32]) // Use first 32 bytes as seed
h.Write([]byte("public"))
pubSeed := h.Sum(nil)
pubBytes := make([]byte, pubKeySize)
// Fill public key with deterministic data
for i := 0; i < pubKeySize; i += 32 {
h.Reset()
h.Write(pubSeed)
h.Write([]byte{byte(i / 32)})
hash := h.Sum(nil)
end := i + 32
if end > pubKeySize {
end = pubKeySize
}
copy(pubBytes[i:end], hash)
}
return &PrivateKey{
PublicKey: &PublicKey{
mode: mode,
data: pubBytes,
},
data: privBytes,
}, nil
}
// Sign creates a signature for the given message
func (priv *PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
if priv == nil || priv.PublicKey == nil {
return nil, errors.New("private key is nil")
}
var sigSize int
switch priv.PublicKey.mode {
case MLDSA44:
sigSize = MLDSA44SignatureSize
case MLDSA65:
sigSize = MLDSA65SignatureSize
case MLDSA87:
sigSize = MLDSA87SignatureSize
default:
return nil, errors.New("invalid ML-DSA mode")
}
// Placeholder: create deterministic signature that can be verified
// Start with hash of public key and message (what Verify expects)
h := sha256.New()
h.Write(priv.PublicKey.data)
h.Write(message)
hash := h.Sum(nil)
signature := make([]byte, sigSize)
// Copy the hash to the beginning of signature
copy(signature[:32], hash)
// Fill rest with deterministic data based on private key
h.Reset()
h.Write(priv.data)
h.Write(message)
privHash := h.Sum(nil)
for i := 32; i < sigSize; i += len(privHash) {
end := i + len(privHash)
if end > sigSize {
end = sigSize
}
copy(signature[i:end], privHash)
h.Write(privHash) // Generate more data
privHash = h.Sum(nil)
}
return signature, nil
}
// Verify verifies a signature using the public key
func (pub *PublicKey) Verify(message, signature []byte, opts crypto.SignerOpts) bool {
if pub == nil {
return false
}
var expectedSigSize int
switch pub.mode {
case MLDSA44:
expectedSigSize = MLDSA44SignatureSize
case MLDSA65:
expectedSigSize = MLDSA65SignatureSize
case MLDSA87:
expectedSigSize = MLDSA87SignatureSize
default:
return false
}
// Check signature size
if len(signature) != expectedSigSize {
return false
}
// Placeholder verification: recompute expected signature based on public key and message
// In real implementation, this would use lattice-based verification
h := sha256.New()
h.Write(pub.data)
h.Write(message)
expectedSigStart := h.Sum(nil)
// Check if first 32 bytes of signature match expected
// This is a simplified check for our placeholder
if len(signature) < 32 {
return false
}
// Compare first 32 bytes
for i := 0; i < 32; i++ {
if signature[i] != expectedSigStart[i] {
return false
}
}
return true
}
// String returns the string representation of the mode
func (m Mode) String() string {
switch m {
case MLDSA44:
return "ML-DSA-44"
case MLDSA65:
return "ML-DSA-65"
case MLDSA87:
return "ML-DSA-87"
default:
return "Unknown"
}
}
// NewPrivateKey creates a new private key with the given mode
func NewPrivateKey(mode Mode) *PrivateKey {
return &PrivateKey{
PublicKey: NewPublicKey(mode),
data: make([]byte, getPrivateKeySize(mode)),
}
}
// NewPublicKey creates a new public key with the given mode
func NewPublicKey(mode Mode) *PublicKey {
return &PublicKey{
mode: mode,
data: make([]byte, getPublicKeySize(mode)),
}
}
// SetBytes sets the key data from bytes
func (sk *PrivateKey) SetBytes(data []byte) {
if sk.data == nil || len(sk.data) != len(data) {
sk.data = make([]byte, len(data))
}
copy(sk.data, data)
}
// SetBytes sets the key data from bytes
func (pk *PublicKey) SetBytes(data []byte) {
if pk.data == nil || len(pk.data) != len(data) {
pk.data = make([]byte, len(data))
}
copy(pk.data, data)
}
// IsDeterministic returns whether the signature scheme is deterministic
func (sk *PrivateKey) IsDeterministic() bool {
// ML-DSA uses randomized signing by default
// Can be made deterministic by using nil rand
return false
}
func getPrivateKeySize(mode Mode) int {
switch mode {
case MLDSA44:
return MLDSA44PrivateKeySize
case MLDSA65:
return MLDSA65PrivateKeySize
case MLDSA87:
return MLDSA87PrivateKeySize
default:
return 0
}
}
func getPublicKeySize(mode Mode) int {
switch mode {
case MLDSA44:
return MLDSA44PublicKeySize
case MLDSA65:
return MLDSA65PublicKeySize
case MLDSA87:
return MLDSA87PublicKeySize
default:
return 0
}
}
// Bytes returns the public key as bytes
func (pub *PublicKey) Bytes() []byte {
return pub.data
}
// Bytes returns the private key as bytes
func (priv *PrivateKey) Bytes() []byte {
return priv.data
}
// PublicKeyFromBytes reconstructs a public key from bytes
func PublicKeyFromBytes(data []byte, mode Mode) (*PublicKey, error) {
var expectedSize int
switch mode {
case MLDSA44:
expectedSize = MLDSA44PublicKeySize
case MLDSA65:
expectedSize = MLDSA65PublicKeySize
case MLDSA87:
expectedSize = MLDSA87PublicKeySize
default:
return nil, errors.New("invalid ML-DSA mode")
}
if len(data) != expectedSize {
return nil, errors.New("invalid public key size")
}
// Make a copy of the data
pubData := make([]byte, expectedSize)
copy(pubData, data)
return &PublicKey{
mode: mode,
data: pubData,
}, nil
}
// PrivateKeyFromBytes reconstructs a private key from bytes
func PrivateKeyFromBytes(data []byte, mode Mode) (*PrivateKey, error) {
var expectedPrivSize, expectedPubSize int
switch mode {
case MLDSA44:
expectedPrivSize = MLDSA44PrivateKeySize
expectedPubSize = MLDSA44PublicKeySize
case MLDSA65:
expectedPrivSize = MLDSA65PrivateKeySize
expectedPubSize = MLDSA65PublicKeySize
case MLDSA87:
expectedPrivSize = MLDSA87PrivateKeySize
expectedPubSize = MLDSA87PublicKeySize
default:
return nil, errors.New("invalid ML-DSA mode")
}
if len(data) != expectedPrivSize {
return nil, errors.New("invalid private key size")
}
// Make a copy of private key data
privData := make([]byte, expectedPrivSize)
copy(privData, data)
// Derive public key from private key (same as GenerateKey)
h := sha256.New()
h.Write(privData[:32]) // Use first 32 bytes as seed
h.Write([]byte("public"))
pubSeed := h.Sum(nil)
pubData := make([]byte, expectedPubSize)
// Fill public key with deterministic data
for i := 0; i < expectedPubSize; i += 32 {
h.Reset()
h.Write(pubSeed)
h.Write([]byte{byte(i / 32)})
hash := h.Sum(nil)
end := i + 32
if end > expectedPubSize {
end = expectedPubSize
}
copy(pubData[i:end], hash)
}
return &PrivateKey{
PublicKey: &PublicKey{
mode: mode,
data: pubData,
},
data: privData,
}, nil
}
-1
View File
@@ -1 +0,0 @@
mode: set
-124
View File
@@ -1,124 +0,0 @@
mode: set
github.com/luxfi/crypto/mlkem/mlkem.go:62.70,65.14 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:66.16,68.39 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:69.16,71.39 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:72.17,74.40 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:75.10,76.48 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:80.2,80.17 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:80.17,82.3 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:85.2,86.56 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:86.56,88.3 1 0
github.com/luxfi/crypto/mlkem/mlkem.go:91.2,98.38 6 1
github.com/luxfi/crypto/mlkem/mlkem.go:98.38,104.23 6 1
github.com/luxfi/crypto/mlkem/mlkem.go:104.23,106.4 1 0
github.com/luxfi/crypto/mlkem/mlkem.go:107.3,107.30 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:110.2,116.8 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:120.81,121.16 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:121.16,123.3 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:125.2,127.18 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:128.16,129.34 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:130.16,131.34 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:132.17,133.35 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:134.10,135.48 1 0
github.com/luxfi/crypto/mlkem/mlkem.go:139.2,140.49 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:140.49,142.3 1 0
github.com/luxfi/crypto/mlkem/mlkem.go:146.2,157.8 5 1
github.com/luxfi/crypto/mlkem/mlkem.go:161.72,162.17 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:162.17,164.3 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:166.2,168.29 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:169.16,170.42 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:171.16,172.42 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:173.17,174.43 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:175.10,176.48 1 0
github.com/luxfi/crypto/mlkem/mlkem.go:179.2,179.39 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:179.39,181.3 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:186.2,191.16 5 1
github.com/luxfi/crypto/mlkem/mlkem.go:195.38,197.2 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:200.40,202.2 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:205.69,208.14 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:209.16,210.39 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:211.16,212.39 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:213.17,214.40 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:215.10,216.48 1 0
github.com/luxfi/crypto/mlkem/mlkem.go:219.2,219.31 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:219.31,221.3 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:223.2,226.8 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:230.71,233.14 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:234.16,236.42 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:237.16,239.42 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:240.17,242.43 2 1
github.com/luxfi/crypto/mlkem/mlkem.go:243.10,244.48 1 0
github.com/luxfi/crypto/mlkem/mlkem.go:247.2,247.35 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:247.35,249.3 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:253.2,260.43 6 1
github.com/luxfi/crypto/mlkem/mlkem.go:260.43,266.28 6 1
github.com/luxfi/crypto/mlkem/mlkem.go:266.28,268.4 1 0
github.com/luxfi/crypto/mlkem/mlkem.go:269.3,269.29 1 1
github.com/luxfi/crypto/mlkem/mlkem.go:272.2,278.8 1 1
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:13.26,15.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:19.33,21.21 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:21.21,23.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:24.2,24.19 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:28.28,29.40 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:29.40,31.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:35.79,38.14 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:39.16,41.39 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:42.16,44.39 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:45.17,47.40 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:48.10,49.48 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:53.2,57.56 4 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:57.56,59.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:62.2,71.8 3 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:75.59,83.29 6 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:83.29,85.22 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:85.22,88.4 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:88.9,90.9 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:93.3,93.20 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:93.20,98.4 4 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:109.61,111.22 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:111.22,113.17 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:113.17,115.4 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:116.3,116.16 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:119.2,122.8 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:126.85,133.24 4 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:133.24,135.20 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:135.20,138.18 3 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:138.18,141.5 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:142.4,142.25 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:146.2,149.29 2 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:149.29,150.17 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:150.17,152.4 1 0
github.com/luxfi/crypto/mlkem/mlkem_optimized.go:155.2,155.21 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:11.75,13.60 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:13.60,15.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:17.2,17.86 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:17.86,19.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:22.2,26.16 3 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:26.16,28.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:31.2,40.8 3 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:44.59,45.14 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:46.16,47.55 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:48.16,49.55 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:50.17,51.57 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:52.10,53.14 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:58.39,59.14 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:60.16,61.32 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:62.16,63.32 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:64.17,65.33 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:66.10,67.11 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:72.86,73.60 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:73.60,75.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:77.2,78.17 2 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:78.17,80.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:83.2,84.16 2 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:84.16,86.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:89.2,94.8 2 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:98.75,100.90 2 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:100.90,102.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:105.2,107.26 2 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:111.83,112.80 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:112.80,117.3 3 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:120.2,122.20 3 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:126.86,127.81 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:127.81,129.3 1 0
github.com/luxfi/crypto/mlkem/mlkem_refactored.go:132.2,134.20 3 0
-248
View File
@@ -1,248 +0,0 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// +build cgo
// Package mlkem provides ML-KEM (FIPS 203) post-quantum key encapsulation
// CGO implementation using pq-crystals/kyber reference code
package mlkem
/*
#cgo CFLAGS: -I${SRCDIR}/c -I${SRCDIR}/c/ref -O3
#cgo LDFLAGS: -L${SRCDIR}/c -lmlkem
#include <stdlib.h>
#include <string.h>
// Kyber/ML-KEM parameter sets
#define KYBER512_PUBLICKEYBYTES 800
#define KYBER512_SECRETKEYBYTES 1632
#define KYBER512_CIPHERTEXTBYTES 768
#define KYBER512_BYTES 32
#define KYBER768_PUBLICKEYBYTES 1184
#define KYBER768_SECRETKEYBYTES 2400
#define KYBER768_CIPHERTEXTBYTES 1088
#define KYBER768_BYTES 32
#define KYBER1024_PUBLICKEYBYTES 1568
#define KYBER1024_SECRETKEYBYTES 3168
#define KYBER1024_CIPHERTEXTBYTES 1568
#define KYBER1024_BYTES 32
// Function declarations (would come from kyber headers)
int crypto_kem_keypair_512(unsigned char *pk, unsigned char *sk);
int crypto_kem_enc_512(unsigned char *ct, unsigned char *ss, const unsigned char *pk);
int crypto_kem_dec_512(unsigned char *ss, const unsigned char *ct, const unsigned char *sk);
int crypto_kem_keypair_768(unsigned char *pk, unsigned char *sk);
int crypto_kem_enc_768(unsigned char *ct, unsigned char *ss, const unsigned char *pk);
int crypto_kem_dec_768(unsigned char *ss, const unsigned char *ct, const unsigned char *sk);
int crypto_kem_keypair_1024(unsigned char *pk, unsigned char *sk);
int crypto_kem_enc_1024(unsigned char *ct, unsigned char *ss, const unsigned char *pk);
int crypto_kem_dec_1024(unsigned char *ss, const unsigned char *ct, const unsigned char *sk);
// Wrapper functions for cleaner Go interface
int mlkem_keypair(unsigned char *pk, unsigned char *sk, int mode) {
switch(mode) {
case 512: return crypto_kem_keypair_512(pk, sk);
case 768: return crypto_kem_keypair_768(pk, sk);
case 1024: return crypto_kem_keypair_1024(pk, sk);
default: return -1;
}
}
int mlkem_encapsulate(unsigned char *ct, unsigned char *ss, const unsigned char *pk, int mode) {
switch(mode) {
case 512: return crypto_kem_enc_512(ct, ss, pk);
case 768: return crypto_kem_enc_768(ct, ss, pk);
case 1024: return crypto_kem_enc_1024(ct, ss, pk);
default: return -1;
}
}
int mlkem_decapsulate(unsigned char *ss, const unsigned char *ct, const unsigned char *sk, int mode) {
switch(mode) {
case 512: return crypto_kem_dec_512(ss, ct, sk);
case 768: return crypto_kem_dec_768(ss, ct, sk);
case 1024: return crypto_kem_dec_1024(ss, ct, sk);
default: return -1;
}
}
// Get sizes for different security levels
int mlkem_publickey_bytes(int mode) {
switch(mode) {
case 512: return KYBER512_PUBLICKEYBYTES;
case 768: return KYBER768_PUBLICKEYBYTES;
case 1024: return KYBER1024_PUBLICKEYBYTES;
default: return 0;
}
}
int mlkem_secretkey_bytes(int mode) {
switch(mode) {
case 512: return KYBER512_SECRETKEYBYTES;
case 768: return KYBER768_SECRETKEYBYTES;
case 1024: return KYBER1024_SECRETKEYBYTES;
default: return 0;
}
}
int mlkem_ciphertext_bytes(int mode) {
switch(mode) {
case 512: return KYBER512_CIPHERTEXTBYTES;
case 768: return KYBER768_CIPHERTEXTBYTES;
case 1024: return KYBER1024_CIPHERTEXTBYTES;
default: return 0;
}
}
int mlkem_sharedsecret_bytes(int mode) {
switch(mode) {
case 512: return KYBER512_BYTES;
case 768: return KYBER768_BYTES;
case 1024: return KYBER1024_BYTES;
default: return 0;
}
}
*/
import "C"
import (
"errors"
"io"
"unsafe"
)
// CGO-based implementation of ML-KEM
// GenerateKeyPairCGO generates a new ML-KEM key pair using C implementation
func GenerateKeyPairCGO(rand io.Reader, mode Mode) (*PrivateKey, error) {
// Map mode to C parameter
var cMode C.int
switch mode {
case MLKEM512:
cMode = 512
case MLKEM768:
cMode = 768
case MLKEM1024:
cMode = 1024
default:
return nil, errors.New("invalid ML-KEM mode")
}
pkSize := int(C.mlkem_publickey_bytes(cMode))
skSize := int(C.mlkem_secretkey_bytes(cMode))
if pkSize == 0 || skSize == 0 {
return nil, errors.New("invalid ML-KEM mode parameters")
}
// Allocate memory for keys
pk := make([]byte, pkSize)
sk := make([]byte, skSize)
// Generate key pair
ret := C.mlkem_keypair(
(*C.uchar)(unsafe.Pointer(&pk[0])),
(*C.uchar)(unsafe.Pointer(&sk[0])),
cMode,
)
if ret != 0 {
return nil, errors.New("key generation failed")
}
return &PrivateKey{
PublicKey: PublicKey{
mode: mode,
data: pk,
},
data: sk,
}, nil
}
// EncapsulateCGO generates a shared secret and ciphertext using the C implementation
func EncapsulateCGO(pub *PublicKey, rand io.Reader) (*EncapsulationResult, error) {
var cMode C.int
switch pub.mode {
case MLKEM512:
cMode = 512
case MLKEM768:
cMode = 768
case MLKEM1024:
cMode = 1024
default:
return nil, errors.New("invalid ML-KEM mode")
}
ctSize := int(C.mlkem_ciphertext_bytes(cMode))
ssSize := int(C.mlkem_sharedsecret_bytes(cMode))
if ctSize == 0 || ssSize == 0 {
return nil, errors.New("invalid parameters")
}
// Allocate memory
ct := make([]byte, ctSize)
ss := make([]byte, ssSize)
// Encapsulate
ret := C.mlkem_encapsulate(
(*C.uchar)(unsafe.Pointer(&ct[0])),
(*C.uchar)(unsafe.Pointer(&ss[0])),
(*C.uchar)(unsafe.Pointer(&pub.data[0])),
cMode,
)
if ret != 0 {
return nil, errors.New("encapsulation failed")
}
return &EncapsulationResult{
Ciphertext: ct,
SharedSecret: ss,
}, nil
}
// DecapsulateCGO recovers the shared secret using the C implementation
func DecapsulateCGO(priv *PrivateKey, ciphertext []byte) ([]byte, error) {
var cMode C.int
switch priv.mode {
case MLKEM512:
cMode = 512
case MLKEM768:
cMode = 768
case MLKEM1024:
cMode = 1024
default:
return nil, errors.New("invalid ML-KEM mode")
}
ssSize := int(C.mlkem_sharedsecret_bytes(cMode))
if ssSize == 0 {
return nil, errors.New("invalid parameters")
}
// Allocate memory for shared secret
ss := make([]byte, ssSize)
// Decapsulate
ret := C.mlkem_decapsulate(
(*C.uchar)(unsafe.Pointer(&ss[0])),
(*C.uchar)(unsafe.Pointer(&ciphertext[0])),
(*C.uchar)(unsafe.Pointer(&priv.data[0])),
cMode,
)
if ret != 0 {
return nil, errors.New("decapsulation failed")
}
return ss, nil
}
// UseCGO returns true if CGO implementation is available
func UseCGO() bool {
return true
}
-279
View File
@@ -1,279 +0,0 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// Package mlkem provides ML-KEM (FIPS 203) key encapsulation mechanism
// This is a placeholder implementation for CI testing
package mlkem
import (
"crypto/sha256"
"errors"
"io"
)
// Security parameters for ML-KEM (Module Lattice Key Encapsulation Mechanism)
const (
// ML-KEM-512 (Level 1 security)
MLKEM512PublicKeySize = 800
MLKEM512PrivateKeySize = 1632
MLKEM512CiphertextSize = 768
MLKEM512SharedSecretSize = 32
// ML-KEM-768 (Level 3 security)
MLKEM768PublicKeySize = 1184
MLKEM768PrivateKeySize = 2400
MLKEM768CiphertextSize = 1088
MLKEM768SharedSecretSize = 32
// ML-KEM-1024 (Level 5 security)
MLKEM1024PublicKeySize = 1568
MLKEM1024PrivateKeySize = 3168
MLKEM1024CiphertextSize = 1568
MLKEM1024SharedSecretSize = 32
)
// Mode represents the ML-KEM parameter set
type Mode int
const (
MLKEM512 Mode = iota + 1
MLKEM768
MLKEM1024
)
// PublicKey represents an ML-KEM public key
type PublicKey struct {
mode Mode
data []byte
}
// PrivateKey represents an ML-KEM private key
type PrivateKey struct {
PublicKey PublicKey
data []byte
}
// EncapsulationResult contains the ciphertext and shared secret
type EncapsulationResult struct {
Ciphertext []byte
SharedSecret []byte
}
// GenerateKeyPair generates a new ML-KEM key pair
func GenerateKeyPair(rand io.Reader, mode Mode) (*PrivateKey, error) {
var pubKeySize, privKeySize int
switch mode {
case MLKEM512:
pubKeySize = MLKEM512PublicKeySize
privKeySize = MLKEM512PrivateKeySize
case MLKEM768:
pubKeySize = MLKEM768PublicKeySize
privKeySize = MLKEM768PrivateKeySize
case MLKEM1024:
pubKeySize = MLKEM1024PublicKeySize
privKeySize = MLKEM1024PrivateKeySize
default:
return nil, errors.New("invalid ML-KEM mode")
}
// Check for nil random source
if rand == nil {
return nil, errors.New("random source is nil")
}
// Placeholder implementation - generate random private key
privBytes := make([]byte, privKeySize)
if _, err := io.ReadFull(rand, privBytes); err != nil {
return nil, err
}
// Derive public key from private key deterministically
h := sha256.New()
h.Write(privBytes[:32]) // Use first 32 bytes as seed
h.Write([]byte("public"))
pubSeed := h.Sum(nil)
pubBytes := make([]byte, pubKeySize)
// Fill public key with deterministic data
for i := 0; i < pubKeySize; i += 32 {
h.Reset()
h.Write(pubSeed)
h.Write([]byte{byte(i / 32)})
hash := h.Sum(nil)
end := i + 32
if end > pubKeySize {
end = pubKeySize
}
copy(pubBytes[i:end], hash)
}
return &PrivateKey{
PublicKey: PublicKey{
mode: mode,
data: pubBytes,
},
data: privBytes,
}, nil
}
// Encapsulate generates a shared secret and ciphertext
func (pub *PublicKey) Encapsulate(rand io.Reader) (*EncapsulationResult, error) {
if pub == nil {
return nil, errors.New("public key is nil")
}
var ctSize int
switch pub.mode {
case MLKEM512:
ctSize = MLKEM512CiphertextSize
case MLKEM768:
ctSize = MLKEM768CiphertextSize
case MLKEM1024:
ctSize = MLKEM1024CiphertextSize
default:
return nil, errors.New("invalid ML-KEM mode")
}
// Placeholder: generate random ciphertext
ct := make([]byte, ctSize)
if _, err := io.ReadFull(rand, ct); err != nil {
return nil, err
}
// Placeholder: derive shared secret deterministically
// Use SHA256(pubkey || ciphertext) for consistency
h := sha256.New()
h.Write(pub.data)
h.Write(ct)
ss := h.Sum(nil)
// Store the ciphertext hash for later decapsulation
// In real implementation, this would be proper KEM
return &EncapsulationResult{
Ciphertext: ct,
SharedSecret: ss,
}, nil
}
// Decapsulate recovers the shared secret from ciphertext
func (priv *PrivateKey) Decapsulate(ciphertext []byte) ([]byte, error) {
if priv == nil {
return nil, errors.New("private key is nil")
}
var expectedCtSize int
switch priv.PublicKey.mode {
case MLKEM512:
expectedCtSize = MLKEM512CiphertextSize
case MLKEM768:
expectedCtSize = MLKEM768CiphertextSize
case MLKEM1024:
expectedCtSize = MLKEM1024CiphertextSize
default:
return nil, errors.New("invalid ML-KEM mode")
}
if len(ciphertext) != expectedCtSize {
return nil, errors.New("invalid ciphertext size")
}
// Placeholder: derive shared secret deterministically
// Use same formula as Encapsulate: SHA256(pubkey || ciphertext)
// This ensures matching shared secrets
h := sha256.New()
h.Write(priv.PublicKey.data)
h.Write(ciphertext)
ss := h.Sum(nil)
return ss, nil
}
// Bytes returns the public key as bytes
func (pub *PublicKey) Bytes() []byte {
return pub.data
}
// Bytes returns the private key as bytes
func (priv *PrivateKey) Bytes() []byte {
return priv.data
}
// PublicKeyFromBytes reconstructs a public key from bytes
func PublicKeyFromBytes(data []byte, mode Mode) (*PublicKey, error) {
var expectedSize int
switch mode {
case MLKEM512:
expectedSize = MLKEM512PublicKeySize
case MLKEM768:
expectedSize = MLKEM768PublicKeySize
case MLKEM1024:
expectedSize = MLKEM1024PublicKeySize
default:
return nil, errors.New("invalid ML-KEM mode")
}
if len(data) != expectedSize {
return nil, errors.New("invalid public key size")
}
return &PublicKey{
mode: mode,
data: data,
}, nil
}
// PrivateKeyFromBytes reconstructs a private key from bytes
func PrivateKeyFromBytes(data []byte, mode Mode) (*PrivateKey, error) {
var expectedPrivSize, expectedPubSize int
switch mode {
case MLKEM512:
expectedPrivSize = MLKEM512PrivateKeySize
expectedPubSize = MLKEM512PublicKeySize
case MLKEM768:
expectedPrivSize = MLKEM768PrivateKeySize
expectedPubSize = MLKEM768PublicKeySize
case MLKEM1024:
expectedPrivSize = MLKEM1024PrivateKeySize
expectedPubSize = MLKEM1024PublicKeySize
default:
return nil, errors.New("invalid ML-KEM mode")
}
if len(data) != expectedPrivSize {
return nil, errors.New("invalid private key size")
}
// For placeholder: derive public key from private key deterministically
// Use first part of private key as seed for public key
h := sha256.New()
h.Write(data[:32]) // Use first 32 bytes as seed
h.Write([]byte("public"))
pubSeed := h.Sum(nil)
pubData := make([]byte, expectedPubSize)
// Fill public key with deterministic data
for i := 0; i < expectedPubSize; i += 32 {
h.Reset()
h.Write(pubSeed)
h.Write([]byte{byte(i / 32)})
hash := h.Sum(nil)
end := i + 32
if end > expectedPubSize {
end = expectedPubSize
}
copy(pubData[i:end], hash)
}
return &PrivateKey{
PublicKey: PublicKey{
mode: mode,
data: pubData,
},
data: data,
}, nil
}
-288
View File
@@ -1,288 +0,0 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// Comprehensive tests for FIPS 203/204/205 post-quantum cryptography
package crypto
import (
"crypto/rand"
"testing"
"time"
"github.com/luxfi/crypto/mldsa"
"github.com/luxfi/crypto/mlkem"
"github.com/luxfi/crypto/slhdsa"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestMLKEM tests ML-KEM (FIPS 203) key encapsulation
func TestMLKEM(t *testing.T) {
modes := []mlkem.Mode{mlkem.MLKEM512, mlkem.MLKEM768, mlkem.MLKEM1024}
names := []string{"ML-KEM-512", "ML-KEM-768", "ML-KEM-1024"}
for i, mode := range modes {
t.Run(names[i], func(t *testing.T) {
// Generate key pair
priv, err := mlkem.GenerateKeyPair(rand.Reader, mode)
require.NoError(t, err)
// Encapsulate
result, err := priv.PublicKey.Encapsulate(rand.Reader)
require.NoError(t, err)
// Decapsulate
sharedSecret, err := priv.Decapsulate(result.Ciphertext)
require.NoError(t, err)
// Verify shared secrets match
assert.Equal(t, result.SharedSecret, sharedSecret)
// Test wrong ciphertext
wrongCT := make([]byte, len(result.Ciphertext))
copy(wrongCT, result.Ciphertext)
wrongCT[0] ^= 0xFF
wrongSecret, err := priv.Decapsulate(wrongCT)
// ML-KEM has implicit rejection, so no error but different secret
assert.NoError(t, err)
assert.NotEqual(t, sharedSecret, wrongSecret)
})
}
}
// TestMLDSA tests ML-DSA (FIPS 204) digital signatures
func TestMLDSA(t *testing.T) {
modes := []mldsa.Mode{mldsa.MLDSA44, mldsa.MLDSA65, mldsa.MLDSA87}
names := []string{"ML-DSA-44", "ML-DSA-65", "ML-DSA-87"}
message := []byte("Post-quantum signature test message")
for i, mode := range modes {
t.Run(names[i], func(t *testing.T) {
// Generate key pair
priv, err := mldsa.GenerateKey(rand.Reader, mode)
require.NoError(t, err)
// Sign message
signature, err := priv.Sign(rand.Reader, message, nil)
require.NoError(t, err)
// Verify signature
valid := priv.PublicKey.Verify(message, signature, nil)
assert.True(t, valid)
// Test wrong message
wrongMsg := []byte("Wrong message")
assert.False(t, priv.PublicKey.Verify(wrongMsg, signature, nil))
// Test corrupted signature
corruptedSig := make([]byte, len(signature))
copy(corruptedSig, signature)
corruptedSig[0] ^= 0xFF
assert.False(t, priv.PublicKey.Verify(message, corruptedSig, nil))
})
}
}
// TestSLHDSA tests SLH-DSA (FIPS 205) hash-based signatures
func TestSLHDSA(t *testing.T) {
// Test only small/fast variants for speed
modes := []slhdsa.Mode{slhdsa.SLHDSA128s, slhdsa.SLHDSA128f}
names := []string{"SLH-DSA-128s", "SLH-DSA-128f"}
message := []byte("Stateless hash-based signature test")
for i, mode := range modes {
t.Run(names[i], func(t *testing.T) {
// Generate key pair
priv, err := slhdsa.GenerateKey(rand.Reader, mode)
require.NoError(t, err)
// Sign message
signature, err := priv.Sign(rand.Reader, message, nil)
require.NoError(t, err)
// Verify signature
valid := priv.PublicKey.Verify(message, signature, nil)
assert.True(t, valid)
// Test stateless property - same signature for same message
signature2, err := priv.Sign(rand.Reader, message, nil)
require.NoError(t, err)
assert.Equal(t, signature, signature2, "SLH-DSA should be deterministic")
// Test wrong message
wrongMsg := []byte("Wrong message")
assert.False(t, priv.PublicKey.Verify(wrongMsg, signature, nil))
})
}
}
// TestPerformance tests performance of pure Go implementations
func TestPerformance(t *testing.T) {
t.Run("ML-KEM Performance", func(t *testing.T) {
// Benchmark pure Go implementation
priv, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
// Encapsulation benchmark
start := time.Now()
for i := 0; i < 100; i++ {
priv.PublicKey.Encapsulate(rand.Reader)
}
duration := time.Since(start)
t.Logf("ML-KEM-768 Encapsulate (100 ops): %v", duration)
assert.Less(t, duration, 5*time.Second, "Should complete 100 encapsulations in under 5 seconds")
})
t.Run("ML-DSA Performance", func(t *testing.T) {
message := make([]byte, 32)
rand.Read(message)
// Benchmark pure Go implementation
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
// Signing benchmark
start := time.Now()
for i := 0; i < 100; i++ {
priv.Sign(rand.Reader, message, nil)
}
duration := time.Since(start)
t.Logf("ML-DSA-65 Sign (100 ops): %v", duration)
assert.Less(t, duration, 5*time.Second, "Should complete 100 signatures in under 5 seconds")
})
t.Run("SLH-DSA Performance", func(t *testing.T) {
message := make([]byte, 32)
rand.Read(message)
// Benchmark pure Go implementation
priv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128f)
start := time.Now()
for i := 0; i < 10; i++ {
priv.Sign(rand.Reader, message, nil)
}
duration := time.Since(start)
t.Logf("SLH-DSA-128f Sign (10 ops): %v", duration)
})
}
// TestHybridCrypto tests combining classical and post-quantum crypto
func TestHybridCrypto(t *testing.T) {
t.Run("Hybrid Key Exchange", func(t *testing.T) {
// Classical ECDH (placeholder)
classicalSecret := make([]byte, 32)
rand.Read(classicalSecret)
// Post-quantum ML-KEM
priv, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
result, _ := priv.PublicKey.Encapsulate(rand.Reader)
pqSecret, _ := priv.Decapsulate(result.Ciphertext)
// Combine secrets (simplified - use proper KDF in production)
hybridSecret := make([]byte, 64)
copy(hybridSecret[:32], classicalSecret)
copy(hybridSecret[32:], pqSecret)
assert.Len(t, hybridSecret, 64)
})
t.Run("Hybrid Signatures", func(t *testing.T) {
message := []byte("Hybrid signature test")
// Classical ECDSA (placeholder)
classicalSig := make([]byte, 64)
rand.Read(classicalSig)
// Post-quantum ML-DSA
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
pqSig, _ := priv.Sign(rand.Reader, message, nil)
// Combine signatures
_ = append(classicalSig, pqSig...) // hybridSig would be used in production
// Verify both
// Classical verification (placeholder - would be ECDSA)
classicalValid := true
// PQ verification
pqValid := priv.PublicKey.Verify(message, pqSig, nil)
// Both must be valid
assert.True(t, classicalValid && pqValid)
})
}
// BenchmarkPostQuantum benchmarks all three standards
func BenchmarkPostQuantum(b *testing.B) {
b.Run("ML-KEM-768", func(b *testing.B) {
priv, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
b.Run("Encapsulate", func(b *testing.B) {
for i := 0; i < b.N; i++ {
priv.PublicKey.Encapsulate(rand.Reader)
}
})
result, _ := priv.PublicKey.Encapsulate(rand.Reader)
b.Run("Decapsulate", func(b *testing.B) {
for i := 0; i < b.N; i++ {
priv.Decapsulate(result.Ciphertext)
}
})
})
b.Run("ML-DSA-65", func(b *testing.B) {
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
message := make([]byte, 32)
b.Run("Sign", func(b *testing.B) {
for i := 0; i < b.N; i++ {
priv.Sign(rand.Reader, message, nil)
}
})
sig, _ := priv.Sign(rand.Reader, message, nil)
b.Run("Verify", func(b *testing.B) {
for i := 0; i < b.N; i++ {
priv.PublicKey.Verify(message, sig, nil)
}
})
})
b.Run("SLH-DSA-128f", func(b *testing.B) {
priv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128f)
message := make([]byte, 32)
b.Run("Sign", func(b *testing.B) {
for i := 0; i < b.N; i++ {
priv.Sign(rand.Reader, message, nil)
}
})
sig, _ := priv.Sign(rand.Reader, message, nil)
b.Run("Verify", func(b *testing.B) {
for i := 0; i < b.N; i++ {
priv.PublicKey.Verify(message, sig, nil)
}
})
})
}
// TestSizesAndParameters verifies all parameter sizes match FIPS specifications
func TestSizesAndParameters(t *testing.T) {
// ML-KEM sizes (FIPS 203)
assert.Equal(t, 1184, mlkem.MLKEM768PublicKeySize)
assert.Equal(t, 1088, mlkem.MLKEM768CiphertextSize)
// ML-DSA sizes (FIPS 204)
assert.Equal(t, 1952, mldsa.MLDSA65PublicKeySize)
assert.Equal(t, 3293, mldsa.MLDSA65SignatureSize)
// SLH-DSA sizes (FIPS 205) - placeholder values
// These would be the actual values once full implementation is done
t.Skip("SLH-DSA constants not yet defined")
}
-463
View File
@@ -1,463 +0,0 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// Corona post-quantum ring signature precompiled contracts
// Based on lattice cryptography for privacy-preserving quantum-resistant signatures
package precompile
import (
"errors"
"math/big"
"github.com/luxfi/geth/common"
"github.com/luxfi/corona/sign"
"github.com/luxfi/corona/primitives"
"github.com/luxfi/lattice/v6/ring"
"github.com/luxfi/lattice/v6/utils/sampling"
)
// Corona precompile addresses
var (
// Corona ring signature operations
CoronaVerifyAddress = common.HexToAddress("0x0000000000000000000000000000000000000170")
CoronaBatchVerifyAddress = common.HexToAddress("0x0000000000000000000000000000000000000171")
CoronaLinkableVerifyAddress = common.HexToAddress("0x0000000000000000000000000000000000000172")
// Corona key management
CoronaKeyAggregateAddress = common.HexToAddress("0x0000000000000000000000000000000000000173")
CoronaRingHashAddress = common.HexToAddress("0x0000000000000000000000000000000000000174")
// Threshold ring signatures
CoronaThresholdVerifyAddress = common.HexToAddress("0x0000000000000000000000000000000000000175")
)
// Gas costs for Corona operations
const (
// Ring signature verification is expensive due to lattice operations
coronaVerifyGas = 500000 // 500K gas for ring verification
coronaBatchVerifyBaseGas = 300000 // Base cost for batch
coronaBatchVerifyPerSigGas = 400000 // Per signature in batch
coronaLinkableVerifyGas = 600000 // Linkable signatures are more expensive
// Key operations
coronaKeyAggregateGas = 100000
coronaRingHashGas = 50000
// Threshold operations
coronaThresholdVerifyGas = 800000
// Ring sizes
smallRingSize = 8
mediumRingSize = 16
largeRingSize = 32
xlargeRingSize = 64
)
// CoronaVerify implements lattice-based ring signature verification
type CoronaVerify struct{}
func (r *CoronaVerify) RequiredGas(input []byte) uint64 {
// Gas scales with ring size
if len(input) < 1 {
return coronaVerifyGas
}
ringSize := uint64(input[0])
return coronaVerifyGas + ringSize*50000
}
func (r *CoronaVerify) Run(input []byte) ([]byte, error) {
// Input format: [1 byte ring_size][ring_public_keys][signature][message]
if len(input) < 1 {
return nil, errors.New("input too short")
}
ringSize := int(input[0])
if ringSize < 2 || ringSize > xlargeRingSize {
return nil, errors.New("invalid ring size")
}
// Initialize lattice parameters
randomKey := make([]byte, sign.KeySize)
r, err := ring.NewRing(1<<sign.LogN, []uint64{sign.Q})
if err != nil {
return nil, err
}
r_xi, err := ring.NewRing(1<<sign.LogN, []uint64{sign.QXi})
if err != nil {
return nil, err
}
r_nu, err := ring.NewRing(1<<sign.LogN, []uint64{sign.QNu})
if err != nil {
return nil, err
}
// Parse ring public keys
offset := 1
expectedKeySize := ringSize * sign.PublicKeySize // Assuming fixed public key size
if len(input) < offset+expectedKeySize {
return nil, errors.New("invalid input size for ring keys")
}
ringKeys := make([][]byte, ringSize)
for i := 0; i < ringSize; i++ {
ringKeys[i] = input[offset : offset+sign.PublicKeySize]
offset += sign.PublicKeySize
}
// Parse signature
if len(input) < offset+sign.SignatureSize {
return nil, errors.New("invalid input size for signature")
}
signature := input[offset : offset+sign.SignatureSize]
offset += sign.SignatureSize
// Parse message (remainder)
message := input[offset:]
// Verify ring signature using Corona
// This is simplified - actual implementation would use full Corona verification
valid := verifyCoronaSignature(r, r_xi, r_nu, ringKeys, signature, message)
result := make([]byte, 32)
if valid {
result[31] = 0x01
}
return result, nil
}
// CoronaBatchVerify verifies multiple ring signatures
type CoronaBatchVerify struct{}
func (r *CoronaBatchVerify) RequiredGas(input []byte) uint64 {
if len(input) < 1 {
return coronaBatchVerifyBaseGas
}
numSigs := uint64(input[0])
return coronaBatchVerifyBaseGas + numSigs*coronaBatchVerifyPerSigGas
}
func (r *CoronaBatchVerify) Run(input []byte) ([]byte, error) {
// Input: [1 byte num_sigs][signatures_and_rings...]
if len(input) < 1 {
return nil, errors.New("input too short")
}
numSigs := input[0]
results := make([]byte, numSigs)
allValid := true
// Process each signature
offset := 1
for i := byte(0); i < numSigs; i++ {
// Each entry: [1 byte ring_size][ring_keys][signature][4 bytes msg_len][message]
if len(input) < offset+1 {
results[i] = 0x00
allValid = false
continue
}
ringSize := int(input[offset])
offset++
// Extract ring keys
keySize := ringSize * sign.PublicKeySize
if len(input) < offset+keySize {
results[i] = 0x00
allValid = false
continue
}
ringKeys := make([][]byte, ringSize)
for j := 0; j < ringSize; j++ {
ringKeys[j] = input[offset : offset+sign.PublicKeySize]
offset += sign.PublicKeySize
}
// Extract signature
if len(input) < offset+sign.SignatureSize {
results[i] = 0x00
allValid = false
continue
}
signature := input[offset : offset+sign.SignatureSize]
offset += sign.SignatureSize
// Extract message length
if len(input) < offset+4 {
results[i] = 0x00
allValid = false
continue
}
msgLen := binary.BigEndian.Uint32(input[offset : offset+4])
offset += 4
// Extract message
if len(input) < offset+int(msgLen) {
results[i] = 0x00
allValid = false
continue
}
message := input[offset : offset+int(msgLen)]
offset += int(msgLen)
// Verify this signature
r, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.Q})
r_xi, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.QXi})
r_nu, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.QNu})
if verifyCoronaSignature(r, r_xi, r_nu, ringKeys, signature, message) {
results[i] = 0x01
} else {
results[i] = 0x00
allValid = false
}
}
// Return: [overall_valid][individual_results...]
output := make([]byte, 1+len(results))
if allValid {
output[0] = 0x01
}
copy(output[1:], results)
return output, nil
}
// CoronaLinkableVerify verifies linkable ring signatures
type CoronaLinkableVerify struct{}
func (r *CoronaLinkableVerify) RequiredGas(input []byte) uint64 {
return coronaLinkableVerifyGas
}
func (r *CoronaLinkableVerify) Run(input []byte) ([]byte, error) {
// Input: [1 byte ring_size][ring_keys][signature][32 bytes linking_tag][message]
if len(input) < 33 {
return nil, errors.New("input too short")
}
ringSize := int(input[0])
if ringSize < 2 || ringSize > xlargeRingSize {
return nil, errors.New("invalid ring size")
}
// Parse components
offset := 1
keySize := ringSize * sign.PublicKeySize
if len(input) < offset+keySize+sign.SignatureSize+32 {
return nil, errors.New("invalid input size")
}
// Extract ring keys
ringKeys := make([][]byte, ringSize)
for i := 0; i < ringSize; i++ {
ringKeys[i] = input[offset : offset+sign.PublicKeySize]
offset += sign.PublicKeySize
}
// Extract signature
signature := input[offset : offset+sign.SignatureSize]
offset += sign.SignatureSize
// Extract linking tag
linkingTag := input[offset : offset+32]
offset += 32
// Extract message
message := input[offset:]
// Verify linkable signature
r, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.Q})
r_xi, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.QXi})
r_nu, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.QNu})
// Verify both the signature and the linking tag
valid := verifyCoronaSignature(r, r_xi, r_nu, ringKeys, signature, message)
valid = valid && verifyLinkingTag(linkingTag, signature)
result := make([]byte, 32)
if valid {
result[31] = 0x01
}
return result, nil
}
// CoronaKeyAggregate aggregates public keys for ring construction
type CoronaKeyAggregate struct{}
func (r *CoronaKeyAggregate) RequiredGas(input []byte) uint64 {
return coronaKeyAggregateGas
}
func (r *CoronaKeyAggregate) Run(input []byte) ([]byte, error) {
// Input: [1 byte num_keys][public_keys...]
if len(input) < 1 {
return nil, errors.New("input too short")
}
numKeys := int(input[0])
expectedSize := 1 + numKeys*sign.PublicKeySize
if len(input) != expectedSize {
return nil, errors.New("invalid input size")
}
// Aggregate keys (simplified - actual would use lattice operations)
aggregatedKey := make([]byte, sign.PublicKeySize)
for i := 0; i < numKeys; i++ {
offset := 1 + i*sign.PublicKeySize
key := input[offset : offset+sign.PublicKeySize]
// XOR for placeholder (actual would use lattice addition)
for j := 0; j < sign.PublicKeySize; j++ {
aggregatedKey[j] ^= key[j]
}
}
return aggregatedKey, nil
}
// CoronaRingHash computes hash of ring for efficient verification
type CoronaRingHash struct{}
func (r *CoronaRingHash) RequiredGas(input []byte) uint64 {
return coronaRingHashGas
}
func (r *CoronaRingHash) Run(input []byte) ([]byte, error) {
// Input: [1 byte ring_size][public_keys...]
if len(input) < 1 {
return nil, errors.New("input too short")
}
ringSize := int(input[0])
expectedSize := 1 + ringSize*sign.PublicKeySize
if len(input) != expectedSize {
return nil, errors.New("invalid input size")
}
// Compute ring hash
hash := common.Keccak256(input)
return hash, nil
}
// CoronaThresholdVerify verifies threshold ring signatures
type CoronaThresholdVerify struct{}
func (r *CoronaThresholdVerify) RequiredGas(input []byte) uint64 {
return coronaThresholdVerifyGas
}
func (r *CoronaThresholdVerify) Run(input []byte) ([]byte, error) {
// Input: [1 byte threshold][1 byte ring_size][ring_keys][shares][message]
if len(input) < 2 {
return nil, errors.New("input too short")
}
threshold := int(input[0])
ringSize := int(input[1])
if threshold > ringSize || threshold < 2 {
return nil, errors.New("invalid threshold")
}
// Initialize lattice parameters
r, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.Q})
r_xi, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.QXi})
r_nu, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.QNu})
// Parse ring keys
offset := 2
ringKeys := make([][]byte, ringSize)
for i := 0; i < ringSize; i++ {
if len(input) < offset+sign.PublicKeySize {
return nil, errors.New("invalid ring key")
}
ringKeys[i] = input[offset : offset+sign.PublicKeySize]
offset += sign.PublicKeySize
}
// Parse signature shares
shares := make([][]byte, threshold)
for i := 0; i < threshold; i++ {
if len(input) < offset+sign.SignatureSize {
return nil, errors.New("invalid share")
}
shares[i] = input[offset : offset+sign.SignatureSize]
offset += sign.SignatureSize
}
// Parse message
message := input[offset:]
// Compute Lagrange coefficients for threshold
T := make([]int, threshold)
for i := 0; i < threshold; i++ {
T[i] = i
}
lagrangeCoeffs := primitives.ComputeLagrangeCoefficients(r, T, big.NewInt(int64(sign.Q)))
// Verify threshold signature (simplified)
valid := verifyThresholdSignature(r, r_xi, r_nu, ringKeys, shares, message, lagrangeCoeffs)
result := make([]byte, 32)
if valid {
result[31] = 0x01
}
return result, nil
}
// Helper functions
func verifyCoronaSignature(r, r_xi, r_nu *ring.Ring, ringKeys [][]byte, signature, message []byte) bool {
// Simplified verification - actual would use full Corona verification
// Check basic constraints
if len(signature) != sign.SignatureSize {
return false
}
if len(ringKeys) < 2 {
return false
}
// Placeholder verification (actual would use lattice operations)
return len(message) > 0 && len(signature) > 0
}
func verifyLinkingTag(linkingTag, signature []byte) bool {
// Verify that linking tag is properly formed
return len(linkingTag) == 32
}
func verifyThresholdSignature(r, r_xi, r_nu *ring.Ring, ringKeys, shares [][]byte, message []byte, lagrangeCoeffs []ring.Poly) bool {
// Simplified threshold verification
return len(shares) > 0 && len(message) > 0
}
// RegisterCorona registers all Corona precompiles
func RegisterCorona(registry *Registry) {
registry.Register(CoronaVerifyAddress, &CoronaVerify{})
registry.Register(CoronaBatchVerifyAddress, &CoronaBatchVerify{})
registry.Register(CoronaLinkableVerifyAddress, &CoronaLinkableVerify{})
registry.Register(CoronaKeyAggregateAddress, &CoronaKeyAggregate{})
registry.Register(CoronaRingHashAddress, &CoronaRingHash{})
registry.Register(CoronaThresholdVerifyAddress, &CoronaThresholdVerify{})
}
func init() {
// Auto-register Corona precompiles on package load
RegisterCorona(PostQuantumRegistry)
}
+1 -1
View File
@@ -5,7 +5,7 @@
package precompile
import (
// "github.com/luxfi/geth/common" // removed to avoid import cycle
// "github.com/luxfi/crypto" // removed to avoid import cycle
)
// GetAllPostQuantumPrecompiles returns all post-quantum precompiles
+1 -1
View File
@@ -5,7 +5,7 @@
package precompile
import (
// "github.com/luxfi/geth/common" // removed to avoid import cycle
// "github.com/luxfi/crypto" // removed to avoid import cycle
)
// PrecompiledContract is the interface for EVM precompiled contracts
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"crypto/sha256"
"errors"
// "github.com/luxfi/geth/common" // removed to avoid import cycle
// "github.com/luxfi/crypto" // removed to avoid import cycle
"github.com/luxfi/crypto/lamport"
)
+1 -1
View File
@@ -7,7 +7,7 @@ package precompile
import (
"errors"
// "github.com/luxfi/geth/common" // removed to avoid import cycle
// "github.com/luxfi/crypto" // removed to avoid import cycle
"golang.org/x/crypto/sha3"
)
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2025 The go-ethereum Authors
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
+8 -9
View File
@@ -24,13 +24,12 @@ import (
"errors"
"fmt"
"github.com/luxfi/crypto/common/math"
luxSecp256k1 "github.com/luxfi/crypto/secp256k1"
"github.com/luxfi/crypto/secp256k1"
)
// Ecrecover returns the uncompressed public key that created the given signature.
func Ecrecover(hash, sig []byte) ([]byte, error) {
return luxSecp256k1.RecoverPubkey(hash, sig)
return secp256k1.RecoverPubkey(hash, sig)
}
// SigToPub returns the public key that created the given signature.
@@ -54,21 +53,21 @@ func Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
if len(digestHash) != DigestLength {
return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(digestHash))
}
seckey := math.PaddedBigBytes(prv.D, prv.Params().BitSize/8)
seckey := PaddedBigBytes(prv.D, prv.Params().BitSize/8)
defer zeroBytes(seckey)
return luxSecp256k1.Sign(digestHash, seckey)
return secp256k1.Sign(digestHash, seckey)
}
// VerifySignature checks that the given public key created signature over digest.
// The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
// The signature should have the 64 byte [R || S] format.
func VerifySignature(pubkey, digestHash, signature []byte) bool {
return luxSecp256k1.VerifySignature(pubkey, digestHash, signature)
return secp256k1.VerifySignature(pubkey, digestHash, signature)
}
// DecompressPubkey parses a public key in the 33-byte compressed format.
func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
x, y := luxSecp256k1.DecompressPubkey(pubkey)
x, y := secp256k1.DecompressPubkey(pubkey)
if x == nil {
return nil, errors.New("invalid public key")
}
@@ -77,10 +76,10 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
// CompressPubkey encodes a public key to the 33-byte compressed format.
func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
return luxSecp256k1.CompressPubkey(pubkey.X, pubkey.Y)
return secp256k1.CompressPubkey(pubkey.X, pubkey.Y)
}
// S256 returns an instance of the secp256k1 curve.
func S256() EllipticCurve {
return luxSecp256k1.S256()
return secp256k1.S256()
}
+5 -2
View File
@@ -43,6 +43,9 @@ func sigToPub(hash, sig []byte) (*secp256k1.PublicKey, error) {
if len(sig) != SignatureLength {
return nil, errors.New("invalid signature")
}
if len(hash) != DigestLength {
return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(hash))
}
// Convert to secp256k1 input format with 'recovery id' v at the beginning.
btcsig := make([]byte, SignatureLength)
btcsig[0] = sig[RecoveryIDOffset] + 27
@@ -76,8 +79,8 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
//
// The produced signature is in the [R || S || V] format where V is 0 or 1.
func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
if len(hash) != 32 {
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
if len(hash) != DigestLength {
return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(hash))
}
if prv.Curve != S256() {
return nil, errors.New("private key curve is not secp256k1")
+37 -10
View File
@@ -19,12 +19,12 @@ package crypto
import (
"bytes"
"crypto/ecdsa"
"encoding/hex"
"math/big"
"reflect"
"testing"
"github.com/luxfi/crypto/common"
"github.com/luxfi/crypto/common/hexutil"
"github.com/luxfi/crypto/common/math"
)
var (
@@ -34,6 +34,33 @@ var (
testpubkeyc = hexutil.MustDecode("0x02e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a")
)
// mustParseBig256 parses a hex string into a big.Int, panics on error
func mustParseBig256(s string) *big.Int {
if len(s) >= 2 && (s[0:2] == "0x" || s[0:2] == "0X") {
s = s[2:]
}
if len(s)%2 == 1 {
s = "0" + s
}
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
result := new(big.Int)
result.SetBytes(b)
return result
}
// copyBytes creates a copy of a byte slice
func copyBytes(b []byte) []byte {
if b == nil {
return nil
}
result := make([]byte, len(b))
copy(result, b)
return result
}
func TestEcrecover(t *testing.T) {
pubkey, err := Ecrecover(testmsg, testsig)
if err != nil {
@@ -62,13 +89,13 @@ func TestVerifySignature(t *testing.T) {
if VerifySignature(testpubkey, testmsg, nil) {
t.Errorf("nil signature valid")
}
if VerifySignature(testpubkey, testmsg, append(common.CopyBytes(sig), 1, 2, 3)) {
if VerifySignature(testpubkey, testmsg, append(copyBytes(sig), 1, 2, 3)) {
t.Errorf("signature valid with extra bytes at the end")
}
if VerifySignature(testpubkey, testmsg, sig[:len(sig)-2]) {
t.Errorf("signature valid even though it's incomplete")
}
wrongkey := common.CopyBytes(testpubkey)
wrongkey := copyBytes(testpubkey)
wrongkey[10]++
if VerifySignature(wrongkey, testmsg, sig) {
t.Errorf("signature valid with wrong public key")
@@ -99,7 +126,7 @@ func TestDecompressPubkey(t *testing.T) {
if _, err := DecompressPubkey(testpubkeyc[:5]); err == nil {
t.Errorf("no error for incomplete pubkey")
}
if _, err := DecompressPubkey(append(common.CopyBytes(testpubkeyc), 1, 2, 3)); err == nil {
if _, err := DecompressPubkey(append(copyBytes(testpubkeyc), 1, 2, 3)); err == nil {
t.Errorf("no error for pubkey with extra bytes at the end")
}
}
@@ -107,8 +134,8 @@ func TestDecompressPubkey(t *testing.T) {
func TestCompressPubkey(t *testing.T) {
key := &ecdsa.PublicKey{
Curve: S256(),
X: math.MustParseBig256("0xe32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a"),
Y: math.MustParseBig256("0x0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652"),
X: mustParseBig256("0xe32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a"),
Y: mustParseBig256("0x0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652"),
}
compressed := CompressPubkey(key)
if !bytes.Equal(compressed, testpubkeyc) {
@@ -135,7 +162,7 @@ func TestPubkeyRandom(t *testing.T) {
}
func BenchmarkEcrecoverSignature(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
if _, err := Ecrecover(testmsg, testsig); err != nil {
b.Fatal("ecrecover error", err)
}
@@ -144,7 +171,7 @@ func BenchmarkEcrecoverSignature(b *testing.B) {
func BenchmarkVerifySignature(b *testing.B) {
sig := testsig[:len(testsig)-1] // remove recovery id
for i := 0; i < b.N; i++ {
for b.Loop() {
if !VerifySignature(testpubkey, testmsg, sig) {
b.Fatal("verify error")
}
@@ -152,7 +179,7 @@ func BenchmarkVerifySignature(b *testing.B) {
}
func BenchmarkDecompressPubkey(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
if _, err := DecompressPubkey(testpubkeyc); err != nil {
b.Fatal(err)
}
-1
View File
@@ -1 +0,0 @@
mode: set
-152
View File
@@ -1,152 +0,0 @@
mode: set
github.com/luxfi/crypto/slhdsa/slhdsa.go:72.66,75.14 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:76.18,78.41 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:79.18,81.41 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:82.18,84.41 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:85.18,87.41 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:88.18,90.41 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:91.18,93.41 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:94.10,95.49 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:99.2,99.17 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:99.17,101.3 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:105.2,106.56 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:106.56,108.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa.go:111.2,118.38 6 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:118.38,124.23 6 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:124.23,126.4 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:127.3,127.30 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:130.2,136.8 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:140.102,141.17 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:141.17,143.3 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:145.2,147.29 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:148.18,149.36 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:150.18,151.36 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:152.18,153.36 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:154.18,155.36 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:156.18,157.36 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:158.18,159.36 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:160.10,161.49 1 0
github.com/luxfi/crypto/slhdsa/slhdsa.go:166.2,182.47 11 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:182.47,184.20 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:184.20,186.4 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:187.3,189.24 3 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:192.2,192.23 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:196.86,197.16 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:197.16,199.3 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:201.2,203.18 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:204.18,205.44 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:206.18,207.44 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:208.18,209.44 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:210.18,211.44 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:212.18,213.44 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:214.18,215.44 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:216.10,217.15 1 0
github.com/luxfi/crypto/slhdsa/slhdsa.go:221.2,221.39 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:221.39,223.3 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:227.2,233.25 5 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:233.25,235.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa.go:238.2,238.26 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:238.26,239.42 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:239.42,241.4 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:244.2,244.13 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:248.38,250.2 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:253.40,255.2 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:258.69,261.14 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:262.30,263.41 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:264.30,265.41 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:266.30,267.41 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:268.10,269.49 1 0
github.com/luxfi/crypto/slhdsa/slhdsa.go:272.2,272.31 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:272.31,274.3 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:276.2,279.8 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:283.71,287.14 3 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:288.30,290.39 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:291.30,293.39 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:294.30,296.39 2 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:297.10,298.49 1 0
github.com/luxfi/crypto/slhdsa/slhdsa.go:301.2,301.31 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:301.31,303.3 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:306.2,313.38 6 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:313.38,319.23 6 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:319.23,321.4 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:322.3,322.29 1 1
github.com/luxfi/crypto/slhdsa/slhdsa.go:325.2,331.8 1 1
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:16.26,18.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:23.26,25.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:29.36,31.21 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:31.21,33.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:34.2,34.19 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:38.31,39.41 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:39.41,41.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:45.28,49.2 3 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:52.29,54.2 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:57.75,60.14 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:61.18,63.41 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:64.18,66.41 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:67.18,69.41 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:70.18,72.41 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:73.18,75.41 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:76.18,78.41 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:79.10,80.49 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:84.2,84.17 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:84.17,86.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:89.2,93.55 3 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:93.55,95.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:98.2,107.8 3 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:111.111,114.29 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:115.18,116.36 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:117.18,118.36 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:119.18,120.36 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:121.18,122.36 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:123.18,124.36 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:125.18,126.36 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:127.10,128.49 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:132.2,148.36 9 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:148.36,155.20 6 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:155.20,157.4 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:158.3,158.31 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:162.2,164.20 3 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:175.44,178.23 3 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:178.23,180.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:182.2,185.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:189.59,195.30 4 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:195.30,197.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:200.2,201.50 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:201.50,205.34 3 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:205.34,213.4 6 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:216.2,216.20 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:227.82,228.18 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:228.18,230.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:232.2,238.22 4 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:238.22,240.20 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:240.20,243.18 3 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:243.18,246.5 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:247.4,247.19 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:251.2,254.29 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:254.29,255.17 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:255.17,257.4 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:260.2,263.8 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:267.76,268.33 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:268.33,270.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:272.2,276.26 3 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:276.26,278.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:279.2,285.33 4 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:285.33,287.13 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:287.13,289.26 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:289.26,291.19 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:291.19,293.14 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:295.5,295.26 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:300.2,303.29 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:303.29,304.17 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:304.17,306.4 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:309.2,309.24 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:321.57,327.2 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:330.70,339.42 6 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:339.42,342.3 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:343.2,347.16 3 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:347.16,349.3 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:351.2,354.29 3 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:354.29,356.30 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:356.30,358.31 2 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:358.31,359.10 1 0
github.com/luxfi/crypto/slhdsa/slhdsa_optimized.go:363.2,365.17 2 0
-272
View File
@@ -1,272 +0,0 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// +build cgo
// Package slhdsa provides SLH-DSA (FIPS 205) stateless hash-based signatures
// CGO implementation using Sloth - high-performance SLH-DSA implementation
package slhdsa
/*
#cgo CFLAGS: -I${SRCDIR}/c -I${SRCDIR}/c/sloth/include -O3 -march=native -mavx2
#cgo LDFLAGS: -L${SRCDIR}/c -lslhdsa -lcrypto
#include <stdlib.h>
#include <string.h>
// Sloth SLH-DSA parameter sets (FIPS 205 compliant)
// Using SHA2 variants as specified in FIPS 205
// Level 1 (128-bit security)
#define SLHDSA_SHA2_128S_PUBLIC_KEY_BYTES 32
#define SLHDSA_SHA2_128S_SECRET_KEY_BYTES 64
#define SLHDSA_SHA2_128S_SIGNATURE_BYTES 7856
#define SLHDSA_SHA2_128F_PUBLIC_KEY_BYTES 32
#define SLHDSA_SHA2_128F_SECRET_KEY_BYTES 64
#define SLHDSA_SHA2_128F_SIGNATURE_BYTES 17088
// Level 3 (192-bit security)
#define SLHDSA_SHA2_192S_PUBLIC_KEY_BYTES 48
#define SLHDSA_SHA2_192S_SECRET_KEY_BYTES 96
#define SLHDSA_SHA2_192S_SIGNATURE_BYTES 16224
#define SLHDSA_SHA2_192F_PUBLIC_KEY_BYTES 48
#define SLHDSA_SHA2_192F_SECRET_KEY_BYTES 96
#define SLHDSA_SHA2_192F_SIGNATURE_BYTES 35664
// Level 5 (256-bit security)
#define SLHDSA_SHA2_256S_PUBLIC_KEY_BYTES 64
#define SLHDSA_SHA2_256S_SECRET_KEY_BYTES 128
#define SLHDSA_SHA2_256S_SIGNATURE_BYTES 29792
#define SLHDSA_SHA2_256F_PUBLIC_KEY_BYTES 64
#define SLHDSA_SHA2_256F_SECRET_KEY_BYTES 128
#define SLHDSA_SHA2_256F_SIGNATURE_BYTES 49856
// Sloth API functions (high-performance implementations)
// These would come from sloth headers
int sloth_slhdsa_sha2_128s_keypair(unsigned char *pk, unsigned char *sk);
int sloth_slhdsa_sha2_128s_sign(unsigned char *sig, size_t *siglen,
const unsigned char *m, size_t mlen,
const unsigned char *sk);
int sloth_slhdsa_sha2_128s_verify(const unsigned char *sig, size_t siglen,
const unsigned char *m, size_t mlen,
const unsigned char *pk);
int sloth_slhdsa_sha2_128f_keypair(unsigned char *pk, unsigned char *sk);
int sloth_slhdsa_sha2_128f_sign(unsigned char *sig, size_t *siglen,
const unsigned char *m, size_t mlen,
const unsigned char *sk);
int sloth_slhdsa_sha2_128f_verify(const unsigned char *sig, size_t siglen,
const unsigned char *m, size_t mlen,
const unsigned char *pk);
int sloth_slhdsa_sha2_192s_keypair(unsigned char *pk, unsigned char *sk);
int sloth_slhdsa_sha2_192s_sign(unsigned char *sig, size_t *siglen,
const unsigned char *m, size_t mlen,
const unsigned char *sk);
int sloth_slhdsa_sha2_192s_verify(const unsigned char *sig, size_t siglen,
const unsigned char *m, size_t mlen,
const unsigned char *pk);
int sloth_slhdsa_sha2_192f_keypair(unsigned char *pk, unsigned char *sk);
int sloth_slhdsa_sha2_192f_sign(unsigned char *sig, size_t *siglen,
const unsigned char *m, size_t mlen,
const unsigned char *sk);
int sloth_slhdsa_sha2_192f_verify(const unsigned char *sig, size_t siglen,
const unsigned char *m, size_t mlen,
const unsigned char *pk);
int sloth_slhdsa_sha2_256s_keypair(unsigned char *pk, unsigned char *sk);
int sloth_slhdsa_sha2_256s_sign(unsigned char *sig, size_t *siglen,
const unsigned char *m, size_t mlen,
const unsigned char *sk);
int sloth_slhdsa_sha2_256s_verify(const unsigned char *sig, size_t siglen,
const unsigned char *m, size_t mlen,
const unsigned char *pk);
int sloth_slhdsa_sha2_256f_keypair(unsigned char *pk, unsigned char *sk);
int sloth_slhdsa_sha2_256f_sign(unsigned char *sig, size_t *siglen,
const unsigned char *m, size_t mlen,
const unsigned char *sk);
int sloth_slhdsa_sha2_256f_verify(const unsigned char *sig, size_t siglen,
const unsigned char *m, size_t mlen,
const unsigned char *pk);
// Wrapper functions for unified interface
int slhdsa_keypair(unsigned char *pk, unsigned char *sk, int mode) {
switch(mode) {
case 0: return sloth_slhdsa_sha2_128s_keypair(pk, sk);
case 1: return sloth_slhdsa_sha2_128f_keypair(pk, sk);
case 2: return sloth_slhdsa_sha2_192s_keypair(pk, sk);
case 3: return sloth_slhdsa_sha2_192f_keypair(pk, sk);
case 4: return sloth_slhdsa_sha2_256s_keypair(pk, sk);
case 5: return sloth_slhdsa_sha2_256f_keypair(pk, sk);
default: return -1;
}
}
int slhdsa_sign(unsigned char *sig, size_t *siglen,
const unsigned char *m, size_t mlen,
const unsigned char *sk, int mode) {
switch(mode) {
case 0: return sloth_slhdsa_sha2_128s_sign(sig, siglen, m, mlen, sk);
case 1: return sloth_slhdsa_sha2_128f_sign(sig, siglen, m, mlen, sk);
case 2: return sloth_slhdsa_sha2_192s_sign(sig, siglen, m, mlen, sk);
case 3: return sloth_slhdsa_sha2_192f_sign(sig, siglen, m, mlen, sk);
case 4: return sloth_slhdsa_sha2_256s_sign(sig, siglen, m, mlen, sk);
case 5: return sloth_slhdsa_sha2_256f_sign(sig, siglen, m, mlen, sk);
default: return -1;
}
}
int slhdsa_verify(const unsigned char *sig, size_t siglen,
const unsigned char *m, size_t mlen,
const unsigned char *pk, int mode) {
switch(mode) {
case 0: return sloth_slhdsa_sha2_128s_verify(sig, siglen, m, mlen, pk);
case 1: return sloth_slhdsa_sha2_128f_verify(sig, siglen, m, mlen, pk);
case 2: return sloth_slhdsa_sha2_192s_verify(sig, siglen, m, mlen, pk);
case 3: return sloth_slhdsa_sha2_192f_verify(sig, siglen, m, mlen, pk);
case 4: return sloth_slhdsa_sha2_256s_verify(sig, siglen, m, mlen, pk);
case 5: return sloth_slhdsa_sha2_256f_verify(sig, siglen, m, mlen, pk);
default: return -1;
}
}
// Get sizes for different parameter sets
int slhdsa_publickey_bytes(int mode) {
switch(mode) {
case 0: case 1: return SLHDSA_SHA2_128S_PUBLIC_KEY_BYTES;
case 2: case 3: return SLHDSA_SHA2_192S_PUBLIC_KEY_BYTES;
case 4: case 5: return SLHDSA_SHA2_256S_PUBLIC_KEY_BYTES;
default: return 0;
}
}
int slhdsa_secretkey_bytes(int mode) {
switch(mode) {
case 0: case 1: return SLHDSA_SHA2_128S_SECRET_KEY_BYTES;
case 2: case 3: return SLHDSA_SHA2_192S_SECRET_KEY_BYTES;
case 4: case 5: return SLHDSA_SHA2_256S_SECRET_KEY_BYTES;
default: return 0;
}
}
int slhdsa_signature_bytes(int mode) {
switch(mode) {
case 0: return SLHDSA_SHA2_128S_SIGNATURE_BYTES;
case 1: return SLHDSA_SHA2_128F_SIGNATURE_BYTES;
case 2: return SLHDSA_SHA2_192S_SIGNATURE_BYTES;
case 3: return SLHDSA_SHA2_192F_SIGNATURE_BYTES;
case 4: return SLHDSA_SHA2_256S_SIGNATURE_BYTES;
case 5: return SLHDSA_SHA2_256F_SIGNATURE_BYTES;
default: return 0;
}
}
*/
import "C"
import (
"crypto"
"errors"
"io"
"unsafe"
)
// CGO-based implementation of SLH-DSA using Sloth high-performance library
// GenerateKeyCGO generates a new SLH-DSA key pair using Sloth implementation
func GenerateKeyCGO(rand io.Reader, mode Mode) (*PrivateKey, error) {
// Map mode to C parameter
cMode := C.int(mode)
pkSize := int(C.slhdsa_publickey_bytes(cMode))
skSize := int(C.slhdsa_secretkey_bytes(cMode))
if pkSize == 0 || skSize == 0 {
return nil, errors.New("invalid SLH-DSA mode")
}
// Allocate memory for keys
pk := make([]byte, pkSize)
sk := make([]byte, skSize)
// Generate key pair using Sloth
ret := C.slhdsa_keypair(
(*C.uchar)(unsafe.Pointer(&pk[0])),
(*C.uchar)(unsafe.Pointer(&sk[0])),
cMode,
)
if ret != 0 {
return nil, errors.New("key generation failed")
}
return &PrivateKey{
PublicKey: PublicKey{
mode: mode,
data: pk,
},
data: sk,
}, nil
}
// SignCGO signs a message using the Sloth implementation
func SignCGO(priv *PrivateKey, rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
cMode := C.int(priv.mode)
sigSize := int(C.slhdsa_signature_bytes(cMode))
if sigSize == 0 {
return nil, errors.New("invalid signature size")
}
// Allocate memory for signature
sig := make([]byte, sigSize)
var sigLen C.size_t
// Sign the message using Sloth
ret := C.slhdsa_sign(
(*C.uchar)(unsafe.Pointer(&sig[0])),
&sigLen,
(*C.uchar)(unsafe.Pointer(&message[0])),
C.size_t(len(message)),
(*C.uchar)(unsafe.Pointer(&priv.data[0])),
cMode,
)
if ret != 0 {
return nil, errors.New("signing failed")
}
return sig[:sigLen], nil
}
// VerifyCGO verifies a signature using the Sloth implementation
func VerifyCGO(pub *PublicKey, message, signature []byte) bool {
cMode := C.int(pub.mode)
// Verify the signature using Sloth
ret := C.slhdsa_verify(
(*C.uchar)(unsafe.Pointer(&signature[0])),
C.size_t(len(signature)),
(*C.uchar)(unsafe.Pointer(&message[0])),
C.size_t(len(message)),
(*C.uchar)(unsafe.Pointer(&pub.data[0])),
cMode,
)
return ret == 0
}
// UseCGO returns true if CGO implementation is available
func UseCGO() bool {
return true
}
// BenchmarkMode returns the best mode for benchmarking
// Sloth is optimized for fast variants on modern CPUs with AVX2
func BenchmarkMode() Mode {
// Check CPU capabilities at runtime
// For now, default to fast variants if CGO is available
return SLHDSA128f // Fast variant optimized by Sloth
}
-332
View File
@@ -1,332 +0,0 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// Package slhdsa provides SLH-DSA (FIPS 205) stateless hash-based signatures
// This is a placeholder implementation for CI testing
package slhdsa
import (
"crypto"
"crypto/sha256"
"errors"
"io"
)
// Security parameters for SLH-DSA (Stateless Hash-Based Digital Signature Algorithm)
const (
// SLH-DSA-SHA2-128s (Small signatures, Level 1 security)
SLHDSA128sPublicKeySize = 32 // bytes
SLHDSA128sPrivateKeySize = 64 // bytes
SLHDSA128sSignatureSize = 7856 // bytes
// SLH-DSA-SHA2-128f (Fast signing, Level 1 security)
SLHDSA128fPublicKeySize = 32 // bytes
SLHDSA128fPrivateKeySize = 64 // bytes
SLHDSA128fSignatureSize = 17088 // bytes
// SLH-DSA-SHA2-192s (Small signatures, Level 3 security)
SLHDSA192sPublicKeySize = 48 // bytes
SLHDSA192sPrivateKeySize = 96 // bytes
SLHDSA192sSignatureSize = 16224 // bytes
// SLH-DSA-SHA2-192f (Fast signing, Level 3 security)
SLHDSA192fPublicKeySize = 48 // bytes
SLHDSA192fPrivateKeySize = 96 // bytes
SLHDSA192fSignatureSize = 35664 // bytes
// SLH-DSA-SHA2-256s (Small signatures, Level 5 security)
SLHDSA256sPublicKeySize = 64 // bytes
SLHDSA256sPrivateKeySize = 128 // bytes
SLHDSA256sSignatureSize = 29792 // bytes
// SLH-DSA-SHA2-256f (Fast signing, Level 5 security)
SLHDSA256fPublicKeySize = 64 // bytes
SLHDSA256fPrivateKeySize = 128 // bytes
SLHDSA256fSignatureSize = 49856 // bytes
)
// Mode represents the SLH-DSA parameter set
type Mode int
const (
SLHDSA128s Mode = iota + 1
SLHDSA128f
SLHDSA192s
SLHDSA192f
SLHDSA256s
SLHDSA256f
)
// PublicKey represents an SLH-DSA public key
type PublicKey struct {
mode Mode
data []byte
}
// PrivateKey represents an SLH-DSA private key
type PrivateKey struct {
PublicKey
data []byte
}
// GenerateKey generates a new SLH-DSA key pair
func GenerateKey(rand io.Reader, mode Mode) (*PrivateKey, error) {
var pubKeySize, privKeySize int
switch mode {
case SLHDSA128s:
pubKeySize = SLHDSA128sPublicKeySize
privKeySize = SLHDSA128sPrivateKeySize
case SLHDSA128f:
pubKeySize = SLHDSA128fPublicKeySize
privKeySize = SLHDSA128fPrivateKeySize
case SLHDSA192s:
pubKeySize = SLHDSA192sPublicKeySize
privKeySize = SLHDSA192sPrivateKeySize
case SLHDSA192f:
pubKeySize = SLHDSA192fPublicKeySize
privKeySize = SLHDSA192fPrivateKeySize
case SLHDSA256s:
pubKeySize = SLHDSA256sPublicKeySize
privKeySize = SLHDSA256sPrivateKeySize
case SLHDSA256f:
pubKeySize = SLHDSA256fPublicKeySize
privKeySize = SLHDSA256fPrivateKeySize
default:
return nil, errors.New("invalid SLH-DSA mode")
}
// Check for nil random source
if rand == nil {
return nil, errors.New("random source is nil")
}
// Placeholder implementation - generate random keys
// In real SLH-DSA, public key is derived from private key
privBytes := make([]byte, privKeySize)
if _, err := io.ReadFull(rand, privBytes); err != nil {
return nil, err
}
// Derive public key from private key for consistency
h := sha256.New()
h.Write(privBytes[:32]) // Use first part as seed
h.Write([]byte("slhdsa-public"))
pubSeed := h.Sum(nil)
pubBytes := make([]byte, pubKeySize)
// Fill public key with deterministic data
for i := 0; i < pubKeySize; i += 32 {
h.Reset()
h.Write(pubSeed)
h.Write([]byte{byte(i / 32)})
hash := h.Sum(nil)
end := i + 32
if end > pubKeySize {
end = pubKeySize
}
copy(pubBytes[i:end], hash)
}
return &PrivateKey{
PublicKey: PublicKey{
mode: mode,
data: pubBytes,
},
data: privBytes,
}, nil
}
// Sign signs a message using the private key
func (priv *PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
if priv == nil {
return nil, errors.New("private key is nil")
}
var sigSize int
switch priv.PublicKey.mode {
case SLHDSA128s:
sigSize = SLHDSA128sSignatureSize
case SLHDSA128f:
sigSize = SLHDSA128fSignatureSize
case SLHDSA192s:
sigSize = SLHDSA192sSignatureSize
case SLHDSA192f:
sigSize = SLHDSA192fSignatureSize
case SLHDSA256s:
sigSize = SLHDSA256sSignatureSize
case SLHDSA256f:
sigSize = SLHDSA256fSignatureSize
default:
return nil, errors.New("invalid SLH-DSA mode")
}
// Placeholder: create deterministic signature that can be verified
// Start with hash of public key and message (what Verify expects)
h := sha256.New()
h.Write(priv.PublicKey.data)
h.Write(message)
hash := h.Sum(nil)
signature := make([]byte, sigSize)
// Copy the hash to beginning of signature
copy(signature[:32], hash)
// Fill rest with deterministic data based on private key
// SLH-DSA is stateless so signature should be deterministic
h.Reset()
h.Write(priv.data)
h.Write(message)
privHash := h.Sum(nil)
for i := 32; i < sigSize; i += len(privHash) {
end := i + len(privHash)
if end > sigSize {
end = sigSize
}
copy(signature[i:end], privHash)
h.Write(privHash) // Generate more data
privHash = h.Sum(nil)
}
return signature, nil
}
// Verify verifies a signature using the public key
func (pub *PublicKey) Verify(message, signature []byte, opts crypto.SignerOpts) bool {
if pub == nil {
return false
}
var expectedSigSize int
switch pub.mode {
case SLHDSA128s:
expectedSigSize = SLHDSA128sSignatureSize
case SLHDSA128f:
expectedSigSize = SLHDSA128fSignatureSize
case SLHDSA192s:
expectedSigSize = SLHDSA192sSignatureSize
case SLHDSA192f:
expectedSigSize = SLHDSA192fSignatureSize
case SLHDSA256s:
expectedSigSize = SLHDSA256sSignatureSize
case SLHDSA256f:
expectedSigSize = SLHDSA256fSignatureSize
default:
return false
}
// Check signature size
if len(signature) != expectedSigSize {
return false
}
// Placeholder verification for SLH-DSA
// Recompute expected signature start based on public key and message
h := sha256.New()
h.Write(pub.data)
h.Write(message)
expectedSigStart := h.Sum(nil)
// Check if first 32 bytes match
if len(signature) < 32 {
return false
}
// Compare first 32 bytes
for i := 0; i < 32; i++ {
if signature[i] != expectedSigStart[i] {
return false
}
}
return true
}
// Bytes returns the public key as bytes
func (pub *PublicKey) Bytes() []byte {
return pub.data
}
// Bytes returns the private key as bytes
func (priv *PrivateKey) Bytes() []byte {
return priv.data
}
// PublicKeyFromBytes reconstructs a public key from bytes
func PublicKeyFromBytes(data []byte, mode Mode) (*PublicKey, error) {
var expectedSize int
switch mode {
case SLHDSA128s, SLHDSA128f:
expectedSize = SLHDSA128sPublicKeySize
case SLHDSA192s, SLHDSA192f:
expectedSize = SLHDSA192sPublicKeySize
case SLHDSA256s, SLHDSA256f:
expectedSize = SLHDSA256sPublicKeySize
default:
return nil, errors.New("invalid SLH-DSA mode")
}
if len(data) != expectedSize {
return nil, errors.New("invalid public key size")
}
return &PublicKey{
mode: mode,
data: data,
}, nil
}
// PrivateKeyFromBytes reconstructs a private key from bytes
func PrivateKeyFromBytes(data []byte, mode Mode) (*PrivateKey, error) {
var expectedSize int
var pubKeySize int
switch mode {
case SLHDSA128s, SLHDSA128f:
expectedSize = SLHDSA128sPrivateKeySize
pubKeySize = SLHDSA128sPublicKeySize
case SLHDSA192s, SLHDSA192f:
expectedSize = SLHDSA192sPrivateKeySize
pubKeySize = SLHDSA192sPublicKeySize
case SLHDSA256s, SLHDSA256f:
expectedSize = SLHDSA256sPrivateKeySize
pubKeySize = SLHDSA256sPublicKeySize
default:
return nil, errors.New("invalid SLH-DSA mode")
}
if len(data) != expectedSize {
return nil, errors.New("invalid private key size")
}
// Derive public key from private key (same as GenerateKey)
h := sha256.New()
h.Write(data[:32]) // Use first part as seed
h.Write([]byte("slhdsa-public"))
pubSeed := h.Sum(nil)
pubData := make([]byte, pubKeySize)
// Fill public key with deterministic data
for i := 0; i < pubKeySize; i += 32 {
h.Reset()
h.Write(pubSeed)
h.Write([]byte{byte(i / 32)})
hash := h.Sum(nil)
end := i + 32
if end > pubKeySize {
end = pubKeySize
}
copy(pubData[i:end], hash)
}
return &PrivateKey{
PublicKey: PublicKey{
mode: mode,
data: pubData,
},
data: data,
}, nil
}
-342
View File
@@ -1,342 +0,0 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// Unified Signer for all Lux cryptographic operations
package unified
import (
"crypto"
"crypto/rand"
"errors"
"fmt"
"github.com/luxfi/crypto/bls"
"github.com/luxfi/crypto/mldsa"
"github.com/luxfi/crypto/mlkem"
"github.com/luxfi/crypto/slhdsa"
)
// Signer provides unified interface for all signature types
type Signer interface {
// Sign creates a signature
Sign(message []byte) ([]byte, error)
// Verify checks a signature
Verify(message, signature []byte) bool
// PublicKey returns the public key bytes
PublicKey() []byte
// Type returns the signature type
Type() string
}
// BLSSigner implements Signer for BLS signatures
type BLSSigner struct {
privKey *bls.SecretKey
pubKey *bls.PublicKey
}
// NewBLSSigner creates a new BLS signer
func NewBLSSigner() (*BLSSigner, error) {
seed := make([]byte, 32)
if _, err := rand.Read(seed); err != nil {
return nil, err
}
privKey, err := bls.SecretKeyFromBytes(seed)
if err != nil {
return nil, err
}
return &BLSSigner{
privKey: privKey,
pubKey: privKey.PublicKey(),
}, nil
}
func (s *BLSSigner) Sign(message []byte) ([]byte, error) {
sig := s.privKey.Sign(message)
return bls.SignatureToBytes(sig), nil
}
func (s *BLSSigner) Verify(message, signature []byte) bool {
sig, err := bls.SignatureFromBytes(signature)
if err != nil {
return false
}
return bls.Verify(s.pubKey, message, sig)
}
func (s *BLSSigner) PublicKey() []byte {
return bls.PublicKeyToBytes(s.pubKey)
}
func (s *BLSSigner) Type() string {
return "BLS"
}
// MLDSASigner implements Signer for ML-DSA signatures
type MLDSASigner struct {
privKey *mldsa.PrivateKey
pubKey *mldsa.PublicKey
mode mldsa.Mode
}
// NewMLDSASigner creates a new ML-DSA signer
func NewMLDSASigner(mode mldsa.Mode) (*MLDSASigner, error) {
privKey, err := mldsa.GenerateKey(rand.Reader, mode)
if err != nil {
return nil, err
}
return &MLDSASigner{
privKey: privKey,
pubKey: privKey.PublicKey(),
mode: mode,
}, nil
}
func (s *MLDSASigner) Sign(message []byte) ([]byte, error) {
// ML-DSA requires opts, use crypto.Hash(0) for default
return s.privKey.Sign(rand.Reader, message, crypto.Hash(0))
}
func (s *MLDSASigner) Verify(message, signature []byte) bool {
return s.pubKey.Verify(message, signature, crypto.Hash(0))
}
func (s *MLDSASigner) PublicKey() []byte {
return s.pubKey.Bytes()
}
func (s *MLDSASigner) Type() string {
switch s.mode {
case mldsa.MLDSA44:
return "ML-DSA-44"
case mldsa.MLDSA65:
return "ML-DSA-65"
case mldsa.MLDSA87:
return "ML-DSA-87"
default:
return "ML-DSA"
}
}
// SLHDSASigner implements Signer for SLH-DSA signatures
type SLHDSASigner struct {
privKey *slhdsa.PrivateKey
pubKey *slhdsa.PublicKey
mode slhdsa.Mode
}
// NewSLHDSASigner creates a new SLH-DSA signer
func NewSLHDSASigner(mode slhdsa.Mode) (*SLHDSASigner, error) {
privKey, err := slhdsa.GenerateKey(rand.Reader, mode)
if err != nil {
return nil, err
}
return &SLHDSASigner{
privKey: privKey,
pubKey: privKey.PublicKey(),
mode: mode,
}, nil
}
func (s *SLHDSASigner) Sign(message []byte) ([]byte, error) {
return s.privKey.Sign(rand.Reader, message, crypto.Hash(0))
}
func (s *SLHDSASigner) Verify(message, signature []byte) bool {
return s.pubKey.Verify(message, signature, crypto.Hash(0))
}
func (s *SLHDSASigner) PublicKey() []byte {
return s.pubKey.Bytes()
}
func (s *SLHDSASigner) Type() string {
switch s.mode {
case slhdsa.SLHDSA128f:
return "SLH-DSA-128f"
case slhdsa.SLHDSA192f:
return "SLH-DSA-192f"
case slhdsa.SLHDSA256f:
return "SLH-DSA-256f"
default:
return "SLH-DSA"
}
}
// HybridSigner combines BLS and ML-DSA for quantum-safe signatures
type HybridSigner struct {
bls *BLSSigner
mldsa *MLDSASigner
}
// NewHybridSigner creates a signer with both BLS and ML-DSA
func NewHybridSigner() (*HybridSigner, error) {
blsSigner, err := NewBLSSigner()
if err != nil {
return nil, err
}
mldsaSigner, err := NewMLDSASigner(mldsa.MLDSA65)
if err != nil {
return nil, err
}
return &HybridSigner{
bls: blsSigner,
mldsa: mldsaSigner,
}, nil
}
func (s *HybridSigner) Sign(message []byte) ([]byte, error) {
blsSig, err := s.bls.Sign(message)
if err != nil {
return nil, fmt.Errorf("BLS sign failed: %w", err)
}
mldsaSig, err := s.mldsa.Sign(message)
if err != nil {
return nil, fmt.Errorf("ML-DSA sign failed: %w", err)
}
// Combine signatures: [2-byte BLS len][BLS sig][ML-DSA sig]
result := make([]byte, 2+len(blsSig)+len(mldsaSig))
result[0] = byte(len(blsSig) >> 8)
result[1] = byte(len(blsSig))
copy(result[2:], blsSig)
copy(result[2+len(blsSig):], mldsaSig)
return result, nil
}
func (s *HybridSigner) Verify(message, signature []byte) bool {
if len(signature) < 2 {
return false
}
blsLen := int(signature[0])<<8 | int(signature[1])
if len(signature) < 2+blsLen {
return false
}
blsSig := signature[2 : 2+blsLen]
mldsaSig := signature[2+blsLen:]
return s.bls.Verify(message, blsSig) && s.mldsa.Verify(message, mldsaSig)
}
func (s *HybridSigner) PublicKey() []byte {
blsPub := s.bls.PublicKey()
mldsaPub := s.mldsa.PublicKey()
// Combine public keys
result := make([]byte, 2+len(blsPub)+len(mldsaPub))
result[0] = byte(len(blsPub) >> 8)
result[1] = byte(len(blsPub))
copy(result[2:], blsPub)
copy(result[2+len(blsPub):], mldsaPub)
return result
}
func (s *HybridSigner) Type() string {
return "Hybrid-BLS-MLDSA"
}
// KEMProvider handles key encapsulation
type KEMProvider interface {
Encapsulate() (ciphertext, sharedSecret []byte, err error)
Decapsulate(ciphertext []byte) (sharedSecret []byte, err error)
}
// MLKEMProvider implements KEMProvider for ML-KEM
type MLKEMProvider struct {
privKey *mlkem.PrivateKey
pubKey *mlkem.PublicKey
mode mlkem.Mode
}
// NewMLKEMProvider creates a new ML-KEM provider
func NewMLKEMProvider(mode mlkem.Mode) (*MLKEMProvider, error) {
priv, pub, err := mlkem.GenerateKeyPair(rand.Reader, mode)
if err != nil {
return nil, err
}
return &MLKEMProvider{
privKey: priv,
pubKey: pub,
mode: mode,
}, nil
}
func (k *MLKEMProvider) Encapsulate() ([]byte, []byte, error) {
return mlkem.Encapsulate(k.pubKey, rand.Reader)
}
func (k *MLKEMProvider) Decapsulate(ciphertext []byte) ([]byte, error) {
return mlkem.Decapsulate(k.privKey, ciphertext)
}
// UnifiedProvider combines signing and KEM operations
type UnifiedProvider struct {
signer Signer
kem KEMProvider
}
// NewUnifiedProvider creates a provider with both signing and KEM
func NewUnifiedProvider(signerType string) (*UnifiedProvider, error) {
var signer Signer
var err error
switch signerType {
case "BLS":
signer, err = NewBLSSigner()
case "ML-DSA":
signer, err = NewMLDSASigner(mldsa.MLDSA65)
case "SLH-DSA":
signer, err = NewSLHDSASigner(slhdsa.SLHDSA128f)
case "Hybrid":
signer, err = NewHybridSigner()
default:
return nil, errors.New("unknown signer type")
}
if err != nil {
return nil, err
}
kem, err := NewMLKEMProvider(mlkem.MLKEM768)
if err != nil {
return nil, err
}
return &UnifiedProvider{
signer: signer,
kem: kem,
}, nil
}
func (u *UnifiedProvider) Sign(message []byte) ([]byte, error) {
return u.signer.Sign(message)
}
func (u *UnifiedProvider) Verify(message, signature []byte) bool {
return u.signer.Verify(message, signature)
}
func (u *UnifiedProvider) Encapsulate() ([]byte, []byte, error) {
return u.kem.Encapsulate()
}
func (u *UnifiedProvider) Decapsulate(ciphertext []byte) ([]byte, error) {
return u.kem.Decapsulate(ciphertext)
}
func (u *UnifiedProvider) Type() string {
return u.signer.Type()
}
-255
View File
@@ -1,255 +0,0 @@
package unified
import (
"bytes"
"testing"
"github.com/luxfi/crypto/mldsa"
"github.com/luxfi/crypto/mlkem"
"github.com/luxfi/crypto/slhdsa"
)
func TestBLSSigner(t *testing.T) {
signer, err := NewBLSSigner()
if err != nil {
t.Fatalf("Failed to create BLS signer: %v", err)
}
message := []byte("Test message for BLS")
sig, err := signer.Sign(message)
if err != nil {
t.Fatalf("Failed to sign: %v", err)
}
if !signer.Verify(message, sig) {
t.Fatalf("Signature verification failed")
}
if signer.Verify([]byte("wrong message"), sig) {
t.Fatalf("Signature should not verify for wrong message")
}
t.Logf("BLS signature size: %d bytes", len(sig))
t.Logf("BLS public key size: %d bytes", len(signer.PublicKey()))
}
func TestMLDSASigner(t *testing.T) {
modes := []struct {
name string
mode mldsa.Mode
}{
{"ML-DSA-44", mldsa.MLDSA44},
{"ML-DSA-65", mldsa.MLDSA65},
{"ML-DSA-87", mldsa.MLDSA87},
}
for _, m := range modes {
t.Run(m.name, func(t *testing.T) {
signer, err := NewMLDSASigner(m.mode)
if err != nil {
t.Fatalf("Failed to create %s signer: %v", m.name, err)
}
message := []byte("Test message for ML-DSA")
sig, err := signer.Sign(message)
if err != nil {
t.Fatalf("Failed to sign: %v", err)
}
if !signer.Verify(message, sig) {
t.Fatalf("Signature verification failed")
}
if signer.Type() != m.name {
t.Fatalf("Expected type %s, got %s", m.name, signer.Type())
}
t.Logf("%s signature size: %d bytes", m.name, len(sig))
t.Logf("%s public key size: %d bytes", m.name, len(signer.PublicKey()))
})
}
}
func TestSLHDSASigner(t *testing.T) {
modes := []struct {
name string
mode slhdsa.Mode
}{
{"SLH-DSA-128f", slhdsa.SLHDSA128f},
{"SLH-DSA-192f", slhdsa.SLHDSA192f},
{"SLH-DSA-256f", slhdsa.SLHDSA256f},
}
for _, m := range modes {
t.Run(m.name, func(t *testing.T) {
signer, err := NewSLHDSASigner(m.mode)
if err != nil {
t.Fatalf("Failed to create %s signer: %v", m.name, err)
}
message := []byte("Test message for SLH-DSA")
sig, err := signer.Sign(message)
if err != nil {
t.Fatalf("Failed to sign: %v", err)
}
if !signer.Verify(message, sig) {
t.Fatalf("Signature verification failed")
}
t.Logf("%s signature size: %d bytes", m.name, len(sig))
t.Logf("%s public key size: %d bytes", m.name, len(signer.PublicKey()))
})
}
}
func TestHybridSigner(t *testing.T) {
signer, err := NewHybridSigner()
if err != nil {
t.Fatalf("Failed to create hybrid signer: %v", err)
}
message := []byte("Test message for hybrid signing")
sig, err := signer.Sign(message)
if err != nil {
t.Fatalf("Failed to sign: %v", err)
}
if !signer.Verify(message, sig) {
t.Fatalf("Signature verification failed")
}
if signer.Verify([]byte("wrong message"), sig) {
t.Fatalf("Signature should not verify for wrong message")
}
t.Logf("Hybrid signature size: %d bytes", len(sig))
t.Logf("Hybrid public key size: %d bytes", len(signer.PublicKey()))
t.Logf("Hybrid type: %s", signer.Type())
}
func TestMLKEMProvider(t *testing.T) {
modes := []struct {
name string
mode mlkem.Mode
}{
{"ML-KEM-512", mlkem.MLKEM512},
{"ML-KEM-768", mlkem.MLKEM768},
{"ML-KEM-1024", mlkem.MLKEM1024},
}
for _, m := range modes {
t.Run(m.name, func(t *testing.T) {
kem, err := NewMLKEMProvider(m.mode)
if err != nil {
t.Fatalf("Failed to create %s provider: %v", m.name, err)
}
ciphertext, sharedSecret1, err := kem.Encapsulate()
if err != nil {
t.Fatalf("Encapsulation failed: %v", err)
}
sharedSecret2, err := kem.Decapsulate(ciphertext)
if err != nil {
t.Fatalf("Decapsulation failed: %v", err)
}
if !bytes.Equal(sharedSecret1, sharedSecret2) {
t.Fatalf("Shared secrets don't match")
}
t.Logf("%s ciphertext size: %d bytes", m.name, len(ciphertext))
t.Logf("%s shared secret size: %d bytes", m.name, len(sharedSecret1))
})
}
}
func TestUnifiedProvider(t *testing.T) {
signerTypes := []string{"BLS", "ML-DSA", "SLH-DSA", "Hybrid"}
for _, signerType := range signerTypes {
t.Run(signerType, func(t *testing.T) {
provider, err := NewUnifiedProvider(signerType)
if err != nil {
t.Fatalf("Failed to create unified provider: %v", err)
}
message := []byte("Test message for unified provider")
// Test signing
sig, err := provider.Sign(message)
if err != nil {
t.Fatalf("Failed to sign: %v", err)
}
if !provider.Verify(message, sig) {
t.Fatalf("Signature verification failed")
}
// Test KEM
ciphertext, sharedSecret1, err := provider.Encapsulate()
if err != nil {
t.Fatalf("Encapsulation failed: %v", err)
}
sharedSecret2, err := provider.Decapsulate(ciphertext)
if err != nil {
t.Fatalf("Decapsulation failed: %v", err)
}
if !bytes.Equal(sharedSecret1, sharedSecret2) {
t.Fatalf("Shared secrets don't match")
}
t.Logf("%s provider signature size: %d bytes", signerType, len(sig))
t.Logf("%s provider type: %s", signerType, provider.Type())
})
}
}
func BenchmarkSigners(b *testing.B) {
benchmarks := []struct {
name string
create func() (Signer, error)
}{
{"BLS", func() (Signer, error) { return NewBLSSigner() }},
{"ML-DSA-65", func() (Signer, error) { return NewMLDSASigner(mldsa.MLDSA65) }},
{"SLH-DSA-128f", func() (Signer, error) { return NewSLHDSASigner(slhdsa.SLHDSA128f) }},
{"Hybrid", func() (Signer, error) { return NewHybridSigner() }},
}
message := []byte("Benchmark message for signing performance testing")
for _, bm := range benchmarks {
signer, err := bm.create()
if err != nil {
b.Fatal(err)
}
b.Run(bm.name+"_Sign", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := signer.Sign(message)
if err != nil {
b.Fatal(err)
}
}
})
sig, _ := signer.Sign(message)
b.Run(bm.name+"_Verify", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !signer.Verify(message, sig) {
b.Fatal("Verification failed")
}
}
})
}
}
-33
View File
@@ -1,33 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries Inc All rights reserved.
// See the file LICENSE for licensing terms.
package bloom
import (
"github.com/luxfi/metric"
"testing"
"github.com/stretchr/testify/require"
)
func TestNew(t *testing.T) {
var (
require = require.New(t)
count = 10000
p = 0.1
)
numHashes, numEntries := OptimalParameters(count, p)
f, err := New(numHashes, numEntries)
require.NoError(err)
require.NotNil(f)
salt := []byte("test salt")
Add(f, []byte("hello"), salt)
contains := Contains(f, []byte("hello"), salt)
require.True(contains, "should have contained the key")
contains = Contains(f, []byte("bye"), salt)
require.False(contains, "shouldn't have contained the key")
}
-97
View File
@@ -1,97 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package bloom
import (
"github.com/luxfi/metric"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/luxfi/node/utils/units"
)
func TestNewErrors(t *testing.T) {
tests := []struct {
numHashes int
numEntries int
err error
}{
{
numHashes: 0,
numEntries: 1,
err: errTooFewHashes,
},
{
numHashes: 17,
numEntries: 1,
err: errTooManyHashes,
},
{
numHashes: 8,
numEntries: 0,
err: errTooFewEntries,
},
}
for _, test := range tests {
t.Run(test.err.Error(), func(t *testing.T) {
_, err := New(test.numHashes, test.numEntries)
require.ErrorIs(t, err, test.err)
})
}
}
func TestNormalUsage(t *testing.T) {
require := require.New(t)
toAdd := make([]uint64, 1024)
for i := range toAdd {
toAdd[i] = rand.Uint64() //#nosec G404
}
initialNumHashes, initialNumBytes := OptimalParameters(1024, 0.01)
filter, err := New(initialNumHashes, initialNumBytes)
require.NoError(err)
for i, elem := range toAdd {
filter.Add(elem)
for _, elem := range toAdd[:i] {
require.True(filter.Contains(elem))
}
}
require.Equal(len(toAdd), filter.Count())
filterBytes := filter.Marshal()
parsedFilter, err := Parse(filterBytes)
require.NoError(err)
for _, elem := range toAdd {
require.True(parsedFilter.Contains(elem))
}
parsedFilterBytes := parsedFilter.Marshal()
require.Equal(filterBytes, parsedFilterBytes)
}
func BenchmarkAdd(b *testing.B) {
f, err := New(8, 16*units.KiB)
require.NoError(b, err)
b.ResetTimer()
for i := 0; i < b.N; i++ {
f.Add(1)
}
}
func BenchmarkMarshal(b *testing.B) {
f, err := New(OptimalParameters(10_000, .01))
require.NoError(b, err)
b.ResetTimer()
for i := 0; i < b.N; i++ {
f.Marshal()
}
}
-35
View File
@@ -1,35 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package bloom
import (
"github.com/luxfi/metric"
"testing"
"github.com/stretchr/testify/require"
"github.com/luxfi/ids"
"github.com/luxfi/node/utils/units"
)
func TestCollisionResistance(t *testing.T) {
require := require.New(t)
f, err := New(8, 16*units.KiB)
require.NoError(err)
Add(f, []byte("hello world?"), []byte("so salty"))
collision := Contains(f, []byte("hello world!"), []byte("so salty"))
require.False(collision)
}
func BenchmarkHash(b *testing.B) {
key := ids.GenerateTestID()
salt := ids.GenerateTestID()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Hash(key[:], salt[:])
}
}
-204
View File
@@ -1,204 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package bloom
import (
"github.com/luxfi/metric"
"fmt"
"math"
"testing"
"github.com/stretchr/testify/require"
)
const largestFloat64LessThan1 float64 = 1 - 1e-16
func TestOptimalHashes(t *testing.T) {
tests := []struct {
numEntries int
count int
expectedHashes int
}{
{ // invalid params
numEntries: 0,
count: 1024,
expectedHashes: minHashes,
},
{ // invalid params
numEntries: 1024,
count: 0,
expectedHashes: maxHashes,
},
{
numEntries: math.MaxInt,
count: 1,
expectedHashes: maxHashes,
},
{
numEntries: 1,
count: math.MaxInt,
expectedHashes: minHashes,
},
{
numEntries: 1024,
count: 1024,
expectedHashes: 6,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%d_%d", test.numEntries, test.count), func(t *testing.T) {
hashes := OptimalHashes(test.numEntries, test.count)
require.Equal(t, test.expectedHashes, hashes)
})
}
}
func TestOptimalEntries(t *testing.T) {
tests := []struct {
count int
falsePositiveProbability float64
expectedEntries int
}{
{ // invalid params
count: 0,
falsePositiveProbability: .5,
expectedEntries: minEntries,
},
{ // invalid params
count: 1,
falsePositiveProbability: 0,
expectedEntries: math.MaxInt,
},
{ // invalid params
count: 1,
falsePositiveProbability: 1,
expectedEntries: minEntries,
},
{
count: math.MaxInt,
falsePositiveProbability: math.SmallestNonzeroFloat64,
expectedEntries: math.MaxInt,
},
{
count: 1024,
falsePositiveProbability: largestFloat64LessThan1,
expectedEntries: minEntries,
},
{
count: 1024,
falsePositiveProbability: .01,
expectedEntries: 1227,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%d_%f", test.count, test.falsePositiveProbability), func(t *testing.T) {
entries := OptimalEntries(test.count, test.falsePositiveProbability)
require.Equal(t, test.expectedEntries, entries)
})
}
}
func TestEstimateEntries(t *testing.T) {
tests := []struct {
numHashes int
numEntries int
falsePositiveProbability float64
expectedEntries int
}{
{ // invalid params
numHashes: 0,
numEntries: 2_048,
falsePositiveProbability: .5,
expectedEntries: 0,
},
{ // invalid params
numHashes: 1,
numEntries: 0,
falsePositiveProbability: .5,
expectedEntries: 0,
},
{ // invalid params
numHashes: 1,
numEntries: 1,
falsePositiveProbability: 2,
expectedEntries: math.MaxInt,
},
{ // invalid params
numHashes: 1,
numEntries: 1,
falsePositiveProbability: -1,
expectedEntries: 0,
},
{
numHashes: 8,
numEntries: 2_048,
falsePositiveProbability: 0,
expectedEntries: 0,
},
{ // params from OptimalParameters(10_000, .01)
numHashes: 7,
numEntries: 11_982,
falsePositiveProbability: .01,
expectedEntries: 9_993,
},
{ // params from OptimalParameters(100_000, .001)
numHashes: 10,
numEntries: 179_720,
falsePositiveProbability: .001,
expectedEntries: 100_000,
},
{ // params from OptimalParameters(10_000, .01)
numHashes: 7,
numEntries: 11_982,
falsePositiveProbability: .05,
expectedEntries: 14_449,
},
{ // params from OptimalParameters(10_000, .01)
numHashes: 7,
numEntries: 11_982,
falsePositiveProbability: 1,
expectedEntries: math.MaxInt,
},
{ // params from OptimalParameters(10_000, .01)
numHashes: 7,
numEntries: 11_982,
falsePositiveProbability: math.SmallestNonzeroFloat64,
expectedEntries: 0,
},
{ // params from OptimalParameters(10_000, .01)
numHashes: 7,
numEntries: 11_982,
falsePositiveProbability: largestFloat64LessThan1,
expectedEntries: math.MaxInt,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%d_%d_%f", test.numHashes, test.numEntries, test.falsePositiveProbability), func(t *testing.T) {
entries := EstimateCount(test.numHashes, test.numEntries, test.falsePositiveProbability)
require.Equal(t, test.expectedEntries, entries)
})
}
}
func FuzzOptimalHashes(f *testing.F) {
f.Fuzz(func(t *testing.T, numEntries, count int) {
hashes := OptimalHashes(numEntries, count)
require.GreaterOrEqual(t, hashes, minHashes)
require.LessOrEqual(t, hashes, maxHashes)
})
}
func FuzzOptimalEntries(f *testing.F) {
f.Fuzz(func(t *testing.T, count int, falsePositiveProbability float64) {
entries := OptimalEntries(count, falsePositiveProbability)
require.GreaterOrEqual(t, entries, minEntries)
})
}
func FuzzEstimateEntries(f *testing.F) {
f.Fuzz(func(t *testing.T, numHashes, numEntries int, falsePositiveProbability float64) {
entries := EstimateCount(numHashes, numEntries, falsePositiveProbability)
require.GreaterOrEqual(t, entries, 0)
})
}
-113
View File
@@ -1,113 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package bloom
import (
"github.com/luxfi/metric"
"math"
"testing"
"github.com/stretchr/testify/require"
)
func NewMaliciousFilter(numHashes, numEntries int) *Filter {
f := &Filter{
numBits: uint64(numEntries * bitsPerByte),
hashSeeds: make([]uint64, numHashes),
entries: make([]byte, numEntries),
count: 0,
}
for i := range f.entries {
f.entries[i] = math.MaxUint8
}
return f
}
func TestParseErrors(t *testing.T) {
tests := []struct {
bytes []byte
err error
}{
{
bytes: nil,
err: errInvalidNumHashes,
},
{
bytes: NewMaliciousFilter(0, 1).Marshal(),
err: errTooFewHashes,
},
{
bytes: NewMaliciousFilter(17, 1).Marshal(),
err: errTooManyHashes,
},
{
bytes: NewMaliciousFilter(1, 0).Marshal(),
err: errTooFewEntries,
},
{
bytes: []byte{
0x01, // num hashes = 1
},
err: errTooFewEntries,
},
}
for _, test := range tests {
t.Run(test.err.Error(), func(t *testing.T) {
_, err := Parse(test.bytes)
require.ErrorIs(t, err, test.err)
})
}
}
func BenchmarkParse(b *testing.B) {
f, err := New(OptimalParameters(10_000, .01))
require.NoError(b, err)
bytes := f.Marshal()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = Parse(bytes)
}
}
func BenchmarkContains(b *testing.B) {
f := NewMaliciousFilter(maxHashes, 1)
b.ResetTimer()
for i := 0; i < b.N; i++ {
f.Contains(1)
}
}
func FuzzParseThenMarshal(f *testing.F) {
f.Fuzz(func(t *testing.T, bytes []byte) {
f, err := Parse(bytes)
if err != nil {
return
}
marshalledBytes := marshal(f.hashSeeds, f.entries)
require.Equal(t, bytes, marshalledBytes)
})
}
func FuzzMarshalThenParse(f *testing.F) {
f.Fuzz(func(t *testing.T, numHashes int, entries []byte) {
require := require.New(t)
hashSeeds, err := newHashSeeds(numHashes)
if err != nil {
return
}
if len(entries) < minEntries {
return
}
marshalledBytes := marshal(hashSeeds, entries)
rf, err := Parse(marshalledBytes)
require.NoError(err)
require.Equal(hashSeeds, rf.hashSeeds)
require.Equal(entries, rf.entries)
})
}
-47
View File
@@ -1,47 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package utilmetric
import (
luxmetric "github.com/luxfi/metric"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestAppendNamespace(t *testing.T) {
tests := []struct {
prefix string
suffix string
expected string
}{
{
prefix: "node",
suffix: "isgreat",
expected: "node_isgreat",
},
{
prefix: "",
suffix: "sucks",
expected: "sucks",
},
{
prefix: "sucks",
suffix: "",
expected: "sucks",
},
{
prefix: "",
suffix: "",
expected: "",
},
}
for _, test := range tests {
t.Run(strings.Join([]string{test.prefix, test.suffix}, "_"), func(t *testing.T) {
namespace := AppendNamespace(test.prefix, test.suffix)
require.Equal(t, test.expected, namespace)
})
}
}