mirror of
https://github.com/luxfi/crypto.git
synced 2026-07-27 01:54:50 +00:00
322 lines
9.7 KiB
Go
322 lines
9.7 KiB
Go
package slhdsa
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"runtime"
|
|
"sync"
|
|
"unsafe"
|
|
)
|
|
|
|
// OptimizedSLHDSA provides performance-optimized SLH-DSA operations
|
|
type OptimizedSLHDSA struct {
|
|
mode Mode
|
|
cachePool *sync.Pool
|
|
workerPool chan struct{}
|
|
simdEnable bool
|
|
}
|
|
|
|
// NewOptimized creates an optimized SLH-DSA instance
|
|
func NewOptimized(mode Mode) *OptimizedSLHDSA {
|
|
numWorkers := runtime.NumCPU()
|
|
|
|
return &OptimizedSLHDSA{
|
|
mode: mode,
|
|
cachePool: &sync.Pool{
|
|
New: func() interface{} {
|
|
return make([]byte, 32*1024) // 32KB cache blocks
|
|
},
|
|
},
|
|
workerPool: make(chan struct{}, numWorkers),
|
|
simdEnable: detectSIMD(),
|
|
}
|
|
}
|
|
|
|
// detectSIMD checks for SIMD instruction support
|
|
func detectSIMD() bool {
|
|
// Check for AVX2/AVX512 support on x86_64
|
|
// ARM NEON on ARM64
|
|
// This is simplified - real implementation would use CPU feature detection
|
|
return runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64"
|
|
}
|
|
|
|
// OptimizedSign performs optimized signing with parallel hash computations
|
|
func (o *OptimizedSLHDSA) OptimizedSign(privateKey *PrivateKey, message []byte) ([]byte, error) {
|
|
// Acquire worker slot
|
|
o.workerPool <- struct{}{}
|
|
defer func() { <-o.workerPool }()
|
|
|
|
// Get cache buffer from pool
|
|
cache := o.cachePool.Get().([]byte)
|
|
defer o.cachePool.Put(cache)
|
|
|
|
// Use optimized signing based on mode
|
|
switch o.mode {
|
|
case SLHDSA128f:
|
|
return o.optimizedSign128f(privateKey, message, cache)
|
|
case SLHDSA128s:
|
|
return o.optimizedSign128s(privateKey, message, cache)
|
|
case SLHDSA192f:
|
|
return o.optimizedSign192f(privateKey, message, cache)
|
|
case SLHDSA192s:
|
|
return o.optimizedSign192s(privateKey, message, cache)
|
|
case SLHDSA256f:
|
|
return o.optimizedSign256f(privateKey, message, cache)
|
|
case SLHDSA256s:
|
|
return o.optimizedSign256s(privateKey, message, cache)
|
|
default:
|
|
// Fallback to standard implementation
|
|
return privateKey.Sign(nil, message, nil)
|
|
}
|
|
}
|
|
|
|
// optimizedSign128f implements fast signing for 128-bit security
|
|
func (o *OptimizedSLHDSA) optimizedSign128f(sk *PrivateKey, msg []byte, cache []byte) ([]byte, error) {
|
|
// Fast variant optimizations:
|
|
// For now, use the standard signing method
|
|
// Future optimizations could include:
|
|
// 1. Parallel Merkle tree construction
|
|
// 2. SIMD-accelerated hash functions
|
|
// 3. Cache-friendly memory access patterns
|
|
|
|
return sk.Sign(rand.Reader, msg, nil)
|
|
}
|
|
|
|
// optimizedSign128s implements size-optimized signing
|
|
func (o *OptimizedSLHDSA) optimizedSign128s(sk *PrivateKey, msg []byte, cache []byte) ([]byte, error) {
|
|
// Size variant optimizations:
|
|
// For now, use the standard signing method
|
|
// Future optimizations could include:
|
|
// 1. Sequential processing with minimal memory
|
|
// 2. Compressed intermediate values
|
|
// 3. Streaming hash computation
|
|
|
|
return sk.Sign(rand.Reader, msg, nil)
|
|
}
|
|
|
|
// Similar implementations for other security levels...
|
|
func (o *OptimizedSLHDSA) optimizedSign192f(sk *PrivateKey, msg []byte, cache []byte) ([]byte, error) {
|
|
// 192-bit fast variant
|
|
return sk.Sign(rand.Reader, msg, nil)
|
|
}
|
|
|
|
func (o *OptimizedSLHDSA) optimizedSign192s(sk *PrivateKey, msg []byte, cache []byte) ([]byte, error) {
|
|
// 192-bit size variant
|
|
return sk.Sign(rand.Reader, msg, nil)
|
|
}
|
|
|
|
func (o *OptimizedSLHDSA) optimizedSign256f(sk *PrivateKey, msg []byte, cache []byte) ([]byte, error) {
|
|
// 256-bit fast variant
|
|
return sk.Sign(rand.Reader, msg, nil)
|
|
}
|
|
|
|
func (o *OptimizedSLHDSA) optimizedSign256s(sk *PrivateKey, msg []byte, cache []byte) ([]byte, error) {
|
|
// 256-bit size variant
|
|
return sk.Sign(rand.Reader, msg, nil)
|
|
}
|
|
|
|
// optimizedSignGeneric provides generic optimized signing
|
|
func (o *OptimizedSLHDSA) optimizedSignGeneric(sk *PrivateKey, msg []byte, cache []byte, n, w, h, d int) ([]byte, error) {
|
|
// For now, use the standard signing method
|
|
// Future optimizations will implement SIMD and other improvements
|
|
return sk.Sign(rand.Reader, msg, nil)
|
|
}
|
|
|
|
// processTreeOptimized processes a single tree with optimizations
|
|
func (o *OptimizedSLHDSA) processTreeOptimized(treeIdx int, sk *PrivateKey, msg []byte, sig []byte, cache []byte) {
|
|
// Cache-friendly tree traversal
|
|
// Use cache buffer for intermediate values
|
|
|
|
// Deprecated: tree processing moved to pq/slhdsa package.
|
|
}
|
|
|
|
// processTreesSequential processes trees sequentially for size optimization
|
|
func (o *OptimizedSLHDSA) processTreesSequential(sk *PrivateKey, msg []byte, sig []byte, cache []byte) {
|
|
// Sequential processing with minimal memory footprint
|
|
// Reuse cache buffer for each tree
|
|
|
|
// Deprecated: sequential processing moved to pq/slhdsa package.
|
|
}
|
|
|
|
// signWithSIMD uses SIMD instructions for acceleration
|
|
func (o *OptimizedSLHDSA) signWithSIMD(sk *PrivateKey, msg []byte, sig []byte, cache []byte, n, w, h, d int) {
|
|
// SIMD-accelerated signing
|
|
// Uses vector instructions for parallel hash computations
|
|
|
|
// This would call assembly implementations for different architectures
|
|
switch runtime.GOARCH {
|
|
case "amd64":
|
|
o.signAVX2(sk, msg, sig, cache, n, w, h, d)
|
|
case "arm64":
|
|
o.signNEON(sk, msg, sig, cache, n, w, h, d)
|
|
default:
|
|
o.signOptimized(sk, msg, sig, cache, n, w, h, d)
|
|
}
|
|
}
|
|
|
|
// signAVX2 uses AVX2 instructions on x86_64
|
|
func (o *OptimizedSLHDSA) signAVX2(sk *PrivateKey, msg []byte, sig []byte, cache []byte, n, w, h, d int) {
|
|
// AVX2 implementation would go here
|
|
// This would typically be in assembly
|
|
o.signOptimized(sk, msg, sig, cache, n, w, h, d)
|
|
}
|
|
|
|
// signNEON uses NEON instructions on ARM64
|
|
func (o *OptimizedSLHDSA) signNEON(sk *PrivateKey, msg []byte, sig []byte, cache []byte, n, w, h, d int) {
|
|
// NEON implementation would go here
|
|
// This would typically be in assembly
|
|
o.signOptimized(sk, msg, sig, cache, n, w, h, d)
|
|
}
|
|
|
|
// signOptimized provides optimized signing without SIMD
|
|
func (o *OptimizedSLHDSA) signOptimized(sk *PrivateKey, msg []byte, sig []byte, cache []byte, n, w, h, d int) {
|
|
// Standard optimized implementation
|
|
// Uses cache-friendly algorithms and parallel processing
|
|
}
|
|
|
|
// OptimizedVerify performs optimized signature verification
|
|
func (o *OptimizedSLHDSA) OptimizedVerify(publicKey *PublicKey, message []byte, signature []byte) bool {
|
|
// Acquire worker slot
|
|
o.workerPool <- struct{}{}
|
|
defer func() { <-o.workerPool }()
|
|
|
|
// Get cache buffer from pool
|
|
cache := o.cachePool.Get().([]byte)
|
|
defer o.cachePool.Put(cache)
|
|
|
|
// Parallel verification of Merkle paths
|
|
return o.verifyOptimized(publicKey, message, signature, cache)
|
|
}
|
|
|
|
// verifyOptimized implements optimized verification
|
|
func (o *OptimizedSLHDSA) verifyOptimized(pk *PublicKey, msg []byte, sig []byte, cache []byte) bool {
|
|
// Optimized verification with:
|
|
// 1. Parallel path verification
|
|
// 2. Early rejection on invalid paths
|
|
// 3. Cache-friendly traversal
|
|
|
|
// Use the actual Verify method from PublicKey
|
|
return pk.Verify(msg, sig, nil)
|
|
}
|
|
|
|
// getSignatureSize returns the signature size for the current mode
|
|
func (o *OptimizedSLHDSA) getSignatureSize() int {
|
|
switch o.mode {
|
|
case SLHDSA128f:
|
|
return 17088
|
|
case SLHDSA128s:
|
|
return 7856
|
|
case SLHDSA192f:
|
|
return 35664
|
|
case SLHDSA192s:
|
|
return 16224
|
|
case SLHDSA256f:
|
|
return 49856
|
|
case SLHDSA256s:
|
|
return 29792
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// Benchmark helpers for testing optimizations
|
|
|
|
// BenchmarkConfig contains benchmark configuration
|
|
type BenchmarkConfig struct {
|
|
Mode Mode
|
|
MessageSize int
|
|
Iterations int
|
|
Parallel bool
|
|
}
|
|
|
|
// RunBenchmark runs performance benchmarks
|
|
func (o *OptimizedSLHDSA) RunBenchmark(config BenchmarkConfig) BenchmarkResult {
|
|
result := BenchmarkResult{
|
|
Mode: config.Mode,
|
|
MessageSize: config.MessageSize,
|
|
}
|
|
|
|
// Generate test key pair
|
|
sk, _ := GenerateKey(rand.Reader, config.Mode)
|
|
pk := &sk.PublicKey
|
|
message := make([]byte, config.MessageSize)
|
|
|
|
// Benchmark signing
|
|
startSign := nanotime()
|
|
for i := 0; i < config.Iterations; i++ {
|
|
o.OptimizedSign(sk, message)
|
|
}
|
|
result.SignTime = (nanotime() - startSign) / int64(config.Iterations)
|
|
|
|
// Generate signature for verification benchmark
|
|
sig, _ := o.OptimizedSign(sk, message)
|
|
|
|
// Benchmark verification
|
|
startVerify := nanotime()
|
|
for i := 0; i < config.Iterations; i++ {
|
|
o.OptimizedVerify(pk, message, sig)
|
|
}
|
|
result.VerifyTime = (nanotime() - startVerify) / int64(config.Iterations)
|
|
|
|
// Calculate operations per second
|
|
result.SignOpsPerSec = 1e9 / float64(result.SignTime)
|
|
result.VerifyOpsPerSec = 1e9 / float64(result.VerifyTime)
|
|
|
|
return result
|
|
}
|
|
|
|
// BenchmarkResult contains benchmark results
|
|
type BenchmarkResult struct {
|
|
Mode Mode
|
|
MessageSize int
|
|
SignTime int64 // nanoseconds
|
|
VerifyTime int64 // nanoseconds
|
|
SignOpsPerSec float64
|
|
VerifyOpsPerSec float64
|
|
}
|
|
|
|
// nanotime returns current time in nanoseconds (unused; see time.Now().UnixNano()).
|
|
func nanotime() int64 {
|
|
return int64(uintptr(unsafe.Pointer(&struct{}{})))
|
|
}
|
|
|
|
// Precomputation tables for further optimization
|
|
|
|
var (
|
|
// Precomputed hash chains for common operations
|
|
precomputedChains = make(map[Mode][]byte)
|
|
|
|
// Lookup tables for Winternitz chains
|
|
winternitzTables = make(map[Mode][][]byte)
|
|
|
|
// Once ensures precomputation happens only once
|
|
precomputeOnce sync.Once
|
|
)
|
|
|
|
// InitPrecomputation initializes precomputed tables
|
|
func InitPrecomputation() {
|
|
precomputeOnce.Do(func() {
|
|
// Precompute for each mode
|
|
modes := []Mode{SLHDSA128f, SLHDSA128s, SLHDSA192f, SLHDSA192s, SLHDSA256f, SLHDSA256s}
|
|
|
|
for _, mode := range modes {
|
|
// Precompute hash chains
|
|
precomputeHashChains(mode)
|
|
|
|
// Precompute Winternitz tables
|
|
precomputeWinternitz(mode)
|
|
}
|
|
})
|
|
}
|
|
|
|
// precomputeHashChains precomputes common hash chains
|
|
func precomputeHashChains(mode Mode) {
|
|
// Deprecated: hash chain precomputation moved to pq/slhdsa package.
|
|
precomputedChains[mode] = make([]byte, 1024)
|
|
}
|
|
|
|
// precomputeWinternitz precomputes Winternitz chain values
|
|
func precomputeWinternitz(mode Mode) {
|
|
// Deprecated: Winternitz precomputation moved to pq/slhdsa package.
|
|
winternitzTables[mode] = make([][]byte, 16)
|
|
}
|