mirror of
https://github.com/luxfi/crypto.git
synced 2026-07-27 01:54:50 +00:00
Sync with geth
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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=
|
||||
@@ -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
@@ -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,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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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,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
@@ -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
@@ -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
@@ -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 +0,0 @@
|
||||
mode: set
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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[:])
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user