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
|
# AI Assistant Knowledge Base
|
||||||
|
|
||||||
**Last Updated**: $(date +%Y-%m-%d)
|
**Last Updated**: 2025-08-15
|
||||||
**Project**: $(basename "$REPO_PATH")
|
**Project**: crypto
|
||||||
**Organization**: $(basename "$(dirname "$REPO_PATH")")
|
**Organization**: lux
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
This repository is part of the $(basename "$(dirname "$REPO_PATH")") organization.
|
This repository contains the Lux post-quantum cryptography implementation.
|
||||||
|
|
||||||
## Essential Commands
|
## Essential Commands
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
```bash
|
```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
|
## 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
|
## Key Technologies
|
||||||
|
|
||||||
|
- Go 1.21+
|
||||||
|
- Cloudflare CIRCL library for post-quantum cryptography
|
||||||
|
- CGO optimizations for performance
|
||||||
|
- Comprehensive test coverage
|
||||||
|
|
||||||
## Development Workflow
|
## 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
|
## Context for All AI Assistants
|
||||||
|
|
||||||
This file (`LLM.md`) is symlinked as:
|
This file (`LLM.md`) serves as the central knowledge base for AI assistants working on this project.
|
||||||
- `.AGENTS.md`
|
|
||||||
- `CLAUDE.md`
|
|
||||||
- `QWEN.md`
|
|
||||||
- `GEMINI.md`
|
|
||||||
|
|
||||||
All files reference the same knowledge base. Updates here propagate to all AI systems.
|
|
||||||
|
|
||||||
## Rules for AI Assistants
|
## Rules for AI Assistants
|
||||||
|
|
||||||
1. **ALWAYS** update LLM.md with significant discoveries
|
1. **ALWAYS** update LLM.md with significant discoveries
|
||||||
2. **NEVER** commit symlinked files (.AGENTS.md, CLAUDE.md, etc.) - they're in .gitignore
|
2. **NEVER** commit AI-generated summary files
|
||||||
3. **NEVER** create random summary files - update THIS file
|
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[:]...)
|
return append(b, a[:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:unused,deadcode
|
//nolint:unused
|
||||||
func appendUint32(b []byte, x uint32) []byte {
|
func appendUint32(b []byte, x uint32) []byte {
|
||||||
var a [4]byte
|
var a [4]byte
|
||||||
binary.BigEndian.PutUint32(a[:], x)
|
binary.BigEndian.PutUint32(a[:], x)
|
||||||
@@ -314,7 +314,7 @@ func consumeUint64(b []byte) ([]byte, uint64) {
|
|||||||
return b[8:], x
|
return b[8:], x
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:unused,deadcode
|
//nolint:unused
|
||||||
func consumeUint32(b []byte) ([]byte, uint32) {
|
func consumeUint32(b []byte) ([]byte, uint32) {
|
||||||
x := binary.BigEndian.Uint32(b)
|
x := binary.BigEndian.Uint32(b)
|
||||||
return b[4:], x
|
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},
|
{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) {
|
func hashBlocksGeneric(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
||||||
var m [16]uint64
|
var m [16]uint64
|
||||||
c0, c1 := c[0], c[1]
|
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)
|
data := make([]byte, size)
|
||||||
b.SetBytes(int64(size))
|
b.SetBytes(int64(size))
|
||||||
b.ResetTimer()
|
for b.Loop() {
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Sum512(data)
|
Sum512(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,8 +318,7 @@ func benchmarkWrite(b *testing.B, size int, sse4, avx, avx2 bool) {
|
|||||||
data := make([]byte, size)
|
data := make([]byte, size)
|
||||||
h, _ := New512(nil)
|
h, _ := New512(nil)
|
||||||
b.SetBytes(int64(size))
|
b.SetBytes(int64(size))
|
||||||
b.ResetTimer()
|
for b.Loop() {
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
h.Write(data)
|
h.Write(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -9,18 +9,18 @@
|
|||||||
package bn256
|
package bn256
|
||||||
|
|
||||||
import (
|
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
|
// 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.
|
// 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
|
// 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.
|
// 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.
|
// PairingCheck calculates the Optimal Ate pairing for a set of points.
|
||||||
func PairingCheck(a []*G1, b []*G2) bool {
|
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"
|
"golang.org/x/sys/cpu"
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:varcheck,unused,deadcode
|
//nolint:unused
|
||||||
var hasBMI2 = cpu.X86.HasBMI2
|
var hasBMI2 = cpu.X86.HasBMI2
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
|
|||||||
+2
-10
@@ -5,6 +5,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc/bn254"
|
"github.com/consensys/gnark-crypto/ecc/bn254"
|
||||||
|
"github.com/luxfi/bitutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// G1 is the affine representation of a G1 group element.
|
// 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")
|
return 0, errors.New("invalid G1 point size")
|
||||||
}
|
}
|
||||||
|
|
||||||
if allZeroes(buf[:64]) {
|
if !bitutil.TestBytes(buf[:64]) {
|
||||||
// point at infinity
|
// point at infinity
|
||||||
g.inner.X.SetZero()
|
g.inner.X.SetZero()
|
||||||
g.inner.Y.SetZero()
|
g.inner.Y.SetZero()
|
||||||
@@ -82,12 +83,3 @@ func (p *G1) Marshal() []byte {
|
|||||||
|
|
||||||
return output
|
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"
|
"errors"
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc/bn254"
|
"github.com/consensys/gnark-crypto/ecc/bn254"
|
||||||
|
"github.com/luxfi/bitutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// G2 is the affine representation of a G2 group element.
|
// 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")
|
return 0, errors.New("invalid G2 point size")
|
||||||
}
|
}
|
||||||
|
|
||||||
if allZeroes(buf[:128]) {
|
if !bitutil.TestBytes(buf[:128]) {
|
||||||
// point at infinity
|
// point at infinity
|
||||||
g.inner.X.A0.SetZero()
|
g.inner.X.A0.SetZero()
|
||||||
g.inner.X.A1.SetZero()
|
g.inner.X.A1.SetZero()
|
||||||
|
|||||||
-3808
File diff suppressed because it is too large
Load Diff
@@ -28,10 +28,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/luxfi/crypto/rlp"
|
"github.com/luxfi/crypto/rlp"
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignatureLength indicates the byte length required to carry a signature with recovery id.
|
// 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.
|
// RecoveryIDOffset points to the byte offset within the signature that contains the recovery id.
|
||||||
const RecoveryIDOffset = 64
|
const RecoveryIDOffset = 64
|
||||||
|
|
||||||
// DigestLength sets the signature digest exact length
|
|
||||||
const DigestLength = 32
|
|
||||||
|
|
||||||
// HashLength is the expected length of the hash
|
// HashLength is the expected length of the hash
|
||||||
const HashLength = 32
|
const HashLength = 32
|
||||||
|
|
||||||
// AddressLength is the expected length of the address
|
// AddressLength is the expected length of the address
|
||||||
const AddressLength = 20
|
const AddressLength = 20
|
||||||
|
|
||||||
|
// DigestLength sets the signature digest exact length
|
||||||
|
const DigestLength = 32
|
||||||
|
|
||||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||||
type Hash [HashLength]byte
|
type Hash [HashLength]byte
|
||||||
|
|
||||||
@@ -145,59 +143,7 @@ type KeccakState interface {
|
|||||||
Read([]byte) (int, error)
|
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.
|
// HexToAddress returns Address with byte values of s.
|
||||||
func HexToAddress(s string) Address {
|
func HexToAddress(s string) Address {
|
||||||
@@ -273,15 +219,14 @@ func FromECDSA(priv *ecdsa.PrivateKey) []byte {
|
|||||||
|
|
||||||
// UnmarshalPubkey converts bytes to a secp256k1 public key.
|
// UnmarshalPubkey converts bytes to a secp256k1 public key.
|
||||||
func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
|
func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
|
||||||
curve := S256().(EllipticCurve)
|
x, y := S256().Unmarshal(pub)
|
||||||
x, y := curve.Unmarshal(pub)
|
|
||||||
if x == nil {
|
if x == nil {
|
||||||
return nil, errInvalidPubkey
|
return nil, errInvalidPubkey
|
||||||
}
|
}
|
||||||
if !curve.IsOnCurve(x, y) {
|
if !S256().IsOnCurve(x, y) {
|
||||||
return nil, errInvalidPubkey
|
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.
|
// FromECDSAPub converts a secp256k1 public key to bytes.
|
||||||
@@ -316,7 +261,7 @@ func LoadECDSA(file string) (*ecdsa.PrivateKey, error) {
|
|||||||
r := bufio.NewReader(fd)
|
r := bufio.NewReader(fd)
|
||||||
buf := make([]byte, 64)
|
buf := make([]byte, 64)
|
||||||
n, err := readASCII(buf, r)
|
n, err := readASCII(buf, r)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if n != len(buf) {
|
} else if n != len(buf) {
|
||||||
return nil, errors.New("key file too short, want 64 hex characters")
|
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
|
// wordBytes is the number of bytes in a big.Word
|
||||||
const wordBytes = int(32 << (uint64(^big.Word(0)) >> 63))
|
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) {
|
func zeroBytes(bytes []byte) {
|
||||||
clear(bytes)
|
clear(bytes)
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-9
@@ -26,7 +26,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/luxfi/crypto/common"
|
|
||||||
"github.com/luxfi/crypto/common/hexutil"
|
"github.com/luxfi/crypto/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,7 +59,7 @@ func TestToECDSAErrors(t *testing.T) {
|
|||||||
|
|
||||||
func BenchmarkSha3(b *testing.B) {
|
func BenchmarkSha3(b *testing.B) {
|
||||||
a := []byte("hello world")
|
a := []byte("hello world")
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
Keccak256(a)
|
Keccak256(a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,9 +230,9 @@ func TestValidateSignatureValues(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
minusOne := big.NewInt(-1)
|
minusOne := big.NewInt(-1)
|
||||||
one := common.Big1
|
one := Big1
|
||||||
zero := common.Big0
|
zero := Big0
|
||||||
secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, common.Big1)
|
secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, Big1)
|
||||||
|
|
||||||
// correct v,r,s
|
// correct v,r,s
|
||||||
check(true, 0, one, one)
|
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) {
|
func checkAddr(t *testing.T, addr0, addr1 Address) {
|
||||||
if addr0 != addr1 {
|
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
|
// test to help Python team with integration of libsecp256k1
|
||||||
// skip but keep it after they are done
|
// skip but keep it after they are done
|
||||||
func TestPythonIntegration(t *testing.T) {
|
func TestPythonIntegration(t *testing.T) {
|
||||||
@@ -292,7 +303,7 @@ func TestPythonIntegration(t *testing.T) {
|
|||||||
msg0 := Keccak256([]byte("foo"))
|
msg0 := Keccak256([]byte("foo"))
|
||||||
sig0, _ := Sign(msg0, k0)
|
sig0, _ := Sign(msg0, k0)
|
||||||
|
|
||||||
msg1 := common.FromHex("00000000000000000000000000000000")
|
msg1 := FromHex("00000000000000000000000000000000")
|
||||||
sig1, _ := Sign(msg0, k0)
|
sig1, _ := Sign(msg0, k0)
|
||||||
|
|
||||||
t.Logf("msg: %x, privkey: %s sig: %x\n", msg0, kh, sig0)
|
t.Logf("msg: %x, privkey: %s sig: %x\n", msg0, kh, sig0)
|
||||||
@@ -310,7 +321,7 @@ func BenchmarkKeccak256Hash(b *testing.B) {
|
|||||||
rand.Read(input[:])
|
rand.Read(input[:])
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
Keccak256Hash(input[:])
|
Keccak256Hash(input[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,7 +340,7 @@ func BenchmarkHashData(b *testing.B) {
|
|||||||
rand.Read(input[:])
|
rand.Read(input[:])
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
HashData(buffer, input[:])
|
HashData(buffer, input[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -164,7 +164,7 @@ func TestTooBigSharedKey(t *testing.T) {
|
|||||||
|
|
||||||
// Benchmark the generation of P256 keys.
|
// Benchmark the generation of P256 keys.
|
||||||
func BenchmarkGenerateKeyP256(b *testing.B) {
|
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 {
|
if _, err := GenerateKey(rand.Reader, elliptic.P256(), nil); err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -177,8 +177,7 @@ func BenchmarkGenSharedKeyP256(b *testing.B) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
for b.Loop() {
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := prv.GenerateShared(&prv.PublicKey, 16, 16)
|
_, err := prv.GenerateShared(&prv.PublicKey, 16, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
@@ -192,8 +191,7 @@ func BenchmarkGenSharedKeyS256(b *testing.B) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
for b.Loop() {
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := prv.GenerateShared(&prv.PublicKey, 16, 16)
|
_, err := prv.GenerateShared(&prv.PublicKey, 16, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
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
|
package precompile
|
||||||
|
|
||||||
import (
|
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
|
// GetAllPostQuantumPrecompiles returns all post-quantum precompiles
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
package precompile
|
package precompile
|
||||||
|
|
||||||
import (
|
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
|
// PrecompiledContract is the interface for EVM precompiled contracts
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"errors"
|
"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"
|
"github.com/luxfi/crypto/lamport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ package precompile
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"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"
|
"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.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|||||||
+8
-9
@@ -24,13 +24,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/luxfi/crypto/common/math"
|
"github.com/luxfi/crypto/secp256k1"
|
||||||
luxSecp256k1 "github.com/luxfi/crypto/secp256k1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ecrecover returns the uncompressed public key that created the given signature.
|
// Ecrecover returns the uncompressed public key that created the given signature.
|
||||||
func Ecrecover(hash, sig []byte) ([]byte, error) {
|
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.
|
// 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 {
|
if len(digestHash) != DigestLength {
|
||||||
return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(digestHash))
|
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)
|
defer zeroBytes(seckey)
|
||||||
return luxSecp256k1.Sign(digestHash, seckey)
|
return secp256k1.Sign(digestHash, seckey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifySignature checks that the given public key created signature over digest.
|
// 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 public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
|
||||||
// The signature should have the 64 byte [R || S] format.
|
// The signature should have the 64 byte [R || S] format.
|
||||||
func VerifySignature(pubkey, digestHash, signature []byte) bool {
|
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.
|
// DecompressPubkey parses a public key in the 33-byte compressed format.
|
||||||
func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
|
func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
|
||||||
x, y := luxSecp256k1.DecompressPubkey(pubkey)
|
x, y := secp256k1.DecompressPubkey(pubkey)
|
||||||
if x == nil {
|
if x == nil {
|
||||||
return nil, errors.New("invalid public key")
|
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.
|
// CompressPubkey encodes a public key to the 33-byte compressed format.
|
||||||
func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
|
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.
|
// S256 returns an instance of the secp256k1 curve.
|
||||||
func S256() EllipticCurve {
|
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 {
|
if len(sig) != SignatureLength {
|
||||||
return nil, errors.New("invalid signature")
|
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.
|
// Convert to secp256k1 input format with 'recovery id' v at the beginning.
|
||||||
btcsig := make([]byte, SignatureLength)
|
btcsig := make([]byte, SignatureLength)
|
||||||
btcsig[0] = sig[RecoveryIDOffset] + 27
|
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.
|
// 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) {
|
func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
|
||||||
if len(hash) != 32 {
|
if len(hash) != DigestLength {
|
||||||
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
|
return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(hash))
|
||||||
}
|
}
|
||||||
if prv.Curve != S256() {
|
if prv.Curve != S256() {
|
||||||
return nil, errors.New("private key curve is not secp256k1")
|
return nil, errors.New("private key curve is not secp256k1")
|
||||||
|
|||||||
+37
-10
@@ -19,12 +19,12 @@ package crypto
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"encoding/hex"
|
||||||
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/luxfi/crypto/common"
|
|
||||||
"github.com/luxfi/crypto/common/hexutil"
|
"github.com/luxfi/crypto/common/hexutil"
|
||||||
"github.com/luxfi/crypto/common/math"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -34,6 +34,33 @@ var (
|
|||||||
testpubkeyc = hexutil.MustDecode("0x02e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a")
|
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) {
|
func TestEcrecover(t *testing.T) {
|
||||||
pubkey, err := Ecrecover(testmsg, testsig)
|
pubkey, err := Ecrecover(testmsg, testsig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -62,13 +89,13 @@ func TestVerifySignature(t *testing.T) {
|
|||||||
if VerifySignature(testpubkey, testmsg, nil) {
|
if VerifySignature(testpubkey, testmsg, nil) {
|
||||||
t.Errorf("nil signature valid")
|
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")
|
t.Errorf("signature valid with extra bytes at the end")
|
||||||
}
|
}
|
||||||
if VerifySignature(testpubkey, testmsg, sig[:len(sig)-2]) {
|
if VerifySignature(testpubkey, testmsg, sig[:len(sig)-2]) {
|
||||||
t.Errorf("signature valid even though it's incomplete")
|
t.Errorf("signature valid even though it's incomplete")
|
||||||
}
|
}
|
||||||
wrongkey := common.CopyBytes(testpubkey)
|
wrongkey := copyBytes(testpubkey)
|
||||||
wrongkey[10]++
|
wrongkey[10]++
|
||||||
if VerifySignature(wrongkey, testmsg, sig) {
|
if VerifySignature(wrongkey, testmsg, sig) {
|
||||||
t.Errorf("signature valid with wrong public key")
|
t.Errorf("signature valid with wrong public key")
|
||||||
@@ -99,7 +126,7 @@ func TestDecompressPubkey(t *testing.T) {
|
|||||||
if _, err := DecompressPubkey(testpubkeyc[:5]); err == nil {
|
if _, err := DecompressPubkey(testpubkeyc[:5]); err == nil {
|
||||||
t.Errorf("no error for incomplete pubkey")
|
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")
|
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) {
|
func TestCompressPubkey(t *testing.T) {
|
||||||
key := &ecdsa.PublicKey{
|
key := &ecdsa.PublicKey{
|
||||||
Curve: S256(),
|
Curve: S256(),
|
||||||
X: math.MustParseBig256("0xe32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a"),
|
X: mustParseBig256("0xe32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a"),
|
||||||
Y: math.MustParseBig256("0x0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652"),
|
Y: mustParseBig256("0x0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652"),
|
||||||
}
|
}
|
||||||
compressed := CompressPubkey(key)
|
compressed := CompressPubkey(key)
|
||||||
if !bytes.Equal(compressed, testpubkeyc) {
|
if !bytes.Equal(compressed, testpubkeyc) {
|
||||||
@@ -135,7 +162,7 @@ func TestPubkeyRandom(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkEcrecoverSignature(b *testing.B) {
|
func BenchmarkEcrecoverSignature(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
if _, err := Ecrecover(testmsg, testsig); err != nil {
|
if _, err := Ecrecover(testmsg, testsig); err != nil {
|
||||||
b.Fatal("ecrecover error", err)
|
b.Fatal("ecrecover error", err)
|
||||||
}
|
}
|
||||||
@@ -144,7 +171,7 @@ func BenchmarkEcrecoverSignature(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkVerifySignature(b *testing.B) {
|
func BenchmarkVerifySignature(b *testing.B) {
|
||||||
sig := testsig[:len(testsig)-1] // remove recovery id
|
sig := testsig[:len(testsig)-1] // remove recovery id
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
if !VerifySignature(testpubkey, testmsg, sig) {
|
if !VerifySignature(testpubkey, testmsg, sig) {
|
||||||
b.Fatal("verify error")
|
b.Fatal("verify error")
|
||||||
}
|
}
|
||||||
@@ -152,7 +179,7 @@ func BenchmarkVerifySignature(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkDecompressPubkey(b *testing.B) {
|
func BenchmarkDecompressPubkey(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
if _, err := DecompressPubkey(testpubkeyc); err != nil {
|
if _, err := DecompressPubkey(testpubkeyc); err != nil {
|
||||||
b.Fatal(err)
|
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