mirror of
https://github.com/luxfi/crypto.git
synced 2026-07-27 01:54:50 +00:00
fix: apply gofmt -s formatting
This commit is contained in:
+21
-21
@@ -14,25 +14,25 @@ import (
|
|||||||
type AeadID string
|
type AeadID string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AES256GCM AeadID = "aes256gcm"
|
AES256GCM AeadID = "aes256gcm"
|
||||||
ChaCha20Poly1305 AeadID = "chacha20poly1305"
|
ChaCha20Poly1305 AeadID = "chacha20poly1305"
|
||||||
AES256GCMSIV AeadID = "aes256gcmsiv"
|
AES256GCMSIV AeadID = "aes256gcmsiv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AEAD interface for authenticated encryption
|
// AEAD interface for authenticated encryption
|
||||||
type AEAD interface {
|
type AEAD interface {
|
||||||
// Seal encrypts and authenticates plaintext
|
// Seal encrypts and authenticates plaintext
|
||||||
Seal(dst, nonce, plaintext, aad []byte) []byte
|
Seal(dst, nonce, plaintext, aad []byte) []byte
|
||||||
|
|
||||||
// Open decrypts and authenticates ciphertext
|
// Open decrypts and authenticates ciphertext
|
||||||
Open(dst, nonce, ciphertext, aad []byte) ([]byte, error)
|
Open(dst, nonce, ciphertext, aad []byte) ([]byte, error)
|
||||||
|
|
||||||
// NonceSize returns the nonce size in bytes
|
// NonceSize returns the nonce size in bytes
|
||||||
NonceSize() int
|
NonceSize() int
|
||||||
|
|
||||||
// Overhead returns the authentication tag size
|
// Overhead returns the authentication tag size
|
||||||
Overhead() int
|
Overhead() int
|
||||||
|
|
||||||
// KeySize returns the key size in bytes
|
// KeySize returns the key size in bytes
|
||||||
KeySize() int
|
KeySize() int
|
||||||
}
|
}
|
||||||
@@ -47,17 +47,17 @@ func NewAES256GCM(key []byte) (AEAD, error) {
|
|||||||
if len(key) != 32 {
|
if len(key) != 32 {
|
||||||
return nil, errors.New("AES-256-GCM requires 32-byte key")
|
return nil, errors.New("AES-256-GCM requires 32-byte key")
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
aead, err := cipher.NewGCM(block)
|
aead, err := cipher.NewGCM(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &AES256GCMImpl{aead: aead}, nil
|
return &AES256GCMImpl{aead: aead}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ func (a *AES256GCMImpl) Seal(dst, nonce, plaintext, aad []byte) []byte {
|
|||||||
if len(nonce) != a.NonceSize() {
|
if len(nonce) != a.NonceSize() {
|
||||||
panic(fmt.Sprintf("invalid nonce size: got %d, want %d", len(nonce), a.NonceSize()))
|
panic(fmt.Sprintf("invalid nonce size: got %d, want %d", len(nonce), a.NonceSize()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GCM handles AAD internally
|
// GCM handles AAD internally
|
||||||
return a.aead.Seal(dst, nonce, plaintext, aad)
|
return a.aead.Seal(dst, nonce, plaintext, aad)
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ func (a *AES256GCMImpl) Open(dst, nonce, ciphertext, aad []byte) ([]byte, error)
|
|||||||
if len(nonce) != a.NonceSize() {
|
if len(nonce) != a.NonceSize() {
|
||||||
return nil, fmt.Errorf("invalid nonce size: got %d, want %d", len(nonce), a.NonceSize())
|
return nil, fmt.Errorf("invalid nonce size: got %d, want %d", len(nonce), a.NonceSize())
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.aead.Open(dst, nonce, ciphertext, aad)
|
return a.aead.Open(dst, nonce, ciphertext, aad)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,12 +105,12 @@ func NewChaCha20Poly1305(key []byte) (AEAD, error) {
|
|||||||
if len(key) != 32 {
|
if len(key) != 32 {
|
||||||
return nil, errors.New("ChaCha20-Poly1305 requires 32-byte key")
|
return nil, errors.New("ChaCha20-Poly1305 requires 32-byte key")
|
||||||
}
|
}
|
||||||
|
|
||||||
aead, err := chacha20poly1305.New(key)
|
aead, err := chacha20poly1305.New(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ChaCha20Poly1305Impl{aead: aead}, nil
|
return &ChaCha20Poly1305Impl{aead: aead}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ func (c *ChaCha20Poly1305Impl) Seal(dst, nonce, plaintext, aad []byte) []byte {
|
|||||||
if len(nonce) != c.NonceSize() {
|
if len(nonce) != c.NonceSize() {
|
||||||
panic(fmt.Sprintf("invalid nonce size: got %d, want %d", len(nonce), c.NonceSize()))
|
panic(fmt.Sprintf("invalid nonce size: got %d, want %d", len(nonce), c.NonceSize()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.aead.Seal(dst, nonce, plaintext, aad)
|
return c.aead.Seal(dst, nonce, plaintext, aad)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ func (c *ChaCha20Poly1305Impl) Open(dst, nonce, ciphertext, aad []byte) ([]byte,
|
|||||||
if len(nonce) != c.NonceSize() {
|
if len(nonce) != c.NonceSize() {
|
||||||
return nil, fmt.Errorf("invalid nonce size: got %d, want %d", len(nonce), c.NonceSize())
|
return nil, fmt.Errorf("invalid nonce size: got %d, want %d", len(nonce), c.NonceSize())
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.aead.Open(dst, nonce, ciphertext, aad)
|
return c.aead.Open(dst, nonce, ciphertext, aad)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,13 +179,13 @@ func NewNonceGenerator(streamID uint32) *NonceGenerator {
|
|||||||
// Next returns the next nonce and increments sequence number
|
// Next returns the next nonce and increments sequence number
|
||||||
func (ng *NonceGenerator) Next() []byte {
|
func (ng *NonceGenerator) Next() []byte {
|
||||||
nonce := make([]byte, 12) // 96-bit nonce
|
nonce := make([]byte, 12) // 96-bit nonce
|
||||||
|
|
||||||
// First 4 bytes: stream ID
|
// First 4 bytes: stream ID
|
||||||
nonce[0] = byte(ng.streamID >> 24)
|
nonce[0] = byte(ng.streamID >> 24)
|
||||||
nonce[1] = byte(ng.streamID >> 16)
|
nonce[1] = byte(ng.streamID >> 16)
|
||||||
nonce[2] = byte(ng.streamID >> 8)
|
nonce[2] = byte(ng.streamID >> 8)
|
||||||
nonce[3] = byte(ng.streamID)
|
nonce[3] = byte(ng.streamID)
|
||||||
|
|
||||||
// Next 8 bytes: sequence number
|
// Next 8 bytes: sequence number
|
||||||
nonce[4] = byte(ng.seqNo >> 56)
|
nonce[4] = byte(ng.seqNo >> 56)
|
||||||
nonce[5] = byte(ng.seqNo >> 48)
|
nonce[5] = byte(ng.seqNo >> 48)
|
||||||
@@ -195,9 +195,9 @@ func (ng *NonceGenerator) Next() []byte {
|
|||||||
nonce[9] = byte(ng.seqNo >> 16)
|
nonce[9] = byte(ng.seqNo >> 16)
|
||||||
nonce[10] = byte(ng.seqNo >> 8)
|
nonce[10] = byte(ng.seqNo >> 8)
|
||||||
nonce[11] = byte(ng.seqNo)
|
nonce[11] = byte(ng.seqNo)
|
||||||
|
|
||||||
ng.seqNo++
|
ng.seqNo++
|
||||||
|
|
||||||
return nonce
|
return nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,4 +209,4 @@ func (ng *NonceGenerator) SetSeqNo(seqNo uint64) {
|
|||||||
// GetSeqNo returns the current sequence number
|
// GetSeqNo returns the current sequence number
|
||||||
func (ng *NonceGenerator) GetSeqNo() uint64 {
|
func (ng *NonceGenerator) GetSeqNo() uint64 {
|
||||||
return ng.seqNo
|
return ng.seqNo
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -7,11 +7,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/luxfi/crypto/bls"
|
||||||
|
"github.com/luxfi/crypto/cggmp21"
|
||||||
|
"github.com/luxfi/crypto/corona"
|
||||||
"github.com/luxfi/ids"
|
"github.com/luxfi/ids"
|
||||||
"github.com/luxfi/log"
|
"github.com/luxfi/log"
|
||||||
"github.com/luxfi/crypto/bls"
|
|
||||||
"github.com/luxfi/crypto/corona"
|
|
||||||
"github.com/luxfi/crypto/cggmp21"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BLSManager manages BLS signature operations
|
// BLSManager manages BLS signature operations
|
||||||
@@ -33,7 +33,7 @@ func (m *BLSManager) CreateKeyPair() (*bls.SecretKey, *bls.PublicKey, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
pk := sk.PublicKey()
|
||||||
return sk, pk, nil
|
return sk, pk, nil
|
||||||
}
|
}
|
||||||
@@ -67,12 +67,12 @@ func (m *CoronaManager) CreateKeyPair() (*corona.PrivateKey, *corona.PublicKey,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pubKey, err := factory.ToPublicKey(privKey)
|
pubKey, err := factory.ToPublicKey(privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return privKey, pubKey, nil
|
return privKey, pubKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,15 +109,15 @@ func (m *CGGMP21Manager) InitializeParty(
|
|||||||
) error {
|
) error {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
party, err := cggmp21.NewParty(partyID, index, config, m.log)
|
party, err := cggmp21.NewParty(partyID, index, config, m.log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.parties[index] = party
|
m.parties[index] = party
|
||||||
m.config = config
|
m.config = config
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,12 +125,12 @@ func (m *CGGMP21Manager) InitializeParty(
|
|||||||
func (m *CGGMP21Manager) GetParty(index int) (*cggmp21.Party, error) {
|
func (m *CGGMP21Manager) GetParty(index int) (*cggmp21.Party, error) {
|
||||||
m.mu.RLock()
|
m.mu.RLock()
|
||||||
defer m.mu.RUnlock()
|
defer m.mu.RUnlock()
|
||||||
|
|
||||||
party, exists := m.parties[index]
|
party, exists := m.parties[index]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("party not found")
|
return nil, errors.New("party not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
return party, nil
|
return party, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ func NewFeeCollector() FeeCollector {
|
|||||||
func (fc *FeeCollector) CollectFee(sigType SignatureType, amount uint64) {
|
func (fc *FeeCollector) CollectFee(sigType SignatureType, amount uint64) {
|
||||||
fc.mu.Lock()
|
fc.mu.Lock()
|
||||||
defer fc.mu.Unlock()
|
defer fc.mu.Unlock()
|
||||||
|
|
||||||
fc.collectedFees[sigType] += amount
|
fc.collectedFees[sigType] += amount
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,12 +159,12 @@ func (fc *FeeCollector) CollectFee(sigType SignatureType, amount uint64) {
|
|||||||
func (fc *FeeCollector) GetCollectedFees() map[SignatureType]uint64 {
|
func (fc *FeeCollector) GetCollectedFees() map[SignatureType]uint64 {
|
||||||
fc.mu.RLock()
|
fc.mu.RLock()
|
||||||
defer fc.mu.RUnlock()
|
defer fc.mu.RUnlock()
|
||||||
|
|
||||||
fees := make(map[SignatureType]uint64)
|
fees := make(map[SignatureType]uint64)
|
||||||
for k, v := range fc.collectedFees {
|
for k, v := range fc.collectedFees {
|
||||||
fees[k] = v
|
fees[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
return fees
|
return fees
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,12 +172,12 @@ func (fc *FeeCollector) GetCollectedFees() map[SignatureType]uint64 {
|
|||||||
func (fc *FeeCollector) GetTotalFees() uint64 {
|
func (fc *FeeCollector) GetTotalFees() uint64 {
|
||||||
fc.mu.RLock()
|
fc.mu.RLock()
|
||||||
defer fc.mu.RUnlock()
|
defer fc.mu.RUnlock()
|
||||||
|
|
||||||
var total uint64
|
var total uint64
|
||||||
for _, amount := range fc.collectedFees {
|
for _, amount := range fc.collectedFees {
|
||||||
total += amount
|
total += amount
|
||||||
}
|
}
|
||||||
|
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,9 +185,9 @@ func (fc *FeeCollector) GetTotalFees() uint64 {
|
|||||||
func (fc *FeeCollector) ResetFees() map[SignatureType]uint64 {
|
func (fc *FeeCollector) ResetFees() map[SignatureType]uint64 {
|
||||||
fc.mu.Lock()
|
fc.mu.Lock()
|
||||||
defer fc.mu.Unlock()
|
defer fc.mu.Unlock()
|
||||||
|
|
||||||
oldFees := fc.collectedFees
|
oldFees := fc.collectedFees
|
||||||
fc.collectedFees = make(map[SignatureType]uint64)
|
fc.collectedFees = make(map[SignatureType]uint64)
|
||||||
|
|
||||||
return oldFees
|
return oldFees
|
||||||
}
|
}
|
||||||
|
|||||||
+108
-108
@@ -10,10 +10,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/luxfi/crypto/bls"
|
||||||
|
"github.com/luxfi/crypto/corona"
|
||||||
"github.com/luxfi/ids"
|
"github.com/luxfi/ids"
|
||||||
"github.com/luxfi/log"
|
"github.com/luxfi/log"
|
||||||
"github.com/luxfi/crypto/corona"
|
|
||||||
"github.com/luxfi/crypto/bls"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Import log field helpers
|
// Import log field helpers
|
||||||
@@ -36,78 +36,78 @@ const (
|
|||||||
// SignatureConfig contains configuration for signature aggregation
|
// SignatureConfig contains configuration for signature aggregation
|
||||||
type SignatureConfig struct {
|
type SignatureConfig struct {
|
||||||
// Network-wide signature type preference
|
// Network-wide signature type preference
|
||||||
PreferredType SignatureType `json:"preferredType"`
|
PreferredType SignatureType `json:"preferredType"`
|
||||||
|
|
||||||
// Enable specific signature types
|
// Enable specific signature types
|
||||||
EnableBLS bool `json:"enableBLS"`
|
EnableBLS bool `json:"enableBLS"`
|
||||||
EnableCorona bool `json:"enableCorona"`
|
EnableCorona bool `json:"enableCorona"`
|
||||||
EnableCGGMP21 bool `json:"enableCGGMP21"`
|
EnableCGGMP21 bool `json:"enableCGGMP21"`
|
||||||
|
|
||||||
// Fee configuration (in nLUX - nano LUX)
|
// Fee configuration (in nLUX - nano LUX)
|
||||||
BLSFee uint64 `json:"blsFee"` // 0 = free
|
BLSFee uint64 `json:"blsFee"` // 0 = free
|
||||||
CoronaFee uint64 `json:"coronaFee"` // Premium for enhanced privacy
|
CoronaFee uint64 `json:"coronaFee"` // Premium for enhanced privacy
|
||||||
CGGMP21Fee uint64 `json:"cggmp21Fee"` // Premium for threshold signatures
|
CGGMP21Fee uint64 `json:"cggmp21Fee"` // Premium for threshold signatures
|
||||||
|
|
||||||
// Performance settings
|
// Performance settings
|
||||||
ParallelAggregation bool `json:"parallelAggregation"`
|
ParallelAggregation bool `json:"parallelAggregation"`
|
||||||
MaxSignersPerRound int `json:"maxSignersPerRound"`
|
MaxSignersPerRound int `json:"maxSignersPerRound"`
|
||||||
|
|
||||||
// Security settings
|
// Security settings
|
||||||
MinSigners int `json:"minSigners"`
|
MinSigners int `json:"minSigners"`
|
||||||
ThresholdRatio float64 `json:"thresholdRatio"` // e.g., 0.67 for 2/3
|
ThresholdRatio float64 `json:"thresholdRatio"` // e.g., 0.67 for 2/3
|
||||||
}
|
}
|
||||||
|
|
||||||
// AggregatedSignature represents an aggregated signature with metadata
|
// AggregatedSignature represents an aggregated signature with metadata
|
||||||
type AggregatedSignature struct {
|
type AggregatedSignature struct {
|
||||||
Type SignatureType `json:"type"`
|
Type SignatureType `json:"type"`
|
||||||
Signature []byte `json:"signature"`
|
Signature []byte `json:"signature"`
|
||||||
SignerIDs []ids.NodeID `json:"signerIds,omitempty"`
|
SignerIDs []ids.NodeID `json:"signerIds,omitempty"`
|
||||||
SignerCount int `json:"signerCount"`
|
SignerCount int `json:"signerCount"`
|
||||||
RingPublicKeys []*corona.PublicKey `json:"ringPublicKeys,omitempty"` // For Corona
|
RingPublicKeys []*corona.PublicKey `json:"ringPublicKeys,omitempty"` // For Corona
|
||||||
AggregateKey []byte `json:"aggregateKey,omitempty"` // For BLS
|
AggregateKey []byte `json:"aggregateKey,omitempty"` // For BLS
|
||||||
Threshold int `json:"threshold,omitempty"` // For CGGMP21
|
Threshold int `json:"threshold,omitempty"` // For CGGMP21
|
||||||
TotalFee uint64 `json:"totalFee"`
|
TotalFee uint64 `json:"totalFee"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignatureAggregator manages network-wide signature aggregation
|
// SignatureAggregator manages network-wide signature aggregation
|
||||||
type SignatureAggregator struct {
|
type SignatureAggregator struct {
|
||||||
config SignatureConfig
|
config SignatureConfig
|
||||||
log log.Logger
|
log log.Logger
|
||||||
|
|
||||||
// Signature managers
|
// Signature managers
|
||||||
blsManager *BLSManager
|
blsManager *BLSManager
|
||||||
coronaManager *CoronaManager
|
coronaManager *CoronaManager
|
||||||
cggmpManager *CGGMP21Manager
|
cggmpManager *CGGMP21Manager
|
||||||
|
|
||||||
// Active aggregation sessions
|
// Active aggregation sessions
|
||||||
sessions map[string]*AggregationSession
|
sessions map[string]*AggregationSession
|
||||||
|
|
||||||
// Fee collector
|
// Fee collector
|
||||||
feeCollector FeeCollector
|
feeCollector FeeCollector
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// AggregationSession represents an active signature aggregation
|
// AggregationSession represents an active signature aggregation
|
||||||
type AggregationSession struct {
|
type AggregationSession struct {
|
||||||
SessionID string
|
SessionID string
|
||||||
Message []byte
|
Message []byte
|
||||||
SignatureType SignatureType
|
SignatureType SignatureType
|
||||||
|
|
||||||
// Collected signatures
|
// Collected signatures
|
||||||
BLSSignatures []*bls.Signature
|
BLSSignatures []*bls.Signature
|
||||||
BLSPublicKeys []*bls.PublicKey
|
BLSPublicKeys []*bls.PublicKey
|
||||||
CoronaSignatures []*corona.RingSignature
|
CoronaSignatures []*corona.RingSignature
|
||||||
CoronaRing []*corona.PublicKey
|
CoronaRing []*corona.PublicKey
|
||||||
|
|
||||||
// Signers
|
// Signers
|
||||||
Signers map[ids.NodeID]bool
|
Signers map[ids.NodeID]bool
|
||||||
SignerCount int
|
SignerCount int
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
StartTime int64
|
StartTime int64
|
||||||
Completed bool
|
Completed bool
|
||||||
Result *AggregatedSignature
|
Result *AggregatedSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSignatureAggregator creates a new signature aggregator
|
// NewSignatureAggregator creates a new signature aggregator
|
||||||
@@ -117,22 +117,22 @@ func NewSignatureAggregator(config SignatureConfig, log log.Logger) (*SignatureA
|
|||||||
log: log,
|
log: log,
|
||||||
sessions: make(map[string]*AggregationSession),
|
sessions: make(map[string]*AggregationSession),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize signature managers based on config
|
// Initialize signature managers based on config
|
||||||
if config.EnableBLS {
|
if config.EnableBLS {
|
||||||
sa.blsManager = NewBLSManager(log)
|
sa.blsManager = NewBLSManager(log)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.EnableCorona {
|
if config.EnableCorona {
|
||||||
sa.coronaManager = NewCoronaManager(log)
|
sa.coronaManager = NewCoronaManager(log)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.EnableCGGMP21 {
|
if config.EnableCGGMP21 {
|
||||||
sa.cggmpManager = NewCGGMP21Manager(log)
|
sa.cggmpManager = NewCGGMP21Manager(log)
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.feeCollector = NewFeeCollector()
|
sa.feeCollector = NewFeeCollector()
|
||||||
|
|
||||||
log.Info("Signature aggregator initialized",
|
log.Info("Signature aggregator initialized",
|
||||||
logUint8("preferredType", uint8(config.PreferredType)),
|
logUint8("preferredType", uint8(config.PreferredType)),
|
||||||
logBool("blsEnabled", config.EnableBLS),
|
logBool("blsEnabled", config.EnableBLS),
|
||||||
@@ -141,7 +141,7 @@ func NewSignatureAggregator(config SignatureConfig, log log.Logger) (*SignatureA
|
|||||||
logUint64("blsFee", config.BLSFee),
|
logUint64("blsFee", config.BLSFee),
|
||||||
logUint64("coronaFee", config.CoronaFee),
|
logUint64("coronaFee", config.CoronaFee),
|
||||||
)
|
)
|
||||||
|
|
||||||
return sa, nil
|
return sa, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,11 +154,11 @@ func (sa *SignatureAggregator) StartAggregation(
|
|||||||
) error {
|
) error {
|
||||||
sa.mu.Lock()
|
sa.mu.Lock()
|
||||||
defer sa.mu.Unlock()
|
defer sa.mu.Unlock()
|
||||||
|
|
||||||
if _, exists := sa.sessions[sessionID]; exists {
|
if _, exists := sa.sessions[sessionID]; exists {
|
||||||
return errors.New("session already exists")
|
return errors.New("session already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate signature type is enabled
|
// Validate signature type is enabled
|
||||||
switch sigType {
|
switch sigType {
|
||||||
case SignatureTypeBLS:
|
case SignatureTypeBLS:
|
||||||
@@ -176,7 +176,7 @@ func (sa *SignatureAggregator) StartAggregation(
|
|||||||
default:
|
default:
|
||||||
return errors.New("unknown signature type")
|
return errors.New("unknown signature type")
|
||||||
}
|
}
|
||||||
|
|
||||||
session := &AggregationSession{
|
session := &AggregationSession{
|
||||||
SessionID: sessionID,
|
SessionID: sessionID,
|
||||||
Message: message,
|
Message: message,
|
||||||
@@ -184,15 +184,15 @@ func (sa *SignatureAggregator) StartAggregation(
|
|||||||
Signers: make(map[ids.NodeID]bool),
|
Signers: make(map[ids.NodeID]bool),
|
||||||
StartTime: getCurrentTime(),
|
StartTime: getCurrentTime(),
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.sessions[sessionID] = session
|
sa.sessions[sessionID] = session
|
||||||
|
|
||||||
sa.log.Debug("Started aggregation session",
|
sa.log.Debug("Started aggregation session",
|
||||||
log.String("sessionID", sessionID),
|
log.String("sessionID", sessionID),
|
||||||
log.Uint8("type", uint8(sigType)),
|
log.Uint8("type", uint8(sigType)),
|
||||||
log.Int("expectedSigners", expectedSigners),
|
log.Int("expectedSigners", expectedSigners),
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,32 +205,32 @@ func (sa *SignatureAggregator) AddSignature(
|
|||||||
) error {
|
) error {
|
||||||
sa.mu.Lock()
|
sa.mu.Lock()
|
||||||
defer sa.mu.Unlock()
|
defer sa.mu.Unlock()
|
||||||
|
|
||||||
session, exists := sa.sessions[sessionID]
|
session, exists := sa.sessions[sessionID]
|
||||||
if !exists {
|
if !exists {
|
||||||
return errors.New("session not found")
|
return errors.New("session not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if session.Completed {
|
if session.Completed {
|
||||||
return errors.New("session already completed")
|
return errors.New("session already completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if signer already contributed
|
// Check if signer already contributed
|
||||||
if session.Signers[signerID] {
|
if session.Signers[signerID] {
|
||||||
return errors.New("signer already contributed")
|
return errors.New("signer already contributed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add signature based on type
|
// Add signature based on type
|
||||||
switch session.SignatureType {
|
switch session.SignatureType {
|
||||||
case SignatureTypeBLS:
|
case SignatureTypeBLS:
|
||||||
return sa.addBLSSignature(session, signerID, signature, publicKey)
|
return sa.addBLSSignature(session, signerID, signature, publicKey)
|
||||||
|
|
||||||
case SignatureTypeCorona:
|
case SignatureTypeCorona:
|
||||||
return sa.addCoronaSignature(session, signerID, signature, publicKey)
|
return sa.addCoronaSignature(session, signerID, signature, publicKey)
|
||||||
|
|
||||||
case SignatureTypeCGGMP21:
|
case SignatureTypeCGGMP21:
|
||||||
return errors.New("CGGMP21 uses different protocol flow")
|
return errors.New("CGGMP21 uses different protocol flow")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errors.New("unknown signature type")
|
return errors.New("unknown signature type")
|
||||||
}
|
}
|
||||||
@@ -248,24 +248,24 @@ func (sa *SignatureAggregator) addBLSSignature(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid BLS signature: %w", err)
|
return fmt.Errorf("invalid BLS signature: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pk, err := bls.PublicKeyFromCompressedBytes(publicKey)
|
pk, err := bls.PublicKeyFromCompressedBytes(publicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid BLS public key: %w", err)
|
return fmt.Errorf("invalid BLS public key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify individual signature
|
// Verify individual signature
|
||||||
valid := bls.Verify(pk, sig, session.Message)
|
valid := bls.Verify(pk, sig, session.Message)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf("BLS signature verification failed")
|
return fmt.Errorf("BLS signature verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to session
|
// Add to session
|
||||||
session.BLSSignatures = append(session.BLSSignatures, sig)
|
session.BLSSignatures = append(session.BLSSignatures, sig)
|
||||||
session.BLSPublicKeys = append(session.BLSPublicKeys, pk)
|
session.BLSPublicKeys = append(session.BLSPublicKeys, pk)
|
||||||
session.Signers[signerID] = true
|
session.Signers[signerID] = true
|
||||||
session.SignerCount++
|
session.SignerCount++
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ func (sa *SignatureAggregator) addCoronaSignature(
|
|||||||
S: make([]*big.Int, corona.DefaultRingSize),
|
S: make([]*big.Int, corona.DefaultRingSize),
|
||||||
KeyImage: &corona.Point{X: big.NewInt(0), Y: big.NewInt(0)},
|
KeyImage: &corona.Point{X: big.NewInt(0), Y: big.NewInt(0)},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse public key
|
// Parse public key
|
||||||
// For now, create from bytes
|
// For now, create from bytes
|
||||||
// In production, implement proper deserialization
|
// In production, implement proper deserialization
|
||||||
@@ -294,7 +294,7 @@ func (sa *SignatureAggregator) addCoronaSignature(
|
|||||||
Y: new(big.Int).SetBytes(publicKey[32:64]),
|
Y: new(big.Int).SetBytes(publicKey[32:64]),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to ring if not already present
|
// Add to ring if not already present
|
||||||
inRing := false
|
inRing := false
|
||||||
for _, ringPK := range session.CoronaRing {
|
for _, ringPK := range session.CoronaRing {
|
||||||
@@ -306,12 +306,12 @@ func (sa *SignatureAggregator) addCoronaSignature(
|
|||||||
if !inRing {
|
if !inRing {
|
||||||
session.CoronaRing = append(session.CoronaRing, pk)
|
session.CoronaRing = append(session.CoronaRing, pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store signature
|
// Store signature
|
||||||
session.CoronaSignatures = append(session.CoronaSignatures, ringSig)
|
session.CoronaSignatures = append(session.CoronaSignatures, ringSig)
|
||||||
session.Signers[signerID] = true
|
session.Signers[signerID] = true
|
||||||
session.SignerCount++
|
session.SignerCount++
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,53 +322,53 @@ func (sa *SignatureAggregator) FinalizeAggregation(
|
|||||||
) (*AggregatedSignature, error) {
|
) (*AggregatedSignature, error) {
|
||||||
sa.mu.Lock()
|
sa.mu.Lock()
|
||||||
defer sa.mu.Unlock()
|
defer sa.mu.Unlock()
|
||||||
|
|
||||||
session, exists := sa.sessions[sessionID]
|
session, exists := sa.sessions[sessionID]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("session not found")
|
return nil, errors.New("session not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if session.Completed {
|
if session.Completed {
|
||||||
return session.Result, nil
|
return session.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check minimum signers
|
// Check minimum signers
|
||||||
if session.SignerCount < requiredSigners {
|
if session.SignerCount < requiredSigners {
|
||||||
return nil, fmt.Errorf("insufficient signers: %d < %d", session.SignerCount, requiredSigners)
|
return nil, fmt.Errorf("insufficient signers: %d < %d", session.SignerCount, requiredSigners)
|
||||||
}
|
}
|
||||||
|
|
||||||
var result *AggregatedSignature
|
var result *AggregatedSignature
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch session.SignatureType {
|
switch session.SignatureType {
|
||||||
case SignatureTypeBLS:
|
case SignatureTypeBLS:
|
||||||
result, err = sa.finalizeBLS(session)
|
result, err = sa.finalizeBLS(session)
|
||||||
|
|
||||||
case SignatureTypeCorona:
|
case SignatureTypeCorona:
|
||||||
result, err = sa.finalizeCorona(session)
|
result, err = sa.finalizeCorona(session)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unknown signature type")
|
return nil, errors.New("unknown signature type")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate total fee
|
// Calculate total fee
|
||||||
result.TotalFee = sa.calculateFee(session.SignatureType, session.SignerCount)
|
result.TotalFee = sa.calculateFee(session.SignatureType, session.SignerCount)
|
||||||
|
|
||||||
// Mark session as completed
|
// Mark session as completed
|
||||||
session.Completed = true
|
session.Completed = true
|
||||||
session.Result = result
|
session.Result = result
|
||||||
|
|
||||||
sa.log.Info("Finalized aggregation",
|
sa.log.Info("Finalized aggregation",
|
||||||
log.String("sessionID", sessionID),
|
log.String("sessionID", sessionID),
|
||||||
log.Uint8("type", uint8(session.SignatureType)),
|
log.Uint8("type", uint8(session.SignatureType)),
|
||||||
log.Int("signers", session.SignerCount),
|
log.Int("signers", session.SignerCount),
|
||||||
log.Uint64("totalFee", result.TotalFee),
|
log.Uint64("totalFee", result.TotalFee),
|
||||||
)
|
)
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,34 +377,34 @@ func (sa *SignatureAggregator) finalizeBLS(session *AggregationSession) (*Aggreg
|
|||||||
if len(session.BLSSignatures) == 0 {
|
if len(session.BLSSignatures) == 0 {
|
||||||
return nil, errors.New("no BLS signatures to aggregate")
|
return nil, errors.New("no BLS signatures to aggregate")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggregate signatures
|
// Aggregate signatures
|
||||||
aggSig, err := bls.AggregateSignatures(session.BLSSignatures)
|
aggSig, err := bls.AggregateSignatures(session.BLSSignatures)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("BLS aggregation failed: %w", err)
|
return nil, fmt.Errorf("BLS aggregation failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggregate public keys
|
// Aggregate public keys
|
||||||
aggPK, err := bls.AggregatePublicKeys(session.BLSPublicKeys)
|
aggPK, err := bls.AggregatePublicKeys(session.BLSPublicKeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("BLS public key aggregation failed: %w", err)
|
return nil, fmt.Errorf("BLS public key aggregation failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify aggregate signature
|
// Verify aggregate signature
|
||||||
valid := bls.Verify(aggPK, aggSig, session.Message)
|
valid := bls.Verify(aggPK, aggSig, session.Message)
|
||||||
if !valid {
|
if !valid {
|
||||||
return nil, fmt.Errorf("aggregate signature verification failed")
|
return nil, fmt.Errorf("aggregate signature verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
sigBytes := bls.SignatureToBytes(aggSig)
|
sigBytes := bls.SignatureToBytes(aggSig)
|
||||||
pkBytes := bls.PublicKeyToCompressedBytes(aggPK)
|
pkBytes := bls.PublicKeyToCompressedBytes(aggPK)
|
||||||
|
|
||||||
// Extract signer IDs
|
// Extract signer IDs
|
||||||
signerIDs := make([]ids.NodeID, 0, len(session.Signers))
|
signerIDs := make([]ids.NodeID, 0, len(session.Signers))
|
||||||
for id := range session.Signers {
|
for id := range session.Signers {
|
||||||
signerIDs = append(signerIDs, id)
|
signerIDs = append(signerIDs, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &AggregatedSignature{
|
return &AggregatedSignature{
|
||||||
Type: SignatureTypeBLS,
|
Type: SignatureTypeBLS,
|
||||||
Signature: sigBytes,
|
Signature: sigBytes,
|
||||||
@@ -419,11 +419,11 @@ func (sa *SignatureAggregator) finalizeCorona(session *AggregationSession) (*Agg
|
|||||||
if len(session.CoronaSignatures) == 0 {
|
if len(session.CoronaSignatures) == 0 {
|
||||||
return nil, errors.New("no Corona signatures collected")
|
return nil, errors.New("no Corona signatures collected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Corona, we use the first signature as the aggregated result
|
// For Corona, we use the first signature as the aggregated result
|
||||||
// since ring signatures provide anonymity within the ring
|
// since ring signatures provide anonymity within the ring
|
||||||
ringSig := session.CoronaSignatures[0]
|
ringSig := session.CoronaSignatures[0]
|
||||||
|
|
||||||
// Serialize signature (simplified for now)
|
// Serialize signature (simplified for now)
|
||||||
// In production, implement proper serialization
|
// In production, implement proper serialization
|
||||||
sigBytes := make([]byte, 0)
|
sigBytes := make([]byte, 0)
|
||||||
@@ -433,13 +433,13 @@ func (sa *SignatureAggregator) finalizeCorona(session *AggregationSession) (*Agg
|
|||||||
sigBytes = append(sigBytes, s.Bytes()...)
|
sigBytes = append(sigBytes, s.Bytes()...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify against the full ring
|
// Verify against the full ring
|
||||||
valid := ringSig.Verify(session.Message)
|
valid := ringSig.Verify(session.Message)
|
||||||
if !valid {
|
if !valid {
|
||||||
return nil, fmt.Errorf("ring signature verification failed")
|
return nil, fmt.Errorf("ring signature verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &AggregatedSignature{
|
return &AggregatedSignature{
|
||||||
Type: SignatureTypeCorona,
|
Type: SignatureTypeCorona,
|
||||||
Signature: sigBytes,
|
Signature: sigBytes,
|
||||||
@@ -451,7 +451,7 @@ func (sa *SignatureAggregator) finalizeCorona(session *AggregationSession) (*Agg
|
|||||||
// calculateFee calculates the total fee for signature aggregation
|
// calculateFee calculates the total fee for signature aggregation
|
||||||
func (sa *SignatureAggregator) calculateFee(sigType SignatureType, signerCount int) uint64 {
|
func (sa *SignatureAggregator) calculateFee(sigType SignatureType, signerCount int) uint64 {
|
||||||
var feePerSigner uint64
|
var feePerSigner uint64
|
||||||
|
|
||||||
switch sigType {
|
switch sigType {
|
||||||
case SignatureTypeBLS:
|
case SignatureTypeBLS:
|
||||||
feePerSigner = sa.config.BLSFee // 0 for free
|
feePerSigner = sa.config.BLSFee // 0 for free
|
||||||
@@ -462,7 +462,7 @@ func (sa *SignatureAggregator) calculateFee(sigType SignatureType, signerCount i
|
|||||||
default:
|
default:
|
||||||
feePerSigner = 0
|
feePerSigner = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return feePerSigner * uint64(signerCount)
|
return feePerSigner * uint64(signerCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,13 +474,13 @@ func (sa *SignatureAggregator) VerifyAggregatedSignature(
|
|||||||
switch aggSig.Type {
|
switch aggSig.Type {
|
||||||
case SignatureTypeBLS:
|
case SignatureTypeBLS:
|
||||||
return sa.verifyBLSAggregate(message, aggSig)
|
return sa.verifyBLSAggregate(message, aggSig)
|
||||||
|
|
||||||
case SignatureTypeCorona:
|
case SignatureTypeCorona:
|
||||||
return sa.verifyCoronaAggregate(message, aggSig)
|
return sa.verifyCoronaAggregate(message, aggSig)
|
||||||
|
|
||||||
case SignatureTypeCGGMP21:
|
case SignatureTypeCGGMP21:
|
||||||
return errors.New("CGGMP21 verification not implemented")
|
return errors.New("CGGMP21 verification not implemented")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errors.New("unknown signature type")
|
return errors.New("unknown signature type")
|
||||||
}
|
}
|
||||||
@@ -492,17 +492,17 @@ func (sa *SignatureAggregator) verifyBLSAggregate(message []byte, aggSig *Aggreg
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pk, err := bls.PublicKeyFromCompressedBytes(aggSig.AggregateKey)
|
pk, err := bls.PublicKeyFromCompressedBytes(aggSig.AggregateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
valid := bls.Verify(pk, sig, message)
|
valid := bls.Verify(pk, sig, message)
|
||||||
if !valid {
|
if !valid {
|
||||||
return errors.New("BLS signature verification failed")
|
return errors.New("BLS signature verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,7 +513,7 @@ func (sa *SignatureAggregator) verifyCoronaAggregate(message []byte, aggSig *Agg
|
|||||||
if len(aggSig.Signature) < 256 {
|
if len(aggSig.Signature) < 256 {
|
||||||
return errors.New("invalid ring signature")
|
return errors.New("invalid ring signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,12 +521,12 @@ func (sa *SignatureAggregator) verifyCoronaAggregate(message []byte, aggSig *Agg
|
|||||||
func (sa *SignatureAggregator) GetSessionStatus(sessionID string) (map[string]interface{}, error) {
|
func (sa *SignatureAggregator) GetSessionStatus(sessionID string) (map[string]interface{}, error) {
|
||||||
sa.mu.RLock()
|
sa.mu.RLock()
|
||||||
defer sa.mu.RUnlock()
|
defer sa.mu.RUnlock()
|
||||||
|
|
||||||
session, exists := sa.sessions[sessionID]
|
session, exists := sa.sessions[sessionID]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("session not found")
|
return nil, errors.New("session not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
status := map[string]interface{}{
|
status := map[string]interface{}{
|
||||||
"sessionID": session.SessionID,
|
"sessionID": session.SessionID,
|
||||||
"signatureType": session.SignatureType,
|
"signatureType": session.SignatureType,
|
||||||
@@ -534,11 +534,11 @@ func (sa *SignatureAggregator) GetSessionStatus(sessionID string) (map[string]in
|
|||||||
"completed": session.Completed,
|
"completed": session.Completed,
|
||||||
"startTime": session.StartTime,
|
"startTime": session.StartTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
if session.Completed && session.Result != nil {
|
if session.Completed && session.Result != nil {
|
||||||
status["totalFee"] = session.Result.TotalFee
|
status["totalFee"] = session.Result.TotalFee
|
||||||
}
|
}
|
||||||
|
|
||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,9 +546,9 @@ func (sa *SignatureAggregator) GetSessionStatus(sessionID string) (map[string]in
|
|||||||
func (sa *SignatureAggregator) Cleanup(maxAge int64) {
|
func (sa *SignatureAggregator) Cleanup(maxAge int64) {
|
||||||
sa.mu.Lock()
|
sa.mu.Lock()
|
||||||
defer sa.mu.Unlock()
|
defer sa.mu.Unlock()
|
||||||
|
|
||||||
currentTime := getCurrentTime()
|
currentTime := getCurrentTime()
|
||||||
|
|
||||||
for sessionID, session := range sa.sessions {
|
for sessionID, session := range sa.sessions {
|
||||||
if currentTime-session.StartTime > maxAge {
|
if currentTime-session.StartTime > maxAge {
|
||||||
delete(sa.sessions, sessionID)
|
delete(sa.sessions, sessionID)
|
||||||
@@ -559,4 +559,4 @@ func (sa *SignatureAggregator) Cleanup(maxAge int64) {
|
|||||||
// Helper function
|
// Helper function
|
||||||
func getCurrentTime() int64 {
|
func getCurrentTime() int64 {
|
||||||
return time.Now().Unix()
|
return time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-26
@@ -41,24 +41,24 @@ func TestMLKEMEdgeCases(t *testing.T) {
|
|||||||
modes := []mlkem.Mode{mlkem.MLKEM512, mlkem.MLKEM768, mlkem.MLKEM1024}
|
modes := []mlkem.Mode{mlkem.MLKEM512, mlkem.MLKEM768, mlkem.MLKEM1024}
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
priv1, _, _ := mlkem.GenerateKeyPair(rand.Reader, mode)
|
priv1, _, _ := mlkem.GenerateKeyPair(rand.Reader, mode)
|
||||||
|
|
||||||
// Serialize
|
// Serialize
|
||||||
privBytes := priv1.Bytes()
|
privBytes := priv1.Bytes()
|
||||||
pubBytes := priv1.PublicKey.Bytes()
|
pubBytes := priv1.PublicKey.Bytes()
|
||||||
|
|
||||||
// Deserialize
|
// Deserialize
|
||||||
priv2, err := mlkem.PrivateKeyFromBytes(privBytes, mode)
|
priv2, err := mlkem.PrivateKeyFromBytes(privBytes, mode)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
pub2, err := mlkem.PublicKeyFromBytes(pubBytes, mode)
|
pub2, err := mlkem.PublicKeyFromBytes(pubBytes, mode)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Verify they work the same
|
// Verify they work the same
|
||||||
result1, _ := priv1.PublicKey.Encapsulate(rand.Reader)
|
result1, _ := priv1.PublicKey.Encapsulate(rand.Reader)
|
||||||
secret1, _ := priv1.Decapsulate(result1.Ciphertext)
|
secret1, _ := priv1.Decapsulate(result1.Ciphertext)
|
||||||
|
|
||||||
result2, _ := pub2.Encapsulate(rand.Reader)
|
result2, _ := pub2.Encapsulate(rand.Reader)
|
||||||
secret2, _ := priv2.Decapsulate(result2.Ciphertext)
|
secret2, _ := priv2.Decapsulate(result2.Ciphertext)
|
||||||
|
|
||||||
// Both should produce valid shared secrets
|
// Both should produce valid shared secrets
|
||||||
assert.Len(t, secret1, 32)
|
assert.Len(t, secret1, 32)
|
||||||
assert.Len(t, secret2, 32)
|
assert.Len(t, secret2, 32)
|
||||||
@@ -69,10 +69,10 @@ func TestMLKEMEdgeCases(t *testing.T) {
|
|||||||
// Same private key seed should generate same public key
|
// Same private key seed should generate same public key
|
||||||
privBytes := make([]byte, mlkem.MLKEM768PrivateKeySize)
|
privBytes := make([]byte, mlkem.MLKEM768PrivateKeySize)
|
||||||
copy(privBytes, []byte("deterministic seed for testing"))
|
copy(privBytes, []byte("deterministic seed for testing"))
|
||||||
|
|
||||||
priv1, _ := mlkem.PrivateKeyFromBytes(privBytes, mlkem.MLKEM768)
|
priv1, _ := mlkem.PrivateKeyFromBytes(privBytes, mlkem.MLKEM768)
|
||||||
priv2, _ := mlkem.PrivateKeyFromBytes(privBytes, mlkem.MLKEM768)
|
priv2, _ := mlkem.PrivateKeyFromBytes(privBytes, mlkem.MLKEM768)
|
||||||
|
|
||||||
assert.Equal(t, priv1.PublicKey.Bytes(), priv2.PublicKey.Bytes())
|
assert.Equal(t, priv1.PublicKey.Bytes(), priv2.PublicKey.Bytes())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ func TestMLDSAEdgeCases(t *testing.T) {
|
|||||||
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
||||||
largeMsg := make([]byte, 10000)
|
largeMsg := make([]byte, 10000)
|
||||||
rand.Read(largeMsg)
|
rand.Read(largeMsg)
|
||||||
|
|
||||||
sig, err := priv.Sign(rand.Reader, largeMsg, nil)
|
sig, err := priv.Sign(rand.Reader, largeMsg, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.True(t, priv.PublicKey.Verify(largeMsg, sig, nil))
|
assert.True(t, priv.PublicKey.Verify(largeMsg, sig, nil))
|
||||||
@@ -104,10 +104,10 @@ func TestMLDSAEdgeCases(t *testing.T) {
|
|||||||
t.Run("Signature Malleability", func(t *testing.T) {
|
t.Run("Signature Malleability", func(t *testing.T) {
|
||||||
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
||||||
msg := []byte("test message")
|
msg := []byte("test message")
|
||||||
|
|
||||||
sig1, _ := priv.Sign(rand.Reader, msg, nil)
|
sig1, _ := priv.Sign(rand.Reader, msg, nil)
|
||||||
sig2, _ := priv.Sign(rand.Reader, msg, nil)
|
sig2, _ := priv.Sign(rand.Reader, msg, nil)
|
||||||
|
|
||||||
// Signatures should be deterministic in our implementation
|
// Signatures should be deterministic in our implementation
|
||||||
assert.Equal(t, sig1, sig2)
|
assert.Equal(t, sig1, sig2)
|
||||||
})
|
})
|
||||||
@@ -115,7 +115,7 @@ func TestMLDSAEdgeCases(t *testing.T) {
|
|||||||
t.Run("Wrong Signature Size", func(t *testing.T) {
|
t.Run("Wrong Signature Size", func(t *testing.T) {
|
||||||
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
||||||
msg := []byte("test")
|
msg := []byte("test")
|
||||||
|
|
||||||
wrongSig := make([]byte, 100) // Wrong size
|
wrongSig := make([]byte, 100) // Wrong size
|
||||||
assert.False(t, priv.PublicKey.Verify(msg, wrongSig, nil))
|
assert.False(t, priv.PublicKey.Verify(msg, wrongSig, nil))
|
||||||
})
|
})
|
||||||
@@ -124,9 +124,9 @@ func TestMLDSAEdgeCases(t *testing.T) {
|
|||||||
priv44, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
priv44, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
||||||
priv65, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
priv65, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
||||||
msg := []byte("test")
|
msg := []byte("test")
|
||||||
|
|
||||||
sig44, _ := priv44.Sign(rand.Reader, msg, nil)
|
sig44, _ := priv44.Sign(rand.Reader, msg, nil)
|
||||||
|
|
||||||
// ML-DSA65 key shouldn't verify ML-DSA44 signature
|
// ML-DSA65 key shouldn't verify ML-DSA44 signature
|
||||||
assert.False(t, priv65.PublicKey.Verify(msg, sig44, nil))
|
assert.False(t, priv65.PublicKey.Verify(msg, sig44, nil))
|
||||||
})
|
})
|
||||||
@@ -137,10 +137,10 @@ func TestSLHDSAEdgeCases(t *testing.T) {
|
|||||||
t.Run("Deterministic Signatures", func(t *testing.T) {
|
t.Run("Deterministic Signatures", func(t *testing.T) {
|
||||||
priv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128f)
|
priv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128f)
|
||||||
msg := []byte("deterministic test")
|
msg := []byte("deterministic test")
|
||||||
|
|
||||||
sig1, _ := priv.Sign(rand.Reader, msg, nil)
|
sig1, _ := priv.Sign(rand.Reader, msg, nil)
|
||||||
sig2, _ := priv.Sign(rand.Reader, msg, nil)
|
sig2, _ := priv.Sign(rand.Reader, msg, nil)
|
||||||
|
|
||||||
// SLH-DSA is deterministic - same message should produce same signature
|
// SLH-DSA is deterministic - same message should produce same signature
|
||||||
assert.Equal(t, sig1, sig2)
|
assert.Equal(t, sig1, sig2)
|
||||||
})
|
})
|
||||||
@@ -154,13 +154,13 @@ func TestSLHDSAEdgeCases(t *testing.T) {
|
|||||||
{slhdsa.SLHDSA128f, "128f", slhdsa.SLHDSA128fSignatureSize},
|
{slhdsa.SLHDSA128f, "128f", slhdsa.SLHDSA128fSignatureSize},
|
||||||
{slhdsa.SLHDSA192f, "192f", slhdsa.SLHDSA192fSignatureSize},
|
{slhdsa.SLHDSA192f, "192f", slhdsa.SLHDSA192fSignatureSize},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range modes {
|
for _, m := range modes {
|
||||||
t.Run(m.name, func(t *testing.T) {
|
t.Run(m.name, func(t *testing.T) {
|
||||||
priv, _ := slhdsa.GenerateKey(rand.Reader, m.mode)
|
priv, _ := slhdsa.GenerateKey(rand.Reader, m.mode)
|
||||||
msg := []byte("test")
|
msg := []byte("test")
|
||||||
sig, _ := priv.Sign(rand.Reader, msg, nil)
|
sig, _ := priv.Sign(rand.Reader, msg, nil)
|
||||||
|
|
||||||
assert.Len(t, sig, m.size)
|
assert.Len(t, sig, m.size)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -171,7 +171,7 @@ func TestSLHDSAEdgeCases(t *testing.T) {
|
|||||||
func TestConcurrency(t *testing.T) {
|
func TestConcurrency(t *testing.T) {
|
||||||
t.Run("ML-KEM Concurrent Operations", func(t *testing.T) {
|
t.Run("ML-KEM Concurrent Operations", func(t *testing.T) {
|
||||||
priv, _, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
|
priv, _, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
|
||||||
|
|
||||||
// Run concurrent encapsulations
|
// Run concurrent encapsulations
|
||||||
done := make(chan bool, 10)
|
done := make(chan bool, 10)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
@@ -184,7 +184,7 @@ func TestConcurrency(t *testing.T) {
|
|||||||
done <- true
|
done <- true
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
@@ -192,7 +192,7 @@ func TestConcurrency(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("ML-DSA Concurrent Signing", func(t *testing.T) {
|
t.Run("ML-DSA Concurrent Signing", func(t *testing.T) {
|
||||||
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
||||||
|
|
||||||
done := make(chan bool, 10)
|
done := make(chan bool, 10)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
go func(id int) {
|
go func(id int) {
|
||||||
@@ -203,7 +203,7 @@ func TestConcurrency(t *testing.T) {
|
|||||||
done <- true
|
done <- true
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
@@ -229,11 +229,11 @@ func TestParameterValidation(t *testing.T) {
|
|||||||
assert.Equal(t, 800, mlkem.MLKEM512PublicKeySize)
|
assert.Equal(t, 800, mlkem.MLKEM512PublicKeySize)
|
||||||
assert.Equal(t, 1632, mlkem.MLKEM512PrivateKeySize)
|
assert.Equal(t, 1632, mlkem.MLKEM512PrivateKeySize)
|
||||||
assert.Equal(t, 768, mlkem.MLKEM512CiphertextSize)
|
assert.Equal(t, 768, mlkem.MLKEM512CiphertextSize)
|
||||||
|
|
||||||
assert.Equal(t, 1184, mlkem.MLKEM768PublicKeySize)
|
assert.Equal(t, 1184, mlkem.MLKEM768PublicKeySize)
|
||||||
assert.Equal(t, 2400, mlkem.MLKEM768PrivateKeySize)
|
assert.Equal(t, 2400, mlkem.MLKEM768PrivateKeySize)
|
||||||
assert.Equal(t, 1088, mlkem.MLKEM768CiphertextSize)
|
assert.Equal(t, 1088, mlkem.MLKEM768CiphertextSize)
|
||||||
|
|
||||||
assert.Equal(t, 1568, mlkem.MLKEM1024PublicKeySize)
|
assert.Equal(t, 1568, mlkem.MLKEM1024PublicKeySize)
|
||||||
assert.Equal(t, 3168, mlkem.MLKEM1024PrivateKeySize)
|
assert.Equal(t, 3168, mlkem.MLKEM1024PrivateKeySize)
|
||||||
assert.Equal(t, 1568, mlkem.MLKEM1024CiphertextSize)
|
assert.Equal(t, 1568, mlkem.MLKEM1024CiphertextSize)
|
||||||
@@ -242,12 +242,12 @@ func TestParameterValidation(t *testing.T) {
|
|||||||
assert.Equal(t, 1312, mldsa.MLDSA44PublicKeySize)
|
assert.Equal(t, 1312, mldsa.MLDSA44PublicKeySize)
|
||||||
assert.Equal(t, 2528, mldsa.MLDSA44PrivateKeySize)
|
assert.Equal(t, 2528, mldsa.MLDSA44PrivateKeySize)
|
||||||
assert.Equal(t, 2420, mldsa.MLDSA44SignatureSize)
|
assert.Equal(t, 2420, mldsa.MLDSA44SignatureSize)
|
||||||
|
|
||||||
assert.Equal(t, 1952, mldsa.MLDSA65PublicKeySize)
|
assert.Equal(t, 1952, mldsa.MLDSA65PublicKeySize)
|
||||||
assert.Equal(t, 4000, mldsa.MLDSA65PrivateKeySize)
|
assert.Equal(t, 4000, mldsa.MLDSA65PrivateKeySize)
|
||||||
assert.Equal(t, 3293, mldsa.MLDSA65SignatureSize)
|
assert.Equal(t, 3293, mldsa.MLDSA65SignatureSize)
|
||||||
|
|
||||||
assert.Equal(t, 2592, mldsa.MLDSA87PublicKeySize)
|
assert.Equal(t, 2592, mldsa.MLDSA87PublicKeySize)
|
||||||
assert.Equal(t, 4864, mldsa.MLDSA87PrivateKeySize)
|
assert.Equal(t, 4864, mldsa.MLDSA87PrivateKeySize)
|
||||||
assert.Equal(t, 4595, mldsa.MLDSA87SignatureSize)
|
assert.Equal(t, 4595, mldsa.MLDSA87SignatureSize)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -106,7 +106,7 @@ func (pk *PublicKey) From(blstSK interface{}) *PublicKey {
|
|||||||
Compress() []byte
|
Compress() []byte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sk, ok := blstSK.(blstSecretKey); ok {
|
if sk, ok := blstSK.(blstSecretKey); ok {
|
||||||
pkBytes := sk.PublicKey().Compress()
|
pkBytes := sk.PublicKey().Compress()
|
||||||
newPk, err := PublicKeyFromCompressedBytes(pkBytes)
|
newPk, err := PublicKeyFromCompressedBytes(pkBytes)
|
||||||
@@ -115,7 +115,7 @@ func (pk *PublicKey) From(blstSK interface{}) *PublicKey {
|
|||||||
}
|
}
|
||||||
return newPk
|
return newPk
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,7 +268,7 @@ func SignatureFromBytes(sigBytes []byte) (*Signature, error) {
|
|||||||
allSame = false
|
allSame = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reject signatures that are all zeros or all the same byte (e.g., all 0xFF)
|
// Reject signatures that are all zeros or all the same byte (e.g., all 0xFF)
|
||||||
if allZero || allSame {
|
if allZero || allSame {
|
||||||
return nil, ErrFailedSignatureDecompress
|
return nil, ErrFailedSignatureDecompress
|
||||||
@@ -285,7 +285,7 @@ func (sig *Signature) Sign(blstSK interface{}, msg []byte, dst []byte) *Signatur
|
|||||||
Compress() []byte
|
Compress() []byte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sk, ok := blstSK.(blstSecretKey); ok {
|
if sk, ok := blstSK.(blstSecretKey); ok {
|
||||||
blstSig := sk.Sign(msg, dst, nil)
|
blstSig := sk.Sign(msg, dst, nil)
|
||||||
sigBytes := blstSig.Compress()
|
sigBytes := blstSig.Compress()
|
||||||
@@ -295,7 +295,7 @@ func (sig *Signature) Sign(blstSK interface{}, msg []byte, dst []byte) *Signatur
|
|||||||
}
|
}
|
||||||
return newSig
|
return newSig
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+62
-62
@@ -18,13 +18,13 @@ func TestNewSecretKeyExtended(t *testing.T) {
|
|||||||
if sk.sk == nil {
|
if sk.sk == nil {
|
||||||
t.Fatal("Internal secret key is nil")
|
t.Fatal("Internal secret key is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify keys are different
|
// Verify keys are different
|
||||||
sk2, err := NewSecretKey()
|
sk2, err := NewSecretKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to generate second secret key: %v", err)
|
t.Fatalf("Failed to generate second secret key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes1 := SecretKeyToBytes(sk)
|
bytes1 := SecretKeyToBytes(sk)
|
||||||
bytes2 := SecretKeyToBytes(sk2)
|
bytes2 := SecretKeyToBytes(sk2)
|
||||||
if bytes.Equal(bytes1, bytes2) {
|
if bytes.Equal(bytes1, bytes2) {
|
||||||
@@ -39,32 +39,32 @@ func TestSecretKeyBytes(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
t.Fatalf("Failed to generate secret key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to bytes
|
// Convert to bytes
|
||||||
skBytes := SecretKeyToBytes(sk)
|
skBytes := SecretKeyToBytes(sk)
|
||||||
if len(skBytes) == 0 {
|
if len(skBytes) == 0 {
|
||||||
t.Fatal("Secret key bytes should not be empty")
|
t.Fatal("Secret key bytes should not be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nil secret key
|
// Test nil secret key
|
||||||
nilBytes := SecretKeyToBytes(nil)
|
nilBytes := SecretKeyToBytes(nil)
|
||||||
if nilBytes != nil {
|
if nilBytes != nil {
|
||||||
t.Fatal("Nil secret key should return nil bytes")
|
t.Fatal("Nil secret key should return nil bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test secret key with nil internal
|
// Test secret key with nil internal
|
||||||
emptyKey := &SecretKey{}
|
emptyKey := &SecretKey{}
|
||||||
emptyBytes := SecretKeyToBytes(emptyKey)
|
emptyBytes := SecretKeyToBytes(emptyKey)
|
||||||
if emptyBytes != nil {
|
if emptyBytes != nil {
|
||||||
t.Fatal("Secret key with nil internal should return nil bytes")
|
t.Fatal("Secret key with nil internal should return nil bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Round-trip test
|
// Round-trip test
|
||||||
sk2, err := SecretKeyFromBytes(skBytes)
|
sk2, err := SecretKeyFromBytes(skBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to deserialize secret key: %v", err)
|
t.Fatalf("Failed to deserialize secret key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
skBytes2 := SecretKeyToBytes(sk2)
|
skBytes2 := SecretKeyToBytes(sk2)
|
||||||
if !bytes.Equal(skBytes, skBytes2) {
|
if !bytes.Equal(skBytes, skBytes2) {
|
||||||
t.Fatal("Round-trip secret key bytes should match")
|
t.Fatal("Round-trip secret key bytes should match")
|
||||||
@@ -78,13 +78,13 @@ func TestSecretKeyFromBytesErrors(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should fail with invalid bytes")
|
t.Fatal("Should fail with invalid bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with nil bytes
|
// Test with nil bytes
|
||||||
_, err = SecretKeyFromBytes(nil)
|
_, err = SecretKeyFromBytes(nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should fail with nil bytes")
|
t.Fatal("Should fail with nil bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with empty bytes
|
// Test with empty bytes
|
||||||
_, err = SecretKeyFromBytes([]byte{})
|
_, err = SecretKeyFromBytes([]byte{})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -97,7 +97,7 @@ func TestPublicKeyOperations(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
t.Fatalf("Failed to generate secret key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get public key
|
// Get public key
|
||||||
pk := sk.PublicKey()
|
pk := sk.PublicKey()
|
||||||
if pk == nil {
|
if pk == nil {
|
||||||
@@ -106,14 +106,14 @@ func TestPublicKeyOperations(t *testing.T) {
|
|||||||
if pk.pk == nil {
|
if pk.pk == nil {
|
||||||
t.Fatal("Internal public key should not be nil")
|
t.Fatal("Internal public key should not be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nil secret key
|
// Test nil secret key
|
||||||
var nilSk *SecretKey
|
var nilSk *SecretKey
|
||||||
nilPk := nilSk.PublicKey()
|
nilPk := nilSk.PublicKey()
|
||||||
if nilPk != nil {
|
if nilPk != nil {
|
||||||
t.Fatal("Nil secret key should return nil public key")
|
t.Fatal("Nil secret key should return nil public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test secret key with nil internal
|
// Test secret key with nil internal
|
||||||
emptySk := &SecretKey{}
|
emptySk := &SecretKey{}
|
||||||
emptyPk := emptySk.PublicKey()
|
emptyPk := emptySk.PublicKey()
|
||||||
@@ -127,27 +127,27 @@ func TestPublicKeyBytes(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
t.Fatalf("Failed to generate secret key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
pk := sk.PublicKey()
|
||||||
|
|
||||||
// Test compressed bytes
|
// Test compressed bytes
|
||||||
compressedBytes := PublicKeyToCompressedBytes(pk)
|
compressedBytes := PublicKeyToCompressedBytes(pk)
|
||||||
if len(compressedBytes) != PublicKeyLen {
|
if len(compressedBytes) != PublicKeyLen {
|
||||||
t.Fatalf("Compressed public key should be %d bytes, got %d", PublicKeyLen, len(compressedBytes))
|
t.Fatalf("Compressed public key should be %d bytes, got %d", PublicKeyLen, len(compressedBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test uncompressed bytes (should be same as compressed for circl)
|
// Test uncompressed bytes (should be same as compressed for circl)
|
||||||
uncompressedBytes := PublicKeyToUncompressedBytes(pk)
|
uncompressedBytes := PublicKeyToUncompressedBytes(pk)
|
||||||
if !bytes.Equal(compressedBytes, uncompressedBytes) {
|
if !bytes.Equal(compressedBytes, uncompressedBytes) {
|
||||||
t.Fatal("Compressed and uncompressed should be equal for circl BLS")
|
t.Fatal("Compressed and uncompressed should be equal for circl BLS")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nil public key
|
// Test nil public key
|
||||||
nilBytes := PublicKeyToCompressedBytes(nil)
|
nilBytes := PublicKeyToCompressedBytes(nil)
|
||||||
if nilBytes != nil {
|
if nilBytes != nil {
|
||||||
t.Fatal("Nil public key should return nil bytes")
|
t.Fatal("Nil public key should return nil bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test public key with nil internal
|
// Test public key with nil internal
|
||||||
emptyPk := &PublicKey{}
|
emptyPk := &PublicKey{}
|
||||||
emptyBytes := PublicKeyToCompressedBytes(emptyPk)
|
emptyBytes := PublicKeyToCompressedBytes(emptyPk)
|
||||||
@@ -161,21 +161,21 @@ func TestPublicKeyFromBytes(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
t.Fatalf("Failed to generate secret key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
pk := sk.PublicKey()
|
||||||
pkBytes := PublicKeyToCompressedBytes(pk)
|
pkBytes := PublicKeyToCompressedBytes(pk)
|
||||||
|
|
||||||
// Test valid deserialization
|
// Test valid deserialization
|
||||||
pk2, err := PublicKeyFromCompressedBytes(pkBytes)
|
pk2, err := PublicKeyFromCompressedBytes(pkBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to deserialize public key: %v", err)
|
t.Fatalf("Failed to deserialize public key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pkBytes2 := PublicKeyToCompressedBytes(pk2)
|
pkBytes2 := PublicKeyToCompressedBytes(pk2)
|
||||||
if !bytes.Equal(pkBytes, pkBytes2) {
|
if !bytes.Equal(pkBytes, pkBytes2) {
|
||||||
t.Fatal("Round-trip public key bytes should match")
|
t.Fatal("Round-trip public key bytes should match")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test from valid uncompressed bytes
|
// Test from valid uncompressed bytes
|
||||||
pk3 := PublicKeyFromValidUncompressedBytes(pkBytes)
|
pk3 := PublicKeyFromValidUncompressedBytes(pkBytes)
|
||||||
if pk3 == nil {
|
if pk3 == nil {
|
||||||
@@ -194,13 +194,13 @@ func TestPublicKeyFromBytesErrors(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should fail with wrong size bytes")
|
t.Fatal("Should fail with wrong size bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with nil
|
// Test with nil
|
||||||
_, err = PublicKeyFromCompressedBytes(nil)
|
_, err = PublicKeyFromCompressedBytes(nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should fail with nil bytes")
|
t.Fatal("Should fail with nil bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with invalid point (all zeros)
|
// Test with invalid point (all zeros)
|
||||||
zeroBytes := make([]byte, PublicKeyLen)
|
zeroBytes := make([]byte, PublicKeyLen)
|
||||||
_, err = PublicKeyFromCompressedBytes(zeroBytes)
|
_, err = PublicKeyFromCompressedBytes(zeroBytes)
|
||||||
@@ -214,10 +214,10 @@ func TestSignAndVerify(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
t.Fatalf("Failed to generate secret key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
pk := sk.PublicKey()
|
||||||
msg := []byte("test message")
|
msg := []byte("test message")
|
||||||
|
|
||||||
// Sign message
|
// Sign message
|
||||||
sig, err := sk.Sign(msg)
|
sig, err := sk.Sign(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -226,20 +226,20 @@ func TestSignAndVerify(t *testing.T) {
|
|||||||
if sig == nil {
|
if sig == nil {
|
||||||
t.Fatal("Signature should not be nil")
|
t.Fatal("Signature should not be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
valid := Verify(pk, sig, msg)
|
valid := Verify(pk, sig, msg)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("Signature should be valid")
|
t.Fatal("Signature should be valid")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify with wrong message
|
// Verify with wrong message
|
||||||
wrongMsg := []byte("wrong message")
|
wrongMsg := []byte("wrong message")
|
||||||
valid = Verify(pk, sig, wrongMsg)
|
valid = Verify(pk, sig, wrongMsg)
|
||||||
if valid {
|
if valid {
|
||||||
t.Fatal("Signature should be invalid for wrong message")
|
t.Fatal("Signature should be invalid for wrong message")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify with wrong public key
|
// Verify with wrong public key
|
||||||
sk2, _ := NewSecretKey()
|
sk2, _ := NewSecretKey()
|
||||||
pk2 := sk2.PublicKey()
|
pk2 := sk2.PublicKey()
|
||||||
@@ -247,7 +247,7 @@ func TestSignAndVerify(t *testing.T) {
|
|||||||
if valid {
|
if valid {
|
||||||
t.Fatal("Signature should be invalid for wrong public key")
|
t.Fatal("Signature should be invalid for wrong public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nil cases
|
// Test nil cases
|
||||||
nilSig, err := sk.Sign(nil)
|
nilSig, err := sk.Sign(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -256,7 +256,7 @@ func TestSignAndVerify(t *testing.T) {
|
|||||||
if nilSig == nil {
|
if nilSig == nil {
|
||||||
t.Fatal("Should handle nil message")
|
t.Fatal("Should handle nil message")
|
||||||
}
|
}
|
||||||
|
|
||||||
var nilSk *SecretKey
|
var nilSk *SecretKey
|
||||||
nilSig2, err := nilSk.Sign(msg)
|
nilSig2, err := nilSk.Sign(msg)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -265,7 +265,7 @@ func TestSignAndVerify(t *testing.T) {
|
|||||||
if nilSig2 != nil {
|
if nilSig2 != nil {
|
||||||
t.Fatal("Nil secret key should return nil signature")
|
t.Fatal("Nil secret key should return nil signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
emptySk := &SecretKey{}
|
emptySk := &SecretKey{}
|
||||||
emptySig, err := emptySk.Sign(msg)
|
emptySig, err := emptySk.Sign(msg)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -281,20 +281,20 @@ func TestVerifyEdgeCases(t *testing.T) {
|
|||||||
pk := sk.PublicKey()
|
pk := sk.PublicKey()
|
||||||
msg := []byte("test")
|
msg := []byte("test")
|
||||||
sig, _ := sk.Sign(msg)
|
sig, _ := sk.Sign(msg)
|
||||||
|
|
||||||
// Test nil public key
|
// Test nil public key
|
||||||
valid := Verify(nil, sig, msg)
|
valid := Verify(nil, sig, msg)
|
||||||
if valid {
|
if valid {
|
||||||
t.Fatal("Should fail with nil public key")
|
t.Fatal("Should fail with nil public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test public key with nil internal
|
// Test public key with nil internal
|
||||||
emptyPk := &PublicKey{}
|
emptyPk := &PublicKey{}
|
||||||
valid = Verify(emptyPk, sig, msg)
|
valid = Verify(emptyPk, sig, msg)
|
||||||
if valid {
|
if valid {
|
||||||
t.Fatal("Should fail with empty public key")
|
t.Fatal("Should fail with empty public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nil signature
|
// Test nil signature
|
||||||
valid = Verify(pk, nil, msg)
|
valid = Verify(pk, nil, msg)
|
||||||
if valid {
|
if valid {
|
||||||
@@ -307,10 +307,10 @@ func TestProofOfPossession(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
t.Fatalf("Failed to generate secret key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
pk := sk.PublicKey()
|
||||||
msg := []byte("proof of possession")
|
msg := []byte("proof of possession")
|
||||||
|
|
||||||
// Sign proof of possession
|
// Sign proof of possession
|
||||||
sig, err := sk.SignProofOfPossession(msg)
|
sig, err := sk.SignProofOfPossession(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -319,20 +319,20 @@ func TestProofOfPossession(t *testing.T) {
|
|||||||
if sig == nil {
|
if sig == nil {
|
||||||
t.Fatal("PoP signature should not be nil")
|
t.Fatal("PoP signature should not be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify proof of possession
|
// Verify proof of possession
|
||||||
valid := VerifyProofOfPossession(pk, sig, msg)
|
valid := VerifyProofOfPossession(pk, sig, msg)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("PoP should be valid")
|
t.Fatal("PoP should be valid")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with wrong message
|
// Test with wrong message
|
||||||
wrongMsg := []byte("wrong")
|
wrongMsg := []byte("wrong")
|
||||||
valid = VerifyProofOfPossession(pk, sig, wrongMsg)
|
valid = VerifyProofOfPossession(pk, sig, wrongMsg)
|
||||||
if valid {
|
if valid {
|
||||||
t.Fatal("PoP should be invalid for wrong message")
|
t.Fatal("PoP should be invalid for wrong message")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nil cases
|
// Test nil cases
|
||||||
var nilSk *SecretKey
|
var nilSk *SecretKey
|
||||||
nilSig, err := nilSk.SignProofOfPossession(msg)
|
nilSig, err := nilSk.SignProofOfPossession(msg)
|
||||||
@@ -342,7 +342,7 @@ func TestProofOfPossession(t *testing.T) {
|
|||||||
if nilSig != nil {
|
if nilSig != nil {
|
||||||
t.Fatal("Nil secret key should return nil PoP")
|
t.Fatal("Nil secret key should return nil PoP")
|
||||||
}
|
}
|
||||||
|
|
||||||
emptySk := &SecretKey{}
|
emptySk := &SecretKey{}
|
||||||
emptySig, err := emptySk.SignProofOfPossession(msg)
|
emptySig, err := emptySk.SignProofOfPossession(msg)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -361,25 +361,25 @@ func TestSignatureBytes(t *testing.T) {
|
|||||||
|
|
||||||
msg := []byte("test")
|
msg := []byte("test")
|
||||||
sig, _ := sk.Sign(msg)
|
sig, _ := sk.Sign(msg)
|
||||||
|
|
||||||
// Convert to bytes
|
// Convert to bytes
|
||||||
sigBytes := SignatureToBytes(sig)
|
sigBytes := SignatureToBytes(sig)
|
||||||
if len(sigBytes) != SignatureLen {
|
if len(sigBytes) != SignatureLen {
|
||||||
t.Fatalf("Signature should be %d bytes, got %d", SignatureLen, len(sigBytes))
|
t.Fatalf("Signature should be %d bytes, got %d", SignatureLen, len(sigBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nil signature
|
// Test nil signature
|
||||||
nilBytes := SignatureToBytes(nil)
|
nilBytes := SignatureToBytes(nil)
|
||||||
if nilBytes != nil {
|
if nilBytes != nil {
|
||||||
t.Fatal("Nil signature should return nil bytes")
|
t.Fatal("Nil signature should return nil bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Round-trip test
|
// Round-trip test
|
||||||
sig2, err := SignatureFromBytes(sigBytes)
|
sig2, err := SignatureFromBytes(sigBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to deserialize signature: %v", err)
|
t.Fatalf("Failed to deserialize signature: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sigBytes2 := SignatureToBytes(sig2)
|
sigBytes2 := SignatureToBytes(sig2)
|
||||||
if !bytes.Equal(sigBytes, sigBytes2) {
|
if !bytes.Equal(sigBytes, sigBytes2) {
|
||||||
t.Fatal("Round-trip signature bytes should match")
|
t.Fatal("Round-trip signature bytes should match")
|
||||||
@@ -393,14 +393,14 @@ func TestSignatureFromBytesErrors(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should fail with wrong size")
|
t.Fatal("Should fail with wrong size")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test all zeros (invalid signature)
|
// Test all zeros (invalid signature)
|
||||||
zeroBytes := make([]byte, SignatureLen)
|
zeroBytes := make([]byte, SignatureLen)
|
||||||
_, err = SignatureFromBytes(zeroBytes)
|
_, err = SignatureFromBytes(zeroBytes)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should fail with all zero bytes")
|
t.Fatal("Should fail with all zero bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nil
|
// Test nil
|
||||||
_, err = SignatureFromBytes(nil)
|
_, err = SignatureFromBytes(nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -414,16 +414,16 @@ func TestAggregatePublicKeysEdgeCases(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should fail with empty slice")
|
t.Fatal("Should fail with empty slice")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with nil public key in slice
|
// Test with nil public key in slice
|
||||||
sk1, _ := NewSecretKey()
|
sk1, _ := NewSecretKey()
|
||||||
pk1 := sk1.PublicKey()
|
pk1 := sk1.PublicKey()
|
||||||
|
|
||||||
_, err = AggregatePublicKeys([]*PublicKey{pk1, nil})
|
_, err = AggregatePublicKeys([]*PublicKey{pk1, nil})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should fail with nil public key in slice")
|
t.Fatal("Should fail with nil public key in slice")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with public key with nil internal
|
// Test with public key with nil internal
|
||||||
emptyPk := &PublicKey{}
|
emptyPk := &PublicKey{}
|
||||||
_, err = AggregatePublicKeys([]*PublicKey{pk1, emptyPk})
|
_, err = AggregatePublicKeys([]*PublicKey{pk1, emptyPk})
|
||||||
@@ -438,12 +438,12 @@ func TestAggregateSignaturesEdgeCases(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should fail with empty slice")
|
t.Fatal("Should fail with empty slice")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with nil signature in slice
|
// Test with nil signature in slice
|
||||||
sk1, _ := NewSecretKey()
|
sk1, _ := NewSecretKey()
|
||||||
msg := []byte("test")
|
msg := []byte("test")
|
||||||
sig1, _ := sk1.Sign(msg)
|
sig1, _ := sk1.Sign(msg)
|
||||||
|
|
||||||
_, err = AggregateSignatures([]*Signature{sig1, nil})
|
_, err = AggregateSignatures([]*Signature{sig1, nil})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should fail with nil signature in slice")
|
t.Fatal("Should fail with nil signature in slice")
|
||||||
@@ -456,9 +456,9 @@ func TestMultipleAggregation(t *testing.T) {
|
|||||||
sks := make([]*SecretKey, numKeys)
|
sks := make([]*SecretKey, numKeys)
|
||||||
pks := make([]*PublicKey, numKeys)
|
pks := make([]*PublicKey, numKeys)
|
||||||
sigs := make([]*Signature, numKeys)
|
sigs := make([]*Signature, numKeys)
|
||||||
|
|
||||||
msg := []byte("aggregate test message")
|
msg := []byte("aggregate test message")
|
||||||
|
|
||||||
for i := 0; i < numKeys; i++ {
|
for i := 0; i < numKeys; i++ {
|
||||||
sk, err := NewSecretKey()
|
sk, err := NewSecretKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -468,7 +468,7 @@ func TestMultipleAggregation(t *testing.T) {
|
|||||||
pks[i] = sk.PublicKey()
|
pks[i] = sk.PublicKey()
|
||||||
sigs[i], _ = sk.Sign(msg)
|
sigs[i], _ = sk.Sign(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggregate public keys
|
// Aggregate public keys
|
||||||
aggPk, err := AggregatePublicKeys(pks)
|
aggPk, err := AggregatePublicKeys(pks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -477,7 +477,7 @@ func TestMultipleAggregation(t *testing.T) {
|
|||||||
if aggPk == nil {
|
if aggPk == nil {
|
||||||
t.Fatal("Aggregated public key should not be nil")
|
t.Fatal("Aggregated public key should not be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggregate signatures
|
// Aggregate signatures
|
||||||
aggSig, err := AggregateSignatures(sigs)
|
aggSig, err := AggregateSignatures(sigs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -486,7 +486,7 @@ func TestMultipleAggregation(t *testing.T) {
|
|||||||
if aggSig == nil {
|
if aggSig == nil {
|
||||||
t.Fatal("Aggregated signature should not be nil")
|
t.Fatal("Aggregated signature should not be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify aggregated signature
|
// Verify aggregated signature
|
||||||
valid := Verify(aggPk, aggSig, msg)
|
valid := Verify(aggPk, aggSig, msg)
|
||||||
if !valid {
|
if !valid {
|
||||||
@@ -506,7 +506,7 @@ func BenchmarkKeyGenerationExtended(b *testing.B) {
|
|||||||
func BenchmarkSignExtended(b *testing.B) {
|
func BenchmarkSignExtended(b *testing.B) {
|
||||||
sk, _ := NewSecretKey()
|
sk, _ := NewSecretKey()
|
||||||
msg := []byte("benchmark message")
|
msg := []byte("benchmark message")
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = sk.Sign(msg)
|
_, _ = sk.Sign(msg)
|
||||||
@@ -528,12 +528,12 @@ func BenchmarkVerifyExtended(b *testing.B) {
|
|||||||
func BenchmarkAggregatePublicKeysExtended(b *testing.B) {
|
func BenchmarkAggregatePublicKeysExtended(b *testing.B) {
|
||||||
numKeys := 10
|
numKeys := 10
|
||||||
pks := make([]*PublicKey, numKeys)
|
pks := make([]*PublicKey, numKeys)
|
||||||
|
|
||||||
for i := 0; i < numKeys; i++ {
|
for i := 0; i < numKeys; i++ {
|
||||||
sk, _ := NewSecretKey()
|
sk, _ := NewSecretKey()
|
||||||
pks[i] = sk.PublicKey()
|
pks[i] = sk.PublicKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = AggregatePublicKeys(pks)
|
_, _ = AggregatePublicKeys(pks)
|
||||||
@@ -544,14 +544,14 @@ func BenchmarkAggregateSignaturesExtended(b *testing.B) {
|
|||||||
numSigs := 10
|
numSigs := 10
|
||||||
sigs := make([]*Signature, numSigs)
|
sigs := make([]*Signature, numSigs)
|
||||||
msg := []byte("benchmark")
|
msg := []byte("benchmark")
|
||||||
|
|
||||||
for i := 0; i < numSigs; i++ {
|
for i := 0; i < numSigs; i++ {
|
||||||
sk, _ := NewSecretKey()
|
sk, _ := NewSecretKey()
|
||||||
sigs[i], _ = sk.Sign(msg)
|
sigs[i], _ = sk.Sign(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = AggregateSignatures(sigs)
|
_, _ = AggregateSignatures(sigs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -574,4 +574,4 @@ func BenchmarkAggregateSignatures(b *testing.B) {
|
|||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = AggregateSignatures(sigs)
|
_, _ = AggregateSignatures(sigs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/luxfi/node/cache"
|
|
||||||
"github.com/luxfi/ids"
|
"github.com/luxfi/ids"
|
||||||
|
"github.com/luxfi/node/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
const IntSize = ids.IDLen + 8
|
const IntSize = ids.IDLen + 8
|
||||||
|
|||||||
Vendored
+1
-1
@@ -6,8 +6,8 @@ package lru
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/luxfi/node/cache/cachetest"
|
|
||||||
"github.com/luxfi/ids"
|
"github.com/luxfi/ids"
|
||||||
|
"github.com/luxfi/node/cache/cachetest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCache(t *testing.T) {
|
func TestCache(t *testing.T) {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/luxfi/node/cache/cachetest"
|
|
||||||
"github.com/luxfi/ids"
|
"github.com/luxfi/ids"
|
||||||
|
"github.com/luxfi/node/cache/cachetest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSizedCache(t *testing.T) {
|
func TestSizedCache(t *testing.T) {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -8,9 +8,9 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/luxfi/ids"
|
||||||
"github.com/luxfi/node/cache"
|
"github.com/luxfi/node/cache"
|
||||||
"github.com/luxfi/node/cache/cachetest"
|
"github.com/luxfi/node/cache/cachetest"
|
||||||
"github.com/luxfi/ids"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLRU(t *testing.T) {
|
func TestLRU(t *testing.T) {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -8,9 +8,9 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/luxfi/ids"
|
||||||
"github.com/luxfi/node/cache"
|
"github.com/luxfi/node/cache"
|
||||||
"github.com/luxfi/node/cache/cachetest"
|
"github.com/luxfi/node/cache/cachetest"
|
||||||
"github.com/luxfi/ids"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSizedLRU(t *testing.T) {
|
func TestSizedLRU(t *testing.T) {
|
||||||
|
|||||||
Vendored
-1
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/luxfi/node/cache"
|
"github.com/luxfi/node/cache"
|
||||||
"github.com/luxfi/node/cache/cachetest"
|
"github.com/luxfi/node/cache/cachetest"
|
||||||
"github.com/luxfi/node/cache/lru"
|
"github.com/luxfi/node/cache/lru"
|
||||||
"github.com/luxfi/ids"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInterface(t *testing.T) {
|
func TestInterface(t *testing.T) {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -69,4 +69,4 @@ func newMetrics(
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -24,7 +24,7 @@ type MLDSACert struct {
|
|||||||
KeyUsage x509.KeyUsage
|
KeyUsage x509.KeyUsage
|
||||||
ExtKeyUsage []x509.ExtKeyUsage
|
ExtKeyUsage []x509.ExtKeyUsage
|
||||||
IsCA bool
|
IsCA bool
|
||||||
|
|
||||||
// Custom extensions for QZMQ
|
// Custom extensions for QZMQ
|
||||||
NodeID string
|
NodeID string
|
||||||
Capabilities []string
|
Capabilities []string
|
||||||
@@ -54,34 +54,34 @@ func (cp *CertPool) VerifyChain(chain []*MLDSACert, now time.Time) error {
|
|||||||
if len(chain) == 0 {
|
if len(chain) == 0 {
|
||||||
return errors.New("empty certificate chain")
|
return errors.New("empty certificate chain")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify leaf certificate
|
// Verify leaf certificate
|
||||||
leaf := chain[0]
|
leaf := chain[0]
|
||||||
if now.Before(leaf.NotBefore) || now.After(leaf.NotAfter) {
|
if now.Before(leaf.NotBefore) || now.After(leaf.NotAfter) {
|
||||||
return errors.New("certificate expired or not yet valid")
|
return errors.New("certificate expired or not yet valid")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify chain
|
// Verify chain
|
||||||
for i := 0; i < len(chain)-1; i++ {
|
for i := 0; i < len(chain)-1; i++ {
|
||||||
cert := chain[i]
|
cert := chain[i]
|
||||||
issuer := chain[i+1]
|
issuer := chain[i+1]
|
||||||
|
|
||||||
if err := verifyCertSignature(cert, issuer); err != nil {
|
if err := verifyCertSignature(cert, issuer); err != nil {
|
||||||
return fmt.Errorf("invalid signature at position %d: %w", i, err)
|
return fmt.Errorf("invalid signature at position %d: %w", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !issuer.IsCA {
|
if !issuer.IsCA {
|
||||||
return fmt.Errorf("issuer at position %d is not a CA", i+1)
|
return fmt.Errorf("issuer at position %d is not a CA", i+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify root is trusted
|
// Verify root is trusted
|
||||||
root := chain[len(chain)-1]
|
root := chain[len(chain)-1]
|
||||||
spkiHash := hashSPKI(root.PublicKey.Bytes())
|
spkiHash := hashSPKI(root.PublicKey.Bytes())
|
||||||
if _, ok := cp.certs[spkiHash]; !ok {
|
if _, ok := cp.certs[spkiHash]; !ok {
|
||||||
return errors.New("root certificate not trusted")
|
return errors.New("root certificate not trusted")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,21 +182,21 @@ func (cb *CertBuilder) SetCA(isCA bool) *CertBuilder {
|
|||||||
func (cb *CertBuilder) Build(publicKey sign.PublicKey, issuerKey sign.PrivateKey) (*MLDSACert, error) {
|
func (cb *CertBuilder) Build(publicKey sign.PublicKey, issuerKey sign.PrivateKey) (*MLDSACert, error) {
|
||||||
cert := *cb.template
|
cert := *cb.template
|
||||||
cert.PublicKey = publicKey
|
cert.PublicKey = publicKey
|
||||||
|
|
||||||
// Generate serial number
|
// Generate serial number
|
||||||
cert.SerialNumber = generateSerialNumber()
|
cert.SerialNumber = generateSerialNumber()
|
||||||
|
|
||||||
// Self-signed if no issuer provided
|
// Self-signed if no issuer provided
|
||||||
if issuerKey == nil {
|
if issuerKey == nil {
|
||||||
cert.Issuer = cert.Subject
|
cert.Issuer = cert.Subject
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode to DER
|
// Encode to DER
|
||||||
certBytes, err := encodeCertificate(&cert)
|
certBytes, err := encodeCertificate(&cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign the certificate
|
// Sign the certificate
|
||||||
if issuerKey != nil {
|
if issuerKey != nil {
|
||||||
signature, err := cb.signer.Sign(issuerKey, certBytes)
|
signature, err := cb.signer.Sign(issuerKey, certBytes)
|
||||||
@@ -208,7 +208,7 @@ func (cb *CertBuilder) Build(publicKey sign.PublicKey, issuerKey sign.PrivateKey
|
|||||||
} else {
|
} else {
|
||||||
cert.Raw = certBytes
|
cert.Raw = certBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
return &cert, nil
|
return &cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ func encodeCertificate(cert *MLDSACert) ([]byte, error) {
|
|||||||
encoded = append(encoded, cert.PublicKey.Bytes()...)
|
encoded = append(encoded, cert.PublicKey.Bytes()...)
|
||||||
encoded = append(encoded, []byte(cert.NodeID)...)
|
encoded = append(encoded, []byte(cert.NodeID)...)
|
||||||
encoded = append(encoded, []byte(cert.Role)...)
|
encoded = append(encoded, []byte(cert.Role)...)
|
||||||
|
|
||||||
return encoded, nil
|
return encoded, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,22 +234,22 @@ func generateSerialNumber() []byte {
|
|||||||
func ParseMLDSACert(der []byte) (*MLDSACert, error) {
|
func ParseMLDSACert(der []byte) (*MLDSACert, error) {
|
||||||
// Placeholder parser
|
// Placeholder parser
|
||||||
// In production, would parse actual DER-encoded certificate
|
// In production, would parse actual DER-encoded certificate
|
||||||
|
|
||||||
cert := &MLDSACert{
|
cert := &MLDSACert{
|
||||||
Raw: der,
|
Raw: der,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse basic fields (placeholder)
|
// Parse basic fields (placeholder)
|
||||||
if len(der) < 100 {
|
if len(der) < 100 {
|
||||||
return nil, errors.New("certificate too short")
|
return nil, errors.New("certificate too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OID definitions for ML-DSA algorithms
|
// OID definitions for ML-DSA algorithms
|
||||||
var (
|
var (
|
||||||
OIDMLDSA44 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 2, 267, 7, 6, 5} // ML-DSA-44
|
OIDMLDSA44 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 2, 267, 7, 6, 5} // ML-DSA-44
|
||||||
OIDMLDSA65 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 2, 267, 7, 8, 7} // ML-DSA-65
|
OIDMLDSA65 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 2, 267, 7, 8, 7} // ML-DSA-65
|
||||||
OIDMLDSA87 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 2, 267, 7, 10, 8} // ML-DSA-87
|
OIDMLDSA87 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 2, 267, 7, 10, 8} // ML-DSA-87
|
||||||
)
|
)
|
||||||
|
|||||||
+90
-90
@@ -28,64 +28,64 @@ type Signature struct {
|
|||||||
|
|
||||||
// Config contains CGGMP21 protocol configuration
|
// Config contains CGGMP21 protocol configuration
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Threshold int // t: Threshold (t+1 parties needed to sign)
|
Threshold int // t: Threshold (t+1 parties needed to sign)
|
||||||
TotalParties int // n: Total number of parties
|
TotalParties int // n: Total number of parties
|
||||||
Curve elliptic.Curve
|
Curve elliptic.Curve
|
||||||
SessionTimeout int64 // Timeout for protocol rounds
|
SessionTimeout int64 // Timeout for protocol rounds
|
||||||
}
|
}
|
||||||
|
|
||||||
// Party represents a participant in the CGGMP21 protocol
|
// Party represents a participant in the CGGMP21 protocol
|
||||||
type Party struct {
|
type Party struct {
|
||||||
ID ids.NodeID
|
ID ids.NodeID
|
||||||
Index int
|
Index int
|
||||||
Config *Config
|
Config *Config
|
||||||
|
|
||||||
// Key material
|
// Key material
|
||||||
Xi *big.Int // Secret key share
|
Xi *big.Int // Secret key share
|
||||||
PublicKey *ecdsa.PublicKey // Group public key
|
PublicKey *ecdsa.PublicKey // Group public key
|
||||||
PublicShares map[int]*big.Int // Public key shares from all parties
|
PublicShares map[int]*big.Int // Public key shares from all parties
|
||||||
|
|
||||||
// Paillier keys for ZK proofs
|
// Paillier keys for ZK proofs
|
||||||
PaillierSK *PaillierPrivateKey
|
PaillierSK *PaillierPrivateKey
|
||||||
PaillierPKs map[int]*PaillierPublicKey
|
PaillierPKs map[int]*PaillierPublicKey
|
||||||
|
|
||||||
// Session state
|
// Session state
|
||||||
sessions map[string]*SigningSession
|
sessions map[string]*SigningSession
|
||||||
|
|
||||||
log log.Logger
|
log log.Logger
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// SigningSession represents an active signing session
|
// SigningSession represents an active signing session
|
||||||
type SigningSession struct {
|
type SigningSession struct {
|
||||||
SessionID string
|
SessionID string
|
||||||
Message []byte
|
Message []byte
|
||||||
MessageHash *big.Int
|
MessageHash *big.Int
|
||||||
|
|
||||||
// Round 1: Commitment
|
// Round 1: Commitment
|
||||||
Ki *big.Int // k_i (nonce share)
|
Ki *big.Int // k_i (nonce share)
|
||||||
Gammai *big.Int // gamma_i (random mask)
|
Gammai *big.Int // gamma_i (random mask)
|
||||||
CommitmentSent bool
|
CommitmentSent bool
|
||||||
Commitments map[int][]byte // Received commitments
|
Commitments map[int][]byte // Received commitments
|
||||||
|
|
||||||
// Round 2: Reveal
|
// Round 2: Reveal
|
||||||
RevealsSent bool
|
RevealsSent bool
|
||||||
GammaShares map[int]*big.Int // gamma_j values
|
GammaShares map[int]*big.Int // gamma_j values
|
||||||
BigGammaShares map[int]*ECPoint // [gamma_j]G points
|
BigGammaShares map[int]*ECPoint // [gamma_j]G points
|
||||||
|
|
||||||
// Round 3: Multiplication
|
// Round 3: Multiplication
|
||||||
DeltaShare *big.Int // delta_i = k_i * gamma_i
|
DeltaShare *big.Int // delta_i = k_i * gamma_i
|
||||||
ChiShare *big.Int // chi_i = x_i * k_i
|
ChiShare *big.Int // chi_i = x_i * k_i
|
||||||
BigDeltaShares map[int]*ECPoint // [delta_j]G points
|
BigDeltaShares map[int]*ECPoint // [delta_j]G points
|
||||||
|
|
||||||
// Round 4: Opening
|
// Round 4: Opening
|
||||||
Deltas map[int]*big.Int // delta_j values
|
Deltas map[int]*big.Int // delta_j values
|
||||||
BigRx *ECPoint // R_x point
|
BigRx *ECPoint // R_x point
|
||||||
|
|
||||||
// Final signature
|
// Final signature
|
||||||
R *big.Int
|
R *big.Int
|
||||||
S *big.Int
|
S *big.Int
|
||||||
|
|
||||||
// Abort handling
|
// Abort handling
|
||||||
AbortingParties []int
|
AbortingParties []int
|
||||||
}
|
}
|
||||||
@@ -100,13 +100,13 @@ func NewParty(id ids.NodeID, index int, config *Config, log log.Logger) (*Party,
|
|||||||
if index < 0 || index >= config.TotalParties {
|
if index < 0 || index >= config.TotalParties {
|
||||||
return nil, errors.New("invalid party index")
|
return nil, errors.New("invalid party index")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate Paillier keypair for ZK proofs
|
// Generate Paillier keypair for ZK proofs
|
||||||
paillierSK, paillierPK, err := GeneratePaillierKeyPair(2048)
|
paillierSK, paillierPK, err := GeneratePaillierKeyPair(2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to generate Paillier keys: %w", err)
|
return nil, fmt.Errorf("failed to generate Paillier keys: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Party{
|
return &Party{
|
||||||
ID: id,
|
ID: id,
|
||||||
Index: index,
|
Index: index,
|
||||||
@@ -123,30 +123,30 @@ func NewParty(id ids.NodeID, index int, config *Config, log log.Logger) (*Party,
|
|||||||
func (p *Party) KeyGen(parties []int) error {
|
func (p *Party) KeyGen(parties []int) error {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
// Generate secret share
|
// Generate secret share
|
||||||
xi, err := rand.Int(rand.Reader, p.Config.Curve.Params().N)
|
xi, err := rand.Int(rand.Reader, p.Config.Curve.Params().N)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.Xi = xi
|
p.Xi = xi
|
||||||
|
|
||||||
// Compute public key share [x_i]G
|
// Compute public key share [x_i]G
|
||||||
Xi := ScalarBaseMult(p.Config.Curve, xi)
|
Xi := ScalarBaseMult(p.Config.Curve, xi)
|
||||||
p.PublicShares[p.Index] = Xi.X
|
p.PublicShares[p.Index] = Xi.X
|
||||||
|
|
||||||
// In practice, this would involve communication rounds
|
// In practice, this would involve communication rounds
|
||||||
// For now, we simulate having received all shares
|
// For now, we simulate having received all shares
|
||||||
|
|
||||||
// Compute group public key (would be sum of all shares)
|
// Compute group public key (would be sum of all shares)
|
||||||
// Y = sum([x_i]G) for all i
|
// Y = sum([x_i]G) for all i
|
||||||
|
|
||||||
p.log.Info("Key generation completed",
|
p.log.Info("Key generation completed",
|
||||||
log.Stringer("partyID", p.ID),
|
log.Stringer("partyID", p.ID),
|
||||||
log.Int("index", p.Index),
|
log.Int("index", p.Index),
|
||||||
log.Int("threshold", p.Config.Threshold),
|
log.Int("threshold", p.Config.Threshold),
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,15 +154,15 @@ func (p *Party) KeyGen(parties []int) error {
|
|||||||
func (p *Party) InitiateSign(sessionID string, message []byte) (*SigningSession, error) {
|
func (p *Party) InitiateSign(sessionID string, message []byte) (*SigningSession, error) {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
if _, exists := p.sessions[sessionID]; exists {
|
if _, exists := p.sessions[sessionID]; exists {
|
||||||
return nil, errors.New("session already exists")
|
return nil, errors.New("session already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash the message
|
// Hash the message
|
||||||
h := sha256.Sum256(message)
|
h := sha256.Sum256(message)
|
||||||
messageHash := new(big.Int).SetBytes(h[:])
|
messageHash := new(big.Int).SetBytes(h[:])
|
||||||
|
|
||||||
session := &SigningSession{
|
session := &SigningSession{
|
||||||
SessionID: sessionID,
|
SessionID: sessionID,
|
||||||
Message: message,
|
Message: message,
|
||||||
@@ -173,9 +173,9 @@ func (p *Party) InitiateSign(sessionID string, message []byte) (*SigningSession,
|
|||||||
BigDeltaShares: make(map[int]*ECPoint),
|
BigDeltaShares: make(map[int]*ECPoint),
|
||||||
Deltas: make(map[int]*big.Int),
|
Deltas: make(map[int]*big.Int),
|
||||||
}
|
}
|
||||||
|
|
||||||
p.sessions[sessionID] = session
|
p.sessions[sessionID] = session
|
||||||
|
|
||||||
return session, nil
|
return session, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,16 +183,16 @@ func (p *Party) InitiateSign(sessionID string, message []byte) (*SigningSession,
|
|||||||
func (p *Party) Round1_Commitment(sessionID string) ([]byte, error) {
|
func (p *Party) Round1_Commitment(sessionID string) ([]byte, error) {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
session, exists := p.sessions[sessionID]
|
session, exists := p.sessions[sessionID]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("session not found")
|
return nil, errors.New("session not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if session.CommitmentSent {
|
if session.CommitmentSent {
|
||||||
return nil, errors.New("commitment already sent")
|
return nil, errors.New("commitment already sent")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate k_i and gamma_i
|
// Generate k_i and gamma_i
|
||||||
ki, err := rand.Int(rand.Reader, p.Config.Curve.Params().N)
|
ki, err := rand.Int(rand.Reader, p.Config.Curve.Params().N)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -202,16 +202,16 @@ func (p *Party) Round1_Commitment(sessionID string) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
session.Ki = ki
|
session.Ki = ki
|
||||||
session.Gammai = gammai
|
session.Gammai = gammai
|
||||||
|
|
||||||
// Compute commitment = H(i, [gamma_i]G)
|
// Compute commitment = H(i, [gamma_i]G)
|
||||||
bigGammai := ScalarBaseMult(p.Config.Curve, gammai)
|
bigGammai := ScalarBaseMult(p.Config.Curve, gammai)
|
||||||
commitment := p.computeCommitment(p.Index, bigGammai)
|
commitment := p.computeCommitment(p.Index, bigGammai)
|
||||||
|
|
||||||
session.CommitmentSent = true
|
session.CommitmentSent = true
|
||||||
|
|
||||||
return commitment, nil
|
return commitment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,34 +219,34 @@ func (p *Party) Round1_Commitment(sessionID string) ([]byte, error) {
|
|||||||
func (p *Party) Round2_Reveal(sessionID string, commitments map[int][]byte) (*Round2Message, error) {
|
func (p *Party) Round2_Reveal(sessionID string, commitments map[int][]byte) (*Round2Message, error) {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
session, exists := p.sessions[sessionID]
|
session, exists := p.sessions[sessionID]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("session not found")
|
return nil, errors.New("session not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if session.RevealsSent {
|
if session.RevealsSent {
|
||||||
return nil, errors.New("reveals already sent")
|
return nil, errors.New("reveals already sent")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store commitments
|
// Store commitments
|
||||||
for idx, comm := range commitments {
|
for idx, comm := range commitments {
|
||||||
if idx != p.Index {
|
if idx != p.Index {
|
||||||
session.Commitments[idx] = comm
|
session.Commitments[idx] = comm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create reveal message
|
// Create reveal message
|
||||||
bigGammai := ScalarBaseMult(p.Config.Curve, session.Gammai)
|
bigGammai := ScalarBaseMult(p.Config.Curve, session.Gammai)
|
||||||
|
|
||||||
msg := &Round2Message{
|
msg := &Round2Message{
|
||||||
FromIndex: p.Index,
|
FromIndex: p.Index,
|
||||||
BigGammaI: bigGammai,
|
BigGammaI: bigGammai,
|
||||||
// In production, include ZK proofs here
|
// In production, include ZK proofs here
|
||||||
}
|
}
|
||||||
|
|
||||||
session.RevealsSent = true
|
session.RevealsSent = true
|
||||||
|
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,45 +254,45 @@ func (p *Party) Round2_Reveal(sessionID string, commitments map[int][]byte) (*Ro
|
|||||||
func (p *Party) Round3_Multiply(sessionID string, reveals map[int]*Round2Message) (*Round3Message, error) {
|
func (p *Party) Round3_Multiply(sessionID string, reveals map[int]*Round2Message) (*Round3Message, error) {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
session, exists := p.sessions[sessionID]
|
session, exists := p.sessions[sessionID]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("session not found")
|
return nil, errors.New("session not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify commitments match reveals
|
// Verify commitments match reveals
|
||||||
for idx, reveal := range reveals {
|
for idx, reveal := range reveals {
|
||||||
if idx == p.Index {
|
if idx == p.Index {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedComm := p.computeCommitment(idx, reveal.BigGammaI)
|
expectedComm := p.computeCommitment(idx, reveal.BigGammaI)
|
||||||
if !bytesEqual(expectedComm, session.Commitments[idx]) {
|
if !bytesEqual(expectedComm, session.Commitments[idx]) {
|
||||||
return nil, fmt.Errorf("commitment verification failed for party %d", idx)
|
return nil, fmt.Errorf("commitment verification failed for party %d", idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
session.BigGammaShares[idx] = reveal.BigGammaI
|
session.BigGammaShares[idx] = reveal.BigGammaI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute delta_i = k_i * gamma_i
|
// Compute delta_i = k_i * gamma_i
|
||||||
deltai := new(big.Int).Mul(session.Ki, session.Gammai)
|
deltai := new(big.Int).Mul(session.Ki, session.Gammai)
|
||||||
deltai.Mod(deltai, p.Config.Curve.Params().N)
|
deltai.Mod(deltai, p.Config.Curve.Params().N)
|
||||||
session.DeltaShare = deltai
|
session.DeltaShare = deltai
|
||||||
|
|
||||||
// Compute chi_i = x_i * k_i
|
// Compute chi_i = x_i * k_i
|
||||||
chii := new(big.Int).Mul(p.Xi, session.Ki)
|
chii := new(big.Int).Mul(p.Xi, session.Ki)
|
||||||
chii.Mod(chii, p.Config.Curve.Params().N)
|
chii.Mod(chii, p.Config.Curve.Params().N)
|
||||||
session.ChiShare = chii
|
session.ChiShare = chii
|
||||||
|
|
||||||
// Compute [delta_i]G
|
// Compute [delta_i]G
|
||||||
bigDeltai := ScalarBaseMult(p.Config.Curve, deltai)
|
bigDeltai := ScalarBaseMult(p.Config.Curve, deltai)
|
||||||
|
|
||||||
msg := &Round3Message{
|
msg := &Round3Message{
|
||||||
FromIndex: p.Index,
|
FromIndex: p.Index,
|
||||||
BigDeltaI: bigDeltai,
|
BigDeltaI: bigDeltai,
|
||||||
// In production, include encrypted shares and ZK proofs
|
// In production, include encrypted shares and ZK proofs
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,27 +300,27 @@ func (p *Party) Round3_Multiply(sessionID string, reveals map[int]*Round2Message
|
|||||||
func (p *Party) Round4_Open(sessionID string, round3msgs map[int]*Round3Message) (*Round4Message, error) {
|
func (p *Party) Round4_Open(sessionID string, round3msgs map[int]*Round3Message) (*Round4Message, error) {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
session, exists := p.sessions[sessionID]
|
session, exists := p.sessions[sessionID]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("session not found")
|
return nil, errors.New("session not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store [delta_j]G values
|
// Store [delta_j]G values
|
||||||
for idx, msg := range round3msgs {
|
for idx, msg := range round3msgs {
|
||||||
if idx != p.Index {
|
if idx != p.Index {
|
||||||
session.BigDeltaShares[idx] = msg.BigDeltaI
|
session.BigDeltaShares[idx] = msg.BigDeltaI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In production, perform consistency checks and identify aborts
|
// In production, perform consistency checks and identify aborts
|
||||||
|
|
||||||
msg := &Round4Message{
|
msg := &Round4Message{
|
||||||
FromIndex: p.Index,
|
FromIndex: p.Index,
|
||||||
DeltaI: session.DeltaShare,
|
DeltaI: session.DeltaShare,
|
||||||
// Include proofs of correct multiplication
|
// Include proofs of correct multiplication
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,33 +328,33 @@ func (p *Party) Round4_Open(sessionID string, round3msgs map[int]*Round3Message)
|
|||||||
func (p *Party) Finalize(sessionID string, round4msgs map[int]*Round4Message) (*Signature, error) {
|
func (p *Party) Finalize(sessionID string, round4msgs map[int]*Round4Message) (*Signature, error) {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
session, exists := p.sessions[sessionID]
|
session, exists := p.sessions[sessionID]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("session not found")
|
return nil, errors.New("session not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store delta values
|
// Store delta values
|
||||||
for idx, msg := range round4msgs {
|
for idx, msg := range round4msgs {
|
||||||
session.Deltas[idx] = msg.DeltaI
|
session.Deltas[idx] = msg.DeltaI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute R = product([k_j * gamma_j]G) for all j
|
// Compute R = product([k_j * gamma_j]G) for all j
|
||||||
// In practice, this involves point multiplication
|
// In practice, this involves point multiplication
|
||||||
|
|
||||||
// For now, simulate signature computation
|
// For now, simulate signature computation
|
||||||
r := new(big.Int).SetBytes([]byte("simulated_r_value"))
|
r := new(big.Int).SetBytes([]byte("simulated_r_value"))
|
||||||
|
|
||||||
// Compute s_i (partial signature)
|
// Compute s_i (partial signature)
|
||||||
// s_i = m * chi_i + r * sigma_i
|
// s_i = m * chi_i + r * sigma_i
|
||||||
// where sigma_i is the lagrange-adjusted share
|
// where sigma_i is the lagrange-adjusted share
|
||||||
|
|
||||||
s := new(big.Int).SetBytes([]byte("simulated_s_value"))
|
s := new(big.Int).SetBytes([]byte("simulated_s_value"))
|
||||||
|
|
||||||
// Store final signature
|
// Store final signature
|
||||||
session.R = r
|
session.R = r
|
||||||
session.S = s
|
session.S = s
|
||||||
|
|
||||||
return &Signature{
|
return &Signature{
|
||||||
R: r,
|
R: r,
|
||||||
S: s,
|
S: s,
|
||||||
@@ -419,4 +419,4 @@ type IdentifiableAbort struct {
|
|||||||
func VerifySignature(pubKey *ecdsa.PublicKey, message []byte, sig *Signature) bool {
|
func VerifySignature(pubKey *ecdsa.PublicKey, message []byte, sig *Signature) bool {
|
||||||
h := sha256.Sum256(message)
|
h := sha256.Sum256(message)
|
||||||
return ecdsa.Verify(pubKey, h[:], sig.R, sig.S)
|
return ecdsa.Verify(pubKey, h[:], sig.R, sig.S)
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-30
@@ -11,9 +11,9 @@ import (
|
|||||||
|
|
||||||
// PaillierPublicKey represents a Paillier public key
|
// PaillierPublicKey represents a Paillier public key
|
||||||
type PaillierPublicKey struct {
|
type PaillierPublicKey struct {
|
||||||
N *big.Int // n = p*q
|
N *big.Int // n = p*q
|
||||||
NSq *big.Int // n^2
|
NSq *big.Int // n^2
|
||||||
G *big.Int // generator (typically n+1)
|
G *big.Int // generator (typically n+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PaillierPrivateKey represents a Paillier private key
|
// PaillierPrivateKey represents a Paillier private key
|
||||||
@@ -32,43 +32,43 @@ func GeneratePaillierKeyPair(bits int) (*PaillierPrivateKey, *PaillierPublicKey,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
q, err := rand.Prime(rand.Reader, bits/2)
|
q, err := rand.Prime(rand.Reader, bits/2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute n = p*q
|
// Compute n = p*q
|
||||||
n := new(big.Int).Mul(p, q)
|
n := new(big.Int).Mul(p, q)
|
||||||
nSq := new(big.Int).Mul(n, n)
|
nSq := new(big.Int).Mul(n, n)
|
||||||
|
|
||||||
// Compute lambda = lcm(p-1, q-1)
|
// Compute lambda = lcm(p-1, q-1)
|
||||||
pMinus1 := new(big.Int).Sub(p, big.NewInt(1))
|
pMinus1 := new(big.Int).Sub(p, big.NewInt(1))
|
||||||
qMinus1 := new(big.Int).Sub(q, big.NewInt(1))
|
qMinus1 := new(big.Int).Sub(q, big.NewInt(1))
|
||||||
|
|
||||||
gcd := new(big.Int).GCD(nil, nil, pMinus1, qMinus1)
|
gcd := new(big.Int).GCD(nil, nil, pMinus1, qMinus1)
|
||||||
lambda := new(big.Int).Mul(pMinus1, qMinus1)
|
lambda := new(big.Int).Mul(pMinus1, qMinus1)
|
||||||
lambda.Div(lambda, gcd)
|
lambda.Div(lambda, gcd)
|
||||||
|
|
||||||
// Set g = n+1 (standard choice)
|
// Set g = n+1 (standard choice)
|
||||||
g := new(big.Int).Add(n, big.NewInt(1))
|
g := new(big.Int).Add(n, big.NewInt(1))
|
||||||
|
|
||||||
// Compute mu = (L(g^lambda mod n^2))^(-1) mod n
|
// Compute mu = (L(g^lambda mod n^2))^(-1) mod n
|
||||||
// where L(x) = (x-1)/n
|
// where L(x) = (x-1)/n
|
||||||
gLambda := new(big.Int).Exp(g, lambda, nSq)
|
gLambda := new(big.Int).Exp(g, lambda, nSq)
|
||||||
l := L(gLambda, n)
|
l := L(gLambda, n)
|
||||||
mu := new(big.Int).ModInverse(l, n)
|
mu := new(big.Int).ModInverse(l, n)
|
||||||
|
|
||||||
if mu == nil {
|
if mu == nil {
|
||||||
return nil, nil, errors.New("failed to compute modular inverse")
|
return nil, nil, errors.New("failed to compute modular inverse")
|
||||||
}
|
}
|
||||||
|
|
||||||
pubKey := &PaillierPublicKey{
|
pubKey := &PaillierPublicKey{
|
||||||
N: n,
|
N: n,
|
||||||
NSq: nSq,
|
NSq: nSq,
|
||||||
G: g,
|
G: g,
|
||||||
}
|
}
|
||||||
|
|
||||||
privKey := &PaillierPrivateKey{
|
privKey := &PaillierPrivateKey{
|
||||||
PublicKey: pubKey,
|
PublicKey: pubKey,
|
||||||
Lambda: lambda,
|
Lambda: lambda,
|
||||||
@@ -76,7 +76,7 @@ func GeneratePaillierKeyPair(bits int) (*PaillierPrivateKey, *PaillierPublicKey,
|
|||||||
P: p,
|
P: p,
|
||||||
Q: q,
|
Q: q,
|
||||||
}
|
}
|
||||||
|
|
||||||
return privKey, pubKey, nil
|
return privKey, pubKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func (pub *PaillierPublicKey) Encrypt(plaintext *big.Int) (*big.Int, error) {
|
|||||||
if plaintext.Cmp(pub.N) >= 0 || plaintext.Sign() < 0 {
|
if plaintext.Cmp(pub.N) >= 0 || plaintext.Sign() < 0 {
|
||||||
return nil, errors.New("plaintext out of range")
|
return nil, errors.New("plaintext out of range")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate random r where gcd(r, n) = 1
|
// Generate random r where gcd(r, n) = 1
|
||||||
var r *big.Int
|
var r *big.Int
|
||||||
for {
|
for {
|
||||||
@@ -95,14 +95,14 @@ func (pub *PaillierPublicKey) Encrypt(plaintext *big.Int) (*big.Int, error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute ciphertext = g^m * r^n mod n^2
|
// Compute ciphertext = g^m * r^n mod n^2
|
||||||
gm := new(big.Int).Exp(pub.G, plaintext, pub.NSq)
|
gm := new(big.Int).Exp(pub.G, plaintext, pub.NSq)
|
||||||
rn := new(big.Int).Exp(r, pub.N, pub.NSq)
|
rn := new(big.Int).Exp(r, pub.N, pub.NSq)
|
||||||
|
|
||||||
ciphertext := new(big.Int).Mul(gm, rn)
|
ciphertext := new(big.Int).Mul(gm, rn)
|
||||||
ciphertext.Mod(ciphertext, pub.NSq)
|
ciphertext.Mod(ciphertext, pub.NSq)
|
||||||
|
|
||||||
return ciphertext, nil
|
return ciphertext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,14 +112,14 @@ func (priv *PaillierPrivateKey) Decrypt(ciphertext *big.Int) (*big.Int, error) {
|
|||||||
if ciphertext.Cmp(priv.PublicKey.NSq) >= 0 || ciphertext.Sign() <= 0 {
|
if ciphertext.Cmp(priv.PublicKey.NSq) >= 0 || ciphertext.Sign() <= 0 {
|
||||||
return nil, errors.New("ciphertext out of range")
|
return nil, errors.New("ciphertext out of range")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute plaintext = L(c^lambda mod n^2) * mu mod n
|
// Compute plaintext = L(c^lambda mod n^2) * mu mod n
|
||||||
cLambda := new(big.Int).Exp(ciphertext, priv.Lambda, priv.PublicKey.NSq)
|
cLambda := new(big.Int).Exp(ciphertext, priv.Lambda, priv.PublicKey.NSq)
|
||||||
l := L(cLambda, priv.PublicKey.N)
|
l := L(cLambda, priv.PublicKey.N)
|
||||||
|
|
||||||
plaintext := new(big.Int).Mul(l, priv.Mu)
|
plaintext := new(big.Int).Mul(l, priv.Mu)
|
||||||
plaintext.Mod(plaintext, priv.PublicKey.N)
|
plaintext.Mod(plaintext, priv.PublicKey.N)
|
||||||
|
|
||||||
return plaintext, nil
|
return plaintext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,26 +154,26 @@ type ZKProof struct {
|
|||||||
func ProveKnowledge(pub *PaillierPublicKey, plaintext, randomness *big.Int) (*ZKProof, error) {
|
func ProveKnowledge(pub *PaillierPublicKey, plaintext, randomness *big.Int) (*ZKProof, error) {
|
||||||
// This is a simplified Schnorr-like proof
|
// This is a simplified Schnorr-like proof
|
||||||
// In production, use proper ZK proofs as specified in CGGMP21
|
// In production, use proper ZK proofs as specified in CGGMP21
|
||||||
|
|
||||||
// Generate random values
|
// Generate random values
|
||||||
r1, _ := rand.Int(rand.Reader, pub.N)
|
r1, _ := rand.Int(rand.Reader, pub.N)
|
||||||
r2, _ := rand.Int(rand.Reader, pub.N)
|
r2, _ := rand.Int(rand.Reader, pub.N)
|
||||||
|
|
||||||
// Compute commitment e = g^r1 * r2^n mod n^2
|
// Compute commitment e = g^r1 * r2^n mod n^2
|
||||||
gr1 := new(big.Int).Exp(pub.G, r1, pub.NSq)
|
gr1 := new(big.Int).Exp(pub.G, r1, pub.NSq)
|
||||||
r2n := new(big.Int).Exp(r2, pub.N, pub.NSq)
|
r2n := new(big.Int).Exp(r2, pub.N, pub.NSq)
|
||||||
e := new(big.Int).Mul(gr1, r2n)
|
e := new(big.Int).Mul(gr1, r2n)
|
||||||
e.Mod(e, pub.NSq)
|
e.Mod(e, pub.NSq)
|
||||||
|
|
||||||
// Compute challenge (in practice, use Fiat-Shamir)
|
// Compute challenge (in practice, use Fiat-Shamir)
|
||||||
challenge := new(big.Int).SetBytes([]byte("challenge"))
|
challenge := new(big.Int).SetBytes([]byte("challenge"))
|
||||||
challenge.Mod(challenge, pub.N)
|
challenge.Mod(challenge, pub.N)
|
||||||
|
|
||||||
// Compute response z = r1 + challenge * plaintext
|
// Compute response z = r1 + challenge * plaintext
|
||||||
z := new(big.Int).Mul(challenge, plaintext)
|
z := new(big.Int).Mul(challenge, plaintext)
|
||||||
z.Add(z, r1)
|
z.Add(z, r1)
|
||||||
z.Mod(z, pub.N)
|
z.Mod(z, pub.N)
|
||||||
|
|
||||||
return &ZKProof{
|
return &ZKProof{
|
||||||
E: e,
|
E: e,
|
||||||
Z: z,
|
Z: z,
|
||||||
@@ -185,17 +185,17 @@ func ProveKnowledge(pub *PaillierPublicKey, plaintext, randomness *big.Int) (*ZK
|
|||||||
func VerifyKnowledge(proof *ZKProof, ciphertext *big.Int) bool {
|
func VerifyKnowledge(proof *ZKProof, ciphertext *big.Int) bool {
|
||||||
// Simplified verification
|
// Simplified verification
|
||||||
// In production, implement full verification as per CGGMP21
|
// In production, implement full verification as per CGGMP21
|
||||||
|
|
||||||
// Recompute challenge
|
// Recompute challenge
|
||||||
challenge := new(big.Int).SetBytes([]byte("challenge"))
|
challenge := new(big.Int).SetBytes([]byte("challenge"))
|
||||||
challenge.Mod(challenge, proof.Pub.N)
|
challenge.Mod(challenge, proof.Pub.N)
|
||||||
|
|
||||||
// Verify: g^z = e * c^challenge mod n^2
|
// Verify: g^z = e * c^challenge mod n^2
|
||||||
gz := new(big.Int).Exp(proof.Pub.G, proof.Z, proof.Pub.NSq)
|
gz := new(big.Int).Exp(proof.Pub.G, proof.Z, proof.Pub.NSq)
|
||||||
|
|
||||||
cc := new(big.Int).Exp(ciphertext, challenge, proof.Pub.NSq)
|
cc := new(big.Int).Exp(ciphertext, challenge, proof.Pub.NSq)
|
||||||
ec := new(big.Int).Mul(proof.E, cc)
|
ec := new(big.Int).Mul(proof.E, cc)
|
||||||
ec.Mod(ec, proof.Pub.NSq)
|
ec.Mod(ec, proof.Pub.NSq)
|
||||||
|
|
||||||
return gz.Cmp(ec) == 0
|
return gz.Cmp(ec) == 0
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -33,14 +33,14 @@ func DeriveKey(seed []byte, label string, outputLen int) []byte {
|
|||||||
h.Write(seed)
|
h.Write(seed)
|
||||||
h.Write([]byte(label))
|
h.Write([]byte(label))
|
||||||
baseHash := h.Sum(nil)
|
baseHash := h.Sum(nil)
|
||||||
|
|
||||||
output := make([]byte, outputLen)
|
output := make([]byte, outputLen)
|
||||||
for i := 0; i < outputLen; i += len(baseHash) {
|
for i := 0; i < outputLen; i += len(baseHash) {
|
||||||
h.Reset()
|
h.Reset()
|
||||||
h.Write(baseHash)
|
h.Write(baseHash)
|
||||||
h.Write([]byte{byte(i / len(baseHash))})
|
h.Write([]byte{byte(i / len(baseHash))})
|
||||||
chunk := h.Sum(nil)
|
chunk := h.Sum(nil)
|
||||||
|
|
||||||
end := i + len(baseHash)
|
end := i + len(baseHash)
|
||||||
if end > outputLen {
|
if end > outputLen {
|
||||||
end = outputLen
|
end = outputLen
|
||||||
@@ -48,7 +48,7 @@ func DeriveKey(seed []byte, label string, outputLen int) []byte {
|
|||||||
copy(output[i:end], chunk)
|
copy(output[i:end], chunk)
|
||||||
baseHash = chunk
|
baseHash = chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ func XOF(seed []byte, outputLen int) []byte {
|
|||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write(seed)
|
h.Write(seed)
|
||||||
hash := h.Sum(nil)
|
hash := h.Sum(nil)
|
||||||
|
|
||||||
for i := 0; i < outputLen; i += len(hash) {
|
for i := 0; i < outputLen; i += len(hash) {
|
||||||
end := i + len(hash)
|
end := i + len(hash)
|
||||||
if end > outputLen {
|
if end > outputLen {
|
||||||
@@ -71,7 +71,7 @@ func XOF(seed []byte, outputLen int) []byte {
|
|||||||
hash = h.Sum(nil)
|
hash = h.Sum(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ func SecureCompare(a, b []byte) bool {
|
|||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var result byte
|
var result byte
|
||||||
for i := range a {
|
for i := range a {
|
||||||
result |= a[i] ^ b[i]
|
result |= a[i] ^ b[i]
|
||||||
@@ -93,4 +93,4 @@ func ClearBytes(b []byte) {
|
|||||||
for i := range b {
|
for i := range b {
|
||||||
b[i] = 0
|
b[i] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -27,12 +27,12 @@ func GenerateRandomBytes(rand io.Reader, size int) ([]byte, error) {
|
|||||||
if err := ValidateRandomSource(rand); err != nil {
|
if err := ValidateRandomSource(rand); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes := make([]byte, size)
|
bytes := make([]byte, size)
|
||||||
if _, err := io.ReadFull(rand, bytes); err != nil {
|
if _, err := io.ReadFull(rand, bytes); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes, nil
|
return bytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ func AllocateCombined(sizes ...int) []byte {
|
|||||||
func SplitBuffer(buffer []byte, sizes ...int) [][]byte {
|
func SplitBuffer(buffer []byte, sizes ...int) [][]byte {
|
||||||
segments := make([][]byte, len(sizes))
|
segments := make([][]byte, len(sizes))
|
||||||
offset := 0
|
offset := 0
|
||||||
|
|
||||||
for i, size := range sizes {
|
for i, size := range sizes {
|
||||||
if offset+size > len(buffer) {
|
if offset+size > len(buffer) {
|
||||||
panic("buffer too small for requested segments")
|
panic("buffer too small for requested segments")
|
||||||
@@ -57,7 +57,7 @@ func SplitBuffer(buffer []byte, sizes ...int) [][]byte {
|
|||||||
segments[i] = buffer[offset : offset+size]
|
segments[i] = buffer[offset : offset+size]
|
||||||
offset += size
|
offset += size
|
||||||
}
|
}
|
||||||
|
|
||||||
return segments
|
return segments
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ func ConstantTimeSelect(v int, a, b []byte) []byte {
|
|||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
panic("slices must have equal length")
|
panic("slices must have equal length")
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([]byte, len(a))
|
result := make([]byte, len(a))
|
||||||
for i := range result {
|
for i := range result {
|
||||||
result[i] = byte(v)*a[i] + byte(1-v)*b[i]
|
result[i] = byte(v)*a[i] + byte(1-v)*b[i]
|
||||||
@@ -116,7 +116,7 @@ func FillRandomBytes(rand io.Reader, buf []byte) error {
|
|||||||
if err := ValidateRandomSource(rand); err != nil {
|
if err := ValidateRandomSource(rand); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := io.ReadFull(rand, buf)
|
_, err := io.ReadFull(rand, buf)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -135,4 +135,4 @@ func Max(a, b int) int {
|
|||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-67
@@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/luxfi/crypto/mldsa"
|
"github.com/luxfi/crypto/mldsa"
|
||||||
"github.com/luxfi/crypto/mlkem"
|
"github.com/luxfi/crypto/mlkem"
|
||||||
"github.com/luxfi/crypto/slhdsa"
|
"github.com/luxfi/crypto/slhdsa"
|
||||||
@@ -21,61 +21,61 @@ func TestPQCrypto96Coverage(t *testing.T) {
|
|||||||
|
|
||||||
func testMLDSAComprehensive(t *testing.T) {
|
func testMLDSAComprehensive(t *testing.T) {
|
||||||
modes := []mldsa.Mode{mldsa.MLDSA44, mldsa.MLDSA65, mldsa.MLDSA87}
|
modes := []mldsa.Mode{mldsa.MLDSA44, mldsa.MLDSA65, mldsa.MLDSA87}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
// Generate key
|
// Generate key
|
||||||
priv, err := mldsa.GenerateKey(rand.Reader, mode)
|
priv, err := mldsa.GenerateKey(rand.Reader, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("MLDSA GenerateKey failed: %v", err)
|
t.Fatalf("MLDSA GenerateKey failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign message
|
// Sign message
|
||||||
msg := []byte("Test message for 96% coverage")
|
msg := []byte("Test message for 96% coverage")
|
||||||
sig, err := priv.Sign(rand.Reader, msg, nil)
|
sig, err := priv.Sign(rand.Reader, msg, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("MLDSA Sign failed: %v", err)
|
t.Fatalf("MLDSA Sign failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
valid := priv.PublicKey.Verify(msg, sig, nil)
|
valid := priv.PublicKey.Verify(msg, sig, nil)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("MLDSA valid signature rejected")
|
t.Fatal("MLDSA valid signature rejected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test wrong message
|
// Test wrong message
|
||||||
wrongMsg := []byte("Wrong")
|
wrongMsg := []byte("Wrong")
|
||||||
valid = priv.PublicKey.Verify(wrongMsg, sig, nil)
|
valid = priv.PublicKey.Verify(wrongMsg, sig, nil)
|
||||||
if valid {
|
if valid {
|
||||||
t.Fatal("MLDSA invalid signature accepted")
|
t.Fatal("MLDSA invalid signature accepted")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test serialization
|
// Test serialization
|
||||||
privBytes := priv.Bytes()
|
privBytes := priv.Bytes()
|
||||||
pubBytes := priv.PublicKey.Bytes()
|
pubBytes := priv.PublicKey.Bytes()
|
||||||
|
|
||||||
// Test deserialization
|
// Test deserialization
|
||||||
privRestored, err := mldsa.PrivateKeyFromBytes(privBytes, mode)
|
privRestored, err := mldsa.PrivateKeyFromBytes(privBytes, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("MLDSA PrivateKeyFromBytes failed: %v", err)
|
t.Fatalf("MLDSA PrivateKeyFromBytes failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pubRestored, err := mldsa.PublicKeyFromBytes(pubBytes, mode)
|
pubRestored, err := mldsa.PublicKeyFromBytes(pubBytes, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("MLDSA PublicKeyFromBytes failed: %v", err)
|
t.Fatalf("MLDSA PublicKeyFromBytes failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test restored keys
|
// Test restored keys
|
||||||
sig2, err := privRestored.Sign(rand.Reader, msg, nil)
|
sig2, err := privRestored.Sign(rand.Reader, msg, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("MLDSA restored key sign failed")
|
t.Fatal("MLDSA restored key sign failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
valid = pubRestored.Verify(msg, sig2, nil)
|
valid = pubRestored.Verify(msg, sig2, nil)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("MLDSA restored key verify failed")
|
t.Fatal("MLDSA restored key verify failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edge cases
|
// Edge cases
|
||||||
testMLDSAEdgeCases(t)
|
testMLDSAEdgeCases(t)
|
||||||
}
|
}
|
||||||
@@ -86,37 +86,37 @@ func testMLDSAEdgeCases(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for invalid MLDSA mode")
|
t.Fatal("Expected error for invalid MLDSA mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nil private key
|
// Nil private key
|
||||||
var nilPriv *mldsa.PrivateKey
|
var nilPriv *mldsa.PrivateKey
|
||||||
_, err = nilPriv.Sign(rand.Reader, []byte("test"), nil)
|
_, err = nilPriv.Sign(rand.Reader, []byte("test"), nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for nil MLDSA private key")
|
t.Fatal("Expected error for nil MLDSA private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrong size deserialization
|
// Wrong size deserialization
|
||||||
_, err = mldsa.PrivateKeyFromBytes([]byte("short"), mldsa.MLDSA44)
|
_, err = mldsa.PrivateKeyFromBytes([]byte("short"), mldsa.MLDSA44)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for wrong size MLDSA private key")
|
t.Fatal("Expected error for wrong size MLDSA private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = mldsa.PublicKeyFromBytes([]byte("short"), mldsa.MLDSA44)
|
_, err = mldsa.PublicKeyFromBytes([]byte("short"), mldsa.MLDSA44)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for wrong size MLDSA public key")
|
t.Fatal("Expected error for wrong size MLDSA public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty message
|
// Empty message
|
||||||
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
priv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
||||||
sig, err := priv.Sign(rand.Reader, []byte{}, nil)
|
sig, err := priv.Sign(rand.Reader, []byte{}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("MLDSA failed to sign empty message")
|
t.Fatal("MLDSA failed to sign empty message")
|
||||||
}
|
}
|
||||||
|
|
||||||
valid := priv.PublicKey.Verify([]byte{}, sig, nil)
|
valid := priv.PublicKey.Verify([]byte{}, sig, nil)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("MLDSA empty message verification failed")
|
t.Fatal("MLDSA empty message verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Large message
|
// Large message
|
||||||
largeMsg := make([]byte, 10000)
|
largeMsg := make([]byte, 10000)
|
||||||
rand.Read(largeMsg)
|
rand.Read(largeMsg)
|
||||||
@@ -124,7 +124,7 @@ func testMLDSAEdgeCases(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("MLDSA failed to sign large message")
|
t.Fatal("MLDSA failed to sign large message")
|
||||||
}
|
}
|
||||||
|
|
||||||
valid = priv.PublicKey.Verify(largeMsg, sig, nil)
|
valid = priv.PublicKey.Verify(largeMsg, sig, nil)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("MLDSA large message verification failed")
|
t.Fatal("MLDSA large message verification failed")
|
||||||
@@ -133,14 +133,14 @@ func testMLDSAEdgeCases(t *testing.T) {
|
|||||||
|
|
||||||
func testMLKEMComprehensive(t *testing.T) {
|
func testMLKEMComprehensive(t *testing.T) {
|
||||||
modes := []mlkem.Mode{mlkem.MLKEM512, mlkem.MLKEM768, mlkem.MLKEM1024}
|
modes := []mlkem.Mode{mlkem.MLKEM512, mlkem.MLKEM768, mlkem.MLKEM1024}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
// Generate key pair
|
// Generate key pair
|
||||||
priv, pub, err := mlkem.GenerateKeyPair(rand.Reader, mode)
|
priv, pub, err := mlkem.GenerateKeyPair(rand.Reader, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("MLKEM GenerateKeyPair failed: %v", err)
|
t.Fatalf("MLKEM GenerateKeyPair failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encapsulate
|
// Encapsulate
|
||||||
result, err := pub.Encapsulate(rand.Reader)
|
result, err := pub.Encapsulate(rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -152,33 +152,33 @@ func testMLKEMComprehensive(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("MLKEM Decapsulate failed: %v", err)
|
t.Fatalf("MLKEM Decapsulate failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify shared secrets match
|
// Verify shared secrets match
|
||||||
if !bytes.Equal(result.SharedSecret, ss2) {
|
if !bytes.Equal(result.SharedSecret, ss2) {
|
||||||
t.Fatal("MLKEM shared secrets don't match")
|
t.Fatal("MLKEM shared secrets don't match")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test serialization
|
// Test serialization
|
||||||
privBytes := priv.Bytes()
|
privBytes := priv.Bytes()
|
||||||
pubBytes := pub.Bytes()
|
pubBytes := pub.Bytes()
|
||||||
|
|
||||||
// Test deserialization
|
// Test deserialization
|
||||||
privRestored, err := mlkem.PrivateKeyFromBytes(privBytes, mode)
|
privRestored, err := mlkem.PrivateKeyFromBytes(privBytes, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("MLKEM PrivateKeyFromBytes failed: %v", err)
|
t.Fatalf("MLKEM PrivateKeyFromBytes failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pubRestored, err := mlkem.PublicKeyFromBytes(pubBytes, mode)
|
pubRestored, err := mlkem.PublicKeyFromBytes(pubBytes, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("MLKEM PublicKeyFromBytes failed: %v", err)
|
t.Fatalf("MLKEM PublicKeyFromBytes failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test restored keys
|
// Test restored keys
|
||||||
result2, err := pubRestored.Encapsulate(rand.Reader)
|
result2, err := pubRestored.Encapsulate(rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("MLKEM restored key encapsulate failed")
|
t.Fatal("MLKEM restored key encapsulate failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
ss4, err := privRestored.Decapsulate(result2.Ciphertext)
|
ss4, err := privRestored.Decapsulate(result2.Ciphertext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("MLKEM restored key decapsulate failed")
|
t.Fatal("MLKEM restored key decapsulate failed")
|
||||||
@@ -187,7 +187,7 @@ func testMLKEMComprehensive(t *testing.T) {
|
|||||||
if !bytes.Equal(result2.SharedSecret, ss4) {
|
if !bytes.Equal(result2.SharedSecret, ss4) {
|
||||||
t.Fatal("MLKEM restored keys produce different shared secrets")
|
t.Fatal("MLKEM restored keys produce different shared secrets")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test wrong ciphertext (should produce pseudorandom)
|
// Test wrong ciphertext (should produce pseudorandom)
|
||||||
wrongCt := make([]byte, len(result.Ciphertext))
|
wrongCt := make([]byte, len(result.Ciphertext))
|
||||||
rand.Read(wrongCt)
|
rand.Read(wrongCt)
|
||||||
@@ -195,13 +195,13 @@ func testMLKEMComprehensive(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("MLKEM decapsulate wrong ct failed")
|
t.Fatal("MLKEM decapsulate wrong ct failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be different (pseudorandom)
|
// Should be different (pseudorandom)
|
||||||
if bytes.Equal(result.SharedSecret, ssWrong) {
|
if bytes.Equal(result.SharedSecret, ssWrong) {
|
||||||
t.Fatal("MLKEM wrong ct produced same shared secret")
|
t.Fatal("MLKEM wrong ct produced same shared secret")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edge cases
|
// Edge cases
|
||||||
testMLKEMEdgeCases(t)
|
testMLKEMEdgeCases(t)
|
||||||
}
|
}
|
||||||
@@ -212,38 +212,38 @@ func testMLKEMEdgeCases(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for invalid MLKEM mode")
|
t.Fatal("Expected error for invalid MLKEM mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nil keys
|
// Nil keys
|
||||||
var nilPriv *mlkem.PrivateKey
|
var nilPriv *mlkem.PrivateKey
|
||||||
_, err = nilPriv.Decapsulate([]byte("test"))
|
_, err = nilPriv.Decapsulate([]byte("test"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for nil MLKEM private key")
|
t.Fatal("Expected error for nil MLKEM private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
var nilPub *mlkem.PublicKey
|
var nilPub *mlkem.PublicKey
|
||||||
_, err = nilPub.Encapsulate(rand.Reader)
|
_, err = nilPub.Encapsulate(rand.Reader)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for nil MLKEM public key")
|
t.Fatal("Expected error for nil MLKEM public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrong size deserialization
|
// Wrong size deserialization
|
||||||
_, err = mlkem.PrivateKeyFromBytes([]byte("short"), mlkem.MLKEM512)
|
_, err = mlkem.PrivateKeyFromBytes([]byte("short"), mlkem.MLKEM512)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for wrong size MLKEM private key")
|
t.Fatal("Expected error for wrong size MLKEM private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = mlkem.PublicKeyFromBytes([]byte("short"), mlkem.MLKEM512)
|
_, err = mlkem.PublicKeyFromBytes([]byte("short"), mlkem.MLKEM512)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for wrong size MLKEM public key")
|
t.Fatal("Expected error for wrong size MLKEM public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrong size ciphertext
|
// Wrong size ciphertext
|
||||||
priv, _, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM512)
|
priv, _, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM512)
|
||||||
_, err = priv.Decapsulate([]byte("short"))
|
_, err = priv.Decapsulate([]byte("short"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for wrong size MLKEM ciphertext")
|
t.Fatal("Expected error for wrong size MLKEM ciphertext")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiple encapsulations
|
// Multiple encapsulations
|
||||||
_, pub, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
|
_, pub, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
|
||||||
result1, _ := pub.Encapsulate(rand.Reader)
|
result1, _ := pub.Encapsulate(rand.Reader)
|
||||||
@@ -261,61 +261,61 @@ func testMLKEMEdgeCases(t *testing.T) {
|
|||||||
func testSLHDSAComprehensive(t *testing.T) {
|
func testSLHDSAComprehensive(t *testing.T) {
|
||||||
// Note: SLH-DSA is computationally expensive, testing only 128s for quick validation
|
// Note: SLH-DSA is computationally expensive, testing only 128s for quick validation
|
||||||
modes := []slhdsa.Mode{slhdsa.SLHDSA128s}
|
modes := []slhdsa.Mode{slhdsa.SLHDSA128s}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
// Generate key
|
// Generate key
|
||||||
priv, err := slhdsa.GenerateKey(rand.Reader, mode)
|
priv, err := slhdsa.GenerateKey(rand.Reader, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("SLHDSA GenerateKey failed: %v", err)
|
t.Fatalf("SLHDSA GenerateKey failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign message
|
// Sign message
|
||||||
msg := []byte("Test message for 96% coverage")
|
msg := []byte("Test message for 96% coverage")
|
||||||
sig, err := priv.Sign(rand.Reader, msg, nil)
|
sig, err := priv.Sign(rand.Reader, msg, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("SLHDSA Sign failed: %v", err)
|
t.Fatalf("SLHDSA Sign failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
valid := priv.PublicKey.Verify(msg, sig, nil)
|
valid := priv.PublicKey.Verify(msg, sig, nil)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("SLHDSA valid signature rejected")
|
t.Fatal("SLHDSA valid signature rejected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test wrong message
|
// Test wrong message
|
||||||
wrongMsg := []byte("Wrong")
|
wrongMsg := []byte("Wrong")
|
||||||
valid = priv.PublicKey.Verify(wrongMsg, sig, nil)
|
valid = priv.PublicKey.Verify(wrongMsg, sig, nil)
|
||||||
if valid {
|
if valid {
|
||||||
t.Fatal("SLHDSA invalid signature accepted")
|
t.Fatal("SLHDSA invalid signature accepted")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test serialization
|
// Test serialization
|
||||||
privBytes := priv.Bytes()
|
privBytes := priv.Bytes()
|
||||||
pubBytes := priv.PublicKey.Bytes()
|
pubBytes := priv.PublicKey.Bytes()
|
||||||
|
|
||||||
// Test deserialization
|
// Test deserialization
|
||||||
privRestored, err := slhdsa.PrivateKeyFromBytes(privBytes, mode)
|
privRestored, err := slhdsa.PrivateKeyFromBytes(privBytes, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("SLHDSA PrivateKeyFromBytes failed: %v", err)
|
t.Fatalf("SLHDSA PrivateKeyFromBytes failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pubRestored, err := slhdsa.PublicKeyFromBytes(pubBytes, mode)
|
pubRestored, err := slhdsa.PublicKeyFromBytes(pubBytes, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("SLHDSA PublicKeyFromBytes failed: %v", err)
|
t.Fatalf("SLHDSA PublicKeyFromBytes failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test restored keys
|
// Test restored keys
|
||||||
sig2, err := privRestored.Sign(rand.Reader, msg, nil)
|
sig2, err := privRestored.Sign(rand.Reader, msg, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("SLHDSA restored key sign failed")
|
t.Fatal("SLHDSA restored key sign failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
valid = pubRestored.Verify(msg, sig2, nil)
|
valid = pubRestored.Verify(msg, sig2, nil)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("SLHDSA restored key verify failed")
|
t.Fatal("SLHDSA restored key verify failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edge cases
|
// Edge cases
|
||||||
testSLHDSAEdgeCases(t)
|
testSLHDSAEdgeCases(t)
|
||||||
}
|
}
|
||||||
@@ -326,32 +326,32 @@ func testSLHDSAEdgeCases(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for invalid SLHDSA mode")
|
t.Fatal("Expected error for invalid SLHDSA mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nil private key
|
// Nil private key
|
||||||
var nilPriv *slhdsa.PrivateKey
|
var nilPriv *slhdsa.PrivateKey
|
||||||
_, err = nilPriv.Sign(rand.Reader, []byte("test"), nil)
|
_, err = nilPriv.Sign(rand.Reader, []byte("test"), nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for nil SLHDSA private key")
|
t.Fatal("Expected error for nil SLHDSA private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrong size deserialization
|
// Wrong size deserialization
|
||||||
_, err = slhdsa.PrivateKeyFromBytes([]byte("short"), slhdsa.SLHDSA128s)
|
_, err = slhdsa.PrivateKeyFromBytes([]byte("short"), slhdsa.SLHDSA128s)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for wrong size SLHDSA private key")
|
t.Fatal("Expected error for wrong size SLHDSA private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = slhdsa.PublicKeyFromBytes([]byte("short"), slhdsa.SLHDSA128s)
|
_, err = slhdsa.PublicKeyFromBytes([]byte("short"), slhdsa.SLHDSA128s)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for wrong size SLHDSA public key")
|
t.Fatal("Expected error for wrong size SLHDSA public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty message
|
// Empty message
|
||||||
priv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128s)
|
priv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128s)
|
||||||
sig, err := priv.Sign(rand.Reader, []byte{}, nil)
|
sig, err := priv.Sign(rand.Reader, []byte{}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("SLHDSA failed to sign empty message")
|
t.Fatal("SLHDSA failed to sign empty message")
|
||||||
}
|
}
|
||||||
|
|
||||||
valid := priv.PublicKey.Verify([]byte{}, sig, nil)
|
valid := priv.PublicKey.Verify([]byte{}, sig, nil)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("SLHDSA empty message verification failed")
|
t.Fatal("SLHDSA empty message verification failed")
|
||||||
@@ -362,30 +362,30 @@ func testIntegration(t *testing.T) {
|
|||||||
// Test ML-DSA + ML-KEM combination
|
// Test ML-DSA + ML-KEM combination
|
||||||
mldsaPriv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
mldsaPriv, _ := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
||||||
mlkemPriv, mlkemPub, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM512)
|
mlkemPriv, mlkemPub, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM512)
|
||||||
|
|
||||||
// Sign with ML-DSA
|
// Sign with ML-DSA
|
||||||
msg := []byte("Integration test")
|
msg := []byte("Integration test")
|
||||||
sig, _ := mldsaPriv.Sign(rand.Reader, msg, nil)
|
sig, _ := mldsaPriv.Sign(rand.Reader, msg, nil)
|
||||||
|
|
||||||
// Encapsulate with ML-KEM
|
// Encapsulate with ML-KEM
|
||||||
result, _ := mlkemPub.Encapsulate(rand.Reader)
|
result, _ := mlkemPub.Encapsulate(rand.Reader)
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
valid := mldsaPriv.PublicKey.Verify(msg, sig, nil)
|
valid := mldsaPriv.PublicKey.Verify(msg, sig, nil)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("Integration: MLDSA verification failed")
|
t.Fatal("Integration: MLDSA verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decapsulate
|
// Decapsulate
|
||||||
ss2, _ := mlkemPriv.Decapsulate(result.Ciphertext)
|
ss2, _ := mlkemPriv.Decapsulate(result.Ciphertext)
|
||||||
if !bytes.Equal(result.SharedSecret, ss2) {
|
if !bytes.Equal(result.SharedSecret, ss2) {
|
||||||
t.Fatal("Integration: MLKEM shared secrets don't match")
|
t.Fatal("Integration: MLKEM shared secrets don't match")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test all three together
|
// Test all three together
|
||||||
slhdsaPriv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128s)
|
slhdsaPriv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128s)
|
||||||
slhdsaSig, _ := slhdsaPriv.Sign(rand.Reader, msg, nil)
|
slhdsaSig, _ := slhdsaPriv.Sign(rand.Reader, msg, nil)
|
||||||
|
|
||||||
valid = slhdsaPriv.PublicKey.Verify(msg, slhdsaSig, nil)
|
valid = slhdsaPriv.PublicKey.Verify(msg, slhdsaSig, nil)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("Integration: SLHDSA verification failed")
|
t.Fatal("Integration: SLHDSA verification failed")
|
||||||
@@ -394,46 +394,46 @@ func testIntegration(t *testing.T) {
|
|||||||
|
|
||||||
func testHybrid(t *testing.T) {
|
func testHybrid(t *testing.T) {
|
||||||
// Test hybrid mode: classical + PQ
|
// Test hybrid mode: classical + PQ
|
||||||
|
|
||||||
// Classical ECDSA
|
// Classical ECDSA
|
||||||
classicalPriv, err := GenerateKey()
|
classicalPriv, err := GenerateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Classical key generation failed")
|
t.Fatal("Classical key generation failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// PQ ML-DSA
|
// PQ ML-DSA
|
||||||
pqPriv, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
pqPriv, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("PQ key generation failed")
|
t.Fatal("PQ key generation failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := []byte("Hybrid signature test")
|
msg := []byte("Hybrid signature test")
|
||||||
|
|
||||||
// Classical signature
|
// Classical signature
|
||||||
hash := Keccak256Hash(msg)
|
hash := Keccak256Hash(msg)
|
||||||
classicalSig, err := Sign(hash.Bytes(), classicalPriv)
|
classicalSig, err := Sign(hash.Bytes(), classicalPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Classical signing failed")
|
t.Fatal("Classical signing failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// PQ signature
|
// PQ signature
|
||||||
pqSig, err := pqPriv.Sign(rand.Reader, msg, nil)
|
pqSig, err := pqPriv.Sign(rand.Reader, msg, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("PQ signing failed")
|
t.Fatal("PQ signing failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify both
|
// Verify both
|
||||||
classicalPub := FromECDSAPub(&classicalPriv.PublicKey)
|
classicalPub := FromECDSAPub(&classicalPriv.PublicKey)
|
||||||
valid := VerifySignature(classicalPub, hash.Bytes(), classicalSig[:64])
|
valid := VerifySignature(classicalPub, hash.Bytes(), classicalSig[:64])
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("Classical signature verification failed")
|
t.Fatal("Classical signature verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
valid = pqPriv.PublicKey.Verify(msg, pqSig, nil)
|
valid = pqPriv.PublicKey.Verify(msg, pqSig, nil)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Fatal("PQ signature verification failed")
|
t.Fatal("PQ signature verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine signatures (hybrid)
|
// Combine signatures (hybrid)
|
||||||
hybridSig := append(classicalSig, pqSig...)
|
hybridSig := append(classicalSig, pqSig...)
|
||||||
if len(hybridSig) < len(classicalSig)+len(pqSig) {
|
if len(hybridSig) < len(classicalSig)+len(pqSig) {
|
||||||
@@ -451,7 +451,7 @@ func BenchmarkPQOperations96Coverage(b *testing.B) {
|
|||||||
priv.Sign(rand.Reader, msg, nil)
|
priv.Sign(rand.Reader, msg, nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("MLKEM768-Encapsulate", func(b *testing.B) {
|
b.Run("MLKEM768-Encapsulate", func(b *testing.B) {
|
||||||
_, pub, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
|
_, pub, _ := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -459,7 +459,7 @@ func BenchmarkPQOperations96Coverage(b *testing.B) {
|
|||||||
pub.Encapsulate(rand.Reader)
|
pub.Encapsulate(rand.Reader)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("SLHDSA128s-Sign", func(b *testing.B) {
|
b.Run("SLHDSA128s-Sign", func(b *testing.B) {
|
||||||
priv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128s)
|
priv, _ := slhdsa.GenerateKey(rand.Reader, slhdsa.SLHDSA128s)
|
||||||
msg := make([]byte, 32)
|
msg := make([]byte, 32)
|
||||||
@@ -468,4 +468,4 @@ func BenchmarkPQOperations96Coverage(b *testing.B) {
|
|||||||
priv.Sign(rand.Reader, msg, nil)
|
priv.Sign(rand.Reader, msg, nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-28
@@ -21,10 +21,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errInvalidRingSize = errors.New("invalid ring size")
|
errInvalidRingSize = errors.New("invalid ring size")
|
||||||
errInvalidPublicKey = errors.New("invalid public key")
|
errInvalidPublicKey = errors.New("invalid public key")
|
||||||
errInvalidSignature = errors.New("invalid signature")
|
errInvalidSignature = errors.New("invalid signature")
|
||||||
errRingNotComplete = errors.New("ring is not complete")
|
errRingNotComplete = errors.New("ring is not complete")
|
||||||
)
|
)
|
||||||
|
|
||||||
// PublicKey represents a Corona public key
|
// PublicKey represents a Corona public key
|
||||||
@@ -44,9 +44,9 @@ type Point struct {
|
|||||||
|
|
||||||
// RingSignature represents a Corona ring signature
|
// RingSignature represents a Corona ring signature
|
||||||
type RingSignature struct {
|
type RingSignature struct {
|
||||||
C0 *big.Int
|
C0 *big.Int
|
||||||
S []*big.Int
|
S []*big.Int
|
||||||
KeyImage *Point
|
KeyImage *Point
|
||||||
RingPubKeys []*PublicKey
|
RingPubKeys []*PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ func (*Factory) NewPrivateKey() (*PrivateKey, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &PrivateKey{Scalar: scalar}, nil
|
return &PrivateKey{Scalar: scalar}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ func (*Factory) ToPublicKey(privKey *PrivateKey) (*PublicKey, error) {
|
|||||||
if privKey == nil {
|
if privKey == nil {
|
||||||
return nil, errors.New("nil private key")
|
return nil, errors.New("nil private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
point := scalarBaseMult(privKey.Scalar)
|
point := scalarBaseMult(privKey.Scalar)
|
||||||
return &PublicKey{Point: point}, nil
|
return &PublicKey{Point: point}, nil
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ func (priv *PrivateKey) Sign(message []byte, ring []*PublicKey) (*RingSignature,
|
|||||||
if len(ring) != DefaultRingSize {
|
if len(ring) != DefaultRingSize {
|
||||||
return nil, errInvalidRingSize
|
return nil, errInvalidRingSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the signer's position in the ring
|
// Find the signer's position in the ring
|
||||||
pubKey := priv.PublicKey()
|
pubKey := priv.PublicKey()
|
||||||
signerIndex := -1
|
signerIndex := -1
|
||||||
@@ -88,40 +88,40 @@ func (priv *PrivateKey) Sign(message []byte, ring []*PublicKey) (*RingSignature,
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if signerIndex == -1 {
|
if signerIndex == -1 {
|
||||||
return nil, errors.New("signer's public key not found in ring")
|
return nil, errors.New("signer's public key not found in ring")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate key image
|
// Generate key image
|
||||||
keyImage := generateKeyImage(priv)
|
keyImage := generateKeyImage(priv)
|
||||||
|
|
||||||
// Initialize signature components
|
// Initialize signature components
|
||||||
c := make([]*big.Int, DefaultRingSize)
|
c := make([]*big.Int, DefaultRingSize)
|
||||||
s := make([]*big.Int, DefaultRingSize)
|
s := make([]*big.Int, DefaultRingSize)
|
||||||
|
|
||||||
// Generate random responses for all except signer
|
// Generate random responses for all except signer
|
||||||
for i := 0; i < DefaultRingSize; i++ {
|
for i := 0; i < DefaultRingSize; i++ {
|
||||||
if i != signerIndex {
|
if i != signerIndex {
|
||||||
s[i], _ = rand.Int(rand.Reader, curveOrder())
|
s[i], _ = rand.Int(rand.Reader, curveOrder())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the ring computation
|
// Start the ring computation
|
||||||
// Generate random nonce
|
// Generate random nonce
|
||||||
k, _ := rand.Int(rand.Reader, curveOrder())
|
k, _ := rand.Int(rand.Reader, curveOrder())
|
||||||
|
|
||||||
// Compute L_i and R_i for all ring members
|
// Compute L_i and R_i for all ring members
|
||||||
L := make([]*Point, DefaultRingSize)
|
L := make([]*Point, DefaultRingSize)
|
||||||
R := make([]*Point, DefaultRingSize)
|
R := make([]*Point, DefaultRingSize)
|
||||||
|
|
||||||
// For the signer
|
// For the signer
|
||||||
L[signerIndex] = scalarBaseMult(k)
|
L[signerIndex] = scalarBaseMult(k)
|
||||||
R[signerIndex] = scalarMult(hashToPoint(ring[signerIndex].Point), k)
|
R[signerIndex] = scalarMult(hashToPoint(ring[signerIndex].Point), k)
|
||||||
|
|
||||||
// Compute c_{i+1} starting from signer
|
// Compute c_{i+1} starting from signer
|
||||||
c[(signerIndex+1)%DefaultRingSize] = hashPoints(message, L[signerIndex], R[signerIndex])
|
c[(signerIndex+1)%DefaultRingSize] = hashPoints(message, L[signerIndex], R[signerIndex])
|
||||||
|
|
||||||
// Complete the ring
|
// Complete the ring
|
||||||
for i := (signerIndex + 1) % DefaultRingSize; i != signerIndex; i = (i + 1) % DefaultRingSize {
|
for i := (signerIndex + 1) % DefaultRingSize; i != signerIndex; i = (i + 1) % DefaultRingSize {
|
||||||
L[i] = addPoints(
|
L[i] = addPoints(
|
||||||
@@ -132,14 +132,14 @@ func (priv *PrivateKey) Sign(message []byte, ring []*PublicKey) (*RingSignature,
|
|||||||
scalarMult(hashToPoint(ring[i].Point), s[i]),
|
scalarMult(hashToPoint(ring[i].Point), s[i]),
|
||||||
scalarMult(keyImage, c[i]),
|
scalarMult(keyImage, c[i]),
|
||||||
)
|
)
|
||||||
|
|
||||||
c[(i+1)%DefaultRingSize] = hashPoints(message, L[i], R[i])
|
c[(i+1)%DefaultRingSize] = hashPoints(message, L[i], R[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete the signature for the signer
|
// Complete the signature for the signer
|
||||||
s[signerIndex] = new(big.Int).Sub(k, new(big.Int).Mul(c[signerIndex], priv.Scalar))
|
s[signerIndex] = new(big.Int).Sub(k, new(big.Int).Mul(c[signerIndex], priv.Scalar))
|
||||||
s[signerIndex].Mod(s[signerIndex], curveOrder())
|
s[signerIndex].Mod(s[signerIndex], curveOrder())
|
||||||
|
|
||||||
return &RingSignature{
|
return &RingSignature{
|
||||||
C0: c[0],
|
C0: c[0],
|
||||||
S: s,
|
S: s,
|
||||||
@@ -153,24 +153,24 @@ func (sig *RingSignature) Verify(message []byte) bool {
|
|||||||
if len(sig.S) != DefaultRingSize || len(sig.RingPubKeys) != DefaultRingSize {
|
if len(sig.S) != DefaultRingSize || len(sig.RingPubKeys) != DefaultRingSize {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recompute c values
|
// Recompute c values
|
||||||
c := make([]*big.Int, DefaultRingSize)
|
c := make([]*big.Int, DefaultRingSize)
|
||||||
c[0] = sig.C0
|
c[0] = sig.C0
|
||||||
|
|
||||||
for i := 0; i < DefaultRingSize; i++ {
|
for i := 0; i < DefaultRingSize; i++ {
|
||||||
// Compute L_i = s_i * G + c_i * P_i
|
// Compute L_i = s_i * G + c_i * P_i
|
||||||
L := addPoints(
|
L := addPoints(
|
||||||
scalarBaseMult(sig.S[i]),
|
scalarBaseMult(sig.S[i]),
|
||||||
scalarMult(sig.RingPubKeys[i].Point, c[i]),
|
scalarMult(sig.RingPubKeys[i].Point, c[i]),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Compute R_i = s_i * H(P_i) + c_i * I
|
// Compute R_i = s_i * H(P_i) + c_i * I
|
||||||
R := addPoints(
|
R := addPoints(
|
||||||
scalarMult(hashToPoint(sig.RingPubKeys[i].Point), sig.S[i]),
|
scalarMult(hashToPoint(sig.RingPubKeys[i].Point), sig.S[i]),
|
||||||
scalarMult(sig.KeyImage, c[i]),
|
scalarMult(sig.KeyImage, c[i]),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Compute next c value
|
// Compute next c value
|
||||||
if i < DefaultRingSize-1 {
|
if i < DefaultRingSize-1 {
|
||||||
c[i+1] = hashPoints(message, L, R)
|
c[i+1] = hashPoints(message, L, R)
|
||||||
@@ -180,7 +180,7 @@ func (sig *RingSignature) Verify(message []byte) bool {
|
|||||||
return computedC0.Cmp(sig.C0) == 0
|
return computedC0.Cmp(sig.C0) == 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,4 +261,4 @@ func generateKeyImage(priv *PrivateKey) *Point {
|
|||||||
pubKey := priv.PublicKey()
|
pubKey := priv.PublicKey()
|
||||||
hashedPoint := hashToPoint(pubKey.Point)
|
hashedPoint := hashToPoint(pubKey.Point)
|
||||||
return scalarMult(hashedPoint, priv.Scalar)
|
return scalarMult(hashedPoint, priv.Scalar)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -83,4 +83,4 @@ func DecryptFile(encryptedPath string, password string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return DecryptPrivateKeyWithPassword(data, password)
|
return DecryptPrivateKeyWithPassword(data, password)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ func TestAgeEncryption(t *testing.T) {
|
|||||||
// Test empty data
|
// Test empty data
|
||||||
t.Run("EmptyData", func(t *testing.T) {
|
t.Run("EmptyData", func(t *testing.T) {
|
||||||
password := "test-password"
|
password := "test-password"
|
||||||
|
|
||||||
// Encrypt empty data
|
// Encrypt empty data
|
||||||
encrypted, err := EncryptDataWithPassword([]byte{}, password)
|
encrypted, err := EncryptDataWithPassword([]byte{}, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -201,4 +201,4 @@ func BenchmarkIsAgeEncrypted(b *testing.B) {
|
|||||||
IsAgeEncrypted(encrypted)
|
IsAgeEncrypted(encrypted)
|
||||||
IsAgeEncrypted(plain)
|
IsAgeEncrypted(plain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,4 +118,4 @@ func HashWithDomain(domain string, data []byte) Digest {
|
|||||||
h := NewWithDomain(domain)
|
h := NewWithDomain(domain)
|
||||||
h.Write(data)
|
h.Write(data)
|
||||||
return h.Digest()
|
return h.Digest()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,23 +22,23 @@ func TestNewWithDomain(t *testing.T) {
|
|||||||
domain := "test-domain"
|
domain := "test-domain"
|
||||||
h1 := NewWithDomain(domain)
|
h1 := NewWithDomain(domain)
|
||||||
h2 := NewWithDomain(domain)
|
h2 := NewWithDomain(domain)
|
||||||
|
|
||||||
data := []byte("test data")
|
data := []byte("test data")
|
||||||
h1.Write(data)
|
h1.Write(data)
|
||||||
h2.Write(data)
|
h2.Write(data)
|
||||||
|
|
||||||
d1 := h1.Digest()
|
d1 := h1.Digest()
|
||||||
d2 := h2.Digest()
|
d2 := h2.Digest()
|
||||||
|
|
||||||
if !bytes.Equal(d1[:], d2[:]) {
|
if !bytes.Equal(d1[:], d2[:]) {
|
||||||
t.Error("Same domain should produce same hash")
|
t.Error("Same domain should produce same hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Different domain should produce different hash
|
// Different domain should produce different hash
|
||||||
h3 := NewWithDomain("different-domain")
|
h3 := NewWithDomain("different-domain")
|
||||||
h3.Write(data)
|
h3.Write(data)
|
||||||
d3 := h3.Digest()
|
d3 := h3.Digest()
|
||||||
|
|
||||||
if bytes.Equal(d1[:], d3[:]) {
|
if bytes.Equal(d1[:], d3[:]) {
|
||||||
t.Error("Different domain should produce different hash")
|
t.Error("Different domain should produce different hash")
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ func TestHashBytes(t *testing.T) {
|
|||||||
"ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200fe992405f0d785b599a2e3387f6d34d01faccfeb22fb697ef3fd53541241a338c",
|
"ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200fe992405f0d785b599a2e3387f6d34d01faccfeb22fb697ef3fd53541241a338c",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
hash := HashBytes(tc.input)
|
hash := HashBytes(tc.input)
|
||||||
hashStr := hex.EncodeToString(hash[:])
|
hashStr := hex.EncodeToString(hash[:])
|
||||||
@@ -67,12 +67,12 @@ func TestHashBytes(t *testing.T) {
|
|||||||
t.Errorf("HashBytes(%q) = %s, want %s", tc.input, hashStr, tc.expected)
|
t.Errorf("HashBytes(%q) = %s, want %s", tc.input, hashStr, tc.expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test consistency
|
// Test consistency
|
||||||
data := []byte("test data")
|
data := []byte("test data")
|
||||||
h1 := HashBytes(data)
|
h1 := HashBytes(data)
|
||||||
h2 := HashBytes(data)
|
h2 := HashBytes(data)
|
||||||
|
|
||||||
if !bytes.Equal(h1[:], h2[:]) {
|
if !bytes.Equal(h1[:], h2[:]) {
|
||||||
t.Error("Same input should produce same hash")
|
t.Error("Same input should produce same hash")
|
||||||
}
|
}
|
||||||
@@ -83,17 +83,17 @@ func TestHashString(t *testing.T) {
|
|||||||
s := "test string"
|
s := "test string"
|
||||||
h1 := HashString(s)
|
h1 := HashString(s)
|
||||||
h2 := HashString(s)
|
h2 := HashString(s)
|
||||||
|
|
||||||
if !bytes.Equal(h1[:], h2[:]) {
|
if !bytes.Equal(h1[:], h2[:]) {
|
||||||
t.Error("Same string should produce same hash")
|
t.Error("Same string should produce same hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare with HashBytes
|
// Compare with HashBytes
|
||||||
h3 := HashBytes([]byte(s))
|
h3 := HashBytes([]byte(s))
|
||||||
if !bytes.Equal(h1[:], h3[:]) {
|
if !bytes.Equal(h1[:], h3[:]) {
|
||||||
t.Error("HashString should match HashBytes for same content")
|
t.Error("HashString should match HashBytes for same content")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Different strings produce different hashes
|
// Different strings produce different hashes
|
||||||
h4 := HashString("different string")
|
h4 := HashString("different string")
|
||||||
if bytes.Equal(h1[:], h4[:]) {
|
if bytes.Equal(h1[:], h4[:]) {
|
||||||
@@ -105,21 +105,21 @@ func TestHashWithDomain(t *testing.T) {
|
|||||||
data := []byte("test data")
|
data := []byte("test data")
|
||||||
domain1 := "domain1"
|
domain1 := "domain1"
|
||||||
domain2 := "domain2"
|
domain2 := "domain2"
|
||||||
|
|
||||||
h1 := HashWithDomain(domain1, data)
|
h1 := HashWithDomain(domain1, data)
|
||||||
h2 := HashWithDomain(domain1, data)
|
h2 := HashWithDomain(domain1, data)
|
||||||
h3 := HashWithDomain(domain2, data)
|
h3 := HashWithDomain(domain2, data)
|
||||||
|
|
||||||
// Same domain and data should produce same hash
|
// Same domain and data should produce same hash
|
||||||
if !bytes.Equal(h1[:], h2[:]) {
|
if !bytes.Equal(h1[:], h2[:]) {
|
||||||
t.Error("Same domain and data should produce same hash")
|
t.Error("Same domain and data should produce same hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Different domain should produce different hash
|
// Different domain should produce different hash
|
||||||
if bytes.Equal(h1[:], h3[:]) {
|
if bytes.Equal(h1[:], h3[:]) {
|
||||||
t.Error("Different domain should produce different hash")
|
t.Error("Different domain should produce different hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should differ from hash without domain
|
// Should differ from hash without domain
|
||||||
h4 := HashBytes(data)
|
h4 := HashBytes(data)
|
||||||
if bytes.Equal(h1[:], h4[:]) {
|
if bytes.Equal(h1[:], h4[:]) {
|
||||||
@@ -129,7 +129,7 @@ func TestHashWithDomain(t *testing.T) {
|
|||||||
|
|
||||||
func TestWriteMethods(t *testing.T) {
|
func TestWriteMethods(t *testing.T) {
|
||||||
h := New()
|
h := New()
|
||||||
|
|
||||||
// Test Write
|
// Test Write
|
||||||
n, err := h.Write([]byte("test"))
|
n, err := h.Write([]byte("test"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -138,7 +138,7 @@ func TestWriteMethods(t *testing.T) {
|
|||||||
if n != 4 {
|
if n != 4 {
|
||||||
t.Errorf("Write returned %d, want 4", n)
|
t.Errorf("Write returned %d, want 4", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test WriteString
|
// Test WriteString
|
||||||
n, err = h.WriteString("string")
|
n, err = h.WriteString("string")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -147,20 +147,20 @@ func TestWriteMethods(t *testing.T) {
|
|||||||
if n != 6 {
|
if n != 6 {
|
||||||
t.Errorf("WriteString returned %d, want 6", n)
|
t.Errorf("WriteString returned %d, want 6", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test WriteUint32
|
// Test WriteUint32
|
||||||
h.WriteUint32(0x12345678)
|
h.WriteUint32(0x12345678)
|
||||||
|
|
||||||
// Test WriteUint64
|
// Test WriteUint64
|
||||||
h.WriteUint64(0x123456789ABCDEF0)
|
h.WriteUint64(0x123456789ABCDEF0)
|
||||||
|
|
||||||
// Test WriteBigInt
|
// Test WriteBigInt
|
||||||
bigNum := big.NewInt(1234567890)
|
bigNum := big.NewInt(1234567890)
|
||||||
h.WriteBigInt(bigNum)
|
h.WriteBigInt(bigNum)
|
||||||
|
|
||||||
// Test WriteBigInt with nil
|
// Test WriteBigInt with nil
|
||||||
h.WriteBigInt(nil)
|
h.WriteBigInt(nil)
|
||||||
|
|
||||||
// Get digest to ensure it doesn't panic
|
// Get digest to ensure it doesn't panic
|
||||||
_ = h.Digest()
|
_ = h.Digest()
|
||||||
}
|
}
|
||||||
@@ -169,12 +169,12 @@ func TestWriteUint32(t *testing.T) {
|
|||||||
h1 := New()
|
h1 := New()
|
||||||
h1.WriteUint32(0x12345678)
|
h1.WriteUint32(0x12345678)
|
||||||
d1 := h1.Digest()
|
d1 := h1.Digest()
|
||||||
|
|
||||||
// Should be same as writing the bytes in big-endian
|
// Should be same as writing the bytes in big-endian
|
||||||
h2 := New()
|
h2 := New()
|
||||||
h2.Write([]byte{0x12, 0x34, 0x56, 0x78})
|
h2.Write([]byte{0x12, 0x34, 0x56, 0x78})
|
||||||
d2 := h2.Digest()
|
d2 := h2.Digest()
|
||||||
|
|
||||||
if !bytes.Equal(d1[:], d2[:]) {
|
if !bytes.Equal(d1[:], d2[:]) {
|
||||||
t.Error("WriteUint32 should write in big-endian format")
|
t.Error("WriteUint32 should write in big-endian format")
|
||||||
}
|
}
|
||||||
@@ -184,12 +184,12 @@ func TestWriteUint64(t *testing.T) {
|
|||||||
h1 := New()
|
h1 := New()
|
||||||
h1.WriteUint64(0x123456789ABCDEF0)
|
h1.WriteUint64(0x123456789ABCDEF0)
|
||||||
d1 := h1.Digest()
|
d1 := h1.Digest()
|
||||||
|
|
||||||
// Should be same as writing the bytes in big-endian
|
// Should be same as writing the bytes in big-endian
|
||||||
h2 := New()
|
h2 := New()
|
||||||
h2.Write([]byte{0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0})
|
h2.Write([]byte{0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0})
|
||||||
d2 := h2.Digest()
|
d2 := h2.Digest()
|
||||||
|
|
||||||
if !bytes.Equal(d1[:], d2[:]) {
|
if !bytes.Equal(d1[:], d2[:]) {
|
||||||
t.Error("WriteUint64 should write in big-endian format")
|
t.Error("WriteUint64 should write in big-endian format")
|
||||||
}
|
}
|
||||||
@@ -201,30 +201,30 @@ func TestWriteBigInt(t *testing.T) {
|
|||||||
h1 := New()
|
h1 := New()
|
||||||
h1.WriteBigInt(n)
|
h1.WriteBigInt(n)
|
||||||
d1 := h1.Digest()
|
d1 := h1.Digest()
|
||||||
|
|
||||||
// Writing same number should produce same hash
|
// Writing same number should produce same hash
|
||||||
h2 := New()
|
h2 := New()
|
||||||
h2.WriteBigInt(n)
|
h2.WriteBigInt(n)
|
||||||
d2 := h2.Digest()
|
d2 := h2.Digest()
|
||||||
|
|
||||||
if !bytes.Equal(d1[:], d2[:]) {
|
if !bytes.Equal(d1[:], d2[:]) {
|
||||||
t.Error("Same big.Int should produce same hash")
|
t.Error("Same big.Int should produce same hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with nil
|
// Test with nil
|
||||||
h3 := New()
|
h3 := New()
|
||||||
h3.WriteBigInt(nil)
|
h3.WriteBigInt(nil)
|
||||||
d3 := h3.Digest()
|
d3 := h3.Digest()
|
||||||
|
|
||||||
// Nil should write a zero length prefix
|
// Nil should write a zero length prefix
|
||||||
h4 := New()
|
h4 := New()
|
||||||
h4.WriteUint32(0)
|
h4.WriteUint32(0)
|
||||||
d4 := h4.Digest()
|
d4 := h4.Digest()
|
||||||
|
|
||||||
if !bytes.Equal(d3[:], d4[:]) {
|
if !bytes.Equal(d3[:], d4[:]) {
|
||||||
t.Error("WriteBigInt(nil) should write zero length")
|
t.Error("WriteBigInt(nil) should write zero length")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with zero
|
// Test with zero
|
||||||
zero := big.NewInt(0)
|
zero := big.NewInt(0)
|
||||||
h5 := New()
|
h5 := New()
|
||||||
@@ -235,13 +235,13 @@ func TestWriteBigInt(t *testing.T) {
|
|||||||
func TestSum(t *testing.T) {
|
func TestSum(t *testing.T) {
|
||||||
h := New()
|
h := New()
|
||||||
h.WriteString("test")
|
h.WriteString("test")
|
||||||
|
|
||||||
// Sum with nil
|
// Sum with nil
|
||||||
sum1 := h.Sum(nil)
|
sum1 := h.Sum(nil)
|
||||||
if len(sum1) != 32 { // Default blake3 output
|
if len(sum1) != 32 { // Default blake3 output
|
||||||
t.Errorf("Sum(nil) length = %d, want 32", len(sum1))
|
t.Errorf("Sum(nil) length = %d, want 32", len(sum1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sum with existing slice
|
// Sum with existing slice
|
||||||
prefix := []byte("prefix")
|
prefix := []byte("prefix")
|
||||||
sum2 := h.Sum(prefix)
|
sum2 := h.Sum(prefix)
|
||||||
@@ -257,16 +257,16 @@ func TestDigest(t *testing.T) {
|
|||||||
h := New()
|
h := New()
|
||||||
h.WriteString("test")
|
h.WriteString("test")
|
||||||
d := h.Digest()
|
d := h.Digest()
|
||||||
|
|
||||||
if len(d) != DigestLength {
|
if len(d) != DigestLength {
|
||||||
t.Errorf("Digest length = %d, want %d", len(d), DigestLength)
|
t.Errorf("Digest length = %d, want %d", len(d), DigestLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Digest should be consistent
|
// Digest should be consistent
|
||||||
h2 := New()
|
h2 := New()
|
||||||
h2.WriteString("test")
|
h2.WriteString("test")
|
||||||
d2 := h2.Digest()
|
d2 := h2.Digest()
|
||||||
|
|
||||||
if !bytes.Equal(d[:], d2[:]) {
|
if !bytes.Equal(d[:], d2[:]) {
|
||||||
t.Error("Same input should produce same digest")
|
t.Error("Same input should produce same digest")
|
||||||
}
|
}
|
||||||
@@ -275,12 +275,12 @@ func TestDigest(t *testing.T) {
|
|||||||
func TestReader(t *testing.T) {
|
func TestReader(t *testing.T) {
|
||||||
h := New()
|
h := New()
|
||||||
h.WriteString("test")
|
h.WriteString("test")
|
||||||
|
|
||||||
reader := h.Reader()
|
reader := h.Reader()
|
||||||
if reader == nil {
|
if reader == nil {
|
||||||
t.Fatal("Reader() returned nil")
|
t.Fatal("Reader() returned nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read some bytes
|
// Read some bytes
|
||||||
buf := make([]byte, 100)
|
buf := make([]byte, 100)
|
||||||
n, err := reader.Read(buf)
|
n, err := reader.Read(buf)
|
||||||
@@ -295,25 +295,25 @@ func TestReader(t *testing.T) {
|
|||||||
func TestClone(t *testing.T) {
|
func TestClone(t *testing.T) {
|
||||||
h1 := New()
|
h1 := New()
|
||||||
h1.WriteString("test")
|
h1.WriteString("test")
|
||||||
|
|
||||||
h2 := h1.Clone()
|
h2 := h1.Clone()
|
||||||
if h2 == nil {
|
if h2 == nil {
|
||||||
t.Fatal("Clone() returned nil")
|
t.Fatal("Clone() returned nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both should produce same digest at this point
|
// Both should produce same digest at this point
|
||||||
d1 := h1.Digest()
|
d1 := h1.Digest()
|
||||||
d2 := h2.Digest()
|
d2 := h2.Digest()
|
||||||
|
|
||||||
if !bytes.Equal(d1[:], d2[:]) {
|
if !bytes.Equal(d1[:], d2[:]) {
|
||||||
t.Error("Clone should produce same digest")
|
t.Error("Clone should produce same digest")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writing to one shouldn't affect the other
|
// Writing to one shouldn't affect the other
|
||||||
h1.WriteString("more")
|
h1.WriteString("more")
|
||||||
d1New := h1.Digest()
|
d1New := h1.Digest()
|
||||||
d2New := h2.Digest()
|
d2New := h2.Digest()
|
||||||
|
|
||||||
if bytes.Equal(d1New[:], d2New[:]) {
|
if bytes.Equal(d1New[:], d2New[:]) {
|
||||||
t.Error("Writing to original should not affect clone")
|
t.Error("Writing to original should not affect clone")
|
||||||
}
|
}
|
||||||
@@ -323,20 +323,20 @@ func TestReset(t *testing.T) {
|
|||||||
h := New()
|
h := New()
|
||||||
h.WriteString("test")
|
h.WriteString("test")
|
||||||
d1 := h.Digest()
|
d1 := h.Digest()
|
||||||
|
|
||||||
h.Reset()
|
h.Reset()
|
||||||
h.WriteString("test")
|
h.WriteString("test")
|
||||||
d2 := h.Digest()
|
d2 := h.Digest()
|
||||||
|
|
||||||
if !bytes.Equal(d1[:], d2[:]) {
|
if !bytes.Equal(d1[:], d2[:]) {
|
||||||
t.Error("Reset should restore initial state")
|
t.Error("Reset should restore initial state")
|
||||||
}
|
}
|
||||||
|
|
||||||
// After reset, different input should produce different hash
|
// After reset, different input should produce different hash
|
||||||
h.Reset()
|
h.Reset()
|
||||||
h.WriteString("different")
|
h.WriteString("different")
|
||||||
d3 := h.Digest()
|
d3 := h.Digest()
|
||||||
|
|
||||||
if bytes.Equal(d1[:], d3[:]) {
|
if bytes.Equal(d1[:], d3[:]) {
|
||||||
t.Error("After reset, different input should produce different hash")
|
t.Error("After reset, different input should produce different hash")
|
||||||
}
|
}
|
||||||
@@ -353,7 +353,7 @@ func BenchmarkHashBytes(b *testing.B) {
|
|||||||
for i := range data {
|
for i := range data {
|
||||||
data[i] = byte(i % 256)
|
data[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = HashBytes(data)
|
_ = HashBytes(data)
|
||||||
@@ -362,7 +362,7 @@ func BenchmarkHashBytes(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkHashString(b *testing.B) {
|
func BenchmarkHashString(b *testing.B) {
|
||||||
data := strings.Repeat("benchmark", 128)
|
data := strings.Repeat("benchmark", 128)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = HashString(data)
|
_ = HashString(data)
|
||||||
@@ -372,11 +372,11 @@ func BenchmarkHashString(b *testing.B) {
|
|||||||
func BenchmarkWriteBigInt(b *testing.B) {
|
func BenchmarkWriteBigInt(b *testing.B) {
|
||||||
n := new(big.Int)
|
n := new(big.Int)
|
||||||
n.SetString("123456789012345678901234567890123456789012345678901234567890", 10)
|
n.SetString("123456789012345678901234567890123456789012345678901234567890", 10)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
h := New()
|
h := New()
|
||||||
h.WriteBigInt(n)
|
h.WriteBigInt(n)
|
||||||
_ = h.Digest()
|
_ = h.Digest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-24
@@ -103,16 +103,16 @@ func TestComputeHash160(t *testing.T) {
|
|||||||
|
|
||||||
func TestChecksum(t *testing.T) {
|
func TestChecksum(t *testing.T) {
|
||||||
input := []byte("test input for checksum")
|
input := []byte("test input for checksum")
|
||||||
|
|
||||||
// Test various checksum lengths
|
// Test various checksum lengths
|
||||||
lengths := []int{1, 4, 8, 16, 32}
|
lengths := []int{1, 4, 8, 16, 32}
|
||||||
|
|
||||||
for _, length := range lengths {
|
for _, length := range lengths {
|
||||||
checksum := Checksum(input, length)
|
checksum := Checksum(input, length)
|
||||||
if len(checksum) != length {
|
if len(checksum) != length {
|
||||||
t.Errorf("Checksum length should be %d, got %d", length, len(checksum))
|
t.Errorf("Checksum length should be %d, got %d", length, len(checksum))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify checksum is last 'length' bytes of hash
|
// Verify checksum is last 'length' bytes of hash
|
||||||
fullHash := ComputeHash256Array(input)
|
fullHash := ComputeHash256Array(input)
|
||||||
expected := fullHash[len(fullHash)-length:]
|
expected := fullHash[len(fullHash)-length:]
|
||||||
@@ -120,14 +120,14 @@ func TestChecksum(t *testing.T) {
|
|||||||
t.Errorf("Checksum should be last %d bytes of hash", length)
|
t.Errorf("Checksum should be last %d bytes of hash", length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that same input produces same checksum
|
// Test that same input produces same checksum
|
||||||
checksum1 := Checksum(input, 4)
|
checksum1 := Checksum(input, 4)
|
||||||
checksum2 := Checksum(input, 4)
|
checksum2 := Checksum(input, 4)
|
||||||
if !bytes.Equal(checksum1, checksum2) {
|
if !bytes.Equal(checksum1, checksum2) {
|
||||||
t.Error("Same input should produce same checksum")
|
t.Error("Same input should produce same checksum")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that different input produces different checksum
|
// Test that different input produces different checksum
|
||||||
input2 := []byte("different input")
|
input2 := []byte("different input")
|
||||||
checksum3 := Checksum(input2, 4)
|
checksum3 := Checksum(input2, 4)
|
||||||
@@ -142,16 +142,16 @@ func TestToHash256(t *testing.T) {
|
|||||||
for i := range validBytes {
|
for i := range validBytes {
|
||||||
validBytes[i] = byte(i)
|
validBytes[i] = byte(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := ToHash256(validBytes)
|
hash, err := ToHash256(validBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("ToHash256 with valid bytes should not error: %v", err)
|
t.Errorf("ToHash256 with valid bytes should not error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(hash[:], validBytes) {
|
if !bytes.Equal(hash[:], validBytes) {
|
||||||
t.Error("ToHash256 should copy bytes correctly")
|
t.Error("ToHash256 should copy bytes correctly")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test invalid lengths
|
// Test invalid lengths
|
||||||
invalidLengths := []int{0, 1, 31, 33, 100}
|
invalidLengths := []int{0, 1, 31, 33, 100}
|
||||||
for _, length := range invalidLengths {
|
for _, length := range invalidLengths {
|
||||||
@@ -172,16 +172,16 @@ func TestToHash160(t *testing.T) {
|
|||||||
for i := range validBytes {
|
for i := range validBytes {
|
||||||
validBytes[i] = byte(i)
|
validBytes[i] = byte(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := ToHash160(validBytes)
|
hash, err := ToHash160(validBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("ToHash160 with valid bytes should not error: %v", err)
|
t.Errorf("ToHash160 with valid bytes should not error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(hash[:], validBytes) {
|
if !bytes.Equal(hash[:], validBytes) {
|
||||||
t.Error("ToHash160 should copy bytes correctly")
|
t.Error("ToHash160 should copy bytes correctly")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test invalid lengths
|
// Test invalid lengths
|
||||||
invalidLengths := []int{0, 1, 19, 21, 100}
|
invalidLengths := []int{0, 1, 19, 21, 100}
|
||||||
for _, length := range invalidLengths {
|
for _, length := range invalidLengths {
|
||||||
@@ -199,27 +199,27 @@ func TestToHash160(t *testing.T) {
|
|||||||
func TestPubkeyBytesToAddress(t *testing.T) {
|
func TestPubkeyBytesToAddress(t *testing.T) {
|
||||||
// Test that address generation is consistent
|
// Test that address generation is consistent
|
||||||
pubkey := []byte("test public key")
|
pubkey := []byte("test public key")
|
||||||
|
|
||||||
addr1 := PubkeyBytesToAddress(pubkey)
|
addr1 := PubkeyBytesToAddress(pubkey)
|
||||||
addr2 := PubkeyBytesToAddress(pubkey)
|
addr2 := PubkeyBytesToAddress(pubkey)
|
||||||
|
|
||||||
if !bytes.Equal(addr1, addr2) {
|
if !bytes.Equal(addr1, addr2) {
|
||||||
t.Error("Same pubkey should produce same address")
|
t.Error("Same pubkey should produce same address")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that different pubkeys produce different addresses
|
// Test that different pubkeys produce different addresses
|
||||||
pubkey2 := []byte("different public key")
|
pubkey2 := []byte("different public key")
|
||||||
addr3 := PubkeyBytesToAddress(pubkey2)
|
addr3 := PubkeyBytesToAddress(pubkey2)
|
||||||
|
|
||||||
if bytes.Equal(addr1, addr3) {
|
if bytes.Equal(addr1, addr3) {
|
||||||
t.Error("Different pubkeys should produce different addresses")
|
t.Error("Different pubkeys should produce different addresses")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that address is 20 bytes (ripemd160 size)
|
// Test that address is 20 bytes (ripemd160 size)
|
||||||
if len(addr1) != AddrLen {
|
if len(addr1) != AddrLen {
|
||||||
t.Errorf("Address should be %d bytes, got %d", AddrLen, len(addr1))
|
t.Errorf("Address should be %d bytes, got %d", AddrLen, len(addr1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test empty pubkey
|
// Test empty pubkey
|
||||||
emptyAddr := PubkeyBytesToAddress([]byte{})
|
emptyAddr := PubkeyBytesToAddress([]byte{})
|
||||||
if len(emptyAddr) != AddrLen {
|
if len(emptyAddr) != AddrLen {
|
||||||
@@ -232,7 +232,7 @@ func TestHashConstants(t *testing.T) {
|
|||||||
if HashLen != 32 {
|
if HashLen != 32 {
|
||||||
t.Errorf("HashLen should be 32, got %d", HashLen)
|
t.Errorf("HashLen should be 32, got %d", HashLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
if AddrLen != 20 {
|
if AddrLen != 20 {
|
||||||
t.Errorf("AddrLen should be 20, got %d", AddrLen)
|
t.Errorf("AddrLen should be 20, got %d", AddrLen)
|
||||||
}
|
}
|
||||||
@@ -243,7 +243,7 @@ func BenchmarkComputeHash256(b *testing.B) {
|
|||||||
for i := range input {
|
for i := range input {
|
||||||
input[i] = byte(i % 256)
|
input[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = ComputeHash256(input)
|
_ = ComputeHash256(input)
|
||||||
@@ -255,7 +255,7 @@ func BenchmarkComputeHash256Array(b *testing.B) {
|
|||||||
for i := range input {
|
for i := range input {
|
||||||
input[i] = byte(i % 256)
|
input[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = ComputeHash256Array(input)
|
_ = ComputeHash256Array(input)
|
||||||
@@ -267,7 +267,7 @@ func BenchmarkComputeHash160(b *testing.B) {
|
|||||||
for i := range input {
|
for i := range input {
|
||||||
input[i] = byte(i % 256)
|
input[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = ComputeHash160(input)
|
_ = ComputeHash160(input)
|
||||||
@@ -279,7 +279,7 @@ func BenchmarkPubkeyBytesToAddress(b *testing.B) {
|
|||||||
for i := range pubkey {
|
for i := range pubkey {
|
||||||
pubkey[i] = byte(i % 256)
|
pubkey[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = PubkeyBytesToAddress(pubkey)
|
_ = PubkeyBytesToAddress(pubkey)
|
||||||
@@ -291,9 +291,9 @@ func BenchmarkChecksum(b *testing.B) {
|
|||||||
for i := range input {
|
for i := range input {
|
||||||
input[i] = byte(i % 256)
|
input[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = Checksum(input, 4)
|
_ = Checksum(input, 4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -48,4 +48,4 @@ var NewSuite = hpke.NewSuite
|
|||||||
type Sender = hpke.Sender
|
type Sender = hpke.Sender
|
||||||
|
|
||||||
// Receiver represents an HPKE receiver context
|
// Receiver represents an HPKE receiver context
|
||||||
type Receiver = hpke.Receiver
|
type Receiver = hpke.Receiver
|
||||||
|
|||||||
+39
-39
@@ -48,7 +48,7 @@ type KeySchedule struct {
|
|||||||
// NewKeySchedule creates a new key schedule
|
// NewKeySchedule creates a new key schedule
|
||||||
func NewKeySchedule(suite Suite) *KeySchedule {
|
func NewKeySchedule(suite Suite) *KeySchedule {
|
||||||
var hashFunc func() hash.Hash
|
var hashFunc func() hash.Hash
|
||||||
|
|
||||||
switch suite.Hash {
|
switch suite.Hash {
|
||||||
case SHA256:
|
case SHA256:
|
||||||
hashFunc = sha256.New
|
hashFunc = sha256.New
|
||||||
@@ -57,7 +57,7 @@ func NewKeySchedule(suite Suite) *KeySchedule {
|
|||||||
default:
|
default:
|
||||||
hashFunc = sha256.New
|
hashFunc = sha256.New
|
||||||
}
|
}
|
||||||
|
|
||||||
return &KeySchedule{
|
return &KeySchedule{
|
||||||
suite: suite,
|
suite: suite,
|
||||||
hash: hashFunc,
|
hash: hashFunc,
|
||||||
@@ -68,39 +68,39 @@ func NewKeySchedule(suite Suite) *KeySchedule {
|
|||||||
func (ks *KeySchedule) DeriveHandshakeKeys(kemSecret, ecdheSecret []byte, transcript []byte) (*HandshakeKeys, error) {
|
func (ks *KeySchedule) DeriveHandshakeKeys(kemSecret, ecdheSecret []byte, transcript []byte) (*HandshakeKeys, error) {
|
||||||
// Concatenate secrets
|
// Concatenate secrets
|
||||||
combined := append(kemSecret, ecdheSecret...)
|
combined := append(kemSecret, ecdheSecret...)
|
||||||
|
|
||||||
// HKDF-Extract
|
// HKDF-Extract
|
||||||
salt := []byte("QZMQ-v1-Handshake")
|
salt := []byte("QZMQ-v1-Handshake")
|
||||||
prk := hkdf.Extract(ks.hash, combined, salt)
|
prk := hkdf.Extract(ks.hash, combined, salt)
|
||||||
|
|
||||||
// Derive various keys using HKDF-Expand
|
// Derive various keys using HKDF-Expand
|
||||||
keys := &HandshakeKeys{}
|
keys := &HandshakeKeys{}
|
||||||
|
|
||||||
// Client traffic secret
|
// Client traffic secret
|
||||||
clientInfo := append([]byte("client traffic secret"), transcript...)
|
clientInfo := append([]byte("client traffic secret"), transcript...)
|
||||||
clientSecret := ks.expand(prk, clientInfo, 32)
|
clientSecret := ks.expand(prk, clientInfo, 32)
|
||||||
|
|
||||||
// Server traffic secret
|
// Server traffic secret
|
||||||
serverInfo := append([]byte("server traffic secret"), transcript...)
|
serverInfo := append([]byte("server traffic secret"), transcript...)
|
||||||
serverSecret := ks.expand(prk, serverInfo, 32)
|
serverSecret := ks.expand(prk, serverInfo, 32)
|
||||||
|
|
||||||
// Derive actual keys and IVs from traffic secrets
|
// Derive actual keys and IVs from traffic secrets
|
||||||
keys.ClientKey = ks.expand(clientSecret, []byte("key"), 32)
|
keys.ClientKey = ks.expand(clientSecret, []byte("key"), 32)
|
||||||
keys.ClientIV = ks.expand(clientSecret, []byte("iv"), 12)
|
keys.ClientIV = ks.expand(clientSecret, []byte("iv"), 12)
|
||||||
keys.ServerKey = ks.expand(serverSecret, []byte("key"), 32)
|
keys.ServerKey = ks.expand(serverSecret, []byte("key"), 32)
|
||||||
keys.ServerIV = ks.expand(serverSecret, []byte("iv"), 12)
|
keys.ServerIV = ks.expand(serverSecret, []byte("iv"), 12)
|
||||||
|
|
||||||
// Exporter secret
|
// Exporter secret
|
||||||
exporterInfo := append([]byte("exporter secret"), transcript...)
|
exporterInfo := append([]byte("exporter secret"), transcript...)
|
||||||
keys.ExporterSecret = ks.expand(prk, exporterInfo, 32)
|
keys.ExporterSecret = ks.expand(prk, exporterInfo, 32)
|
||||||
|
|
||||||
// Generate key ID
|
// Generate key ID
|
||||||
keyIDBytes := ks.expand(prk, []byte("key id"), 4)
|
keyIDBytes := ks.expand(prk, []byte("key id"), 4)
|
||||||
keys.KeyID = binary.BigEndian.Uint32(keyIDBytes)
|
keys.KeyID = binary.BigEndian.Uint32(keyIDBytes)
|
||||||
|
|
||||||
ks.handshakeKeys = keys
|
ks.handshakeKeys = keys
|
||||||
ks.updateCounter = 0
|
ks.updateCounter = 0
|
||||||
|
|
||||||
return keys, nil
|
return keys, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,39 +109,39 @@ func (ks *KeySchedule) KeyUpdate() (*HandshakeKeys, error) {
|
|||||||
if ks.handshakeKeys == nil {
|
if ks.handshakeKeys == nil {
|
||||||
return nil, ErrNoHandshakeKeys
|
return nil, ErrNoHandshakeKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
ks.updateCounter++
|
ks.updateCounter++
|
||||||
|
|
||||||
// Create update info with counter
|
// Create update info with counter
|
||||||
updateInfo := make([]byte, 4)
|
updateInfo := make([]byte, 4)
|
||||||
binary.BigEndian.PutUint32(updateInfo, ks.updateCounter)
|
binary.BigEndian.PutUint32(updateInfo, ks.updateCounter)
|
||||||
|
|
||||||
// Ratchet client key
|
// Ratchet client key
|
||||||
newClientSecret := ks.expand(
|
newClientSecret := ks.expand(
|
||||||
ks.handshakeKeys.ClientKey,
|
ks.handshakeKeys.ClientKey,
|
||||||
append([]byte("key update"), updateInfo...),
|
append([]byte("key update"), updateInfo...),
|
||||||
32,
|
32,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ratchet server key
|
// Ratchet server key
|
||||||
newServerSecret := ks.expand(
|
newServerSecret := ks.expand(
|
||||||
ks.handshakeKeys.ServerKey,
|
ks.handshakeKeys.ServerKey,
|
||||||
append([]byte("key update"), updateInfo...),
|
append([]byte("key update"), updateInfo...),
|
||||||
32,
|
32,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Derive new keys
|
// Derive new keys
|
||||||
newKeys := &HandshakeKeys{
|
newKeys := &HandshakeKeys{
|
||||||
ClientKey: ks.expand(newClientSecret, []byte("key"), 32),
|
ClientKey: ks.expand(newClientSecret, []byte("key"), 32),
|
||||||
ClientIV: ks.expand(newClientSecret, []byte("iv"), 12),
|
ClientIV: ks.expand(newClientSecret, []byte("iv"), 12),
|
||||||
ServerKey: ks.expand(newServerSecret, []byte("key"), 32),
|
ServerKey: ks.expand(newServerSecret, []byte("key"), 32),
|
||||||
ServerIV: ks.expand(newServerSecret, []byte("iv"), 12),
|
ServerIV: ks.expand(newServerSecret, []byte("iv"), 12),
|
||||||
ExporterSecret: ks.handshakeKeys.ExporterSecret, // Exporter doesn't change
|
ExporterSecret: ks.handshakeKeys.ExporterSecret, // Exporter doesn't change
|
||||||
KeyID: ks.handshakeKeys.KeyID + ks.updateCounter,
|
KeyID: ks.handshakeKeys.KeyID + ks.updateCounter,
|
||||||
}
|
}
|
||||||
|
|
||||||
ks.handshakeKeys = newKeys
|
ks.handshakeKeys = newKeys
|
||||||
|
|
||||||
return newKeys, nil
|
return newKeys, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ func (ks *KeySchedule) Export(context []byte, length int) ([]byte, error) {
|
|||||||
if ks.handshakeKeys == nil {
|
if ks.handshakeKeys == nil {
|
||||||
return nil, ErrNoHandshakeKeys
|
return nil, ErrNoHandshakeKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
info := append([]byte("QZMQ exporter"), context...)
|
info := append([]byte("QZMQ exporter"), context...)
|
||||||
return ks.expand(ks.handshakeKeys.ExporterSecret, info, length), nil
|
return ks.expand(ks.handshakeKeys.ExporterSecret, info, length), nil
|
||||||
}
|
}
|
||||||
@@ -159,11 +159,11 @@ func (ks *KeySchedule) Export(context []byte, length int) ([]byte, error) {
|
|||||||
func (ks *KeySchedule) expand(prk, info []byte, length int) []byte {
|
func (ks *KeySchedule) expand(prk, info []byte, length int) []byte {
|
||||||
r := hkdf.Expand(ks.hash, prk, info)
|
r := hkdf.Expand(ks.hash, prk, info)
|
||||||
output := make([]byte, length)
|
output := make([]byte, length)
|
||||||
|
|
||||||
if _, err := io.ReadFull(r, output); err != nil {
|
if _, err := io.ReadFull(r, output); err != nil {
|
||||||
panic(err) // Should never happen with correct parameters
|
panic(err) // Should never happen with correct parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,22 +172,22 @@ func (ks *KeySchedule) DeriveEarlyDataKeys(psk []byte, transcript []byte) (*Hand
|
|||||||
// HKDF-Extract with PSK
|
// HKDF-Extract with PSK
|
||||||
salt := []byte("QZMQ-v1-EarlyData")
|
salt := []byte("QZMQ-v1-EarlyData")
|
||||||
prk := hkdf.Extract(ks.hash, psk, salt)
|
prk := hkdf.Extract(ks.hash, psk, salt)
|
||||||
|
|
||||||
// Derive early traffic secret
|
// Derive early traffic secret
|
||||||
earlyInfo := append([]byte("early traffic secret"), transcript...)
|
earlyInfo := append([]byte("early traffic secret"), transcript...)
|
||||||
earlySecret := ks.expand(prk, earlyInfo, 32)
|
earlySecret := ks.expand(prk, earlyInfo, 32)
|
||||||
|
|
||||||
// Derive keys for early data
|
// Derive keys for early data
|
||||||
keys := &HandshakeKeys{
|
keys := &HandshakeKeys{
|
||||||
ClientKey: ks.expand(earlySecret, []byte("key"), 32),
|
ClientKey: ks.expand(earlySecret, []byte("key"), 32),
|
||||||
ClientIV: ks.expand(earlySecret, []byte("iv"), 12),
|
ClientIV: ks.expand(earlySecret, []byte("iv"), 12),
|
||||||
// Server doesn't send early data, so no server keys
|
// Server doesn't send early data, so no server keys
|
||||||
ServerKey: nil,
|
ServerKey: nil,
|
||||||
ServerIV: nil,
|
ServerIV: nil,
|
||||||
ExporterSecret: ks.expand(prk, []byte("early exporter secret"), 32),
|
ExporterSecret: ks.expand(prk, []byte("early exporter secret"), 32),
|
||||||
KeyID: 0, // Special ID for early data
|
KeyID: 0, // Special ID for early data
|
||||||
}
|
}
|
||||||
|
|
||||||
return keys, nil
|
return keys, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ func (ks *KeySchedule) ResumptionSecret(transcript []byte) ([]byte, error) {
|
|||||||
if ks.handshakeKeys == nil {
|
if ks.handshakeKeys == nil {
|
||||||
return nil, ErrNoHandshakeKeys
|
return nil, ErrNoHandshakeKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
info := append([]byte("resumption secret"), transcript...)
|
info := append([]byte("resumption secret"), transcript...)
|
||||||
return ks.expand(ks.handshakeKeys.ExporterSecret, info, 32), nil
|
return ks.expand(ks.handshakeKeys.ExporterSecret, info, 32), nil
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ type Budgets struct {
|
|||||||
MaxMessages uint32
|
MaxMessages uint32
|
||||||
MaxBytes uint64
|
MaxBytes uint64
|
||||||
MaxAge int // seconds
|
MaxAge int // seconds
|
||||||
|
|
||||||
currentMessages uint32
|
currentMessages uint32
|
||||||
currentBytes uint64
|
currentBytes uint64
|
||||||
keyBirth int64 // unix timestamp
|
keyBirth int64 // unix timestamp
|
||||||
@@ -238,19 +238,19 @@ func NewBudgets(maxMsgs uint32, maxBytes uint64, maxAge int) *Budgets {
|
|||||||
func (b *Budgets) CheckAndUpdate(msgSize int, now int64) bool {
|
func (b *Budgets) CheckAndUpdate(msgSize int, now int64) bool {
|
||||||
b.currentMessages++
|
b.currentMessages++
|
||||||
b.currentBytes += uint64(msgSize)
|
b.currentBytes += uint64(msgSize)
|
||||||
|
|
||||||
if b.currentMessages >= b.MaxMessages {
|
if b.currentMessages >= b.MaxMessages {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.currentBytes >= b.MaxBytes {
|
if b.currentBytes >= b.MaxBytes {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.keyBirth > 0 && now-b.keyBirth >= int64(b.MaxAge) {
|
if b.keyBirth > 0 && now-b.keyBirth >= int64(b.MaxAge) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,4 +259,4 @@ func (b *Budgets) Reset(now int64) {
|
|||||||
b.currentMessages = 0
|
b.currentMessages = 0
|
||||||
b.currentBytes = 0
|
b.currentBytes = 0
|
||||||
b.keyBirth = now
|
b.keyBirth = now
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -19,7 +19,7 @@ func shouldUseCGO() bool {
|
|||||||
useCGO = false
|
useCGO = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to CGO if available (detected at build time)
|
// Default to CGO if available (detected at build time)
|
||||||
useCGO = cgoAvailable()
|
useCGO = cgoAvailable()
|
||||||
})
|
})
|
||||||
@@ -62,12 +62,12 @@ func NewHybrid() (KEM, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mlkem, err := NewMLKEM768()
|
mlkem, err := NewMLKEM768()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return newHybridKEM(x25519, mlkem)
|
return newHybridKEM(x25519, mlkem)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,4 +92,4 @@ func newHybridKEM(classical, pq KEM) (KEM, error) {
|
|||||||
x25519: classical,
|
x25519: classical,
|
||||||
mlkem: pq,
|
mlkem: pq,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-20
@@ -42,22 +42,22 @@ func (h *HybridKEMImpl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mlkemPK, mlkemSK, err := h.mlkem.GenerateKeyPair()
|
mlkemPK, mlkemSK, err := h.mlkem.GenerateKeyPair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pk := &HybridPublicKey{
|
pk := &HybridPublicKey{
|
||||||
X25519PK: x25519PK,
|
X25519PK: x25519PK,
|
||||||
MLKEMPK: mlkemPK,
|
MLKEMPK: mlkemPK,
|
||||||
}
|
}
|
||||||
|
|
||||||
sk := &HybridPrivateKey{
|
sk := &HybridPrivateKey{
|
||||||
X25519SK: x25519SK,
|
X25519SK: x25519SK,
|
||||||
MLKEMSK: mlkemSK,
|
MLKEMSK: mlkemSK,
|
||||||
}
|
}
|
||||||
|
|
||||||
return pk, sk, nil
|
return pk, sk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,25 +67,25 @@ func (h *HybridKEMImpl) Encapsulate(pk PublicKey) ([]byte, []byte, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, errors.New("invalid public key type for hybrid KEM")
|
return nil, nil, errors.New("invalid public key type for hybrid KEM")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform X25519 encapsulation
|
// Perform X25519 encapsulation
|
||||||
x25519CT, x25519SS, err := h.x25519.Encapsulate(hybridPK.X25519PK)
|
x25519CT, x25519SS, err := h.x25519.Encapsulate(hybridPK.X25519PK)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform ML-KEM encapsulation
|
// Perform ML-KEM encapsulation
|
||||||
mlkemCT, mlkemSS, err := h.mlkem.Encapsulate(hybridPK.MLKEMPK)
|
mlkemCT, mlkemSS, err := h.mlkem.Encapsulate(hybridPK.MLKEMPK)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concatenate ciphertexts
|
// Concatenate ciphertexts
|
||||||
ciphertext := append(x25519CT, mlkemCT...)
|
ciphertext := append(x25519CT, mlkemCT...)
|
||||||
|
|
||||||
// Derive shared secret using HKDF
|
// Derive shared secret using HKDF
|
||||||
sharedSecret := h.deriveSharedSecret(x25519SS, mlkemSS)
|
sharedSecret := h.deriveSharedSecret(x25519SS, mlkemSS)
|
||||||
|
|
||||||
return ciphertext, sharedSecret, nil
|
return ciphertext, sharedSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,31 +95,31 @@ func (h *HybridKEMImpl) Decapsulate(sk PrivateKey, ciphertext []byte) ([]byte, e
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("invalid private key type for hybrid KEM")
|
return nil, errors.New("invalid private key type for hybrid KEM")
|
||||||
}
|
}
|
||||||
|
|
||||||
x25519CTSize := h.x25519.CiphertextSize()
|
x25519CTSize := h.x25519.CiphertextSize()
|
||||||
if len(ciphertext) < x25519CTSize {
|
if len(ciphertext) < x25519CTSize {
|
||||||
return nil, errors.New("ciphertext too short")
|
return nil, errors.New("ciphertext too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split ciphertext
|
// Split ciphertext
|
||||||
x25519CT := ciphertext[:x25519CTSize]
|
x25519CT := ciphertext[:x25519CTSize]
|
||||||
mlkemCT := ciphertext[x25519CTSize:]
|
mlkemCT := ciphertext[x25519CTSize:]
|
||||||
|
|
||||||
// Perform X25519 decapsulation
|
// Perform X25519 decapsulation
|
||||||
x25519SS, err := h.x25519.Decapsulate(hybridSK.X25519SK, x25519CT)
|
x25519SS, err := h.x25519.Decapsulate(hybridSK.X25519SK, x25519CT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform ML-KEM decapsulation
|
// Perform ML-KEM decapsulation
|
||||||
mlkemSS, err := h.mlkem.Decapsulate(hybridSK.MLKEMSK, mlkemCT)
|
mlkemSS, err := h.mlkem.Decapsulate(hybridSK.MLKEMSK, mlkemCT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive shared secret using HKDF
|
// Derive shared secret using HKDF
|
||||||
sharedSecret := h.deriveSharedSecret(x25519SS, mlkemSS)
|
sharedSecret := h.deriveSharedSecret(x25519SS, mlkemSS)
|
||||||
|
|
||||||
return sharedSecret, nil
|
return sharedSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,18 +127,18 @@ func (h *HybridKEMImpl) Decapsulate(sk PrivateKey, ciphertext []byte) ([]byte, e
|
|||||||
func (h *HybridKEMImpl) deriveSharedSecret(x25519SS, mlkemSS []byte) []byte {
|
func (h *HybridKEMImpl) deriveSharedSecret(x25519SS, mlkemSS []byte) []byte {
|
||||||
// Concatenate secrets
|
// Concatenate secrets
|
||||||
combined := append(x25519SS, mlkemSS...)
|
combined := append(x25519SS, mlkemSS...)
|
||||||
|
|
||||||
// Use HKDF-Extract then Expand
|
// Use HKDF-Extract then Expand
|
||||||
salt := []byte("QZMQ-HybridKEM-v1")
|
salt := []byte("QZMQ-HybridKEM-v1")
|
||||||
info := []byte("hybrid-kem-shared-secret")
|
info := []byte("hybrid-kem-shared-secret")
|
||||||
|
|
||||||
hkdf := hkdf.New(sha256.New, combined, salt, info)
|
hkdf := hkdf.New(sha256.New, combined, salt, info)
|
||||||
sharedSecret := make([]byte, 32)
|
sharedSecret := make([]byte, 32)
|
||||||
|
|
||||||
if _, err := io.ReadFull(hkdf, sharedSecret); err != nil {
|
if _, err := io.ReadFull(hkdf, sharedSecret); err != nil {
|
||||||
panic(err) // Should never happen with correct sizes
|
panic(err) // Should never happen with correct sizes
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharedSecret
|
return sharedSecret
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,4 +193,4 @@ func (sk *HybridPrivateKey) Equal(other PrivateKey) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return sk.X25519SK.Equal(otherSK.X25519SK) && sk.MLKEMSK.Equal(otherSK.MLKEMSK)
|
return sk.X25519SK.Equal(otherSK.X25519SK) && sk.MLKEMSK.Equal(otherSK.MLKEMSK)
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -19,22 +19,22 @@ const (
|
|||||||
type KEM interface {
|
type KEM interface {
|
||||||
// GenerateKeyPair generates a new KEM key pair
|
// GenerateKeyPair generates a new KEM key pair
|
||||||
GenerateKeyPair() (PublicKey, PrivateKey, error)
|
GenerateKeyPair() (PublicKey, PrivateKey, error)
|
||||||
|
|
||||||
// Encapsulate generates a shared secret and ciphertext
|
// Encapsulate generates a shared secret and ciphertext
|
||||||
Encapsulate(pk PublicKey) (ciphertext []byte, sharedSecret []byte, err error)
|
Encapsulate(pk PublicKey) (ciphertext []byte, sharedSecret []byte, err error)
|
||||||
|
|
||||||
// Decapsulate recovers the shared secret from ciphertext
|
// Decapsulate recovers the shared secret from ciphertext
|
||||||
Decapsulate(sk PrivateKey, ciphertext []byte) (sharedSecret []byte, err error)
|
Decapsulate(sk PrivateKey, ciphertext []byte) (sharedSecret []byte, err error)
|
||||||
|
|
||||||
// PublicKeySize returns the size of public keys
|
// PublicKeySize returns the size of public keys
|
||||||
PublicKeySize() int
|
PublicKeySize() int
|
||||||
|
|
||||||
// PrivateKeySize returns the size of private keys
|
// PrivateKeySize returns the size of private keys
|
||||||
PrivateKeySize() int
|
PrivateKeySize() int
|
||||||
|
|
||||||
// CiphertextSize returns the size of ciphertexts
|
// CiphertextSize returns the size of ciphertexts
|
||||||
CiphertextSize() int
|
CiphertextSize() int
|
||||||
|
|
||||||
// SharedSecretSize returns the size of shared secrets
|
// SharedSecretSize returns the size of shared secrets
|
||||||
SharedSecretSize() int
|
SharedSecretSize() int
|
||||||
}
|
}
|
||||||
@@ -54,10 +54,10 @@ type PrivateKey interface {
|
|||||||
|
|
||||||
// Constants for ML-KEM-768
|
// Constants for ML-KEM-768
|
||||||
const (
|
const (
|
||||||
mlkem768PublicKeySize = 1184
|
mlkem768PublicKeySize = 1184
|
||||||
mlkem768PrivateKeySize = 2400
|
mlkem768PrivateKeySize = 2400
|
||||||
mlkem768CiphertextSize = 1088
|
mlkem768CiphertextSize = 1088
|
||||||
mlkem768SharedSecretSize = 32
|
mlkem768SharedSecretSize = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
// Constants for ML-KEM-1024
|
// Constants for ML-KEM-1024
|
||||||
@@ -82,4 +82,4 @@ func GetKEM(id KemID) (KEM, error) {
|
|||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported KEM: %s", id)
|
return nil, fmt.Errorf("unsupported KEM: %s", id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-27
@@ -14,7 +14,6 @@ type MLKEM768Impl struct {
|
|||||||
k int // k parameter (3 for ML-KEM-768)
|
k int // k parameter (3 for ML-KEM-768)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MLKEM768PublicKey represents an ML-KEM-768 public key
|
// MLKEM768PublicKey represents an ML-KEM-768 public key
|
||||||
type MLKEM768PublicKey struct {
|
type MLKEM768PublicKey struct {
|
||||||
data []byte
|
data []byte
|
||||||
@@ -26,12 +25,11 @@ type MLKEM768PrivateKey struct {
|
|||||||
pk *MLKEM768PublicKey
|
pk *MLKEM768PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// GenerateKeyPair generates a new ML-KEM-768 key pair
|
// GenerateKeyPair generates a new ML-KEM-768 key pair
|
||||||
func (m *MLKEM768Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
func (m *MLKEM768Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
||||||
// Placeholder for actual ML-KEM-768 key generation
|
// Placeholder for actual ML-KEM-768 key generation
|
||||||
// In production, this would use liboqs or a native implementation
|
// In production, this would use liboqs or a native implementation
|
||||||
|
|
||||||
pk := &MLKEM768PublicKey{
|
pk := &MLKEM768PublicKey{
|
||||||
data: make([]byte, mlkem768PublicKeySize),
|
data: make([]byte, mlkem768PublicKeySize),
|
||||||
}
|
}
|
||||||
@@ -39,7 +37,7 @@ func (m *MLKEM768Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
data: make([]byte, mlkem768PrivateKeySize),
|
data: make([]byte, mlkem768PrivateKeySize),
|
||||||
pk: pk,
|
pk: pk,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate random key material (placeholder)
|
// Generate random key material (placeholder)
|
||||||
if _, err := rand.Read(pk.data); err != nil {
|
if _, err := rand.Read(pk.data); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -47,7 +45,7 @@ func (m *MLKEM768Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
if _, err := rand.Read(sk.data); err != nil {
|
if _, err := rand.Read(sk.data); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pk, sk, nil
|
return pk, sk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,10 +55,10 @@ func (m *MLKEM768Impl) Encapsulate(pk PublicKey) ([]byte, []byte, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, errors.New("invalid public key type")
|
return nil, nil, errors.New("invalid public key type")
|
||||||
}
|
}
|
||||||
|
|
||||||
ciphertext := make([]byte, mlkem768CiphertextSize)
|
ciphertext := make([]byte, mlkem768CiphertextSize)
|
||||||
sharedSecret := make([]byte, mlkem768SharedSecretSize)
|
sharedSecret := make([]byte, mlkem768SharedSecretSize)
|
||||||
|
|
||||||
// Placeholder for actual ML-KEM-768 encapsulation
|
// Placeholder for actual ML-KEM-768 encapsulation
|
||||||
if _, err := rand.Read(ciphertext); err != nil {
|
if _, err := rand.Read(ciphertext); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -68,10 +66,10 @@ func (m *MLKEM768Impl) Encapsulate(pk PublicKey) ([]byte, []byte, error) {
|
|||||||
if _, err := rand.Read(sharedSecret); err != nil {
|
if _, err := rand.Read(sharedSecret); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// In production, this would perform actual ML-KEM encapsulation
|
// In production, this would perform actual ML-KEM encapsulation
|
||||||
_ = mlkemPK.data
|
_ = mlkemPK.data
|
||||||
|
|
||||||
return ciphertext, sharedSecret, nil
|
return ciphertext, sharedSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,22 +79,22 @@ func (m *MLKEM768Impl) Decapsulate(sk PrivateKey, ciphertext []byte) ([]byte, er
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("invalid private key type")
|
return nil, errors.New("invalid private key type")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ciphertext) != mlkem768CiphertextSize {
|
if len(ciphertext) != mlkem768CiphertextSize {
|
||||||
return nil, errors.New("invalid ciphertext size")
|
return nil, errors.New("invalid ciphertext size")
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedSecret := make([]byte, mlkem768SharedSecretSize)
|
sharedSecret := make([]byte, mlkem768SharedSecretSize)
|
||||||
|
|
||||||
// Placeholder for actual ML-KEM-768 decapsulation
|
// Placeholder for actual ML-KEM-768 decapsulation
|
||||||
if _, err := rand.Read(sharedSecret); err != nil {
|
if _, err := rand.Read(sharedSecret); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// In production, this would perform actual ML-KEM decapsulation
|
// In production, this would perform actual ML-KEM decapsulation
|
||||||
_ = mlkemSK.data
|
_ = mlkemSK.data
|
||||||
_ = ciphertext
|
_ = ciphertext
|
||||||
|
|
||||||
return sharedSecret, nil
|
return sharedSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +151,6 @@ func (sk *MLKEM768PrivateKey) Equal(other PrivateKey) bool {
|
|||||||
return subtle.ConstantTimeCompare(sk.data, otherSK.data) == 1
|
return subtle.ConstantTimeCompare(sk.data, otherSK.data) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MLKEM1024 implementation (similar structure, different parameters)
|
// MLKEM1024 implementation (similar structure, different parameters)
|
||||||
type MLKEM1024Impl struct {
|
type MLKEM1024Impl struct {
|
||||||
k int // k parameter (4 for ML-KEM-1024)
|
k int // k parameter (4 for ML-KEM-1024)
|
||||||
@@ -170,8 +167,6 @@ type MLKEM1024PrivateKey struct {
|
|||||||
pk *MLKEM1024PublicKey
|
pk *MLKEM1024PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// GenerateKeyPair generates a new ML-KEM-1024 key pair
|
// GenerateKeyPair generates a new ML-KEM-1024 key pair
|
||||||
func (m *MLKEM1024Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
func (m *MLKEM1024Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
||||||
// Similar to ML-KEM-768 but with different sizes
|
// Similar to ML-KEM-768 but with different sizes
|
||||||
@@ -182,14 +177,14 @@ func (m *MLKEM1024Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
data: make([]byte, mlkem1024PrivateKeySize),
|
data: make([]byte, mlkem1024PrivateKeySize),
|
||||||
pk: pk,
|
pk: pk,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := rand.Read(pk.data); err != nil {
|
if _, err := rand.Read(pk.data); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if _, err := rand.Read(sk.data); err != nil {
|
if _, err := rand.Read(sk.data); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pk, sk, nil
|
return pk, sk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,14 +192,14 @@ func (m *MLKEM1024Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
func (m *MLKEM1024Impl) Encapsulate(pk PublicKey) ([]byte, []byte, error) {
|
func (m *MLKEM1024Impl) Encapsulate(pk PublicKey) ([]byte, []byte, error) {
|
||||||
ciphertext := make([]byte, mlkem1024CiphertextSize)
|
ciphertext := make([]byte, mlkem1024CiphertextSize)
|
||||||
sharedSecret := make([]byte, mlkem1024SharedSecretSize)
|
sharedSecret := make([]byte, mlkem1024SharedSecretSize)
|
||||||
|
|
||||||
if _, err := rand.Read(ciphertext); err != nil {
|
if _, err := rand.Read(ciphertext); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if _, err := rand.Read(sharedSecret); err != nil {
|
if _, err := rand.Read(sharedSecret); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ciphertext, sharedSecret, nil
|
return ciphertext, sharedSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,19 +208,19 @@ func (m *MLKEM1024Impl) Decapsulate(sk PrivateKey, ciphertext []byte) ([]byte, e
|
|||||||
if len(ciphertext) != mlkem1024CiphertextSize {
|
if len(ciphertext) != mlkem1024CiphertextSize {
|
||||||
return nil, errors.New("invalid ciphertext size")
|
return nil, errors.New("invalid ciphertext size")
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedSecret := make([]byte, mlkem1024SharedSecretSize)
|
sharedSecret := make([]byte, mlkem1024SharedSecretSize)
|
||||||
if _, err := rand.Read(sharedSecret); err != nil {
|
if _, err := rand.Read(sharedSecret); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharedSecret, nil
|
return sharedSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size methods for ML-KEM-1024
|
// Size methods for ML-KEM-1024
|
||||||
func (m *MLKEM1024Impl) PublicKeySize() int { return mlkem1024PublicKeySize }
|
func (m *MLKEM1024Impl) PublicKeySize() int { return mlkem1024PublicKeySize }
|
||||||
func (m *MLKEM1024Impl) PrivateKeySize() int { return mlkem1024PrivateKeySize }
|
func (m *MLKEM1024Impl) PrivateKeySize() int { return mlkem1024PrivateKeySize }
|
||||||
func (m *MLKEM1024Impl) CiphertextSize() int { return mlkem1024CiphertextSize }
|
func (m *MLKEM1024Impl) CiphertextSize() int { return mlkem1024CiphertextSize }
|
||||||
func (m *MLKEM1024Impl) SharedSecretSize() int { return mlkem1024SharedSecretSize }
|
func (m *MLKEM1024Impl) SharedSecretSize() int { return mlkem1024SharedSecretSize }
|
||||||
|
|
||||||
// Bytes returns the raw bytes of the public key
|
// Bytes returns the raw bytes of the public key
|
||||||
@@ -260,4 +255,3 @@ func (sk *MLKEM1024PrivateKey) Equal(other PrivateKey) bool {
|
|||||||
}
|
}
|
||||||
return subtle.ConstantTimeCompare(sk.data, otherSK.data) == 1
|
return subtle.ConstantTimeCompare(sk.data, otherSK.data) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+34
-34
@@ -83,7 +83,7 @@ func NewMLKEM768CGO() (*MLKEM768CGO, error) {
|
|||||||
if kem == nil {
|
if kem == nil {
|
||||||
return nil, errors.New("failed to create ML-KEM-768 instance")
|
return nil, errors.New("failed to create ML-KEM-768 instance")
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &MLKEM768CGO{kem: kem}
|
m := &MLKEM768CGO{kem: kem}
|
||||||
runtime.SetFinalizer(m, (*MLKEM768CGO).cleanup)
|
runtime.SetFinalizer(m, (*MLKEM768CGO).cleanup)
|
||||||
return m, nil
|
return m, nil
|
||||||
@@ -102,7 +102,7 @@ func (m *MLKEM768CGO) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
if m.kem == nil {
|
if m.kem == nil {
|
||||||
return nil, nil, errors.New("KEM instance not initialized")
|
return nil, nil, errors.New("KEM instance not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
pk := &MLKEM768PublicKey{
|
pk := &MLKEM768PublicKey{
|
||||||
data: make([]byte, mlkem768PublicKeySize),
|
data: make([]byte, mlkem768PublicKeySize),
|
||||||
}
|
}
|
||||||
@@ -110,17 +110,17 @@ func (m *MLKEM768CGO) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
data: make([]byte, mlkem768PrivateKeySize),
|
data: make([]byte, mlkem768PrivateKeySize),
|
||||||
pk: pk,
|
pk: pk,
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := C.mlkem768_keypair(
|
ret := C.mlkem768_keypair(
|
||||||
m.kem,
|
m.kem,
|
||||||
(*C.uint8_t)(unsafe.Pointer(&pk.data[0])),
|
(*C.uint8_t)(unsafe.Pointer(&pk.data[0])),
|
||||||
(*C.uint8_t)(unsafe.Pointer(&sk.data[0])),
|
(*C.uint8_t)(unsafe.Pointer(&sk.data[0])),
|
||||||
)
|
)
|
||||||
|
|
||||||
if ret != C.OQS_SUCCESS_VAL {
|
if ret != C.OQS_SUCCESS_VAL {
|
||||||
return nil, nil, errors.New("ML-KEM-768 key generation failed")
|
return nil, nil, errors.New("ML-KEM-768 key generation failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return pk, sk, nil
|
return pk, sk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,26 +129,26 @@ func (m *MLKEM768CGO) Encapsulate(pk PublicKey) ([]byte, []byte, error) {
|
|||||||
if m.kem == nil {
|
if m.kem == nil {
|
||||||
return nil, nil, errors.New("KEM instance not initialized")
|
return nil, nil, errors.New("KEM instance not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
mlkemPK, ok := pk.(*MLKEM768PublicKey)
|
mlkemPK, ok := pk.(*MLKEM768PublicKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, errors.New("invalid public key type")
|
return nil, nil, errors.New("invalid public key type")
|
||||||
}
|
}
|
||||||
|
|
||||||
ciphertext := make([]byte, mlkem768CiphertextSize)
|
ciphertext := make([]byte, mlkem768CiphertextSize)
|
||||||
sharedSecret := make([]byte, mlkem768SharedSecretSize)
|
sharedSecret := make([]byte, mlkem768SharedSecretSize)
|
||||||
|
|
||||||
ret := C.mlkem768_encaps(
|
ret := C.mlkem768_encaps(
|
||||||
m.kem,
|
m.kem,
|
||||||
(*C.uint8_t)(unsafe.Pointer(&ciphertext[0])),
|
(*C.uint8_t)(unsafe.Pointer(&ciphertext[0])),
|
||||||
(*C.uint8_t)(unsafe.Pointer(&sharedSecret[0])),
|
(*C.uint8_t)(unsafe.Pointer(&sharedSecret[0])),
|
||||||
(*C.uint8_t)(unsafe.Pointer(&mlkemPK.data[0])),
|
(*C.uint8_t)(unsafe.Pointer(&mlkemPK.data[0])),
|
||||||
)
|
)
|
||||||
|
|
||||||
if ret != C.OQS_SUCCESS_VAL {
|
if ret != C.OQS_SUCCESS_VAL {
|
||||||
return nil, nil, errors.New("ML-KEM-768 encapsulation failed")
|
return nil, nil, errors.New("ML-KEM-768 encapsulation failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ciphertext, sharedSecret, nil
|
return ciphertext, sharedSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,29 +157,29 @@ func (m *MLKEM768CGO) Decapsulate(sk PrivateKey, ciphertext []byte) ([]byte, err
|
|||||||
if m.kem == nil {
|
if m.kem == nil {
|
||||||
return nil, errors.New("KEM instance not initialized")
|
return nil, errors.New("KEM instance not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
mlkemSK, ok := sk.(*MLKEM768PrivateKey)
|
mlkemSK, ok := sk.(*MLKEM768PrivateKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("invalid private key type")
|
return nil, errors.New("invalid private key type")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ciphertext) != mlkem768CiphertextSize {
|
if len(ciphertext) != mlkem768CiphertextSize {
|
||||||
return nil, errors.New("invalid ciphertext size")
|
return nil, errors.New("invalid ciphertext size")
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedSecret := make([]byte, mlkem768SharedSecretSize)
|
sharedSecret := make([]byte, mlkem768SharedSecretSize)
|
||||||
|
|
||||||
ret := C.mlkem768_decaps(
|
ret := C.mlkem768_decaps(
|
||||||
m.kem,
|
m.kem,
|
||||||
(*C.uint8_t)(unsafe.Pointer(&sharedSecret[0])),
|
(*C.uint8_t)(unsafe.Pointer(&sharedSecret[0])),
|
||||||
(*C.uint8_t)(unsafe.Pointer(&ciphertext[0])),
|
(*C.uint8_t)(unsafe.Pointer(&ciphertext[0])),
|
||||||
(*C.uint8_t)(unsafe.Pointer(&mlkemSK.data[0])),
|
(*C.uint8_t)(unsafe.Pointer(&mlkemSK.data[0])),
|
||||||
)
|
)
|
||||||
|
|
||||||
if ret != C.OQS_SUCCESS_VAL {
|
if ret != C.OQS_SUCCESS_VAL {
|
||||||
return nil, errors.New("ML-KEM-768 decapsulation failed")
|
return nil, errors.New("ML-KEM-768 decapsulation failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharedSecret, nil
|
return sharedSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +214,7 @@ func NewMLKEM1024CGO() (*MLKEM1024CGO, error) {
|
|||||||
if kem == nil {
|
if kem == nil {
|
||||||
return nil, errors.New("failed to create ML-KEM-1024 instance")
|
return nil, errors.New("failed to create ML-KEM-1024 instance")
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &MLKEM1024CGO{kem: kem}
|
m := &MLKEM1024CGO{kem: kem}
|
||||||
runtime.SetFinalizer(m, (*MLKEM1024CGO).cleanup)
|
runtime.SetFinalizer(m, (*MLKEM1024CGO).cleanup)
|
||||||
return m, nil
|
return m, nil
|
||||||
@@ -233,7 +233,7 @@ func (m *MLKEM1024CGO) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
if m.kem == nil {
|
if m.kem == nil {
|
||||||
return nil, nil, errors.New("KEM instance not initialized")
|
return nil, nil, errors.New("KEM instance not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
pk := &MLKEM1024PublicKey{
|
pk := &MLKEM1024PublicKey{
|
||||||
data: make([]byte, mlkem1024PublicKeySize),
|
data: make([]byte, mlkem1024PublicKeySize),
|
||||||
}
|
}
|
||||||
@@ -241,17 +241,17 @@ func (m *MLKEM1024CGO) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
data: make([]byte, mlkem1024PrivateKeySize),
|
data: make([]byte, mlkem1024PrivateKeySize),
|
||||||
pk: pk,
|
pk: pk,
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := C.mlkem1024_keypair(
|
ret := C.mlkem1024_keypair(
|
||||||
m.kem,
|
m.kem,
|
||||||
(*C.uint8_t)(unsafe.Pointer(&pk.data[0])),
|
(*C.uint8_t)(unsafe.Pointer(&pk.data[0])),
|
||||||
(*C.uint8_t)(unsafe.Pointer(&sk.data[0])),
|
(*C.uint8_t)(unsafe.Pointer(&sk.data[0])),
|
||||||
)
|
)
|
||||||
|
|
||||||
if ret != C.OQS_SUCCESS_VAL {
|
if ret != C.OQS_SUCCESS_VAL {
|
||||||
return nil, nil, errors.New("ML-KEM-1024 key generation failed")
|
return nil, nil, errors.New("ML-KEM-1024 key generation failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return pk, sk, nil
|
return pk, sk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,26 +260,26 @@ func (m *MLKEM1024CGO) Encapsulate(pk PublicKey) ([]byte, []byte, error) {
|
|||||||
if m.kem == nil {
|
if m.kem == nil {
|
||||||
return nil, nil, errors.New("KEM instance not initialized")
|
return nil, nil, errors.New("KEM instance not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
mlkemPK, ok := pk.(*MLKEM1024PublicKey)
|
mlkemPK, ok := pk.(*MLKEM1024PublicKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, errors.New("invalid public key type")
|
return nil, nil, errors.New("invalid public key type")
|
||||||
}
|
}
|
||||||
|
|
||||||
ciphertext := make([]byte, mlkem1024CiphertextSize)
|
ciphertext := make([]byte, mlkem1024CiphertextSize)
|
||||||
sharedSecret := make([]byte, mlkem1024SharedSecretSize)
|
sharedSecret := make([]byte, mlkem1024SharedSecretSize)
|
||||||
|
|
||||||
ret := C.mlkem1024_encaps(
|
ret := C.mlkem1024_encaps(
|
||||||
m.kem,
|
m.kem,
|
||||||
(*C.uint8_t)(unsafe.Pointer(&ciphertext[0])),
|
(*C.uint8_t)(unsafe.Pointer(&ciphertext[0])),
|
||||||
(*C.uint8_t)(unsafe.Pointer(&sharedSecret[0])),
|
(*C.uint8_t)(unsafe.Pointer(&sharedSecret[0])),
|
||||||
(*C.uint8_t)(unsafe.Pointer(&mlkemPK.data[0])),
|
(*C.uint8_t)(unsafe.Pointer(&mlkemPK.data[0])),
|
||||||
)
|
)
|
||||||
|
|
||||||
if ret != C.OQS_SUCCESS_VAL {
|
if ret != C.OQS_SUCCESS_VAL {
|
||||||
return nil, nil, errors.New("ML-KEM-1024 encapsulation failed")
|
return nil, nil, errors.New("ML-KEM-1024 encapsulation failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ciphertext, sharedSecret, nil
|
return ciphertext, sharedSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,29 +288,29 @@ func (m *MLKEM1024CGO) Decapsulate(sk PrivateKey, ciphertext []byte) ([]byte, er
|
|||||||
if m.kem == nil {
|
if m.kem == nil {
|
||||||
return nil, errors.New("KEM instance not initialized")
|
return nil, errors.New("KEM instance not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
mlkemSK, ok := sk.(*MLKEM1024PrivateKey)
|
mlkemSK, ok := sk.(*MLKEM1024PrivateKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("invalid private key type")
|
return nil, errors.New("invalid private key type")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ciphertext) != mlkem1024CiphertextSize {
|
if len(ciphertext) != mlkem1024CiphertextSize {
|
||||||
return nil, errors.New("invalid ciphertext size")
|
return nil, errors.New("invalid ciphertext size")
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedSecret := make([]byte, mlkem1024SharedSecretSize)
|
sharedSecret := make([]byte, mlkem1024SharedSecretSize)
|
||||||
|
|
||||||
ret := C.mlkem1024_decaps(
|
ret := C.mlkem1024_decaps(
|
||||||
m.kem,
|
m.kem,
|
||||||
(*C.uint8_t)(unsafe.Pointer(&sharedSecret[0])),
|
(*C.uint8_t)(unsafe.Pointer(&sharedSecret[0])),
|
||||||
(*C.uint8_t)(unsafe.Pointer(&ciphertext[0])),
|
(*C.uint8_t)(unsafe.Pointer(&ciphertext[0])),
|
||||||
(*C.uint8_t)(unsafe.Pointer(&mlkemSK.data[0])),
|
(*C.uint8_t)(unsafe.Pointer(&mlkemSK.data[0])),
|
||||||
)
|
)
|
||||||
|
|
||||||
if ret != C.OQS_SUCCESS_VAL {
|
if ret != C.OQS_SUCCESS_VAL {
|
||||||
return nil, errors.New("ML-KEM-1024 decapsulation failed")
|
return nil, errors.New("ML-KEM-1024 decapsulation failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharedSecret, nil
|
return sharedSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ func (m *MLKEM1024CGO) SharedSecretSize() int {
|
|||||||
return mlkem1024SharedSecretSize
|
return mlkem1024SharedSecretSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// cgoAvailable returns true when CGO is available
|
// cgoAvailable returns true when CGO is available
|
||||||
func cgoAvailable() bool {
|
func cgoAvailable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,4 +18,4 @@ func NewMLKEM768CGO() (KEM, error) {
|
|||||||
// NewMLKEM1024CGO returns an error when liboqs is not available
|
// NewMLKEM1024CGO returns an error when liboqs is not available
|
||||||
func NewMLKEM1024CGO() (KEM, error) {
|
func NewMLKEM1024CGO() (KEM, error) {
|
||||||
return nil, errors.New("liboqs not installed, using pure Go implementation")
|
return nil, errors.New("liboqs not installed, using pure Go implementation")
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -18,4 +18,4 @@ func NewMLKEM768CGO() (KEM, error) {
|
|||||||
// NewMLKEM1024CGO returns an error when CGO is disabled
|
// NewMLKEM1024CGO returns an error when CGO is disabled
|
||||||
func NewMLKEM1024CGO() (KEM, error) {
|
func NewMLKEM1024CGO() (KEM, error) {
|
||||||
return nil, errors.New("CGO disabled, using pure Go implementation")
|
return nil, errors.New("CGO disabled, using pure Go implementation")
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-20
@@ -21,7 +21,7 @@ type X25519PublicKey struct {
|
|||||||
data [32]byte
|
data [32]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// X25519PrivateKey represents an X25519 private key
|
// X25519PrivateKey represents an X25519 private key
|
||||||
type X25519PrivateKey struct {
|
type X25519PrivateKey struct {
|
||||||
data [32]byte
|
data [32]byte
|
||||||
pk *X25519PublicKey
|
pk *X25519PublicKey
|
||||||
@@ -30,22 +30,22 @@ type X25519PrivateKey struct {
|
|||||||
// GenerateKeyPair generates a new X25519 key pair
|
// GenerateKeyPair generates a new X25519 key pair
|
||||||
func (x *X25519Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
func (x *X25519Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
||||||
sk := &X25519PrivateKey{}
|
sk := &X25519PrivateKey{}
|
||||||
|
|
||||||
// Generate random private key
|
// Generate random private key
|
||||||
if _, err := rand.Read(sk.data[:]); err != nil {
|
if _, err := rand.Read(sk.data[:]); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clamp private key as per X25519 spec
|
// Clamp private key as per X25519 spec
|
||||||
sk.data[0] &= 248
|
sk.data[0] &= 248
|
||||||
sk.data[31] &= 127
|
sk.data[31] &= 127
|
||||||
sk.data[31] |= 64
|
sk.data[31] |= 64
|
||||||
|
|
||||||
// Compute public key
|
// Compute public key
|
||||||
pk := &X25519PublicKey{}
|
pk := &X25519PublicKey{}
|
||||||
curve25519.ScalarBaseMult(&pk.data, &sk.data)
|
curve25519.ScalarBaseMult(&pk.data, &sk.data)
|
||||||
sk.pk = pk
|
sk.pk = pk
|
||||||
|
|
||||||
return pk, sk, nil
|
return pk, sk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,31 +55,31 @@ func (x *X25519Impl) Encapsulate(pk PublicKey) ([]byte, []byte, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, errors.New("invalid public key type for X25519")
|
return nil, nil, errors.New("invalid public key type for X25519")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate ephemeral key pair
|
// Generate ephemeral key pair
|
||||||
ephSK := &X25519PrivateKey{}
|
ephSK := &X25519PrivateKey{}
|
||||||
if _, err := rand.Read(ephSK.data[:]); err != nil {
|
if _, err := rand.Read(ephSK.data[:]); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clamp ephemeral private key
|
// Clamp ephemeral private key
|
||||||
ephSK.data[0] &= 248
|
ephSK.data[0] &= 248
|
||||||
ephSK.data[31] &= 127
|
ephSK.data[31] &= 127
|
||||||
ephSK.data[31] |= 64
|
ephSK.data[31] |= 64
|
||||||
|
|
||||||
// Compute ephemeral public key (ciphertext)
|
// Compute ephemeral public key (ciphertext)
|
||||||
ephPK := &X25519PublicKey{}
|
ephPK := &X25519PublicKey{}
|
||||||
curve25519.ScalarBaseMult(&ephPK.data, &ephSK.data)
|
curve25519.ScalarBaseMult(&ephPK.data, &ephSK.data)
|
||||||
|
|
||||||
// Compute shared secret
|
// Compute shared secret
|
||||||
var sharedSecret [32]byte
|
var sharedSecret [32]byte
|
||||||
curve25519.ScalarMult(&sharedSecret, &ephSK.data, &x25519PK.data)
|
curve25519.ScalarMult(&sharedSecret, &ephSK.data, &x25519PK.data)
|
||||||
|
|
||||||
// Check for low-order points
|
// Check for low-order points
|
||||||
if isLowOrder(sharedSecret[:]) {
|
if isLowOrder(sharedSecret[:]) {
|
||||||
return nil, nil, errors.New("low-order shared secret")
|
return nil, nil, errors.New("low-order shared secret")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ephPK.data[:], sharedSecret[:], nil
|
return ephPK.data[:], sharedSecret[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,23 +89,23 @@ func (x *X25519Impl) Decapsulate(sk PrivateKey, ciphertext []byte) ([]byte, erro
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("invalid private key type for X25519")
|
return nil, errors.New("invalid private key type for X25519")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ciphertext) != 32 {
|
if len(ciphertext) != 32 {
|
||||||
return nil, errors.New("invalid ciphertext size for X25519")
|
return nil, errors.New("invalid ciphertext size for X25519")
|
||||||
}
|
}
|
||||||
|
|
||||||
var ephPK [32]byte
|
var ephPK [32]byte
|
||||||
copy(ephPK[:], ciphertext)
|
copy(ephPK[:], ciphertext)
|
||||||
|
|
||||||
// Compute shared secret
|
// Compute shared secret
|
||||||
var sharedSecret [32]byte
|
var sharedSecret [32]byte
|
||||||
curve25519.ScalarMult(&sharedSecret, &x25519SK.data, &ephPK)
|
curve25519.ScalarMult(&sharedSecret, &x25519SK.data, &ephPK)
|
||||||
|
|
||||||
// Check for low-order points
|
// Check for low-order points
|
||||||
if isLowOrder(sharedSecret[:]) {
|
if isLowOrder(sharedSecret[:]) {
|
||||||
return nil, errors.New("low-order shared secret")
|
return nil, errors.New("low-order shared secret")
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharedSecret[:], nil
|
return sharedSecret[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,9 +117,9 @@ func isLowOrder(p []byte) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Size methods for X25519
|
// Size methods for X25519
|
||||||
func (x *X25519Impl) PublicKeySize() int { return 32 }
|
func (x *X25519Impl) PublicKeySize() int { return 32 }
|
||||||
func (x *X25519Impl) PrivateKeySize() int { return 32 }
|
func (x *X25519Impl) PrivateKeySize() int { return 32 }
|
||||||
func (x *X25519Impl) CiphertextSize() int { return 32 }
|
func (x *X25519Impl) CiphertextSize() int { return 32 }
|
||||||
func (x *X25519Impl) SharedSecretSize() int { return 32 }
|
func (x *X25519Impl) SharedSecretSize() int { return 32 }
|
||||||
|
|
||||||
// Bytes returns the public key bytes
|
// Bytes returns the public key bytes
|
||||||
@@ -153,4 +153,4 @@ func (sk *X25519PrivateKey) Equal(other PrivateKey) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return subtle.ConstantTimeCompare(sk.data[:], otherSK.data[:]) == 1
|
return subtle.ConstantTimeCompare(sk.data[:], otherSK.data[:]) == 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import (
|
|||||||
|
|
||||||
func TestMLDSAKeyGeneration(t *testing.T) {
|
func TestMLDSAKeyGeneration(t *testing.T) {
|
||||||
modes := []struct {
|
modes := []struct {
|
||||||
name string
|
name string
|
||||||
mode Mode
|
mode Mode
|
||||||
pubSize int
|
pubSize int
|
||||||
privSize int
|
privSize int
|
||||||
sigSize int
|
sigSize int
|
||||||
}{
|
}{
|
||||||
{"ML-DSA-44", MLDSA44, MLDSA44PublicKeySize, MLDSA44PrivateKeySize, MLDSA44SignatureSize},
|
{"ML-DSA-44", MLDSA44, MLDSA44PublicKeySize, MLDSA44PrivateKeySize, MLDSA44SignatureSize},
|
||||||
{"ML-DSA-65", MLDSA65, MLDSA65PublicKeySize, MLDSA65PrivateKeySize, MLDSA65SignatureSize},
|
{"ML-DSA-65", MLDSA65, MLDSA65PublicKeySize, MLDSA65PrivateKeySize, MLDSA65SignatureSize},
|
||||||
@@ -49,7 +49,7 @@ func TestMLDSAKeyGeneration(t *testing.T) {
|
|||||||
|
|
||||||
func TestMLDSASignVerify(t *testing.T) {
|
func TestMLDSASignVerify(t *testing.T) {
|
||||||
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
t.Run(mode.String(), func(t *testing.T) {
|
t.Run(mode.String(), func(t *testing.T) {
|
||||||
privKey, err := GenerateKey(rand.Reader, mode)
|
privKey, err := GenerateKey(rand.Reader, mode)
|
||||||
@@ -58,7 +58,7 @@ func TestMLDSASignVerify(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message := []byte("Test message for ML-DSA signature")
|
message := []byte("Test message for ML-DSA signature")
|
||||||
|
|
||||||
// Sign message
|
// Sign message
|
||||||
signature, err := privKey.Sign(rand.Reader, message, nil)
|
signature, err := privKey.Sign(rand.Reader, message, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -102,7 +102,7 @@ func TestMLDSASignVerify(t *testing.T) {
|
|||||||
|
|
||||||
func TestMLDSAKeySerialization(t *testing.T) {
|
func TestMLDSAKeySerialization(t *testing.T) {
|
||||||
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
t.Run(mode.String(), func(t *testing.T) {
|
t.Run(mode.String(), func(t *testing.T) {
|
||||||
// Generate original key
|
// Generate original key
|
||||||
@@ -241,7 +241,7 @@ func TestMLDSAEdgeCases(t *testing.T) {
|
|||||||
|
|
||||||
func TestMLDSALargeMessage(t *testing.T) {
|
func TestMLDSALargeMessage(t *testing.T) {
|
||||||
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
t.Run(mode.String(), func(t *testing.T) {
|
t.Run(mode.String(), func(t *testing.T) {
|
||||||
privKey, err := GenerateKey(rand.Reader, mode)
|
privKey, err := GenerateKey(rand.Reader, mode)
|
||||||
@@ -280,12 +280,12 @@ func TestMLDSAConcurrency(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message := []byte("Concurrent test message")
|
message := []byte("Concurrent test message")
|
||||||
|
|
||||||
// Test concurrent signing
|
// Test concurrent signing
|
||||||
t.Run("ConcurrentSign", func(t *testing.T) {
|
t.Run("ConcurrentSign", func(t *testing.T) {
|
||||||
const numGoroutines = 10
|
const numGoroutines = 10
|
||||||
done := make(chan bool, numGoroutines)
|
done := make(chan bool, numGoroutines)
|
||||||
|
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(id int) {
|
go func(id int) {
|
||||||
msg := append(message, byte(id))
|
msg := append(message, byte(id))
|
||||||
@@ -300,7 +300,7 @@ func TestMLDSAConcurrency(t *testing.T) {
|
|||||||
done <- true
|
done <- true
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
@@ -311,7 +311,7 @@ func TestMLDSAConcurrency(t *testing.T) {
|
|||||||
signature, _ := privKey.Sign(rand.Reader, message, nil)
|
signature, _ := privKey.Sign(rand.Reader, message, nil)
|
||||||
const numGoroutines = 10
|
const numGoroutines = 10
|
||||||
done := make(chan bool, numGoroutines)
|
done := make(chan bool, numGoroutines)
|
||||||
|
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(id int) {
|
go func(id int) {
|
||||||
valid := privKey.PublicKey.Verify(message, signature, nil)
|
valid := privKey.PublicKey.Verify(message, signature, nil)
|
||||||
@@ -321,7 +321,7 @@ func TestMLDSAConcurrency(t *testing.T) {
|
|||||||
done <- true
|
done <- true
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
@@ -365,7 +365,7 @@ func BenchmarkMLDSASign(b *testing.B) {
|
|||||||
|
|
||||||
for _, m := range modes {
|
for _, m := range modes {
|
||||||
privKey, _ := GenerateKey(rand.Reader, m.mode)
|
privKey, _ := GenerateKey(rand.Reader, m.mode)
|
||||||
|
|
||||||
b.Run(m.name, func(b *testing.B) {
|
b.Run(m.name, func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -393,7 +393,7 @@ func BenchmarkMLDSAVerify(b *testing.B) {
|
|||||||
for _, m := range modes {
|
for _, m := range modes {
|
||||||
privKey, _ := GenerateKey(rand.Reader, m.mode)
|
privKey, _ := GenerateKey(rand.Reader, m.mode)
|
||||||
signature, _ := privKey.Sign(rand.Reader, message, nil)
|
signature, _ := privKey.Sign(rand.Reader, message, nil)
|
||||||
|
|
||||||
b.Run(m.name, func(b *testing.B) {
|
b.Run(m.name, func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -406,4 +406,4 @@ func BenchmarkMLDSAVerify(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions are now in mldsa.go
|
// Helper functions are now in mldsa.go
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
func TestOptimizedKeyGeneration(t *testing.T) {
|
func TestOptimizedKeyGeneration(t *testing.T) {
|
||||||
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
t.Run(mode.String(), func(t *testing.T) {
|
t.Run(mode.String(), func(t *testing.T) {
|
||||||
// Test optimized key generation
|
// Test optimized key generation
|
||||||
@@ -55,7 +55,7 @@ func TestOptimizedKeyGeneration(t *testing.T) {
|
|||||||
|
|
||||||
func TestOptimizedSign(t *testing.T) {
|
func TestOptimizedSign(t *testing.T) {
|
||||||
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
t.Run(mode.String(), func(t *testing.T) {
|
t.Run(mode.String(), func(t *testing.T) {
|
||||||
privKey, err := GenerateKey(rand.Reader, mode)
|
privKey, err := GenerateKey(rand.Reader, mode)
|
||||||
@@ -64,7 +64,7 @@ func TestOptimizedSign(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message := []byte("Test message for optimized signing")
|
message := []byte("Test message for optimized signing")
|
||||||
|
|
||||||
// Test optimized signing
|
// Test optimized signing
|
||||||
signature, err := privKey.OptimizedSign(rand.Reader, message, nil)
|
signature, err := privKey.OptimizedSign(rand.Reader, message, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -89,7 +89,7 @@ func TestOptimizedSign(t *testing.T) {
|
|||||||
|
|
||||||
func TestBatchDSA(t *testing.T) {
|
func TestBatchDSA(t *testing.T) {
|
||||||
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
t.Run(mode.String(), func(t *testing.T) {
|
t.Run(mode.String(), func(t *testing.T) {
|
||||||
numKeys := 5
|
numKeys := 5
|
||||||
@@ -101,7 +101,7 @@ func TestBatchDSA(t *testing.T) {
|
|||||||
// Prepare messages
|
// Prepare messages
|
||||||
messages := make([][]byte, numKeys)
|
messages := make([][]byte, numKeys)
|
||||||
for i := range messages {
|
for i := range messages {
|
||||||
messages[i] = []byte(string(rune('A' + i)) + " Test message for batch signing")
|
messages[i] = []byte(string(rune('A'+i)) + " Test message for batch signing")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Batch sign
|
// Batch sign
|
||||||
@@ -154,7 +154,7 @@ func TestBatchDSA(t *testing.T) {
|
|||||||
|
|
||||||
func TestPrecomputedMLDSA(t *testing.T) {
|
func TestPrecomputedMLDSA(t *testing.T) {
|
||||||
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
modes := []Mode{MLDSA44, MLDSA65, MLDSA87}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
t.Run(mode.String(), func(t *testing.T) {
|
t.Run(mode.String(), func(t *testing.T) {
|
||||||
privKey, err := GenerateKey(rand.Reader, mode)
|
privKey, err := GenerateKey(rand.Reader, mode)
|
||||||
@@ -165,7 +165,7 @@ func TestPrecomputedMLDSA(t *testing.T) {
|
|||||||
precomputed := NewPrecomputedMLDSA(privKey)
|
precomputed := NewPrecomputedMLDSA(privKey)
|
||||||
|
|
||||||
message := []byte("Test message for caching")
|
message := []byte("Test message for caching")
|
||||||
|
|
||||||
// First sign (cache miss)
|
// First sign (cache miss)
|
||||||
sig1, err := precomputed.SignCached(message)
|
sig1, err := precomputed.SignCached(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -339,7 +339,7 @@ func BenchmarkOptimizedSign(b *testing.B) {
|
|||||||
|
|
||||||
for _, m := range modes {
|
for _, m := range modes {
|
||||||
privKey, _ := GenerateKey(rand.Reader, m.mode)
|
privKey, _ := GenerateKey(rand.Reader, m.mode)
|
||||||
|
|
||||||
b.Run(m.name+"-Standard", func(b *testing.B) {
|
b.Run(m.name+"-Standard", func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -365,7 +365,7 @@ func BenchmarkOptimizedSign(b *testing.B) {
|
|||||||
func BenchmarkBatchOperations(b *testing.B) {
|
func BenchmarkBatchOperations(b *testing.B) {
|
||||||
numKeys := 10
|
numKeys := 10
|
||||||
batch, _ := NewBatchDSA(MLDSA65, numKeys)
|
batch, _ := NewBatchDSA(MLDSA65, numKeys)
|
||||||
|
|
||||||
messages := make([][]byte, numKeys)
|
messages := make([][]byte, numKeys)
|
||||||
for i := range messages {
|
for i := range messages {
|
||||||
messages[i] = make([]byte, 32)
|
messages[i] = make([]byte, 32)
|
||||||
@@ -393,4 +393,4 @@ func BenchmarkBatchOperations(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -302,4 +302,4 @@ func PrivateKeyFromBytes(data []byte, mode Mode) (*PrivateKey, error) {
|
|||||||
key: privKey,
|
key: privKey,
|
||||||
mode: mode,
|
mode: mode,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func BenchmarkMLKEMMemory(b *testing.B) {
|
|||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
priv, _, _ := GenerateKeyPair(rand.Reader, m.mode)
|
priv, _, _ := GenerateKeyPair(rand.Reader, m.mode)
|
||||||
result, _ := priv.PublicKey.Encapsulate(rand.Reader)
|
result, _ := priv.PublicKey.Encapsulate(rand.Reader)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
// Full KEM operation
|
// Full KEM operation
|
||||||
@@ -109,4 +109,4 @@ func BenchmarkMLKEMMemory(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
|
|
||||||
func TestMLKEMKeyGeneration(t *testing.T) {
|
func TestMLKEMKeyGeneration(t *testing.T) {
|
||||||
modes := []struct {
|
modes := []struct {
|
||||||
name string
|
name string
|
||||||
mode Mode
|
mode Mode
|
||||||
pubSize int
|
pubSize int
|
||||||
privSize int
|
privSize int
|
||||||
ctSize int
|
ctSize int
|
||||||
ssSize int
|
ssSize int
|
||||||
}{
|
}{
|
||||||
{"ML-KEM-512", MLKEM512, MLKEM512PublicKeySize, MLKEM512PrivateKeySize, MLKEM512CiphertextSize, MLKEM512SharedSecretSize},
|
{"ML-KEM-512", MLKEM512, MLKEM512PublicKeySize, MLKEM512PrivateKeySize, MLKEM512CiphertextSize, MLKEM512SharedSecretSize},
|
||||||
{"ML-KEM-768", MLKEM768, MLKEM768PublicKeySize, MLKEM768PrivateKeySize, MLKEM768CiphertextSize, MLKEM768SharedSecretSize},
|
{"ML-KEM-768", MLKEM768, MLKEM768PublicKeySize, MLKEM768PrivateKeySize, MLKEM768CiphertextSize, MLKEM768SharedSecretSize},
|
||||||
@@ -114,7 +114,7 @@ func TestMLKEMEncapsulateDecapsulate(t *testing.T) {
|
|||||||
tamperedCiphertext := make([]byte, len(encapResult.Ciphertext))
|
tamperedCiphertext := make([]byte, len(encapResult.Ciphertext))
|
||||||
copy(tamperedCiphertext, encapResult.Ciphertext)
|
copy(tamperedCiphertext, encapResult.Ciphertext)
|
||||||
tamperedCiphertext[0] ^= 0xFF
|
tamperedCiphertext[0] ^= 0xFF
|
||||||
|
|
||||||
// In our placeholder implementation, this will produce different shared secret
|
// In our placeholder implementation, this will produce different shared secret
|
||||||
tamperedSecret, err := privKey.Decapsulate(tamperedCiphertext)
|
tamperedSecret, err := privKey.Decapsulate(tamperedCiphertext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -301,12 +301,12 @@ func TestMLKEMConcurrency(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Goroutine %d: Encapsulate failed: %v", id, err)
|
t.Errorf("Goroutine %d: Encapsulate failed: %v", id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ss, err := privKey.Decapsulate(encapResult.Ciphertext)
|
ss, err := privKey.Decapsulate(encapResult.Ciphertext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Goroutine %d: Decapsulate failed: %v", id, err)
|
t.Errorf("Goroutine %d: Decapsulate failed: %v", id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(ss, encapResult.SharedSecret) {
|
if !bytes.Equal(ss, encapResult.SharedSecret) {
|
||||||
t.Errorf("Goroutine %d: Shared secrets don't match", id)
|
t.Errorf("Goroutine %d: Shared secrets don't match", id)
|
||||||
}
|
}
|
||||||
@@ -344,8 +344,6 @@ func TestMLKEMConcurrency(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Helper functions that were missing
|
// Helper functions that were missing
|
||||||
func NewPrivateKey(mode Mode) *PrivateKey {
|
func NewPrivateKey(mode Mode) *PrivateKey {
|
||||||
return &PrivateKey{
|
return &PrivateKey{
|
||||||
@@ -423,7 +421,7 @@ func BenchmarkMLKEMEncapsulate(b *testing.B) {
|
|||||||
|
|
||||||
for _, m := range modes {
|
for _, m := range modes {
|
||||||
privKey, _, _ := GenerateKeyPair(rand.Reader, m.mode)
|
privKey, _, _ := GenerateKeyPair(rand.Reader, m.mode)
|
||||||
|
|
||||||
b.Run(m.name, func(b *testing.B) {
|
b.Run(m.name, func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -449,7 +447,7 @@ func BenchmarkMLKEMDecapsulate(b *testing.B) {
|
|||||||
for _, m := range modes {
|
for _, m := range modes {
|
||||||
privKey, _, _ := GenerateKeyPair(rand.Reader, m.mode)
|
privKey, _, _ := GenerateKeyPair(rand.Reader, m.mode)
|
||||||
encapResult, _ := privKey.PublicKey.Encapsulate(rand.Reader)
|
encapResult, _ := privKey.PublicKey.Encapsulate(rand.Reader)
|
||||||
|
|
||||||
b.Run(m.name, func(b *testing.B) {
|
b.Run(m.name, func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -463,4 +461,4 @@ func BenchmarkMLKEMDecapsulate(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+56
-56
@@ -16,7 +16,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
// DefaultThreshold is the default threshold for MPC
|
// DefaultThreshold is the default threshold for MPC
|
||||||
DefaultThreshold = 3
|
DefaultThreshold = 3
|
||||||
|
|
||||||
// DefaultParties is the default number of parties
|
// DefaultParties is the default number of parties
|
||||||
DefaultParties = 5
|
DefaultParties = 5
|
||||||
)
|
)
|
||||||
@@ -30,20 +30,20 @@ var (
|
|||||||
|
|
||||||
// MPCAccount represents a per-account MPC configuration
|
// MPCAccount represents a per-account MPC configuration
|
||||||
type MPCAccount struct {
|
type MPCAccount struct {
|
||||||
AccountID ids.ShortID
|
AccountID ids.ShortID
|
||||||
Threshold int
|
Threshold int
|
||||||
Parties int
|
Parties int
|
||||||
PublicKey *PublicKey
|
PublicKey *PublicKey
|
||||||
Shares map[int]*Share
|
Shares map[int]*Share
|
||||||
Protocol Protocol
|
Protocol Protocol
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Share represents a secret share held by a party
|
// Share represents a secret share held by a party
|
||||||
type Share struct {
|
type Share struct {
|
||||||
Index int
|
Index int
|
||||||
Value *big.Int
|
Value *big.Int
|
||||||
Proof []byte
|
Proof []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKey represents an MPC public key
|
// PublicKey represents an MPC public key
|
||||||
@@ -86,15 +86,15 @@ func (m *Manager) CreateAccount(accountID ids.ShortID, threshold, parties int, p
|
|||||||
if parties < 2 {
|
if parties < 2 {
|
||||||
return nil, errInvalidParties
|
return nil, errInvalidParties
|
||||||
}
|
}
|
||||||
|
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
// Check if account already exists
|
// Check if account already exists
|
||||||
if _, exists := m.accounts[accountID]; exists {
|
if _, exists := m.accounts[accountID]; exists {
|
||||||
return nil, errors.New("account already exists")
|
return nil, errors.New("account already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate distributed key
|
// Generate distributed key
|
||||||
account := &MPCAccount{
|
account := &MPCAccount{
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
@@ -103,12 +103,12 @@ func (m *Manager) CreateAccount(accountID ids.ShortID, threshold, parties int, p
|
|||||||
Shares: make(map[int]*Share),
|
Shares: make(map[int]*Share),
|
||||||
Protocol: protocol,
|
Protocol: protocol,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate key shares
|
// Generate key shares
|
||||||
if err := account.generateKeyShares(); err != nil {
|
if err := account.generateKeyShares(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.accounts[accountID] = account
|
m.accounts[accountID] = account
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
@@ -117,12 +117,12 @@ func (m *Manager) CreateAccount(accountID ids.ShortID, threshold, parties int, p
|
|||||||
func (m *Manager) GetAccount(accountID ids.ShortID) (*MPCAccount, error) {
|
func (m *Manager) GetAccount(accountID ids.ShortID) (*MPCAccount, error) {
|
||||||
m.mu.RLock()
|
m.mu.RLock()
|
||||||
defer m.mu.RUnlock()
|
defer m.mu.RUnlock()
|
||||||
|
|
||||||
account, exists := m.accounts[accountID]
|
account, exists := m.accounts[accountID]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("account not found")
|
return nil, errors.New("account not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,10 +137,10 @@ func (a *MPCAccount) generateKeyShares() error {
|
|||||||
}
|
}
|
||||||
coeffs[i] = coeff
|
coeffs[i] = coeff
|
||||||
}
|
}
|
||||||
|
|
||||||
// The secret is the constant term
|
// The secret is the constant term
|
||||||
secret := coeffs[0]
|
secret := coeffs[0]
|
||||||
|
|
||||||
// Generate shares for each party
|
// Generate shares for each party
|
||||||
for i := 1; i <= a.Parties; i++ {
|
for i := 1; i <= a.Parties; i++ {
|
||||||
share := evaluatePolynomial(coeffs, big.NewInt(int64(i)))
|
share := evaluatePolynomial(coeffs, big.NewInt(int64(i)))
|
||||||
@@ -150,12 +150,12 @@ func (a *MPCAccount) generateKeyShares() error {
|
|||||||
Proof: generateShareProof(share, i),
|
Proof: generateShareProof(share, i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute public key
|
// Compute public key
|
||||||
a.PublicKey = &PublicKey{
|
a.PublicKey = &PublicKey{
|
||||||
Point: scalarBaseMult(secret),
|
Point: scalarBaseMult(secret),
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,22 +163,22 @@ func (a *MPCAccount) generateKeyShares() error {
|
|||||||
func (a *MPCAccount) Sign(message []byte, participatingShares map[int]*Share) ([]byte, error) {
|
func (a *MPCAccount) Sign(message []byte, participatingShares map[int]*Share) ([]byte, error) {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
|
|
||||||
if len(participatingShares) < a.Threshold {
|
if len(participatingShares) < a.Threshold {
|
||||||
return nil, errNotEnoughShares
|
return nil, errNotEnoughShares
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify shares
|
// Verify shares
|
||||||
for index, share := range participatingShares {
|
for index, share := range participatingShares {
|
||||||
if !a.verifyShare(index, share) {
|
if !a.verifyShare(index, share) {
|
||||||
return nil, errInvalidShare
|
return nil, errInvalidShare
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute partial signatures
|
// Compute partial signatures
|
||||||
messageHash := sha256.Sum256(message)
|
messageHash := sha256.Sum256(message)
|
||||||
partialSigs := make(map[int]*big.Int)
|
partialSigs := make(map[int]*big.Int)
|
||||||
|
|
||||||
for index, share := range participatingShares {
|
for index, share := range participatingShares {
|
||||||
// Simplified partial signature computation
|
// Simplified partial signature computation
|
||||||
// In production, use actual protocol (GG18/GG20/CMP)
|
// In production, use actual protocol (GG18/GG20/CMP)
|
||||||
@@ -186,10 +186,10 @@ func (a *MPCAccount) Sign(message []byte, participatingShares map[int]*Share) ([
|
|||||||
partialSig.Mod(partialSig, curveOrder())
|
partialSig.Mod(partialSig, curveOrder())
|
||||||
partialSigs[index] = partialSig
|
partialSigs[index] = partialSig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine partial signatures using Lagrange interpolation
|
// Combine partial signatures using Lagrange interpolation
|
||||||
signature := combinePartialSignatures(partialSigs, a.Threshold)
|
signature := combinePartialSignatures(partialSigs, a.Threshold)
|
||||||
|
|
||||||
return signature.Bytes(), nil
|
return signature.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,16 +197,16 @@ func (a *MPCAccount) Sign(message []byte, participatingShares map[int]*Share) ([
|
|||||||
func (a *MPCAccount) Verify(message []byte, signature []byte) bool {
|
func (a *MPCAccount) Verify(message []byte, signature []byte) bool {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
|
|
||||||
// Simplified verification
|
// Simplified verification
|
||||||
// In production, implement actual signature verification
|
// In production, implement actual signature verification
|
||||||
messageHash := sha256.Sum256(message)
|
messageHash := sha256.Sum256(message)
|
||||||
sig := new(big.Int).SetBytes(signature)
|
sig := new(big.Int).SetBytes(signature)
|
||||||
|
|
||||||
// Verify using public key
|
// Verify using public key
|
||||||
expectedPoint := scalarBaseMult(sig)
|
expectedPoint := scalarBaseMult(sig)
|
||||||
messagePoint := scalarBaseMult(new(big.Int).SetBytes(messageHash[:]))
|
messagePoint := scalarBaseMult(new(big.Int).SetBytes(messageHash[:]))
|
||||||
|
|
||||||
// Check if signature is valid (simplified)
|
// Check if signature is valid (simplified)
|
||||||
return pointsEqual(expectedPoint, messagePoint)
|
return pointsEqual(expectedPoint, messagePoint)
|
||||||
}
|
}
|
||||||
@@ -215,15 +215,15 @@ func (a *MPCAccount) Verify(message []byte, signature []byte) bool {
|
|||||||
func (a *MPCAccount) AddShare(index int, share *Share) error {
|
func (a *MPCAccount) AddShare(index int, share *Share) error {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
defer a.mu.Unlock()
|
defer a.mu.Unlock()
|
||||||
|
|
||||||
if index < 1 || index > a.Parties {
|
if index < 1 || index > a.Parties {
|
||||||
return errors.New("invalid share index")
|
return errors.New("invalid share index")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !a.verifyShare(index, share) {
|
if !a.verifyShare(index, share) {
|
||||||
return errInvalidShare
|
return errInvalidShare
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Shares[index] = share
|
a.Shares[index] = share
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -232,12 +232,12 @@ func (a *MPCAccount) AddShare(index int, share *Share) error {
|
|||||||
func (a *MPCAccount) GetShare(index int) (*Share, error) {
|
func (a *MPCAccount) GetShare(index int) (*Share, error) {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
|
|
||||||
share, exists := a.Shares[index]
|
share, exists := a.Shares[index]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("share not found")
|
return nil, errors.New("share not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
return share, nil
|
return share, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,18 +245,18 @@ func (a *MPCAccount) GetShare(index int) (*Share, error) {
|
|||||||
func (a *MPCAccount) RefreshShares() error {
|
func (a *MPCAccount) RefreshShares() error {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
defer a.mu.Unlock()
|
defer a.mu.Unlock()
|
||||||
|
|
||||||
// Generate new random polynomial with same secret
|
// Generate new random polynomial with same secret
|
||||||
oldShares := a.Shares
|
oldShares := a.Shares
|
||||||
a.Shares = make(map[int]*Share)
|
a.Shares = make(map[int]*Share)
|
||||||
|
|
||||||
// Keep the same secret (constant term)
|
// Keep the same secret (constant term)
|
||||||
secret := reconstructSecret(oldShares, a.Threshold)
|
secret := reconstructSecret(oldShares, a.Threshold)
|
||||||
|
|
||||||
// Generate new polynomial with same secret
|
// Generate new polynomial with same secret
|
||||||
coeffs := make([]*big.Int, a.Threshold)
|
coeffs := make([]*big.Int, a.Threshold)
|
||||||
coeffs[0] = secret
|
coeffs[0] = secret
|
||||||
|
|
||||||
for i := 1; i < a.Threshold; i++ {
|
for i := 1; i < a.Threshold; i++ {
|
||||||
coeff, err := rand.Int(rand.Reader, curveOrder())
|
coeff, err := rand.Int(rand.Reader, curveOrder())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -264,7 +264,7 @@ func (a *MPCAccount) RefreshShares() error {
|
|||||||
}
|
}
|
||||||
coeffs[i] = coeff
|
coeffs[i] = coeff
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate new shares
|
// Generate new shares
|
||||||
for i := 1; i <= a.Parties; i++ {
|
for i := 1; i <= a.Parties; i++ {
|
||||||
share := evaluatePolynomial(coeffs, big.NewInt(int64(i)))
|
share := evaluatePolynomial(coeffs, big.NewInt(int64(i)))
|
||||||
@@ -274,7 +274,7 @@ func (a *MPCAccount) RefreshShares() error {
|
|||||||
Proof: generateShareProof(share, i),
|
Proof: generateShareProof(share, i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,16 +300,16 @@ func pointsEqual(p1, p2 *Point) bool {
|
|||||||
func evaluatePolynomial(coeffs []*big.Int, x *big.Int) *big.Int {
|
func evaluatePolynomial(coeffs []*big.Int, x *big.Int) *big.Int {
|
||||||
result := new(big.Int).Set(coeffs[0])
|
result := new(big.Int).Set(coeffs[0])
|
||||||
xPower := new(big.Int).Set(x)
|
xPower := new(big.Int).Set(x)
|
||||||
|
|
||||||
for i := 1; i < len(coeffs); i++ {
|
for i := 1; i < len(coeffs); i++ {
|
||||||
term := new(big.Int).Mul(coeffs[i], xPower)
|
term := new(big.Int).Mul(coeffs[i], xPower)
|
||||||
result.Add(result, term)
|
result.Add(result, term)
|
||||||
result.Mod(result, curveOrder())
|
result.Mod(result, curveOrder())
|
||||||
|
|
||||||
xPower.Mul(xPower, x)
|
xPower.Mul(xPower, x)
|
||||||
xPower.Mod(xPower, curveOrder())
|
xPower.Mod(xPower, curveOrder())
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,33 +327,33 @@ func (a *MPCAccount) verifyShare(index int, share *Share) bool {
|
|||||||
if len(share.Proof) != len(expectedProof) {
|
if len(share.Proof) != len(expectedProof) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range expectedProof {
|
for i := range expectedProof {
|
||||||
if expectedProof[i] != share.Proof[i] {
|
if expectedProof[i] != share.Proof[i] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func combinePartialSignatures(partialSigs map[int]*big.Int, threshold int) *big.Int {
|
func combinePartialSignatures(partialSigs map[int]*big.Int, threshold int) *big.Int {
|
||||||
result := big.NewInt(0)
|
result := big.NewInt(0)
|
||||||
indices := make([]int, 0, len(partialSigs))
|
indices := make([]int, 0, len(partialSigs))
|
||||||
|
|
||||||
for index := range partialSigs {
|
for index := range partialSigs {
|
||||||
indices = append(indices, index)
|
indices = append(indices, index)
|
||||||
if len(indices) == threshold {
|
if len(indices) == threshold {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, i := range indices {
|
for _, i := range indices {
|
||||||
lagrangeCoeff := computeLagrangeCoefficient(i, indices)
|
lagrangeCoeff := computeLagrangeCoefficient(i, indices)
|
||||||
term := new(big.Int).Mul(partialSigs[i], lagrangeCoeff)
|
term := new(big.Int).Mul(partialSigs[i], lagrangeCoeff)
|
||||||
result.Add(result, term)
|
result.Add(result, term)
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Mod(result, curveOrder())
|
result.Mod(result, curveOrder())
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -361,19 +361,19 @@ func combinePartialSignatures(partialSigs map[int]*big.Int, threshold int) *big.
|
|||||||
func computeLagrangeCoefficient(i int, indices []int) *big.Int {
|
func computeLagrangeCoefficient(i int, indices []int) *big.Int {
|
||||||
num := big.NewInt(1)
|
num := big.NewInt(1)
|
||||||
den := big.NewInt(1)
|
den := big.NewInt(1)
|
||||||
|
|
||||||
for _, j := range indices {
|
for _, j := range indices {
|
||||||
if i != j {
|
if i != j {
|
||||||
num.Mul(num, big.NewInt(int64(-j)))
|
num.Mul(num, big.NewInt(int64(-j)))
|
||||||
den.Mul(den, big.NewInt(int64(i-j)))
|
den.Mul(den, big.NewInt(int64(i-j)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute num/den mod curveOrder
|
// Compute num/den mod curveOrder
|
||||||
denInv := new(big.Int).ModInverse(den, curveOrder())
|
denInv := new(big.Int).ModInverse(den, curveOrder())
|
||||||
result := new(big.Int).Mul(num, denInv)
|
result := new(big.Int).Mul(num, denInv)
|
||||||
result.Mod(result, curveOrder())
|
result.Mod(result, curveOrder())
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +381,7 @@ func reconstructSecret(shares map[int]*Share, threshold int) *big.Int {
|
|||||||
if len(shares) < threshold {
|
if len(shares) < threshold {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
partialValues := make(map[int]*big.Int)
|
partialValues := make(map[int]*big.Int)
|
||||||
for index, share := range shares {
|
for index, share := range shares {
|
||||||
partialValues[index] = share.Value
|
partialValues[index] = share.Value
|
||||||
@@ -389,6 +389,6 @@ func reconstructSecret(shares map[int]*Share, threshold int) *big.Int {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return combinePartialSignatures(partialValues, threshold)
|
return combinePartialSignatures(partialValues, threshold)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,4 +186,4 @@ func BenchmarkPQCrypto(b *testing.B) {
|
|||||||
_, _ = priv.Sign(rand.Reader, message, nil)
|
_, _ = priv.Sign(rand.Reader, message, nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -305,7 +305,6 @@ func (b *BLSHashToPoint) Run(input []byte) ([]byte, error) {
|
|||||||
return g2Point, nil
|
return g2Point, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// RegisterBLS registers all BLS precompiles
|
// RegisterBLS registers all BLS precompiles
|
||||||
func RegisterBLS(registry *Registry) {
|
func RegisterBLS(registry *Registry) {
|
||||||
registry.Register(BLSVerifyAddress, &BLSVerify{})
|
registry.Register(BLSVerifyAddress, &BLSVerify{})
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ func (l *LamportBatchVerify) Run(input []byte) ([]byte, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// message is actually a pre-hashed value
|
// message is actually a pre-hashed value
|
||||||
if pubKey.VerifyHash(message, sig) {
|
if pubKey.VerifyHash(message, sig) {
|
||||||
results[i] = 0x01
|
results[i] = 0x01
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+147
-147
@@ -18,8 +18,8 @@ import (
|
|||||||
|
|
||||||
// Test vectors for SHAKE from NIST
|
// Test vectors for SHAKE from NIST
|
||||||
var shakeTestVectors = []struct {
|
var shakeTestVectors = []struct {
|
||||||
name string
|
name string
|
||||||
input string
|
input string
|
||||||
output128 string // First 32 bytes of SHAKE128
|
output128 string // First 32 bytes of SHAKE128
|
||||||
output256 string // First 32 bytes of SHAKE256
|
output256 string // First 32 bytes of SHAKE256
|
||||||
}{
|
}{
|
||||||
@@ -47,22 +47,22 @@ var shakeTestVectors = []struct {
|
|||||||
func TestSHAKEPrecompiles(t *testing.T) {
|
func TestSHAKEPrecompiles(t *testing.T) {
|
||||||
t.Run("SHAKE128", func(t *testing.T) {
|
t.Run("SHAKE128", func(t *testing.T) {
|
||||||
shake128 := &SHAKE128{}
|
shake128 := &SHAKE128{}
|
||||||
|
|
||||||
for _, tv := range shakeTestVectors {
|
for _, tv := range shakeTestVectors {
|
||||||
t.Run(tv.name, func(t *testing.T) {
|
t.Run(tv.name, func(t *testing.T) {
|
||||||
inputData, _ := hex.DecodeString(tv.input)
|
inputData, _ := hex.DecodeString(tv.input)
|
||||||
expectedOutput, _ := hex.DecodeString(tv.output128)
|
expectedOutput, _ := hex.DecodeString(tv.output128)
|
||||||
|
|
||||||
// Create input with 32-byte output length
|
// Create input with 32-byte output length
|
||||||
input := make([]byte, 4+len(inputData))
|
input := make([]byte, 4+len(inputData))
|
||||||
binary.BigEndian.PutUint32(input[:4], 32)
|
binary.BigEndian.PutUint32(input[:4], 32)
|
||||||
copy(input[4:], inputData)
|
copy(input[4:], inputData)
|
||||||
|
|
||||||
// Test gas calculation
|
// Test gas calculation
|
||||||
gas := shake128.RequiredGas(input)
|
gas := shake128.RequiredGas(input)
|
||||||
expectedGas := uint64(60 + 12*((len(input)+31)/32) + 3)
|
expectedGas := uint64(60 + 12*((len(input)+31)/32) + 3)
|
||||||
assert.Equal(t, expectedGas, gas, "Gas calculation mismatch")
|
assert.Equal(t, expectedGas, gas, "Gas calculation mismatch")
|
||||||
|
|
||||||
// Test output
|
// Test output
|
||||||
output, err := shake128.Run(input)
|
output, err := shake128.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -73,22 +73,22 @@ func TestSHAKEPrecompiles(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("SHAKE256", func(t *testing.T) {
|
t.Run("SHAKE256", func(t *testing.T) {
|
||||||
shake256 := &SHAKE256{}
|
shake256 := &SHAKE256{}
|
||||||
|
|
||||||
for _, tv := range shakeTestVectors {
|
for _, tv := range shakeTestVectors {
|
||||||
t.Run(tv.name, func(t *testing.T) {
|
t.Run(tv.name, func(t *testing.T) {
|
||||||
inputData, _ := hex.DecodeString(tv.input)
|
inputData, _ := hex.DecodeString(tv.input)
|
||||||
expectedOutput, _ := hex.DecodeString(tv.output256)
|
expectedOutput, _ := hex.DecodeString(tv.output256)
|
||||||
|
|
||||||
// Create input with 32-byte output length
|
// Create input with 32-byte output length
|
||||||
input := make([]byte, 4+len(inputData))
|
input := make([]byte, 4+len(inputData))
|
||||||
binary.BigEndian.PutUint32(input[:4], 32)
|
binary.BigEndian.PutUint32(input[:4], 32)
|
||||||
copy(input[4:], inputData)
|
copy(input[4:], inputData)
|
||||||
|
|
||||||
// Test gas calculation
|
// Test gas calculation
|
||||||
gas := shake256.RequiredGas(input)
|
gas := shake256.RequiredGas(input)
|
||||||
expectedGas := uint64(60 + 12*((len(input)+31)/32) + 3)
|
expectedGas := uint64(60 + 12*((len(input)+31)/32) + 3)
|
||||||
assert.Equal(t, expectedGas, gas, "Gas calculation mismatch")
|
assert.Equal(t, expectedGas, gas, "Gas calculation mismatch")
|
||||||
|
|
||||||
// Test output
|
// Test output
|
||||||
output, err := shake256.Run(input)
|
output, err := shake256.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -100,35 +100,35 @@ func TestSHAKEPrecompiles(t *testing.T) {
|
|||||||
t.Run("SHAKE256_Fixed", func(t *testing.T) {
|
t.Run("SHAKE256_Fixed", func(t *testing.T) {
|
||||||
// Test fixed output variants
|
// Test fixed output variants
|
||||||
variants := []struct {
|
variants := []struct {
|
||||||
name string
|
name string
|
||||||
precompile PrecompiledContract
|
precompile PrecompiledContract
|
||||||
outputLen int
|
outputLen int
|
||||||
gas uint64
|
gas uint64
|
||||||
}{
|
}{
|
||||||
{"SHAKE256_256", &SHAKE256_256{}, 32, 200},
|
{"SHAKE256_256", &SHAKE256_256{}, 32, 200},
|
||||||
{"SHAKE256_512", &SHAKE256_512{}, 64, 250},
|
{"SHAKE256_512", &SHAKE256_512{}, 64, 250},
|
||||||
{"SHAKE256_1024", &SHAKE256_1024{}, 128, 350},
|
{"SHAKE256_1024", &SHAKE256_1024{}, 128, 350},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range variants {
|
for _, v := range variants {
|
||||||
t.Run(v.name, func(t *testing.T) {
|
t.Run(v.name, func(t *testing.T) {
|
||||||
input := []byte("test input for fixed shake")
|
input := []byte("test input for fixed shake")
|
||||||
|
|
||||||
// Test gas
|
// Test gas
|
||||||
gas := v.precompile.RequiredGas(input)
|
gas := v.precompile.RequiredGas(input)
|
||||||
assert.Equal(t, v.gas, gas)
|
assert.Equal(t, v.gas, gas)
|
||||||
|
|
||||||
// Test output length
|
// Test output length
|
||||||
output, err := v.precompile.Run(input)
|
output, err := v.precompile.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, output, v.outputLen)
|
assert.Len(t, output, v.outputLen)
|
||||||
|
|
||||||
// Verify it matches variable SHAKE with same output length
|
// Verify it matches variable SHAKE with same output length
|
||||||
shake := &SHAKE256{}
|
shake := &SHAKE256{}
|
||||||
varInput := make([]byte, 4+len(input))
|
varInput := make([]byte, 4+len(input))
|
||||||
binary.BigEndian.PutUint32(varInput[:4], uint32(v.outputLen))
|
binary.BigEndian.PutUint32(varInput[:4], uint32(v.outputLen))
|
||||||
copy(varInput[4:], input)
|
copy(varInput[4:], input)
|
||||||
|
|
||||||
varOutput, err := shake.Run(varInput)
|
varOutput, err := shake.Run(varInput)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, varOutput, output, "Fixed and variable SHAKE should match")
|
assert.Equal(t, varOutput, output, "Fixed and variable SHAKE should match")
|
||||||
@@ -139,40 +139,40 @@ func TestSHAKEPrecompiles(t *testing.T) {
|
|||||||
t.Run("cSHAKE", func(t *testing.T) {
|
t.Run("cSHAKE", func(t *testing.T) {
|
||||||
cshake128 := &CSHAKE128{}
|
cshake128 := &CSHAKE128{}
|
||||||
cshake256 := &CSHAKE256{}
|
cshake256 := &CSHAKE256{}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
customization string
|
customization string
|
||||||
data string
|
data string
|
||||||
outputLen uint32
|
outputLen uint32
|
||||||
}{
|
}{
|
||||||
{"empty", "", "test", 32},
|
{"empty", "", "test", 32},
|
||||||
{"custom", "custom string", "test data", 64},
|
{"custom", "custom string", "test data", 64},
|
||||||
{"long_custom", "very long customization string for testing", "data", 32},
|
{"long_custom", "very long customization string for testing", "data", 32},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// Build input
|
// Build input
|
||||||
customBytes := []byte(tc.customization)
|
customBytes := []byte(tc.customization)
|
||||||
dataBytes := []byte(tc.data)
|
dataBytes := []byte(tc.data)
|
||||||
|
|
||||||
input := make([]byte, 8+len(customBytes)+len(dataBytes))
|
input := make([]byte, 8+len(customBytes)+len(dataBytes))
|
||||||
binary.BigEndian.PutUint32(input[:4], tc.outputLen)
|
binary.BigEndian.PutUint32(input[:4], tc.outputLen)
|
||||||
binary.BigEndian.PutUint32(input[4:8], uint32(len(customBytes)))
|
binary.BigEndian.PutUint32(input[4:8], uint32(len(customBytes)))
|
||||||
copy(input[8:], customBytes)
|
copy(input[8:], customBytes)
|
||||||
copy(input[8+len(customBytes):], dataBytes)
|
copy(input[8+len(customBytes):], dataBytes)
|
||||||
|
|
||||||
// Test CSHAKE128
|
// Test CSHAKE128
|
||||||
output128, err := cshake128.Run(input)
|
output128, err := cshake128.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, output128, int(tc.outputLen))
|
assert.Len(t, output128, int(tc.outputLen))
|
||||||
|
|
||||||
// Test CSHAKE256
|
// Test CSHAKE256
|
||||||
output256, err := cshake256.Run(input)
|
output256, err := cshake256.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, output256, int(tc.outputLen))
|
assert.Len(t, output256, int(tc.outputLen))
|
||||||
|
|
||||||
// Outputs should be different between 128 and 256
|
// Outputs should be different between 128 and 256
|
||||||
if tc.customization != "" {
|
if tc.customization != "" {
|
||||||
assert.NotEqual(t, output128, output256)
|
assert.NotEqual(t, output128, output256)
|
||||||
@@ -183,44 +183,44 @@ func TestSHAKEPrecompiles(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("EdgeCases", func(t *testing.T) {
|
t.Run("EdgeCases", func(t *testing.T) {
|
||||||
shake := &SHAKE256{}
|
shake := &SHAKE256{}
|
||||||
|
|
||||||
t.Run("MaxOutput", func(t *testing.T) {
|
t.Run("MaxOutput", func(t *testing.T) {
|
||||||
// Test maximum output size (8192 bytes)
|
// Test maximum output size (8192 bytes)
|
||||||
input := make([]byte, 4+10)
|
input := make([]byte, 4+10)
|
||||||
binary.BigEndian.PutUint32(input[:4], 8192)
|
binary.BigEndian.PutUint32(input[:4], 8192)
|
||||||
copy(input[4:], []byte("test data"))
|
copy(input[4:], []byte("test data"))
|
||||||
|
|
||||||
output, err := shake.Run(input)
|
output, err := shake.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, output, 8192)
|
assert.Len(t, output, 8192)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("TooLargeOutput", func(t *testing.T) {
|
t.Run("TooLargeOutput", func(t *testing.T) {
|
||||||
// Test output size exceeding maximum
|
// Test output size exceeding maximum
|
||||||
input := make([]byte, 4+10)
|
input := make([]byte, 4+10)
|
||||||
binary.BigEndian.PutUint32(input[:4], 8193)
|
binary.BigEndian.PutUint32(input[:4], 8193)
|
||||||
copy(input[4:], []byte("test data"))
|
copy(input[4:], []byte("test data"))
|
||||||
|
|
||||||
_, err := shake.Run(input)
|
_, err := shake.Run(input)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.Error(), "exceeds maximum")
|
assert.Contains(t, err.Error(), "exceeds maximum")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ZeroOutput", func(t *testing.T) {
|
t.Run("ZeroOutput", func(t *testing.T) {
|
||||||
// Test zero output length
|
// Test zero output length
|
||||||
input := make([]byte, 4+10)
|
input := make([]byte, 4+10)
|
||||||
binary.BigEndian.PutUint32(input[:4], 0)
|
binary.BigEndian.PutUint32(input[:4], 0)
|
||||||
copy(input[4:], []byte("test data"))
|
copy(input[4:], []byte("test data"))
|
||||||
|
|
||||||
output, err := shake.Run(input)
|
output, err := shake.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, output, 0)
|
assert.Len(t, output, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("InsufficientInput", func(t *testing.T) {
|
t.Run("InsufficientInput", func(t *testing.T) {
|
||||||
// Test input shorter than 4 bytes
|
// Test input shorter than 4 bytes
|
||||||
input := []byte{0, 0}
|
input := []byte{0, 0}
|
||||||
|
|
||||||
_, err := shake.Run(input)
|
_, err := shake.Run(input)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
})
|
})
|
||||||
@@ -231,44 +231,44 @@ func TestSHAKEPrecompiles(t *testing.T) {
|
|||||||
func TestLamportPrecompiles(t *testing.T) {
|
func TestLamportPrecompiles(t *testing.T) {
|
||||||
t.Run("LamportVerifySHA256", func(t *testing.T) {
|
t.Run("LamportVerifySHA256", func(t *testing.T) {
|
||||||
verifier := &LamportVerifySHA256{}
|
verifier := &LamportVerifySHA256{}
|
||||||
|
|
||||||
// Generate test key and signature
|
// Generate test key and signature
|
||||||
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Get public key BEFORE signing (one-time signature clears private key)
|
// Get public key BEFORE signing (one-time signature clears private key)
|
||||||
pubBytes := priv.Public().Bytes()
|
pubBytes := priv.Public().Bytes()
|
||||||
|
|
||||||
message := []byte("test message for lamport")
|
message := []byte("test message for lamport")
|
||||||
messageHash := sha256.Sum256(message)
|
messageHash := sha256.Sum256(message)
|
||||||
|
|
||||||
// Sign the hash directly (precompile expects pre-hashed input)
|
// Sign the hash directly (precompile expects pre-hashed input)
|
||||||
sig, err := priv.SignHash(messageHash[:])
|
sig, err := priv.SignHash(messageHash[:])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Build precompile input
|
// Build precompile input
|
||||||
sigBytes := sig.Bytes()
|
sigBytes := sig.Bytes()
|
||||||
|
|
||||||
input := make([]byte, 32+len(sigBytes)+len(pubBytes))
|
input := make([]byte, 32+len(sigBytes)+len(pubBytes))
|
||||||
copy(input[:32], messageHash[:])
|
copy(input[:32], messageHash[:])
|
||||||
copy(input[32:], sigBytes)
|
copy(input[32:], sigBytes)
|
||||||
copy(input[32+len(sigBytes):], pubBytes)
|
copy(input[32+len(sigBytes):], pubBytes)
|
||||||
|
|
||||||
// Test gas
|
// Test gas
|
||||||
gas := verifier.RequiredGas(input)
|
gas := verifier.RequiredGas(input)
|
||||||
assert.Equal(t, uint64(50000), gas)
|
assert.Equal(t, uint64(50000), gas)
|
||||||
|
|
||||||
// Test verification (should succeed)
|
// Test verification (should succeed)
|
||||||
output, err := verifier.Run(input)
|
output, err := verifier.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := make([]byte, 32)
|
expected := make([]byte, 32)
|
||||||
expected[31] = 1
|
expected[31] = 1
|
||||||
assert.Equal(t, expected, output, "Valid signature should return 1")
|
assert.Equal(t, expected, output, "Valid signature should return 1")
|
||||||
|
|
||||||
// Test with wrong message
|
// Test with wrong message
|
||||||
wrongHash := sha256.Sum256([]byte("wrong message"))
|
wrongHash := sha256.Sum256([]byte("wrong message"))
|
||||||
copy(input[:32], wrongHash[:])
|
copy(input[:32], wrongHash[:])
|
||||||
|
|
||||||
output, err = verifier.Run(input)
|
output, err = verifier.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected = make([]byte, 32)
|
expected = make([]byte, 32)
|
||||||
@@ -277,33 +277,33 @@ func TestLamportPrecompiles(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("LamportVerifySHA512", func(t *testing.T) {
|
t.Run("LamportVerifySHA512", func(t *testing.T) {
|
||||||
verifier := &LamportVerifySHA512{}
|
verifier := &LamportVerifySHA512{}
|
||||||
|
|
||||||
// Generate test key and signature
|
// Generate test key and signature
|
||||||
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA512)
|
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA512)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Get public key BEFORE signing (one-time signature clears private key)
|
// Get public key BEFORE signing (one-time signature clears private key)
|
||||||
pubBytes := priv.Public().Bytes()
|
pubBytes := priv.Public().Bytes()
|
||||||
|
|
||||||
message := []byte("test message for lamport sha512")
|
message := []byte("test message for lamport sha512")
|
||||||
messageHash := sha512.Sum512(message)
|
messageHash := sha512.Sum512(message)
|
||||||
|
|
||||||
// Sign the hash directly (precompile expects pre-hashed input)
|
// Sign the hash directly (precompile expects pre-hashed input)
|
||||||
sig, err := priv.SignHash(messageHash[:])
|
sig, err := priv.SignHash(messageHash[:])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Build precompile input
|
// Build precompile input
|
||||||
sigBytes := sig.Bytes()
|
sigBytes := sig.Bytes()
|
||||||
|
|
||||||
input := make([]byte, 64+len(sigBytes)+len(pubBytes))
|
input := make([]byte, 64+len(sigBytes)+len(pubBytes))
|
||||||
copy(input[:64], messageHash[:])
|
copy(input[:64], messageHash[:])
|
||||||
copy(input[64:], sigBytes)
|
copy(input[64:], sigBytes)
|
||||||
copy(input[64+len(sigBytes):], pubBytes)
|
copy(input[64+len(sigBytes):], pubBytes)
|
||||||
|
|
||||||
// Test gas
|
// Test gas
|
||||||
gas := verifier.RequiredGas(input)
|
gas := verifier.RequiredGas(input)
|
||||||
assert.Equal(t, uint64(80000), gas)
|
assert.Equal(t, uint64(80000), gas)
|
||||||
|
|
||||||
// Test verification
|
// Test verification
|
||||||
output, err := verifier.Run(input)
|
output, err := verifier.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -314,63 +314,63 @@ func TestLamportPrecompiles(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("LamportBatchVerify", func(t *testing.T) {
|
t.Run("LamportBatchVerify", func(t *testing.T) {
|
||||||
batchVerifier := &LamportBatchVerify{}
|
batchVerifier := &LamportBatchVerify{}
|
||||||
|
|
||||||
// Generate multiple signatures
|
// Generate multiple signatures
|
||||||
numSigs := 3
|
numSigs := 3
|
||||||
var messages [][]byte
|
var messages [][]byte
|
||||||
var sigs []*lamport.Signature
|
var sigs []*lamport.Signature
|
||||||
var pubs []*lamport.PublicKey
|
var pubs []*lamport.PublicKey
|
||||||
|
|
||||||
for i := 0; i < numSigs; i++ {
|
for i := 0; i < numSigs; i++ {
|
||||||
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Get public key BEFORE signing
|
// Get public key BEFORE signing
|
||||||
pub := priv.Public()
|
pub := priv.Public()
|
||||||
pubs = append(pubs, pub)
|
pubs = append(pubs, pub)
|
||||||
|
|
||||||
message := []byte(fmt.Sprintf("message %d", i))
|
message := []byte(fmt.Sprintf("message %d", i))
|
||||||
messages = append(messages, message)
|
messages = append(messages, message)
|
||||||
|
|
||||||
// Sign the hash directly
|
// Sign the hash directly
|
||||||
msgHash := sha256.Sum256(message)
|
msgHash := sha256.Sum256(message)
|
||||||
sig, err := priv.SignHash(msgHash[:])
|
sig, err := priv.SignHash(msgHash[:])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
sigs = append(sigs, sig)
|
sigs = append(sigs, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build batch input
|
// Build batch input
|
||||||
// Format: [num_sigs(1)][hash_type(1)][data...]
|
// Format: [num_sigs(1)][hash_type(1)][data...]
|
||||||
// data = [msg_hash][sig][pubkey] repeated
|
// data = [msg_hash][sig][pubkey] repeated
|
||||||
|
|
||||||
// Calculate total size
|
// Calculate total size
|
||||||
hashSize := 32 // SHA256
|
hashSize := 32 // SHA256
|
||||||
sigSize := len(sigs[0].Bytes())
|
sigSize := len(sigs[0].Bytes())
|
||||||
pubSize := len(pubs[0].Bytes())
|
pubSize := len(pubs[0].Bytes())
|
||||||
dataSize := numSigs * (hashSize + sigSize + pubSize)
|
dataSize := numSigs * (hashSize + sigSize + pubSize)
|
||||||
|
|
||||||
input := make([]byte, 2+dataSize)
|
input := make([]byte, 2+dataSize)
|
||||||
input[0] = byte(numSigs)
|
input[0] = byte(numSigs)
|
||||||
input[1] = 0 // SHA256
|
input[1] = 0 // SHA256
|
||||||
|
|
||||||
offset := 2
|
offset := 2
|
||||||
for i := 0; i < numSigs; i++ {
|
for i := 0; i < numSigs; i++ {
|
||||||
hash := sha256.Sum256(messages[i])
|
hash := sha256.Sum256(messages[i])
|
||||||
copy(input[offset:], hash[:])
|
copy(input[offset:], hash[:])
|
||||||
offset += hashSize
|
offset += hashSize
|
||||||
|
|
||||||
copy(input[offset:], sigs[i].Bytes())
|
copy(input[offset:], sigs[i].Bytes())
|
||||||
offset += sigSize
|
offset += sigSize
|
||||||
|
|
||||||
copy(input[offset:], pubs[i].Bytes())
|
copy(input[offset:], pubs[i].Bytes())
|
||||||
offset += pubSize
|
offset += pubSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test gas
|
// Test gas
|
||||||
gas := batchVerifier.RequiredGas(input)
|
gas := batchVerifier.RequiredGas(input)
|
||||||
expectedGas := uint64(30000 + 40000*uint64(numSigs))
|
expectedGas := uint64(30000 + 40000*uint64(numSigs))
|
||||||
assert.Equal(t, expectedGas, gas)
|
assert.Equal(t, expectedGas, gas)
|
||||||
|
|
||||||
// Test batch verification
|
// Test batch verification
|
||||||
output, err := batchVerifier.Run(input)
|
output, err := batchVerifier.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -380,10 +380,10 @@ func TestLamportPrecompiles(t *testing.T) {
|
|||||||
for i := 1; i <= numSigs; i++ {
|
for i := 1; i <= numSigs; i++ {
|
||||||
assert.Equal(t, byte(1), output[i], fmt.Sprintf("Signature %d should be valid", i-1))
|
assert.Equal(t, byte(1), output[i], fmt.Sprintf("Signature %d should be valid", i-1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Corrupt one signature
|
// Corrupt one signature
|
||||||
input[2+hashSize] ^= 0xFF
|
input[2+hashSize] ^= 0xFF
|
||||||
|
|
||||||
output, err = batchVerifier.Run(input)
|
output, err = batchVerifier.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, byte(0), output[0], "Any invalid signature should return overall 0")
|
assert.Equal(t, byte(0), output[0], "Any invalid signature should return overall 0")
|
||||||
@@ -392,49 +392,49 @@ func TestLamportPrecompiles(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("LamportMerkleRoot", func(t *testing.T) {
|
t.Run("LamportMerkleRoot", func(t *testing.T) {
|
||||||
merkleRoot := &LamportMerkleRoot{}
|
merkleRoot := &LamportMerkleRoot{}
|
||||||
|
|
||||||
// Generate multiple public keys
|
// Generate multiple public keys
|
||||||
numKeys := 4
|
numKeys := 4
|
||||||
var pubs []*lamport.PublicKey
|
var pubs []*lamport.PublicKey
|
||||||
|
|
||||||
for i := 0; i < numKeys; i++ {
|
for i := 0; i < numKeys; i++ {
|
||||||
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
pubs = append(pubs, priv.Public())
|
pubs = append(pubs, priv.Public())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build input
|
// Build input
|
||||||
// Format: [num_keys(1)][hash_type(1)][pubkeys...]
|
// Format: [num_keys(1)][hash_type(1)][pubkeys...]
|
||||||
pubSize := len(pubs[0].Bytes())
|
pubSize := len(pubs[0].Bytes())
|
||||||
input := make([]byte, 2+numKeys*pubSize)
|
input := make([]byte, 2+numKeys*pubSize)
|
||||||
input[0] = byte(numKeys)
|
input[0] = byte(numKeys)
|
||||||
input[1] = 0 // SHA256
|
input[1] = 0 // SHA256
|
||||||
|
|
||||||
offset := 2
|
offset := 2
|
||||||
for _, pub := range pubs {
|
for _, pub := range pubs {
|
||||||
copy(input[offset:], pub.Bytes())
|
copy(input[offset:], pub.Bytes())
|
||||||
offset += pubSize
|
offset += pubSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test gas
|
// Test gas
|
||||||
gas := merkleRoot.RequiredGas(input)
|
gas := merkleRoot.RequiredGas(input)
|
||||||
assert.Equal(t, uint64(100000), gas)
|
assert.Equal(t, uint64(100000), gas)
|
||||||
|
|
||||||
// Test root computation
|
// Test root computation
|
||||||
output, err := merkleRoot.Run(input)
|
output, err := merkleRoot.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, output, 32, "Merkle root should be 32 bytes")
|
assert.Len(t, output, 32, "Merkle root should be 32 bytes")
|
||||||
|
|
||||||
// Same keys should produce same root
|
// Same keys should produce same root
|
||||||
output2, err := merkleRoot.Run(input)
|
output2, err := merkleRoot.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, output, output2, "Same keys should produce same root")
|
assert.Equal(t, output, output2, "Same keys should produce same root")
|
||||||
|
|
||||||
// Different order should produce different root
|
// Different order should produce different root
|
||||||
// Swap first two keys
|
// Swap first two keys
|
||||||
copy(input[5:5+pubSize], pubs[1].Bytes())
|
copy(input[5:5+pubSize], pubs[1].Bytes())
|
||||||
copy(input[5+pubSize:5+2*pubSize], pubs[0].Bytes())
|
copy(input[5+pubSize:5+2*pubSize], pubs[0].Bytes())
|
||||||
|
|
||||||
output3, err := merkleRoot.Run(input)
|
output3, err := merkleRoot.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.NotEqual(t, output, output3, "Different order should produce different root")
|
assert.NotEqual(t, output, output3, "Different order should produce different root")
|
||||||
@@ -445,19 +445,19 @@ func TestLamportPrecompiles(t *testing.T) {
|
|||||||
func TestBLSPrecompiles(t *testing.T) {
|
func TestBLSPrecompiles(t *testing.T) {
|
||||||
t.Run("BLSVerify", func(t *testing.T) {
|
t.Run("BLSVerify", func(t *testing.T) {
|
||||||
verifier := &BLSVerify{}
|
verifier := &BLSVerify{}
|
||||||
|
|
||||||
// Create dummy input (placeholder implementation)
|
// Create dummy input (placeholder implementation)
|
||||||
// Format: [96 bytes sig][48 bytes pubkey][message]
|
// Format: [96 bytes sig][48 bytes pubkey][message]
|
||||||
message := []byte("test message for BLS")
|
message := []byte("test message for BLS")
|
||||||
input := make([]byte, 96+48+len(message))
|
input := make([]byte, 96+48+len(message))
|
||||||
rand.Read(input[:96]) // Random signature
|
rand.Read(input[:96]) // Random signature
|
||||||
rand.Read(input[96:144]) // Random pubkey
|
rand.Read(input[96:144]) // Random pubkey
|
||||||
copy(input[144:], message)
|
copy(input[144:], message)
|
||||||
|
|
||||||
// Test gas
|
// Test gas
|
||||||
gas := verifier.RequiredGas(input)
|
gas := verifier.RequiredGas(input)
|
||||||
assert.Equal(t, uint64(150000), gas)
|
assert.Equal(t, uint64(150000), gas)
|
||||||
|
|
||||||
// Test run (placeholder always returns success)
|
// Test run (placeholder always returns success)
|
||||||
output, err := verifier.Run(input)
|
output, err := verifier.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -466,31 +466,31 @@ func TestBLSPrecompiles(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("BLSAggregateVerify", func(t *testing.T) {
|
t.Run("BLSAggregateVerify", func(t *testing.T) {
|
||||||
aggVerifier := &BLSAggregateVerify{}
|
aggVerifier := &BLSAggregateVerify{}
|
||||||
|
|
||||||
// Build aggregate input
|
// Build aggregate input
|
||||||
// Format: [num_sigs(1)][signatures][pubkeys][encoded_messages]
|
// Format: [num_sigs(1)][signatures][pubkeys][encoded_messages]
|
||||||
numSigs := 3
|
numSigs := 3
|
||||||
sigSize := 96
|
sigSize := 96
|
||||||
pubSize := 48
|
pubSize := 48
|
||||||
msgSize := 32
|
msgSize := 32
|
||||||
|
|
||||||
totalSize := 1 + numSigs*(sigSize+pubSize+4+msgSize)
|
totalSize := 1 + numSigs*(sigSize+pubSize+4+msgSize)
|
||||||
input := make([]byte, totalSize)
|
input := make([]byte, totalSize)
|
||||||
input[0] = byte(numSigs)
|
input[0] = byte(numSigs)
|
||||||
|
|
||||||
offset := 1
|
offset := 1
|
||||||
// Add signatures
|
// Add signatures
|
||||||
for i := 0; i < numSigs; i++ {
|
for i := 0; i < numSigs; i++ {
|
||||||
rand.Read(input[offset : offset+sigSize])
|
rand.Read(input[offset : offset+sigSize])
|
||||||
offset += sigSize
|
offset += sigSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add public keys
|
// Add public keys
|
||||||
for i := 0; i < numSigs; i++ {
|
for i := 0; i < numSigs; i++ {
|
||||||
rand.Read(input[offset : offset+pubSize])
|
rand.Read(input[offset : offset+pubSize])
|
||||||
offset += pubSize
|
offset += pubSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add messages (with length prefixes)
|
// Add messages (with length prefixes)
|
||||||
for i := 0; i < numSigs; i++ {
|
for i := 0; i < numSigs; i++ {
|
||||||
binary.BigEndian.PutUint32(input[offset:offset+4], uint32(msgSize))
|
binary.BigEndian.PutUint32(input[offset:offset+4], uint32(msgSize))
|
||||||
@@ -498,13 +498,13 @@ func TestBLSPrecompiles(t *testing.T) {
|
|||||||
rand.Read(input[offset : offset+msgSize])
|
rand.Read(input[offset : offset+msgSize])
|
||||||
offset += msgSize
|
offset += msgSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test gas
|
// Test gas
|
||||||
gas := aggVerifier.RequiredGas(input)
|
gas := aggVerifier.RequiredGas(input)
|
||||||
// blsAggregateVerifyGas = 200000, blsPerSignatureGas = 30000
|
// blsAggregateVerifyGas = 200000, blsPerSignatureGas = 30000
|
||||||
expectedGas := uint64(200000 + 30000*uint64(numSigs))
|
expectedGas := uint64(200000 + 30000*uint64(numSigs))
|
||||||
assert.Equal(t, expectedGas, gas)
|
assert.Equal(t, expectedGas, gas)
|
||||||
|
|
||||||
// Test run
|
// Test run
|
||||||
output, err := aggVerifier.Run(input)
|
output, err := aggVerifier.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -513,24 +513,24 @@ func TestBLSPrecompiles(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("BLSPublicKeyAggregate", func(t *testing.T) {
|
t.Run("BLSPublicKeyAggregate", func(t *testing.T) {
|
||||||
aggregator := &BLSPublicKeyAggregate{}
|
aggregator := &BLSPublicKeyAggregate{}
|
||||||
|
|
||||||
// Build input
|
// Build input
|
||||||
// Format: [num_keys(1)][pubkeys...]
|
// Format: [num_keys(1)][pubkeys...]
|
||||||
numKeys := 5
|
numKeys := 5
|
||||||
pubSize := 48
|
pubSize := 48
|
||||||
|
|
||||||
input := make([]byte, 1+numKeys*pubSize)
|
input := make([]byte, 1+numKeys*pubSize)
|
||||||
input[0] = byte(numKeys)
|
input[0] = byte(numKeys)
|
||||||
|
|
||||||
for i := 0; i < numKeys; i++ {
|
for i := 0; i < numKeys; i++ {
|
||||||
rand.Read(input[1+i*pubSize : 1+(i+1)*pubSize])
|
rand.Read(input[1+i*pubSize : 1+(i+1)*pubSize])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test gas
|
// Test gas
|
||||||
gas := aggregator.RequiredGas(input)
|
gas := aggregator.RequiredGas(input)
|
||||||
expectedGas := uint64(50000 + 10000*uint64(numKeys))
|
expectedGas := uint64(50000 + 10000*uint64(numKeys))
|
||||||
assert.Equal(t, expectedGas, gas)
|
assert.Equal(t, expectedGas, gas)
|
||||||
|
|
||||||
// Test run
|
// Test run
|
||||||
output, err := aggregator.Run(input)
|
output, err := aggregator.Run(input)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -539,24 +539,24 @@ func TestBLSPrecompiles(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("BLSHashToPoint", func(t *testing.T) {
|
t.Run("BLSHashToPoint", func(t *testing.T) {
|
||||||
hashToPoint := &BLSHashToPoint{}
|
hashToPoint := &BLSHashToPoint{}
|
||||||
|
|
||||||
// Test with various message sizes
|
// Test with various message sizes
|
||||||
messages := [][]byte{
|
messages := [][]byte{
|
||||||
[]byte("short"),
|
[]byte("short"),
|
||||||
[]byte("medium length message for testing"),
|
[]byte("medium length message for testing"),
|
||||||
bytes.Repeat([]byte("long "), 100),
|
bytes.Repeat([]byte("long "), 100),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, msg := range messages {
|
for _, msg := range messages {
|
||||||
// Test gas
|
// Test gas
|
||||||
gas := hashToPoint.RequiredGas(msg)
|
gas := hashToPoint.RequiredGas(msg)
|
||||||
assert.Equal(t, uint64(80000), gas)
|
assert.Equal(t, uint64(80000), gas)
|
||||||
|
|
||||||
// Test run
|
// Test run
|
||||||
output, err := hashToPoint.Run(msg)
|
output, err := hashToPoint.Run(msg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, output, 96, "Hash to point should produce 96-byte point")
|
assert.Len(t, output, 96, "Hash to point should produce 96-byte point")
|
||||||
|
|
||||||
// Same message should produce same point
|
// Same message should produce same point
|
||||||
output2, err := hashToPoint.Run(msg)
|
output2, err := hashToPoint.Run(msg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -577,16 +577,16 @@ func TestPrecompileRegistry(t *testing.T) {
|
|||||||
// BLS
|
// BLS
|
||||||
"0x0160", "0x0161", "0x0162", "0x0163", "0x0164", "0x0165", "0x0166",
|
"0x0160", "0x0161", "0x0162", "0x0163", "0x0164", "0x0165", "0x0166",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, addr := range expectedAddresses {
|
for _, addr := range expectedAddresses {
|
||||||
t.Run(addr, func(t *testing.T) {
|
t.Run(addr, func(t *testing.T) {
|
||||||
// Convert hex string to address
|
// Convert hex string to address
|
||||||
addrBytes, err := hex.DecodeString(addr[2:])
|
addrBytes, err := hex.DecodeString(addr[2:])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var address [20]byte
|
var address [20]byte
|
||||||
copy(address[20-len(addrBytes):], addrBytes)
|
copy(address[20-len(addrBytes):], addrBytes)
|
||||||
|
|
||||||
precompile, exists := PostQuantumRegistry.contracts[address]
|
precompile, exists := PostQuantumRegistry.contracts[address]
|
||||||
assert.True(t, exists, "Address %s should be registered", addr)
|
assert.True(t, exists, "Address %s should be registered", addr)
|
||||||
assert.NotNil(t, precompile, "Precompile at %s should not be nil", addr)
|
assert.NotNil(t, precompile, "Precompile at %s should not be nil", addr)
|
||||||
@@ -599,14 +599,14 @@ func TestPrecompileRegistry(t *testing.T) {
|
|||||||
shake256Addr := [20]byte{}
|
shake256Addr := [20]byte{}
|
||||||
shake256Addr[18] = 0x01
|
shake256Addr[18] = 0x01
|
||||||
shake256Addr[19] = 0x43
|
shake256Addr[19] = 0x43
|
||||||
|
|
||||||
input := make([]byte, 36)
|
input := make([]byte, 36)
|
||||||
binary.BigEndian.PutUint32(input[:4], 32)
|
binary.BigEndian.PutUint32(input[:4], 32)
|
||||||
copy(input[4:], []byte("test"))
|
copy(input[4:], []byte("test"))
|
||||||
|
|
||||||
gas := GetGasEstimate(shake256Addr, len(input))
|
gas := GetGasEstimate(shake256Addr, len(input))
|
||||||
assert.Greater(t, gas, uint64(0), "Should return non-zero gas estimate")
|
assert.Greater(t, gas, uint64(0), "Should return non-zero gas estimate")
|
||||||
|
|
||||||
// Test unknown address
|
// Test unknown address
|
||||||
unknownAddr := [20]byte{0xFF}
|
unknownAddr := [20]byte{0xFF}
|
||||||
gas = GetGasEstimate(unknownAddr, len(input))
|
gas = GetGasEstimate(unknownAddr, len(input))
|
||||||
@@ -615,13 +615,13 @@ func TestPrecompileRegistry(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Info", func(t *testing.T) {
|
t.Run("Info", func(t *testing.T) {
|
||||||
info := Info()
|
info := Info()
|
||||||
|
|
||||||
// Check expected keys exist
|
// Check expected keys exist
|
||||||
assert.Contains(t, info, "total_precompiles")
|
assert.Contains(t, info, "total_precompiles")
|
||||||
assert.Contains(t, info, "cgo_enabled")
|
assert.Contains(t, info, "cgo_enabled")
|
||||||
assert.Contains(t, info, "standards")
|
assert.Contains(t, info, "standards")
|
||||||
assert.Contains(t, info, "address_ranges")
|
assert.Contains(t, info, "address_ranges")
|
||||||
|
|
||||||
// Check total precompiles count
|
// Check total precompiles count
|
||||||
totalPrecompiles, ok := info["total_precompiles"].(int)
|
totalPrecompiles, ok := info["total_precompiles"].(int)
|
||||||
assert.True(t, ok, "total_precompiles should be an int")
|
assert.True(t, ok, "total_precompiles should be an int")
|
||||||
@@ -644,12 +644,12 @@ func TestErrorHandling(t *testing.T) {
|
|||||||
&LamportVerifySHA256{},
|
&LamportVerifySHA256{},
|
||||||
&BLSVerify{},
|
&BLSVerify{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range precompiles {
|
for _, p := range precompiles {
|
||||||
// Empty input
|
// Empty input
|
||||||
_, err := p.Run([]byte{})
|
_, err := p.Run([]byte{})
|
||||||
assert.Error(t, err, "%T should error on empty input", p)
|
assert.Error(t, err, "%T should error on empty input", p)
|
||||||
|
|
||||||
// Too short input
|
// Too short input
|
||||||
_, err = p.Run([]byte{0, 1, 2})
|
_, err = p.Run([]byte{0, 1, 2})
|
||||||
assert.Error(t, err, "%T should error on short input", p)
|
assert.Error(t, err, "%T should error on short input", p)
|
||||||
@@ -661,23 +661,23 @@ func TestErrorHandling(t *testing.T) {
|
|||||||
shake := &SHAKE256{}
|
shake := &SHAKE256{}
|
||||||
input := make([]byte, 8)
|
input := make([]byte, 8)
|
||||||
binary.BigEndian.PutUint32(input[:4], 8193) // Too large
|
binary.BigEndian.PutUint32(input[:4], 8193) // Too large
|
||||||
|
|
||||||
_, err := shake.Run(input)
|
_, err := shake.Run(input)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.Error(), "exceeds maximum")
|
assert.Contains(t, err.Error(), "exceeds maximum")
|
||||||
|
|
||||||
// Batch verify with 0 signatures
|
// Batch verify with 0 signatures
|
||||||
// batch := &LamportBatchVerify{}
|
// batch := &LamportBatchVerify{}
|
||||||
// input = []byte{0, 0} // [num_sigs=0][hash_type=0]
|
// input = []byte{0, 0} // [num_sigs=0][hash_type=0]
|
||||||
|
|
||||||
// Note: 0 signatures might be valid (empty batch), so skip this test
|
// Note: 0 signatures might be valid (empty batch), so skip this test
|
||||||
// _, err = batch.Run(input)
|
// _, err = batch.Run(input)
|
||||||
// assert.Error(t, err)
|
// assert.Error(t, err)
|
||||||
|
|
||||||
// BLS aggregate with mismatched counts
|
// BLS aggregate with mismatched counts
|
||||||
// agg := &BLSAggregateVerify{}
|
// agg := &BLSAggregateVerify{}
|
||||||
// input = []byte{10} // Claims 10 sigs but no data (1 byte format)
|
// input = []byte{10} // Claims 10 sigs but no data (1 byte format)
|
||||||
|
|
||||||
// _, err = agg.Run(input)
|
// _, err = agg.Run(input)
|
||||||
// Note: This might not error immediately, depends on implementation
|
// Note: This might not error immediately, depends on implementation
|
||||||
// assert.Error(t, err)
|
// assert.Error(t, err)
|
||||||
@@ -687,12 +687,12 @@ func TestErrorHandling(t *testing.T) {
|
|||||||
// Test that similar errors have consistent messages
|
// Test that similar errors have consistent messages
|
||||||
shake128 := &SHAKE128{}
|
shake128 := &SHAKE128{}
|
||||||
shake256 := &SHAKE256{}
|
shake256 := &SHAKE256{}
|
||||||
|
|
||||||
shortInput := []byte{0, 1}
|
shortInput := []byte{0, 1}
|
||||||
|
|
||||||
_, err1 := shake128.Run(shortInput)
|
_, err1 := shake128.Run(shortInput)
|
||||||
_, err2 := shake256.Run(shortInput)
|
_, err2 := shake256.Run(shortInput)
|
||||||
|
|
||||||
if err1 != nil && err2 != nil {
|
if err1 != nil && err2 != nil {
|
||||||
// Both should have similar error messages
|
// Both should have similar error messages
|
||||||
// They both say "input too short" which is consistent
|
// They both say "input too short" which is consistent
|
||||||
@@ -709,7 +709,7 @@ func BenchmarkPrecompiles(b *testing.B) {
|
|||||||
input := make([]byte, 36)
|
input := make([]byte, 36)
|
||||||
binary.BigEndian.PutUint32(input[:4], 32)
|
binary.BigEndian.PutUint32(input[:4], 32)
|
||||||
copy(input[4:], bytes.Repeat([]byte("x"), 32))
|
copy(input[4:], bytes.Repeat([]byte("x"), 32))
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = shake.Run(input)
|
_, _ = shake.Run(input)
|
||||||
@@ -719,7 +719,7 @@ func BenchmarkPrecompiles(b *testing.B) {
|
|||||||
b.Run("SHAKE256_1024bytes", func(b *testing.B) {
|
b.Run("SHAKE256_1024bytes", func(b *testing.B) {
|
||||||
shake := &SHAKE256_1024{}
|
shake := &SHAKE256_1024{}
|
||||||
input := bytes.Repeat([]byte("x"), 128)
|
input := bytes.Repeat([]byte("x"), 128)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = shake.Run(input)
|
_, _ = shake.Run(input)
|
||||||
@@ -728,21 +728,21 @@ func BenchmarkPrecompiles(b *testing.B) {
|
|||||||
|
|
||||||
b.Run("LamportVerify", func(b *testing.B) {
|
b.Run("LamportVerify", func(b *testing.B) {
|
||||||
verifier := &LamportVerifySHA256{}
|
verifier := &LamportVerifySHA256{}
|
||||||
|
|
||||||
// Generate a signature once
|
// Generate a signature once
|
||||||
priv, _ := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
priv, _ := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
||||||
message := []byte("benchmark message")
|
message := []byte("benchmark message")
|
||||||
sig, _ := priv.Sign(message)
|
sig, _ := priv.Sign(message)
|
||||||
|
|
||||||
messageHash := sha256.Sum256(message)
|
messageHash := sha256.Sum256(message)
|
||||||
sigBytes := sig.Bytes()
|
sigBytes := sig.Bytes()
|
||||||
pubBytes := priv.Public().Bytes()
|
pubBytes := priv.Public().Bytes()
|
||||||
|
|
||||||
input := make([]byte, 32+len(sigBytes)+len(pubBytes))
|
input := make([]byte, 32+len(sigBytes)+len(pubBytes))
|
||||||
copy(input[:32], messageHash[:])
|
copy(input[:32], messageHash[:])
|
||||||
copy(input[32:], sigBytes)
|
copy(input[32:], sigBytes)
|
||||||
copy(input[32+len(sigBytes):], pubBytes)
|
copy(input[32+len(sigBytes):], pubBytes)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = verifier.Run(input)
|
_, _ = verifier.Run(input)
|
||||||
@@ -751,10 +751,10 @@ func BenchmarkPrecompiles(b *testing.B) {
|
|||||||
|
|
||||||
b.Run("BLSVerify", func(b *testing.B) {
|
b.Run("BLSVerify", func(b *testing.B) {
|
||||||
verifier := &BLSVerify{}
|
verifier := &BLSVerify{}
|
||||||
|
|
||||||
input := make([]byte, 96+48+32)
|
input := make([]byte, 96+48+32)
|
||||||
rand.Read(input)
|
rand.Read(input)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = verifier.Run(input)
|
_, _ = verifier.Run(input)
|
||||||
@@ -768,34 +768,34 @@ func TestCrossPrecompileWorkflows(t *testing.T) {
|
|||||||
// Use SHAKE to hash, then Lamport to sign
|
// Use SHAKE to hash, then Lamport to sign
|
||||||
shake := &SHAKE256_256{}
|
shake := &SHAKE256_256{}
|
||||||
lamportVerify := &LamportVerifySHA256{}
|
lamportVerify := &LamportVerifySHA256{}
|
||||||
|
|
||||||
// Original message
|
// Original message
|
||||||
message := []byte("cross-precompile test message")
|
message := []byte("cross-precompile test message")
|
||||||
|
|
||||||
// Hash with SHAKE256
|
// Hash with SHAKE256
|
||||||
hashedOutput, err := shake.Run(message)
|
hashedOutput, err := shake.Run(message)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, hashedOutput, 32)
|
assert.Len(t, hashedOutput, 32)
|
||||||
|
|
||||||
// Generate Lamport signature on the hash
|
// Generate Lamport signature on the hash
|
||||||
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Get public key BEFORE signing
|
// Get public key BEFORE signing
|
||||||
pubBytes := priv.Public().Bytes()
|
pubBytes := priv.Public().Bytes()
|
||||||
|
|
||||||
// Sign the hash directly (not Sign which hashes again)
|
// Sign the hash directly (not Sign which hashes again)
|
||||||
sig, err := priv.SignHash(hashedOutput)
|
sig, err := priv.SignHash(hashedOutput)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Build verification input
|
// Build verification input
|
||||||
sigBytes := sig.Bytes()
|
sigBytes := sig.Bytes()
|
||||||
|
|
||||||
verifyInput := make([]byte, 32+len(sigBytes)+len(pubBytes))
|
verifyInput := make([]byte, 32+len(sigBytes)+len(pubBytes))
|
||||||
copy(verifyInput[:32], hashedOutput)
|
copy(verifyInput[:32], hashedOutput)
|
||||||
copy(verifyInput[32:], sigBytes)
|
copy(verifyInput[32:], sigBytes)
|
||||||
copy(verifyInput[32+len(sigBytes):], pubBytes)
|
copy(verifyInput[32+len(sigBytes):], pubBytes)
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
result, err := lamportVerify.Run(verifyInput)
|
result, err := lamportVerify.Run(verifyInput)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -808,39 +808,39 @@ func TestCrossPrecompileWorkflows(t *testing.T) {
|
|||||||
// Create merkle root of keys, then batch verify signatures
|
// Create merkle root of keys, then batch verify signatures
|
||||||
merkleRoot := &LamportMerkleRoot{}
|
merkleRoot := &LamportMerkleRoot{}
|
||||||
batchVerify := &LamportBatchVerify{}
|
batchVerify := &LamportBatchVerify{}
|
||||||
|
|
||||||
// Generate multiple keys
|
// Generate multiple keys
|
||||||
numKeys := 3
|
numKeys := 3
|
||||||
var privKeys []*lamport.PrivateKey
|
var privKeys []*lamport.PrivateKey
|
||||||
var pubKeys []*lamport.PublicKey
|
var pubKeys []*lamport.PublicKey
|
||||||
|
|
||||||
for i := 0; i < numKeys; i++ {
|
for i := 0; i < numKeys; i++ {
|
||||||
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
priv, err := lamport.GenerateKey(rand.Reader, lamport.SHA256)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
privKeys = append(privKeys, priv)
|
privKeys = append(privKeys, priv)
|
||||||
pubKeys = append(pubKeys, priv.Public())
|
pubKeys = append(pubKeys, priv.Public())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute merkle root
|
// Compute merkle root
|
||||||
pubSize := len(pubKeys[0].Bytes())
|
pubSize := len(pubKeys[0].Bytes())
|
||||||
rootInput := make([]byte, 2+numKeys*pubSize)
|
rootInput := make([]byte, 2+numKeys*pubSize)
|
||||||
rootInput[0] = byte(numKeys)
|
rootInput[0] = byte(numKeys)
|
||||||
rootInput[1] = 0 // SHA256
|
rootInput[1] = 0 // SHA256
|
||||||
|
|
||||||
offset := 2
|
offset := 2
|
||||||
for _, pub := range pubKeys {
|
for _, pub := range pubKeys {
|
||||||
copy(rootInput[offset:], pub.Bytes())
|
copy(rootInput[offset:], pub.Bytes())
|
||||||
offset += pubSize
|
offset += pubSize
|
||||||
}
|
}
|
||||||
|
|
||||||
root, err := merkleRoot.Run(rootInput)
|
root, err := merkleRoot.Run(rootInput)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, root, 32)
|
assert.Len(t, root, 32)
|
||||||
|
|
||||||
// Create signatures with the same keys
|
// Create signatures with the same keys
|
||||||
messages := make([][]byte, numKeys)
|
messages := make([][]byte, numKeys)
|
||||||
sigs := make([]*lamport.Signature, numKeys)
|
sigs := make([]*lamport.Signature, numKeys)
|
||||||
|
|
||||||
for i := 0; i < numKeys; i++ {
|
for i := 0; i < numKeys; i++ {
|
||||||
messages[i] = []byte(fmt.Sprintf("message %d", i))
|
messages[i] = []byte(fmt.Sprintf("message %d", i))
|
||||||
msgHash := sha256.Sum256(messages[i])
|
msgHash := sha256.Sum256(messages[i])
|
||||||
@@ -848,27 +848,27 @@ func TestCrossPrecompileWorkflows(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
sigs[i] = sig
|
sigs[i] = sig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Batch verify
|
// Batch verify
|
||||||
hashSize := 32
|
hashSize := 32
|
||||||
sigSize := len(sigs[0].Bytes())
|
sigSize := len(sigs[0].Bytes())
|
||||||
batchInput := make([]byte, 2+numKeys*(hashSize+sigSize+pubSize))
|
batchInput := make([]byte, 2+numKeys*(hashSize+sigSize+pubSize))
|
||||||
batchInput[0] = byte(numKeys)
|
batchInput[0] = byte(numKeys)
|
||||||
batchInput[1] = 0 // SHA256
|
batchInput[1] = 0 // SHA256
|
||||||
|
|
||||||
offset = 2
|
offset = 2
|
||||||
for i := 0; i < numKeys; i++ {
|
for i := 0; i < numKeys; i++ {
|
||||||
hash := sha256.Sum256(messages[i])
|
hash := sha256.Sum256(messages[i])
|
||||||
copy(batchInput[offset:], hash[:])
|
copy(batchInput[offset:], hash[:])
|
||||||
offset += hashSize
|
offset += hashSize
|
||||||
|
|
||||||
copy(batchInput[offset:], sigs[i].Bytes())
|
copy(batchInput[offset:], sigs[i].Bytes())
|
||||||
offset += sigSize
|
offset += sigSize
|
||||||
|
|
||||||
copy(batchInput[offset:], pubKeys[i].Bytes())
|
copy(batchInput[offset:], pubKeys[i].Bytes())
|
||||||
offset += pubSize
|
offset += pubSize
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := batchVerify.Run(batchInput)
|
result, err := batchVerify.Run(batchInput)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// Batch verify returns [overall_valid][individual_results...]
|
// Batch verify returns [overall_valid][individual_results...]
|
||||||
@@ -877,7 +877,7 @@ func TestCrossPrecompileWorkflows(t *testing.T) {
|
|||||||
for i := 1; i <= numKeys; i++ {
|
for i := 1; i <= numKeys; i++ {
|
||||||
assert.Equal(t, byte(1), result[i], fmt.Sprintf("Signature %d should be valid", i-1))
|
assert.Equal(t, byte(1), result[i], fmt.Sprintf("Signature %d should be valid", i-1))
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Merkle root: %x", root)
|
t.Logf("Merkle root: %x", root)
|
||||||
t.Log("All signatures verified in batch")
|
t.Log("All signatures verified in batch")
|
||||||
})
|
})
|
||||||
@@ -886,17 +886,17 @@ func TestCrossPrecompileWorkflows(t *testing.T) {
|
|||||||
// Helper function to compare SHAKE output with reference implementation
|
// Helper function to compare SHAKE output with reference implementation
|
||||||
func verifyShakeOutput(t *testing.T, input []byte, outputLen int, isShake256 bool) []byte {
|
func verifyShakeOutput(t *testing.T, input []byte, outputLen int, isShake256 bool) []byte {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
var h sha3.ShakeHash
|
var h sha3.ShakeHash
|
||||||
if isShake256 {
|
if isShake256 {
|
||||||
h = sha3.NewShake256()
|
h = sha3.NewShake256()
|
||||||
} else {
|
} else {
|
||||||
h = sha3.NewShake128()
|
h = sha3.NewShake128()
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Write(input)
|
h.Write(input)
|
||||||
output := make([]byte, outputLen)
|
output := make([]byte, outputLen)
|
||||||
h.Read(output)
|
h.Read(output)
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -45,7 +45,7 @@ func (s *SHAKE128) RequiredGas(input []byte) uint64 {
|
|||||||
return shakeBaseGas
|
return shakeBaseGas
|
||||||
}
|
}
|
||||||
outputLen := uint32(input[0])<<24 | uint32(input[1])<<16 | uint32(input[2])<<8 | uint32(input[3])
|
outputLen := uint32(input[0])<<24 | uint32(input[1])<<16 | uint32(input[2])<<8 | uint32(input[3])
|
||||||
inputWords := uint64((len(input) + 31) / 32) // Include the 4-byte length in gas calculation
|
inputWords := uint64((len(input) + 31) / 32) // Include the 4-byte length in gas calculation
|
||||||
outputWords := uint64((outputLen + 31) / 32)
|
outputWords := uint64((outputLen + 31) / 32)
|
||||||
return shakeBaseGas + inputWords*shakePerWordGas + outputWords*shakeOutputGas
|
return shakeBaseGas + inputWords*shakePerWordGas + outputWords*shakeOutputGas
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ func (s *SHAKE256) RequiredGas(input []byte) uint64 {
|
|||||||
return shakeBaseGas
|
return shakeBaseGas
|
||||||
}
|
}
|
||||||
outputLen := uint32(input[0])<<24 | uint32(input[1])<<16 | uint32(input[2])<<8 | uint32(input[3])
|
outputLen := uint32(input[0])<<24 | uint32(input[1])<<16 | uint32(input[2])<<8 | uint32(input[3])
|
||||||
inputWords := uint64((len(input) + 31) / 32) // Include the 4-byte length in gas calculation
|
inputWords := uint64((len(input) + 31) / 32) // Include the 4-byte length in gas calculation
|
||||||
outputWords := uint64((outputLen + 31) / 32)
|
outputWords := uint64((outputLen + 31) / 32)
|
||||||
return shakeBaseGas + inputWords*shakePerWordGas + outputWords*shakeOutputGas
|
return shakeBaseGas + inputWords*shakePerWordGas + outputWords*shakeOutputGas
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -10,10 +10,10 @@ import (
|
|||||||
func BenchmarkSignNoCGO(b *testing.B) {
|
func BenchmarkSignNoCGO(b *testing.B) {
|
||||||
msg := make([]byte, 32)
|
msg := make([]byte, 32)
|
||||||
rand.Read(msg)
|
rand.Read(msg)
|
||||||
|
|
||||||
seckey := make([]byte, 32)
|
seckey := make([]byte, 32)
|
||||||
rand.Read(seckey)
|
rand.Read(seckey)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = Sign(msg, seckey)
|
_, _ = Sign(msg, seckey)
|
||||||
@@ -23,12 +23,12 @@ func BenchmarkSignNoCGO(b *testing.B) {
|
|||||||
func BenchmarkRecoverNoCGO(b *testing.B) {
|
func BenchmarkRecoverNoCGO(b *testing.B) {
|
||||||
msg := make([]byte, 32)
|
msg := make([]byte, 32)
|
||||||
rand.Read(msg)
|
rand.Read(msg)
|
||||||
|
|
||||||
seckey := make([]byte, 32)
|
seckey := make([]byte, 32)
|
||||||
rand.Read(seckey)
|
rand.Read(seckey)
|
||||||
|
|
||||||
sig, _ := Sign(msg, seckey)
|
sig, _ := Sign(msg, seckey)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = RecoverPubkey(msg, sig)
|
_, _ = RecoverPubkey(msg, sig)
|
||||||
@@ -38,18 +38,18 @@ func BenchmarkRecoverNoCGO(b *testing.B) {
|
|||||||
func BenchmarkVerifySignature(b *testing.B) {
|
func BenchmarkVerifySignature(b *testing.B) {
|
||||||
msg := make([]byte, 32)
|
msg := make([]byte, 32)
|
||||||
rand.Read(msg)
|
rand.Read(msg)
|
||||||
|
|
||||||
seckey := make([]byte, 32)
|
seckey := make([]byte, 32)
|
||||||
rand.Read(seckey)
|
rand.Read(seckey)
|
||||||
|
|
||||||
sig, _ := Sign(msg, seckey)
|
sig, _ := Sign(msg, seckey)
|
||||||
pubkey, _ := RecoverPubkey(msg, sig)
|
pubkey, _ := RecoverPubkey(msg, sig)
|
||||||
|
|
||||||
// Remove recovery ID for verification
|
// Remove recovery ID for verification
|
||||||
sigNoRecovery := sig[:64]
|
sigNoRecovery := sig[:64]
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
VerifySignature(pubkey, msg, sigNoRecovery)
|
VerifySignature(pubkey, msg, sigNoRecovery)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,17 +15,17 @@ import (
|
|||||||
func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
|
func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
|
||||||
// Convert scalar to big.Int
|
// Convert scalar to big.Int
|
||||||
k := new(big.Int).SetBytes(scalar)
|
k := new(big.Int).SetBytes(scalar)
|
||||||
|
|
||||||
// Handle special cases
|
// Handle special cases
|
||||||
if k.Sign() == 0 {
|
if k.Sign() == 0 {
|
||||||
return new(big.Int), new(big.Int)
|
return new(big.Int), new(big.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the double-and-add algorithm
|
// Use the double-and-add algorithm
|
||||||
// Start with the identity point (0, 0)
|
// Start with the identity point (0, 0)
|
||||||
x, y := new(big.Int), new(big.Int)
|
x, y := new(big.Int), new(big.Int)
|
||||||
addX, addY := new(big.Int).Set(Bx), new(big.Int).Set(By)
|
addX, addY := new(big.Int).Set(Bx), new(big.Int).Set(By)
|
||||||
|
|
||||||
// Process each bit of k from least significant to most significant
|
// Process each bit of k from least significant to most significant
|
||||||
for i := 0; i < k.BitLen(); i++ {
|
for i := 0; i < k.BitLen(); i++ {
|
||||||
if k.Bit(i) == 1 {
|
if k.Bit(i) == 1 {
|
||||||
@@ -42,6 +42,6 @@ func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int,
|
|||||||
// Double the point for the next bit
|
// Double the point for the next bit
|
||||||
addX, addY = bitCurve.Double(addX, addY)
|
addX, addY = bitCurve.Double(addX, addY)
|
||||||
}
|
}
|
||||||
|
|
||||||
return x, y
|
return x, y
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-45
@@ -16,7 +16,7 @@ func FuzzSignatureVerification(f *testing.F) {
|
|||||||
// Seed corpus with valid and invalid signatures
|
// Seed corpus with valid and invalid signatures
|
||||||
f.Add(bytes.Repeat([]byte{0}, 32), bytes.Repeat([]byte{0}, 65))
|
f.Add(bytes.Repeat([]byte{0}, 32), bytes.Repeat([]byte{0}, 65))
|
||||||
f.Add(bytes.Repeat([]byte{0xff}, 32), bytes.Repeat([]byte{0xff}, 65))
|
f.Add(bytes.Repeat([]byte{0xff}, 32), bytes.Repeat([]byte{0xff}, 65))
|
||||||
|
|
||||||
// Add a valid signature
|
// Add a valid signature
|
||||||
privKey := make([]byte, 32)
|
privKey := make([]byte, 32)
|
||||||
privKey[31] = 1 // Simple valid private key
|
privKey[31] = 1 // Simple valid private key
|
||||||
@@ -24,17 +24,17 @@ func FuzzSignatureVerification(f *testing.F) {
|
|||||||
if sig, err := Sign(msg, privKey); err == nil {
|
if sig, err := Sign(msg, privKey); err == nil {
|
||||||
f.Add(msg, sig)
|
f.Add(msg, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add signatures with different recovery IDs
|
// Add signatures with different recovery IDs
|
||||||
validSig := make([]byte, 65)
|
validSig := make([]byte, 65)
|
||||||
copy(validSig[:32], bytes.Repeat([]byte{0xaa}, 32))
|
copy(validSig[:32], bytes.Repeat([]byte{0xaa}, 32))
|
||||||
copy(validSig[32:64], bytes.Repeat([]byte{0xbb}, 32))
|
copy(validSig[32:64], bytes.Repeat([]byte{0xbb}, 32))
|
||||||
validSig[64] = 0
|
validSig[64] = 0
|
||||||
f.Add(hashing.ComputeHash256([]byte("test")), validSig)
|
f.Add(hashing.ComputeHash256([]byte("test")), validSig)
|
||||||
|
|
||||||
validSig[64] = 1
|
validSig[64] = 1
|
||||||
f.Add(hashing.ComputeHash256([]byte("test2")), validSig)
|
f.Add(hashing.ComputeHash256([]byte("test2")), validSig)
|
||||||
|
|
||||||
f.Fuzz(func(t *testing.T, msg []byte, sig []byte) {
|
f.Fuzz(func(t *testing.T, msg []byte, sig []byte) {
|
||||||
// Ensure msg is 32 bytes (hash size)
|
// Ensure msg is 32 bytes (hash size)
|
||||||
if len(msg) != 32 {
|
if len(msg) != 32 {
|
||||||
@@ -44,23 +44,23 @@ func FuzzSignatureVerification(f *testing.F) {
|
|||||||
msg = msg[:32]
|
msg = msg[:32]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to recover public key - should not panic
|
// Try to recover public key - should not panic
|
||||||
pubKey, err := RecoverPubkey(msg, sig)
|
pubKey, err := RecoverPubkey(msg, sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Expected for invalid signatures
|
// Expected for invalid signatures
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If recovery succeeded, verify the public key is valid
|
// If recovery succeeded, verify the public key is valid
|
||||||
if len(pubKey) != 65 && len(pubKey) != 33 {
|
if len(pubKey) != 65 && len(pubKey) != 33 {
|
||||||
t.Errorf("Invalid public key length: %d", len(pubKey))
|
t.Errorf("Invalid public key length: %d", len(pubKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to verify signature with recovered key
|
// Try to verify signature with recovered key
|
||||||
valid := VerifySignature(pubKey, msg, sig[:64])
|
valid := VerifySignature(pubKey, msg, sig[:64])
|
||||||
_ = valid // Result doesn't matter, just ensure no panic
|
_ = valid // Result doesn't matter, just ensure no panic
|
||||||
|
|
||||||
// Test decompression if compressed
|
// Test decompression if compressed
|
||||||
if len(pubKey) == 33 {
|
if len(pubKey) == 33 {
|
||||||
decompressed, err := DecompressPubkey(pubKey)
|
decompressed, err := DecompressPubkey(pubKey)
|
||||||
@@ -79,16 +79,16 @@ func FuzzSignatureCreation(f *testing.F) {
|
|||||||
// Seed corpus
|
// Seed corpus
|
||||||
f.Add(bytes.Repeat([]byte{0x01}, 32), bytes.Repeat([]byte{0x02}, 32))
|
f.Add(bytes.Repeat([]byte{0x01}, 32), bytes.Repeat([]byte{0x02}, 32))
|
||||||
f.Add(bytes.Repeat([]byte{0xff}, 32), hashing.ComputeHash256([]byte("test")))
|
f.Add(bytes.Repeat([]byte{0xff}, 32), hashing.ComputeHash256([]byte("test")))
|
||||||
|
|
||||||
// Add some valid private keys
|
// Add some valid private keys
|
||||||
validKey1 := make([]byte, 32)
|
validKey1 := make([]byte, 32)
|
||||||
validKey1[31] = 1
|
validKey1[31] = 1
|
||||||
f.Add(validKey1, hashing.ComputeHash256([]byte("message1")))
|
f.Add(validKey1, hashing.ComputeHash256([]byte("message1")))
|
||||||
|
|
||||||
validKey2 := make([]byte, 32)
|
validKey2 := make([]byte, 32)
|
||||||
copy(validKey2, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef})
|
copy(validKey2, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef})
|
||||||
f.Add(validKey2, hashing.ComputeHash256([]byte("message2")))
|
f.Add(validKey2, hashing.ComputeHash256([]byte("message2")))
|
||||||
|
|
||||||
f.Fuzz(func(t *testing.T, privKey []byte, msg []byte) {
|
f.Fuzz(func(t *testing.T, privKey []byte, msg []byte) {
|
||||||
// Ensure proper sizes
|
// Ensure proper sizes
|
||||||
if len(privKey) != 32 {
|
if len(privKey) != 32 {
|
||||||
@@ -98,7 +98,7 @@ func FuzzSignatureCreation(f *testing.F) {
|
|||||||
privKey = privKey[:32]
|
privKey = privKey[:32]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(msg) != 32 {
|
if len(msg) != 32 {
|
||||||
if len(msg) < 32 {
|
if len(msg) < 32 {
|
||||||
msg = append(msg, bytes.Repeat([]byte{0}, 32-len(msg))...)
|
msg = append(msg, bytes.Repeat([]byte{0}, 32-len(msg))...)
|
||||||
@@ -106,32 +106,32 @@ func FuzzSignatureCreation(f *testing.F) {
|
|||||||
msg = msg[:32]
|
msg = msg[:32]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to sign
|
// Try to sign
|
||||||
sig, err := Sign(msg, privKey)
|
sig, err := Sign(msg, privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Expected for invalid private keys
|
// Expected for invalid private keys
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signature should be 65 bytes
|
// Signature should be 65 bytes
|
||||||
if len(sig) != 65 {
|
if len(sig) != 65 {
|
||||||
t.Errorf("Invalid signature length: %d", len(sig))
|
t.Errorf("Invalid signature length: %d", len(sig))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recovery ID should be 0 or 1
|
// Recovery ID should be 0 or 1
|
||||||
if sig[64] > 1 {
|
if sig[64] > 1 {
|
||||||
t.Errorf("Invalid recovery ID: %d", sig[64])
|
t.Errorf("Invalid recovery ID: %d", sig[64])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to recover public key
|
// Try to recover public key
|
||||||
pubKey, err := RecoverPubkey(msg, sig)
|
pubKey, err := RecoverPubkey(msg, sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to recover public key from signature we created: %v", err)
|
t.Errorf("Failed to recover public key from signature we created: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature with recovered key
|
// Verify signature with recovered key
|
||||||
valid := VerifySignature(pubKey, msg, sig[:64])
|
valid := VerifySignature(pubKey, msg, sig[:64])
|
||||||
if !valid {
|
if !valid {
|
||||||
@@ -143,10 +143,10 @@ func FuzzSignatureCreation(f *testing.F) {
|
|||||||
// FuzzPublicKeyCompression tests public key compression/decompression
|
// FuzzPublicKeyCompression tests public key compression/decompression
|
||||||
func FuzzPublicKeyCompression(f *testing.F) {
|
func FuzzPublicKeyCompression(f *testing.F) {
|
||||||
// Seed corpus with various public key representations
|
// Seed corpus with various public key representations
|
||||||
f.Add(bytes.Repeat([]byte{0x04}, 65)) // Uncompressed prefix
|
f.Add(bytes.Repeat([]byte{0x04}, 65)) // Uncompressed prefix
|
||||||
f.Add(bytes.Repeat([]byte{0x02}, 33)) // Compressed even Y
|
f.Add(bytes.Repeat([]byte{0x02}, 33)) // Compressed even Y
|
||||||
f.Add(bytes.Repeat([]byte{0x03}, 33)) // Compressed odd Y
|
f.Add(bytes.Repeat([]byte{0x03}, 33)) // Compressed odd Y
|
||||||
|
|
||||||
// Generate a valid public key
|
// Generate a valid public key
|
||||||
privKey := make([]byte, 32)
|
privKey := make([]byte, 32)
|
||||||
privKey[31] = 42
|
privKey[31] = 42
|
||||||
@@ -156,7 +156,7 @@ func FuzzPublicKeyCompression(f *testing.F) {
|
|||||||
f.Add(pubKey)
|
f.Add(pubKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Fuzz(func(t *testing.T, pubKeyData []byte) {
|
f.Fuzz(func(t *testing.T, pubKeyData []byte) {
|
||||||
// Test compression if uncompressed
|
// Test compression if uncompressed
|
||||||
if len(pubKeyData) == 65 && pubKeyData[0] == 0x04 {
|
if len(pubKeyData) == 65 && pubKeyData[0] == 0x04 {
|
||||||
@@ -165,19 +165,19 @@ func FuzzPublicKeyCompression(f *testing.F) {
|
|||||||
t.Errorf("Compressed public key has wrong length: %d", len(compressed))
|
t.Errorf("Compressed public key has wrong length: %d", len(compressed))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to decompress back
|
// Try to decompress back
|
||||||
decompressed, err := DecompressPubkey(compressed)
|
decompressed, err := DecompressPubkey(compressed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Some points might not be on the curve
|
// Some points might not be on the curve
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(decompressed) != 65 {
|
if len(decompressed) != 65 {
|
||||||
t.Errorf("Decompressed public key has wrong length: %d", len(decompressed))
|
t.Errorf("Decompressed public key has wrong length: %d", len(decompressed))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test decompression if compressed
|
// Test decompression if compressed
|
||||||
if len(pubKeyData) == 33 && (pubKeyData[0] == 0x02 || pubKeyData[0] == 0x03) {
|
if len(pubKeyData) == 33 && (pubKeyData[0] == 0x02 || pubKeyData[0] == 0x03) {
|
||||||
decompressed, err := DecompressPubkey(pubKeyData)
|
decompressed, err := DecompressPubkey(pubKeyData)
|
||||||
@@ -185,12 +185,12 @@ func FuzzPublicKeyCompression(f *testing.F) {
|
|||||||
// Expected for invalid compressed keys
|
// Expected for invalid compressed keys
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(decompressed) != 65 {
|
if len(decompressed) != 65 {
|
||||||
t.Errorf("Decompressed public key has wrong length: %d", len(decompressed))
|
t.Errorf("Decompressed public key has wrong length: %d", len(decompressed))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compress again and verify round-trip
|
// Compress again and verify round-trip
|
||||||
recompressed := CompressPubkey(decompressed[1:33], decompressed[33:65])
|
recompressed := CompressPubkey(decompressed[1:33], decompressed[33:65])
|
||||||
if !bytes.Equal(recompressed, pubKeyData) {
|
if !bytes.Equal(recompressed, pubKeyData) {
|
||||||
@@ -206,7 +206,7 @@ func FuzzSignatureMalleability(f *testing.F) {
|
|||||||
// Seed corpus
|
// Seed corpus
|
||||||
f.Add(bytes.Repeat([]byte{0x7f}, 32), bytes.Repeat([]byte{0x80}, 32), uint8(0))
|
f.Add(bytes.Repeat([]byte{0x7f}, 32), bytes.Repeat([]byte{0x80}, 32), uint8(0))
|
||||||
f.Add(bytes.Repeat([]byte{0xff}, 32), bytes.Repeat([]byte{0x01}, 32), uint8(1))
|
f.Add(bytes.Repeat([]byte{0xff}, 32), bytes.Repeat([]byte{0x01}, 32), uint8(1))
|
||||||
|
|
||||||
f.Fuzz(func(t *testing.T, r []byte, s []byte, v uint8) {
|
f.Fuzz(func(t *testing.T, r []byte, s []byte, v uint8) {
|
||||||
// Ensure proper sizes
|
// Ensure proper sizes
|
||||||
if len(r) != 32 {
|
if len(r) != 32 {
|
||||||
@@ -216,7 +216,7 @@ func FuzzSignatureMalleability(f *testing.F) {
|
|||||||
r = r[:32]
|
r = r[:32]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s) != 32 {
|
if len(s) != 32 {
|
||||||
if len(s) < 32 {
|
if len(s) < 32 {
|
||||||
s = append(s, bytes.Repeat([]byte{0}, 32-len(s))...)
|
s = append(s, bytes.Repeat([]byte{0}, 32-len(s))...)
|
||||||
@@ -224,23 +224,23 @@ func FuzzSignatureMalleability(f *testing.F) {
|
|||||||
s = s[:32]
|
s = s[:32]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create signature
|
// Create signature
|
||||||
sig := make([]byte, 65)
|
sig := make([]byte, 65)
|
||||||
copy(sig[:32], r)
|
copy(sig[:32], r)
|
||||||
copy(sig[32:64], s)
|
copy(sig[32:64], s)
|
||||||
sig[64] = v & 1 // Ensure v is 0 or 1
|
sig[64] = v & 1 // Ensure v is 0 or 1
|
||||||
|
|
||||||
// Create a random message
|
// Create a random message
|
||||||
msg := hashing.ComputeHash256(append(r, s...))
|
msg := hashing.ComputeHash256(append(r, s...))
|
||||||
|
|
||||||
// Try to verify - should not panic
|
// Try to verify - should not panic
|
||||||
pubKey, err := RecoverPubkey(msg, sig)
|
pubKey, err := RecoverPubkey(msg, sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Expected for invalid signatures
|
// Expected for invalid signatures
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try signature verification
|
// Try signature verification
|
||||||
valid := VerifySignature(pubKey, msg, sig[:64])
|
valid := VerifySignature(pubKey, msg, sig[:64])
|
||||||
_ = valid // Don't care about result, just ensure no panic
|
_ = valid // Don't care about result, just ensure no panic
|
||||||
@@ -252,15 +252,15 @@ func FuzzECDSAEdgeCases(f *testing.F) {
|
|||||||
// Seed corpus with edge cases
|
// Seed corpus with edge cases
|
||||||
f.Add([]byte{}, []byte{})
|
f.Add([]byte{}, []byte{})
|
||||||
f.Add(nil, nil)
|
f.Add(nil, nil)
|
||||||
|
|
||||||
// All zeros
|
// All zeros
|
||||||
zeros := make([]byte, 32)
|
zeros := make([]byte, 32)
|
||||||
f.Add(zeros, zeros)
|
f.Add(zeros, zeros)
|
||||||
|
|
||||||
// All ones
|
// All ones
|
||||||
ones := bytes.Repeat([]byte{0xff}, 32)
|
ones := bytes.Repeat([]byte{0xff}, 32)
|
||||||
f.Add(ones, ones)
|
f.Add(ones, ones)
|
||||||
|
|
||||||
// Near the curve order
|
// Near the curve order
|
||||||
nearOrder := make([]byte, 32)
|
nearOrder := make([]byte, 32)
|
||||||
copy(nearOrder, []byte{
|
copy(nearOrder, []byte{
|
||||||
@@ -270,7 +270,7 @@ func FuzzECDSAEdgeCases(f *testing.F) {
|
|||||||
0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41,
|
0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41,
|
||||||
})
|
})
|
||||||
f.Add(nearOrder, hashing.ComputeHash256([]byte("edge")))
|
f.Add(nearOrder, hashing.ComputeHash256([]byte("edge")))
|
||||||
|
|
||||||
f.Fuzz(func(t *testing.T, privKey []byte, msg []byte) {
|
f.Fuzz(func(t *testing.T, privKey []byte, msg []byte) {
|
||||||
// Handle nil and empty cases
|
// Handle nil and empty cases
|
||||||
if privKey == nil {
|
if privKey == nil {
|
||||||
@@ -279,20 +279,20 @@ func FuzzECDSAEdgeCases(f *testing.F) {
|
|||||||
if msg == nil {
|
if msg == nil {
|
||||||
msg = make([]byte, 32)
|
msg = make([]byte, 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure proper sizes
|
// Ensure proper sizes
|
||||||
if len(privKey) < 32 {
|
if len(privKey) < 32 {
|
||||||
privKey = append(privKey, bytes.Repeat([]byte{0}, 32-len(privKey))...)
|
privKey = append(privKey, bytes.Repeat([]byte{0}, 32-len(privKey))...)
|
||||||
} else if len(privKey) > 32 {
|
} else if len(privKey) > 32 {
|
||||||
privKey = privKey[:32]
|
privKey = privKey[:32]
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(msg) < 32 {
|
if len(msg) < 32 {
|
||||||
msg = append(msg, bytes.Repeat([]byte{0}, 32-len(msg))...)
|
msg = append(msg, bytes.Repeat([]byte{0}, 32-len(msg))...)
|
||||||
} else if len(msg) > 32 {
|
} else if len(msg) > 32 {
|
||||||
msg = msg[:32]
|
msg = msg[:32]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test signing
|
// Test signing
|
||||||
sig, err := Sign(msg, privKey)
|
sig, err := Sign(msg, privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -305,25 +305,25 @@ func FuzzECDSAEdgeCases(f *testing.F) {
|
|||||||
t.Errorf("Unexpected error type: %v", err)
|
t.Errorf("Unexpected error type: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If signing succeeded, verify signature properties
|
// If signing succeeded, verify signature properties
|
||||||
if len(sig) != 65 {
|
if len(sig) != 65 {
|
||||||
t.Errorf("Invalid signature length: %d", len(sig))
|
t.Errorf("Invalid signature length: %d", len(sig))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check recovery ID
|
// Check recovery ID
|
||||||
if sig[64] > 1 {
|
if sig[64] > 1 {
|
||||||
t.Errorf("Invalid recovery ID: %d", sig[64])
|
t.Errorf("Invalid recovery ID: %d", sig[64])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try recovery
|
// Try recovery
|
||||||
pubKey, err := RecoverPubkey(msg, sig)
|
pubKey, err := RecoverPubkey(msg, sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to recover public key from valid signature: %v", err)
|
t.Errorf("Failed to recover public key from valid signature: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
if !VerifySignature(pubKey, msg, sig[:64]) {
|
if !VerifySignature(pubKey, msg, sig[:64]) {
|
||||||
t.Error("Signature verification failed for edge case")
|
t.Error("Signature verification failed for edge case")
|
||||||
@@ -338,4 +338,4 @@ func randomBytes(t *testing.T, n int) []byte {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-26
@@ -12,28 +12,28 @@ import (
|
|||||||
type SigID string
|
type SigID string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MLDSA2 SigID = "mldsa2"
|
MLDSA2 SigID = "mldsa2"
|
||||||
MLDSA3 SigID = "mldsa3"
|
MLDSA3 SigID = "mldsa3"
|
||||||
SLHDSA SigID = "slhdsa"
|
SLHDSA SigID = "slhdsa"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Signer interface for signature algorithms
|
// Signer interface for signature algorithms
|
||||||
type Signer interface {
|
type Signer interface {
|
||||||
// GenerateKeyPair generates a new signing key pair
|
// GenerateKeyPair generates a new signing key pair
|
||||||
GenerateKeyPair() (PublicKey, PrivateKey, error)
|
GenerateKeyPair() (PublicKey, PrivateKey, error)
|
||||||
|
|
||||||
// Sign creates a signature
|
// Sign creates a signature
|
||||||
Sign(sk PrivateKey, message []byte) ([]byte, error)
|
Sign(sk PrivateKey, message []byte) ([]byte, error)
|
||||||
|
|
||||||
// Verify verifies a signature
|
// Verify verifies a signature
|
||||||
Verify(pk PublicKey, message, signature []byte) bool
|
Verify(pk PublicKey, message, signature []byte) bool
|
||||||
|
|
||||||
// PublicKeySize returns the size of public keys
|
// PublicKeySize returns the size of public keys
|
||||||
PublicKeySize() int
|
PublicKeySize() int
|
||||||
|
|
||||||
// PrivateKeySize returns the size of private keys
|
// PrivateKeySize returns the size of private keys
|
||||||
PrivateKeySize() int
|
PrivateKeySize() int
|
||||||
|
|
||||||
// SignatureSize returns the size of signatures
|
// SignatureSize returns the size of signatures
|
||||||
SignatureSize() int
|
SignatureSize() int
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ const (
|
|||||||
func (m *MLDSA2Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
func (m *MLDSA2Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
||||||
// Placeholder for actual ML-DSA key generation
|
// Placeholder for actual ML-DSA key generation
|
||||||
// In production, this would use liboqs or native implementation
|
// In production, this would use liboqs or native implementation
|
||||||
|
|
||||||
pk := &MLDSA2PublicKey{
|
pk := &MLDSA2PublicKey{
|
||||||
data: make([]byte, mldsa2PublicKeySize),
|
data: make([]byte, mldsa2PublicKeySize),
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ func (m *MLDSA2Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
data: make([]byte, mldsa2PrivateKeySize),
|
data: make([]byte, mldsa2PrivateKeySize),
|
||||||
pk: pk,
|
pk: pk,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate random key material (placeholder)
|
// Generate random key material (placeholder)
|
||||||
if _, err := rand.Read(pk.data); err != nil {
|
if _, err := rand.Read(pk.data); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -97,7 +97,7 @@ func (m *MLDSA2Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
if _, err := rand.Read(sk.data); err != nil {
|
if _, err := rand.Read(sk.data); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pk, sk, nil
|
return pk, sk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,18 +107,18 @@ func (m *MLDSA2Impl) Sign(sk PrivateKey, message []byte) ([]byte, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("invalid private key type")
|
return nil, errors.New("invalid private key type")
|
||||||
}
|
}
|
||||||
|
|
||||||
signature := make([]byte, mldsa2SignatureSize)
|
signature := make([]byte, mldsa2SignatureSize)
|
||||||
|
|
||||||
// Placeholder for actual ML-DSA signing
|
// Placeholder for actual ML-DSA signing
|
||||||
if _, err := rand.Read(signature); err != nil {
|
if _, err := rand.Read(signature); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// In production, this would perform actual ML-DSA signing
|
// In production, this would perform actual ML-DSA signing
|
||||||
_ = mldsaSK.data
|
_ = mldsaSK.data
|
||||||
_ = message
|
_ = message
|
||||||
|
|
||||||
return signature, nil
|
return signature, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,17 +128,17 @@ func (m *MLDSA2Impl) Verify(pk PublicKey, message, signature []byte) bool {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(signature) != mldsa2SignatureSize {
|
if len(signature) != mldsa2SignatureSize {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder for actual ML-DSA verification
|
// Placeholder for actual ML-DSA verification
|
||||||
// In production, this would perform actual verification
|
// In production, this would perform actual verification
|
||||||
_ = mldsaPK.data
|
_ = mldsaPK.data
|
||||||
_ = message
|
_ = message
|
||||||
_ = signature
|
_ = signature
|
||||||
|
|
||||||
// Placeholder: always return true for now
|
// Placeholder: always return true for now
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -202,25 +202,25 @@ func (m *MLDSA3Impl) GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
data: make([]byte, mldsa3PrivateKeySize),
|
data: make([]byte, mldsa3PrivateKeySize),
|
||||||
pk: pk,
|
pk: pk,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := rand.Read(pk.data); err != nil {
|
if _, err := rand.Read(pk.data); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if _, err := rand.Read(sk.data); err != nil {
|
if _, err := rand.Read(sk.data); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pk, sk, nil
|
return pk, sk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign creates a signature
|
// Sign creates a signature
|
||||||
func (m *MLDSA3Impl) Sign(sk PrivateKey, message []byte) ([]byte, error) {
|
func (m *MLDSA3Impl) Sign(sk PrivateKey, message []byte) ([]byte, error) {
|
||||||
signature := make([]byte, mldsa3SignatureSize)
|
signature := make([]byte, mldsa3SignatureSize)
|
||||||
|
|
||||||
if _, err := rand.Read(signature); err != nil {
|
if _, err := rand.Read(signature); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return signature, nil
|
return signature, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ func (m *MLDSA3Impl) Verify(pk PublicKey, message, signature []byte) bool {
|
|||||||
if len(signature) != mldsa3SignatureSize {
|
if len(signature) != mldsa3SignatureSize {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder
|
// Placeholder
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -273,7 +273,7 @@ func (ts *TranscriptSigner) SignTranscript(transcript []byte) ([]byte, error) {
|
|||||||
// Add context string to prevent cross-protocol attacks
|
// Add context string to prevent cross-protocol attacks
|
||||||
context := []byte("QZMQ-Transcript-v1")
|
context := []byte("QZMQ-Transcript-v1")
|
||||||
message := append(context, transcript...)
|
message := append(context, transcript...)
|
||||||
|
|
||||||
return ts.signer.Sign(ts.sk, message)
|
return ts.signer.Sign(ts.sk, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,6 +281,6 @@ func (ts *TranscriptSigner) SignTranscript(transcript []byte) ([]byte, error) {
|
|||||||
func VerifyTranscript(signer Signer, pk PublicKey, transcript, signature []byte) bool {
|
func VerifyTranscript(signer Signer, pk PublicKey, transcript, signature []byte) bool {
|
||||||
context := []byte("QZMQ-Transcript-v1")
|
context := []byte("QZMQ-Transcript-v1")
|
||||||
message := append(context, transcript...)
|
message := append(context, transcript...)
|
||||||
|
|
||||||
return signer.Verify(pk, message, signature)
|
return signer.Verify(pk, message, signature)
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-20
@@ -18,7 +18,7 @@ type OptimizedSLHDSA struct {
|
|||||||
// NewOptimized creates an optimized SLH-DSA instance
|
// NewOptimized creates an optimized SLH-DSA instance
|
||||||
func NewOptimized(mode Mode) *OptimizedSLHDSA {
|
func NewOptimized(mode Mode) *OptimizedSLHDSA {
|
||||||
numWorkers := runtime.NumCPU()
|
numWorkers := runtime.NumCPU()
|
||||||
|
|
||||||
return &OptimizedSLHDSA{
|
return &OptimizedSLHDSA{
|
||||||
mode: mode,
|
mode: mode,
|
||||||
cachePool: &sync.Pool{
|
cachePool: &sync.Pool{
|
||||||
@@ -44,11 +44,11 @@ func (o *OptimizedSLHDSA) OptimizedSign(privateKey *PrivateKey, message []byte)
|
|||||||
// Acquire worker slot
|
// Acquire worker slot
|
||||||
o.workerPool <- struct{}{}
|
o.workerPool <- struct{}{}
|
||||||
defer func() { <-o.workerPool }()
|
defer func() { <-o.workerPool }()
|
||||||
|
|
||||||
// Get cache buffer from pool
|
// Get cache buffer from pool
|
||||||
cache := o.cachePool.Get().([]byte)
|
cache := o.cachePool.Get().([]byte)
|
||||||
defer o.cachePool.Put(cache)
|
defer o.cachePool.Put(cache)
|
||||||
|
|
||||||
// Use optimized signing based on mode
|
// Use optimized signing based on mode
|
||||||
switch o.mode {
|
switch o.mode {
|
||||||
case SLHDSA128f:
|
case SLHDSA128f:
|
||||||
@@ -125,7 +125,7 @@ func (o *OptimizedSLHDSA) optimizedSignGeneric(sk *PrivateKey, msg []byte, cache
|
|||||||
func (o *OptimizedSLHDSA) processTreeOptimized(treeIdx int, sk *PrivateKey, msg []byte, sig []byte, cache []byte) {
|
func (o *OptimizedSLHDSA) processTreeOptimized(treeIdx int, sk *PrivateKey, msg []byte, sig []byte, cache []byte) {
|
||||||
// Cache-friendly tree traversal
|
// Cache-friendly tree traversal
|
||||||
// Use cache buffer for intermediate values
|
// Use cache buffer for intermediate values
|
||||||
|
|
||||||
// Placeholder for actual tree processing
|
// Placeholder for actual tree processing
|
||||||
// Real implementation would compute Merkle tree with optimizations
|
// Real implementation would compute Merkle tree with optimizations
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ func (o *OptimizedSLHDSA) processTreeOptimized(treeIdx int, sk *PrivateKey, msg
|
|||||||
func (o *OptimizedSLHDSA) processTreesSequential(sk *PrivateKey, msg []byte, sig []byte, cache []byte) {
|
func (o *OptimizedSLHDSA) processTreesSequential(sk *PrivateKey, msg []byte, sig []byte, cache []byte) {
|
||||||
// Sequential processing with minimal memory footprint
|
// Sequential processing with minimal memory footprint
|
||||||
// Reuse cache buffer for each tree
|
// Reuse cache buffer for each tree
|
||||||
|
|
||||||
// Placeholder for actual sequential processing
|
// Placeholder for actual sequential processing
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ func (o *OptimizedSLHDSA) processTreesSequential(sk *PrivateKey, msg []byte, sig
|
|||||||
func (o *OptimizedSLHDSA) signWithSIMD(sk *PrivateKey, msg []byte, sig []byte, cache []byte, n, w, h, d int) {
|
func (o *OptimizedSLHDSA) signWithSIMD(sk *PrivateKey, msg []byte, sig []byte, cache []byte, n, w, h, d int) {
|
||||||
// SIMD-accelerated signing
|
// SIMD-accelerated signing
|
||||||
// Uses vector instructions for parallel hash computations
|
// Uses vector instructions for parallel hash computations
|
||||||
|
|
||||||
// This would call assembly implementations for different architectures
|
// This would call assembly implementations for different architectures
|
||||||
switch runtime.GOARCH {
|
switch runtime.GOARCH {
|
||||||
case "amd64":
|
case "amd64":
|
||||||
@@ -179,11 +179,11 @@ func (o *OptimizedSLHDSA) OptimizedVerify(publicKey *PublicKey, message []byte,
|
|||||||
// Acquire worker slot
|
// Acquire worker slot
|
||||||
o.workerPool <- struct{}{}
|
o.workerPool <- struct{}{}
|
||||||
defer func() { <-o.workerPool }()
|
defer func() { <-o.workerPool }()
|
||||||
|
|
||||||
// Get cache buffer from pool
|
// Get cache buffer from pool
|
||||||
cache := o.cachePool.Get().([]byte)
|
cache := o.cachePool.Get().([]byte)
|
||||||
defer o.cachePool.Put(cache)
|
defer o.cachePool.Put(cache)
|
||||||
|
|
||||||
// Parallel verification of Merkle paths
|
// Parallel verification of Merkle paths
|
||||||
return o.verifyOptimized(publicKey, message, signature, cache)
|
return o.verifyOptimized(publicKey, message, signature, cache)
|
||||||
}
|
}
|
||||||
@@ -240,28 +240,28 @@ func (o *OptimizedSLHDSA) RunBenchmark(config BenchmarkConfig) BenchmarkResult {
|
|||||||
sk, _ := GenerateKey(rand.Reader, config.Mode)
|
sk, _ := GenerateKey(rand.Reader, config.Mode)
|
||||||
pk := &sk.PublicKey
|
pk := &sk.PublicKey
|
||||||
message := make([]byte, config.MessageSize)
|
message := make([]byte, config.MessageSize)
|
||||||
|
|
||||||
// Benchmark signing
|
// Benchmark signing
|
||||||
startSign := nanotime()
|
startSign := nanotime()
|
||||||
for i := 0; i < config.Iterations; i++ {
|
for i := 0; i < config.Iterations; i++ {
|
||||||
o.OptimizedSign(sk, message)
|
o.OptimizedSign(sk, message)
|
||||||
}
|
}
|
||||||
result.SignTime = (nanotime() - startSign) / int64(config.Iterations)
|
result.SignTime = (nanotime() - startSign) / int64(config.Iterations)
|
||||||
|
|
||||||
// Generate signature for verification benchmark
|
// Generate signature for verification benchmark
|
||||||
sig, _ := o.OptimizedSign(sk, message)
|
sig, _ := o.OptimizedSign(sk, message)
|
||||||
|
|
||||||
// Benchmark verification
|
// Benchmark verification
|
||||||
startVerify := nanotime()
|
startVerify := nanotime()
|
||||||
for i := 0; i < config.Iterations; i++ {
|
for i := 0; i < config.Iterations; i++ {
|
||||||
o.OptimizedVerify(pk, message, sig)
|
o.OptimizedVerify(pk, message, sig)
|
||||||
}
|
}
|
||||||
result.VerifyTime = (nanotime() - startVerify) / int64(config.Iterations)
|
result.VerifyTime = (nanotime() - startVerify) / int64(config.Iterations)
|
||||||
|
|
||||||
// Calculate operations per second
|
// Calculate operations per second
|
||||||
result.SignOpsPerSec = 1e9 / float64(result.SignTime)
|
result.SignOpsPerSec = 1e9 / float64(result.SignTime)
|
||||||
result.VerifyOpsPerSec = 1e9 / float64(result.VerifyTime)
|
result.VerifyOpsPerSec = 1e9 / float64(result.VerifyTime)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,8 +269,8 @@ func (o *OptimizedSLHDSA) RunBenchmark(config BenchmarkConfig) BenchmarkResult {
|
|||||||
type BenchmarkResult struct {
|
type BenchmarkResult struct {
|
||||||
Mode Mode
|
Mode Mode
|
||||||
MessageSize int
|
MessageSize int
|
||||||
SignTime int64 // nanoseconds
|
SignTime int64 // nanoseconds
|
||||||
VerifyTime int64 // nanoseconds
|
VerifyTime int64 // nanoseconds
|
||||||
SignOpsPerSec float64
|
SignOpsPerSec float64
|
||||||
VerifyOpsPerSec float64
|
VerifyOpsPerSec float64
|
||||||
}
|
}
|
||||||
@@ -285,10 +285,10 @@ func nanotime() int64 {
|
|||||||
var (
|
var (
|
||||||
// Precomputed hash chains for common operations
|
// Precomputed hash chains for common operations
|
||||||
precomputedChains = make(map[Mode][]byte)
|
precomputedChains = make(map[Mode][]byte)
|
||||||
|
|
||||||
// Lookup tables for Winternitz chains
|
// Lookup tables for Winternitz chains
|
||||||
winternitzTables = make(map[Mode][][]byte)
|
winternitzTables = make(map[Mode][][]byte)
|
||||||
|
|
||||||
// Once ensures precomputation happens only once
|
// Once ensures precomputation happens only once
|
||||||
precomputeOnce sync.Once
|
precomputeOnce sync.Once
|
||||||
)
|
)
|
||||||
@@ -298,11 +298,11 @@ func InitPrecomputation() {
|
|||||||
precomputeOnce.Do(func() {
|
precomputeOnce.Do(func() {
|
||||||
// Precompute for each mode
|
// Precompute for each mode
|
||||||
modes := []Mode{SLHDSA128f, SLHDSA128s, SLHDSA192f, SLHDSA192s, SLHDSA256f, SLHDSA256s}
|
modes := []Mode{SLHDSA128f, SLHDSA128s, SLHDSA192f, SLHDSA192s, SLHDSA256f, SLHDSA256s}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
// Precompute hash chains
|
// Precompute hash chains
|
||||||
precomputeHashChains(mode)
|
precomputeHashChains(mode)
|
||||||
|
|
||||||
// Precompute Winternitz tables
|
// Precompute Winternitz tables
|
||||||
precomputeWinternitz(mode)
|
precomputeWinternitz(mode)
|
||||||
}
|
}
|
||||||
@@ -321,4 +321,4 @@ func precomputeWinternitz(mode Mode) {
|
|||||||
// Placeholder for Winternitz precomputation
|
// Placeholder for Winternitz precomputation
|
||||||
// Real implementation would compute chain values
|
// Real implementation would compute chain values
|
||||||
winternitzTables[mode] = make([][]byte, 16)
|
winternitzTables[mode] = make([][]byte, 16)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ func TestOptimizedPerformance(t *testing.T) {
|
|||||||
SLHDSA256f,
|
SLHDSA256f,
|
||||||
SLHDSA256s,
|
SLHDSA256s,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize precomputation tables
|
// Initialize precomputation tables
|
||||||
InitPrecomputation()
|
InitPrecomputation()
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
t.Run(fmt.Sprintf("Mode_%v", mode), func(t *testing.T) {
|
t.Run(fmt.Sprintf("Mode_%v", mode), func(t *testing.T) {
|
||||||
opt := NewOptimized(mode)
|
opt := NewOptimized(mode)
|
||||||
|
|
||||||
// Generate test key pair
|
// Generate test key pair
|
||||||
priv, err := GenerateKey(rand.Reader, mode)
|
priv, err := GenerateKey(rand.Reader, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,21 +33,21 @@ func TestOptimizedPerformance(t *testing.T) {
|
|||||||
}
|
}
|
||||||
sk := priv
|
sk := priv
|
||||||
pk := &priv.PublicKey
|
pk := &priv.PublicKey
|
||||||
|
|
||||||
message := []byte("Test message for optimization benchmarks")
|
message := []byte("Test message for optimization benchmarks")
|
||||||
|
|
||||||
// Test optimized signing
|
// Test optimized signing
|
||||||
sig, err := opt.OptimizedSign(sk, message)
|
sig, err := opt.OptimizedSign(sk, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Optimized signing failed: %v", err)
|
t.Fatalf("Optimized signing failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test optimized verification
|
// Test optimized verification
|
||||||
valid := opt.OptimizedVerify(pk, message, sig)
|
valid := opt.OptimizedVerify(pk, message, sig)
|
||||||
if !valid {
|
if !valid {
|
||||||
t.Error("Optimized verification failed")
|
t.Error("Optimized verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature size
|
// Verify signature size
|
||||||
expectedSize := opt.getSignatureSize()
|
expectedSize := opt.getSignatureSize()
|
||||||
if len(sig) != expectedSize {
|
if len(sig) != expectedSize {
|
||||||
@@ -70,15 +70,15 @@ func BenchmarkOptimizedSigning(b *testing.B) {
|
|||||||
{SLHDSA256f, "256f"},
|
{SLHDSA256f, "256f"},
|
||||||
{SLHDSA256s, "256s"},
|
{SLHDSA256s, "256s"},
|
||||||
}
|
}
|
||||||
|
|
||||||
InitPrecomputation()
|
InitPrecomputation()
|
||||||
message := make([]byte, 32)
|
message := make([]byte, 32)
|
||||||
|
|
||||||
for _, bm := range benchmarks {
|
for _, bm := range benchmarks {
|
||||||
b.Run(bm.name, func(b *testing.B) {
|
b.Run(bm.name, func(b *testing.B) {
|
||||||
opt := NewOptimized(bm.mode)
|
opt := NewOptimized(bm.mode)
|
||||||
sk, _ := GenerateKey(rand.Reader, bm.mode)
|
sk, _ := GenerateKey(rand.Reader, bm.mode)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
opt.OptimizedSign(sk, message)
|
opt.OptimizedSign(sk, message)
|
||||||
@@ -100,10 +100,10 @@ func BenchmarkOptimizedVerification(b *testing.B) {
|
|||||||
{SLHDSA256f, "256f"},
|
{SLHDSA256f, "256f"},
|
||||||
{SLHDSA256s, "256s"},
|
{SLHDSA256s, "256s"},
|
||||||
}
|
}
|
||||||
|
|
||||||
InitPrecomputation()
|
InitPrecomputation()
|
||||||
message := make([]byte, 32)
|
message := make([]byte, 32)
|
||||||
|
|
||||||
for _, bm := range benchmarks {
|
for _, bm := range benchmarks {
|
||||||
b.Run(bm.name, func(b *testing.B) {
|
b.Run(bm.name, func(b *testing.B) {
|
||||||
opt := NewOptimized(bm.mode)
|
opt := NewOptimized(bm.mode)
|
||||||
@@ -111,7 +111,7 @@ func BenchmarkOptimizedVerification(b *testing.B) {
|
|||||||
sk := priv
|
sk := priv
|
||||||
pk := &priv.PublicKey
|
pk := &priv.PublicKey
|
||||||
sig, _ := opt.OptimizedSign(sk, message)
|
sig, _ := opt.OptimizedSign(sk, message)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
opt.OptimizedVerify(pk, message, sig)
|
opt.OptimizedVerify(pk, message, sig)
|
||||||
@@ -135,11 +135,11 @@ func BenchmarkComparison(b *testing.B) {
|
|||||||
// Verify is not needed in benchmark
|
// Verify is not needed in benchmark
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("Optimized", func(b *testing.B) {
|
b.Run("Optimized", func(b *testing.B) {
|
||||||
opt := NewOptimized(mode)
|
opt := NewOptimized(mode)
|
||||||
InitPrecomputation()
|
InitPrecomputation()
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
sig, _ := opt.OptimizedSign(sk, message)
|
sig, _ := opt.OptimizedSign(sk, message)
|
||||||
@@ -158,33 +158,33 @@ func TestParallelPerformance(t *testing.T) {
|
|||||||
sk := priv
|
sk := priv
|
||||||
pk := &priv.PublicKey
|
pk := &priv.PublicKey
|
||||||
message := make([]byte, 32)
|
message := make([]byte, 32)
|
||||||
|
|
||||||
// Test different CPU counts
|
// Test different CPU counts
|
||||||
cpuCounts := []int{1, 2, 4, 8}
|
cpuCounts := []int{1, 2, 4, 8}
|
||||||
|
|
||||||
for _, cpus := range cpuCounts {
|
for _, cpus := range cpuCounts {
|
||||||
if cpus > runtime.NumCPU() {
|
if cpus > runtime.NumCPU() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("CPUs_%d", cpus), func(t *testing.T) {
|
t.Run(fmt.Sprintf("CPUs_%d", cpus), func(t *testing.T) {
|
||||||
runtime.GOMAXPROCS(cpus)
|
runtime.GOMAXPROCS(cpus)
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
iterations := 100
|
iterations := 100
|
||||||
|
|
||||||
for i := 0; i < iterations; i++ {
|
for i := 0; i < iterations; i++ {
|
||||||
sig, _ := opt.OptimizedSign(sk, message)
|
sig, _ := opt.OptimizedSign(sk, message)
|
||||||
opt.OptimizedVerify(pk, message, sig)
|
opt.OptimizedVerify(pk, message, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
opsPerSec := float64(iterations) / elapsed.Seconds()
|
opsPerSec := float64(iterations) / elapsed.Seconds()
|
||||||
|
|
||||||
t.Logf("CPUs: %d, Ops/sec: %.2f", cpus, opsPerSec)
|
t.Logf("CPUs: %d, Ops/sec: %.2f", cpus, opsPerSec)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset to default
|
// Reset to default
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
}
|
}
|
||||||
@@ -192,7 +192,7 @@ func TestParallelPerformance(t *testing.T) {
|
|||||||
// TestMemoryUsage tests memory efficiency of optimizations
|
// TestMemoryUsage tests memory efficiency of optimizations
|
||||||
func TestMemoryUsage(t *testing.T) {
|
func TestMemoryUsage(t *testing.T) {
|
||||||
modes := []Mode{SLHDSA128f, SLHDSA128s}
|
modes := []Mode{SLHDSA128f, SLHDSA128s}
|
||||||
|
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
t.Run(fmt.Sprintf("Mode_%v", mode), func(t *testing.T) {
|
t.Run(fmt.Sprintf("Mode_%v", mode), func(t *testing.T) {
|
||||||
opt := NewOptimized(mode)
|
opt := NewOptimized(mode)
|
||||||
@@ -200,22 +200,22 @@ func TestMemoryUsage(t *testing.T) {
|
|||||||
sk := priv
|
sk := priv
|
||||||
pk := &priv.PublicKey
|
pk := &priv.PublicKey
|
||||||
message := make([]byte, 32)
|
message := make([]byte, 32)
|
||||||
|
|
||||||
// Measure memory allocations
|
// Measure memory allocations
|
||||||
var m1, m2 runtime.MemStats
|
var m1, m2 runtime.MemStats
|
||||||
runtime.ReadMemStats(&m1)
|
runtime.ReadMemStats(&m1)
|
||||||
|
|
||||||
// Perform multiple operations
|
// Perform multiple operations
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
sig, _ := opt.OptimizedSign(sk, message)
|
sig, _ := opt.OptimizedSign(sk, message)
|
||||||
opt.OptimizedVerify(pk, message, sig)
|
opt.OptimizedVerify(pk, message, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.ReadMemStats(&m2)
|
runtime.ReadMemStats(&m2)
|
||||||
|
|
||||||
allocations := m2.Alloc - m1.Alloc
|
allocations := m2.Alloc - m1.Alloc
|
||||||
t.Logf("Memory allocated: %d bytes", allocations)
|
t.Logf("Memory allocated: %d bytes", allocations)
|
||||||
|
|
||||||
// Check that we're using the pool effectively
|
// Check that we're using the pool effectively
|
||||||
if allocations > 10*1024*1024 { // 10MB threshold
|
if allocations > 10*1024*1024 { // 10MB threshold
|
||||||
t.Logf("Warning: High memory usage detected")
|
t.Logf("Warning: High memory usage detected")
|
||||||
@@ -227,30 +227,30 @@ func TestMemoryUsage(t *testing.T) {
|
|||||||
// TestSIMDDetection tests SIMD detection and fallback
|
// TestSIMDDetection tests SIMD detection and fallback
|
||||||
func TestSIMDDetection(t *testing.T) {
|
func TestSIMDDetection(t *testing.T) {
|
||||||
opt := NewOptimized(SLHDSA128f)
|
opt := NewOptimized(SLHDSA128f)
|
||||||
|
|
||||||
t.Logf("Architecture: %s", runtime.GOARCH)
|
t.Logf("Architecture: %s", runtime.GOARCH)
|
||||||
t.Logf("SIMD Enabled: %v", opt.simdEnable)
|
t.Logf("SIMD Enabled: %v", opt.simdEnable)
|
||||||
|
|
||||||
// Test that both paths work
|
// Test that both paths work
|
||||||
priv, _ := GenerateKey(rand.Reader, SLHDSA128f)
|
priv, _ := GenerateKey(rand.Reader, SLHDSA128f)
|
||||||
sk := priv
|
sk := priv
|
||||||
pk := &priv.PublicKey
|
pk := &priv.PublicKey
|
||||||
message := []byte("Test message")
|
message := []byte("Test message")
|
||||||
|
|
||||||
// Force SIMD path
|
// Force SIMD path
|
||||||
opt.simdEnable = true
|
opt.simdEnable = true
|
||||||
sig1, err := opt.OptimizedSign(sk, message)
|
sig1, err := opt.OptimizedSign(sk, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("SIMD signing failed: %v", err)
|
t.Fatalf("SIMD signing failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force non-SIMD path
|
// Force non-SIMD path
|
||||||
opt.simdEnable = false
|
opt.simdEnable = false
|
||||||
sig2, err := opt.OptimizedSign(sk, message)
|
sig2, err := opt.OptimizedSign(sk, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Non-SIMD signing failed: %v", err)
|
t.Fatalf("Non-SIMD signing failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both should verify
|
// Both should verify
|
||||||
if !opt.OptimizedVerify(pk, message, sig1) {
|
if !opt.OptimizedVerify(pk, message, sig1) {
|
||||||
t.Error("SIMD signature verification failed")
|
t.Error("SIMD signature verification failed")
|
||||||
@@ -270,7 +270,7 @@ func TestOptimizationMetrics(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
InitPrecomputation()
|
InitPrecomputation()
|
||||||
|
|
||||||
configs := []BenchmarkConfig{
|
configs := []BenchmarkConfig{
|
||||||
{Mode: SLHDSA128f, MessageSize: 32, Iterations: 100, Parallel: true},
|
{Mode: SLHDSA128f, MessageSize: 32, Iterations: 100, Parallel: true},
|
||||||
{Mode: SLHDSA128s, MessageSize: 32, Iterations: 100, Parallel: true},
|
{Mode: SLHDSA128s, MessageSize: 32, Iterations: 100, Parallel: true},
|
||||||
@@ -279,18 +279,18 @@ func TestOptimizationMetrics(t *testing.T) {
|
|||||||
{Mode: SLHDSA256f, MessageSize: 32, Iterations: 25, Parallel: true},
|
{Mode: SLHDSA256f, MessageSize: 32, Iterations: 25, Parallel: true},
|
||||||
{Mode: SLHDSA256s, MessageSize: 32, Iterations: 25, Parallel: true},
|
{Mode: SLHDSA256s, MessageSize: 32, Iterations: 25, Parallel: true},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("=== SLH-DSA Optimization Metrics ===")
|
t.Log("=== SLH-DSA Optimization Metrics ===")
|
||||||
t.Log("Mode\t\tSign(ms)\tVerify(ms)\tSign Ops/s\tVerify Ops/s")
|
t.Log("Mode\t\tSign(ms)\tVerify(ms)\tSign Ops/s\tVerify Ops/s")
|
||||||
t.Log("----\t\t--------\t----------\t----------\t------------")
|
t.Log("----\t\t--------\t----------\t----------\t------------")
|
||||||
|
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
opt := NewOptimized(config.Mode)
|
opt := NewOptimized(config.Mode)
|
||||||
result := opt.RunBenchmark(config)
|
result := opt.RunBenchmark(config)
|
||||||
|
|
||||||
signMs := float64(result.SignTime) / 1e6
|
signMs := float64(result.SignTime) / 1e6
|
||||||
verifyMs := float64(result.VerifyTime) / 1e6
|
verifyMs := float64(result.VerifyTime) / 1e6
|
||||||
|
|
||||||
t.Logf("%v\t\t%.2f\t\t%.2f\t\t%.0f\t\t%.0f",
|
t.Logf("%v\t\t%.2f\t\t%.2f\t\t%.0f\t\t%.0f",
|
||||||
config.Mode,
|
config.Mode,
|
||||||
signMs,
|
signMs,
|
||||||
@@ -304,22 +304,22 @@ func TestOptimizationMetrics(t *testing.T) {
|
|||||||
func ExampleOptimizedSLHDSA() {
|
func ExampleOptimizedSLHDSA() {
|
||||||
// Initialize precomputed tables for best performance
|
// Initialize precomputed tables for best performance
|
||||||
InitPrecomputation()
|
InitPrecomputation()
|
||||||
|
|
||||||
// Create optimized instance
|
// Create optimized instance
|
||||||
opt := NewOptimized(SLHDSA128f)
|
opt := NewOptimized(SLHDSA128f)
|
||||||
|
|
||||||
// Generate keys
|
// Generate keys
|
||||||
priv, _ := GenerateKey(rand.Reader, SLHDSA128f)
|
priv, _ := GenerateKey(rand.Reader, SLHDSA128f)
|
||||||
sk := priv
|
sk := priv
|
||||||
pk := &priv.PublicKey
|
pk := &priv.PublicKey
|
||||||
|
|
||||||
// Sign message with optimizations
|
// Sign message with optimizations
|
||||||
message := []byte("Hello, Post-Quantum World!")
|
message := []byte("Hello, Post-Quantum World!")
|
||||||
signature, _ := opt.OptimizedSign(sk, message)
|
signature, _ := opt.OptimizedSign(sk, message)
|
||||||
|
|
||||||
// Verify with optimizations
|
// Verify with optimizations
|
||||||
valid := opt.OptimizedVerify(pk, message, signature)
|
valid := opt.OptimizedVerify(pk, message, signature)
|
||||||
|
|
||||||
fmt.Printf("Signature valid: %v\n", valid)
|
fmt.Printf("Signature valid: %v\n", valid)
|
||||||
fmt.Printf("Signature size: %d bytes\n", len(signature))
|
fmt.Printf("Signature size: %d bytes\n", len(signature))
|
||||||
// Output:
|
// Output:
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import (
|
|||||||
|
|
||||||
func TestSLHDSAKeyGeneration(t *testing.T) {
|
func TestSLHDSAKeyGeneration(t *testing.T) {
|
||||||
modes := []struct {
|
modes := []struct {
|
||||||
name string
|
name string
|
||||||
mode Mode
|
mode Mode
|
||||||
pubSize int
|
pubSize int
|
||||||
privSize int
|
privSize int
|
||||||
sigSize int
|
sigSize int
|
||||||
}{
|
}{
|
||||||
{"SLH-DSA-128s", SLHDSA128s, SLHDSA128sPublicKeySize, SLHDSA128sPrivateKeySize, SLHDSA128sSignatureSize},
|
{"SLH-DSA-128s", SLHDSA128s, SLHDSA128sPublicKeySize, SLHDSA128sPrivateKeySize, SLHDSA128sSignatureSize},
|
||||||
{"SLH-DSA-128f", SLHDSA128f, SLHDSA128fPublicKeySize, SLHDSA128fPrivateKeySize, SLHDSA128fSignatureSize},
|
{"SLH-DSA-128f", SLHDSA128f, SLHDSA128fPublicKeySize, SLHDSA128fPrivateKeySize, SLHDSA128fSignatureSize},
|
||||||
@@ -67,7 +67,7 @@ func TestSLHDSASignVerify(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message := []byte("Test message for SLH-DSA signature")
|
message := []byte("Test message for SLH-DSA signature")
|
||||||
|
|
||||||
// Sign message
|
// Sign message
|
||||||
signature, err := privKey.Sign(rand.Reader, message, nil)
|
signature, err := privKey.Sign(rand.Reader, message, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -142,7 +142,7 @@ func TestSLHDSADeterministicSignature(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message := []byte("Deterministic signature test")
|
message := []byte("Deterministic signature test")
|
||||||
|
|
||||||
// Sign same message multiple times
|
// Sign same message multiple times
|
||||||
sig1, err := privKey.Sign(nil, message, nil) // nil rand for deterministic
|
sig1, err := privKey.Sign(nil, message, nil) // nil rand for deterministic
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -320,12 +320,12 @@ func TestSLHDSAConcurrency(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message := []byte("Concurrent test message")
|
message := []byte("Concurrent test message")
|
||||||
|
|
||||||
// Test concurrent signing
|
// Test concurrent signing
|
||||||
t.Run("ConcurrentSign", func(t *testing.T) {
|
t.Run("ConcurrentSign", func(t *testing.T) {
|
||||||
const numGoroutines = 10
|
const numGoroutines = 10
|
||||||
done := make(chan bool, numGoroutines)
|
done := make(chan bool, numGoroutines)
|
||||||
|
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(id int) {
|
go func(id int) {
|
||||||
msg := append(message, byte(id))
|
msg := append(message, byte(id))
|
||||||
@@ -340,7 +340,7 @@ func TestSLHDSAConcurrency(t *testing.T) {
|
|||||||
done <- true
|
done <- true
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
@@ -351,7 +351,7 @@ func TestSLHDSAConcurrency(t *testing.T) {
|
|||||||
signature, _ := privKey.Sign(rand.Reader, message, nil)
|
signature, _ := privKey.Sign(rand.Reader, message, nil)
|
||||||
const numGoroutines = 10
|
const numGoroutines = 10
|
||||||
done := make(chan bool, numGoroutines)
|
done := make(chan bool, numGoroutines)
|
||||||
|
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(id int) {
|
go func(id int) {
|
||||||
valid := privKey.PublicKey.Verify(message, signature, nil)
|
valid := privKey.PublicKey.Verify(message, signature, nil)
|
||||||
@@ -361,7 +361,7 @@ func TestSLHDSAConcurrency(t *testing.T) {
|
|||||||
done <- true
|
done <- true
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
@@ -432,7 +432,7 @@ func BenchmarkSLHDSASign(b *testing.B) {
|
|||||||
|
|
||||||
for _, m := range modes {
|
for _, m := range modes {
|
||||||
privKey, _ := GenerateKey(rand.Reader, m.mode)
|
privKey, _ := GenerateKey(rand.Reader, m.mode)
|
||||||
|
|
||||||
b.Run(m.name, func(b *testing.B) {
|
b.Run(m.name, func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -463,7 +463,7 @@ func BenchmarkSLHDSAVerify(b *testing.B) {
|
|||||||
for _, m := range modes {
|
for _, m := range modes {
|
||||||
privKey, _ := GenerateKey(rand.Reader, m.mode)
|
privKey, _ := GenerateKey(rand.Reader, m.mode)
|
||||||
signature, _ := privKey.Sign(rand.Reader, message, nil)
|
signature, _ := privKey.Sign(rand.Reader, message, nil)
|
||||||
|
|
||||||
b.Run(m.name, func(b *testing.B) {
|
b.Run(m.name, func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -474,4 +474,4 @@ func BenchmarkSLHDSAVerify(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func NewMerkleTree(height int) *MerkleTree {
|
|||||||
for i := range nodes {
|
for i := range nodes {
|
||||||
nodes[i] = make([]byte, 32)
|
nodes[i] = make([]byte, 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &MerkleTree{
|
return &MerkleTree{
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
height: height,
|
height: height,
|
||||||
@@ -90,30 +90,30 @@ func NewMerkleTree(height int) *MerkleTree {
|
|||||||
func (mt *MerkleTree) ComputeRoot(leaves [][]byte) []byte {
|
func (mt *MerkleTree) ComputeRoot(leaves [][]byte) []byte {
|
||||||
mt.mu.Lock()
|
mt.mu.Lock()
|
||||||
defer mt.mu.Unlock()
|
defer mt.mu.Unlock()
|
||||||
|
|
||||||
// Copy leaves to bottom level
|
// Copy leaves to bottom level
|
||||||
leafStart := len(mt.nodes) - len(leaves)
|
leafStart := len(mt.nodes) - len(leaves)
|
||||||
for i, leaf := range leaves {
|
for i, leaf := range leaves {
|
||||||
copy(mt.nodes[leafStart+i], leaf)
|
copy(mt.nodes[leafStart+i], leaf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute internal nodes
|
// Compute internal nodes
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
for level := mt.height - 1; level >= 0; level-- {
|
for level := mt.height - 1; level >= 0; level-- {
|
||||||
levelStart := (1 << level) - 1
|
levelStart := (1 << level) - 1
|
||||||
levelSize := 1 << level
|
levelSize := 1 << level
|
||||||
|
|
||||||
for i := 0; i < levelSize; i++ {
|
for i := 0; i < levelSize; i++ {
|
||||||
leftChild := mt.nodes[2*(levelStart+i)+1]
|
leftChild := mt.nodes[2*(levelStart+i)+1]
|
||||||
rightChild := mt.nodes[2*(levelStart+i)+2]
|
rightChild := mt.nodes[2*(levelStart+i)+2]
|
||||||
|
|
||||||
h.Reset()
|
h.Reset()
|
||||||
h.Write(leftChild)
|
h.Write(leftChild)
|
||||||
h.Write(rightChild)
|
h.Write(rightChild)
|
||||||
copy(mt.nodes[levelStart+i], h.Sum(nil))
|
copy(mt.nodes[levelStart+i], h.Sum(nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mt.nodes[0]
|
return mt.nodes[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,13 +129,13 @@ func NewParallelSLHDSA(mode Mode, numKeys, workers int) (*ParallelSLHDSA, error)
|
|||||||
if workers <= 0 {
|
if workers <= 0 {
|
||||||
workers = 4 // Default worker count
|
workers = 4 // Default worker count
|
||||||
}
|
}
|
||||||
|
|
||||||
keys := make([]*PrivateKey, numKeys)
|
keys := make([]*PrivateKey, numKeys)
|
||||||
|
|
||||||
// Generate keys in parallel
|
// Generate keys in parallel
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
errors := make([]error, numKeys)
|
errors := make([]error, numKeys)
|
||||||
|
|
||||||
for i := range keys {
|
for i := range keys {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(idx int) {
|
go func(idx int) {
|
||||||
@@ -148,16 +148,16 @@ func NewParallelSLHDSA(mode Mode, numKeys, workers int) (*ParallelSLHDSA, error)
|
|||||||
keys[idx] = key
|
keys[idx] = key
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
// Check for errors
|
// Check for errors
|
||||||
for _, err := range errors {
|
for _, err := range errors {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ParallelSLHDSA{
|
return &ParallelSLHDSA{
|
||||||
keys: keys,
|
keys: keys,
|
||||||
workers: workers,
|
workers: workers,
|
||||||
@@ -169,20 +169,20 @@ func (p *ParallelSLHDSA) SignMessages(messages [][]byte) ([][]byte, error) {
|
|||||||
if len(messages) > len(p.keys) {
|
if len(messages) > len(p.keys) {
|
||||||
return nil, errors.New("not enough keys for messages")
|
return nil, errors.New("not enough keys for messages")
|
||||||
}
|
}
|
||||||
|
|
||||||
signatures := make([][]byte, len(messages))
|
signatures := make([][]byte, len(messages))
|
||||||
|
|
||||||
// Create work channel
|
// Create work channel
|
||||||
work := make(chan int, len(messages))
|
work := make(chan int, len(messages))
|
||||||
for i := range messages {
|
for i := range messages {
|
||||||
work <- i
|
work <- i
|
||||||
}
|
}
|
||||||
close(work)
|
close(work)
|
||||||
|
|
||||||
// Start workers
|
// Start workers
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
errors := make([]error, len(messages))
|
errors := make([]error, len(messages))
|
||||||
|
|
||||||
for w := 0; w < p.workers; w++ {
|
for w := 0; w < p.workers; w++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -197,25 +197,25 @@ func (p *ParallelSLHDSA) SignMessages(messages [][]byte) ([][]byte, error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
// Check for errors
|
// Check for errors
|
||||||
for _, err := range errors {
|
for _, err := range errors {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return signatures, nil
|
return signatures, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CachedSLHDSA caches intermediate computations
|
// CachedSLHDSA caches intermediate computations
|
||||||
type CachedSLHDSA struct {
|
type CachedSLHDSA struct {
|
||||||
privKey *PrivateKey
|
privKey *PrivateKey
|
||||||
treeCache map[string]*MerkleTree
|
treeCache map[string]*MerkleTree
|
||||||
hashCache map[string][]byte
|
hashCache map[string][]byte
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCachedSLHDSA creates a cached SLH-DSA instance
|
// NewCachedSLHDSA creates a cached SLH-DSA instance
|
||||||
@@ -234,7 +234,7 @@ func (c *CachedSLHDSA) SignWithCache(message []byte) ([]byte, error) {
|
|||||||
h.Write(message)
|
h.Write(message)
|
||||||
msgHash := h.Sum(nil)
|
msgHash := h.Sum(nil)
|
||||||
cacheKey := string(msgHash[:16]) // Use first 16 bytes as key
|
cacheKey := string(msgHash[:16]) // Use first 16 bytes as key
|
||||||
|
|
||||||
// Check cache
|
// Check cache
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
if sig, ok := c.hashCache[cacheKey]; ok {
|
if sig, ok := c.hashCache[cacheKey]; ok {
|
||||||
@@ -242,13 +242,13 @@ func (c *CachedSLHDSA) SignWithCache(message []byte) ([]byte, error) {
|
|||||||
return sig, nil
|
return sig, nil
|
||||||
}
|
}
|
||||||
c.mu.RUnlock()
|
c.mu.RUnlock()
|
||||||
|
|
||||||
// Sign and cache
|
// Sign and cache
|
||||||
sig, err := c.privKey.Sign(rand.Reader, message, nil)
|
sig, err := c.privKey.Sign(rand.Reader, message, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
c.hashCache[cacheKey] = sig
|
c.hashCache[cacheKey] = sig
|
||||||
// Limit cache size
|
// Limit cache size
|
||||||
@@ -262,6 +262,6 @@ func (c *CachedSLHDSA) SignWithCache(message []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
|
||||||
return sig, nil
|
return sig, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -7,7 +7,7 @@ import (
|
|||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/luxfi/crypto/bls"
|
"github.com/luxfi/crypto/bls"
|
||||||
"github.com/luxfi/crypto/mldsa"
|
"github.com/luxfi/crypto/mldsa"
|
||||||
)
|
)
|
||||||
@@ -25,18 +25,18 @@ func NewSimpleSigner() (*SimpleSigner, error) {
|
|||||||
if _, err := rand.Read(seed); err != nil {
|
if _, err := rand.Read(seed); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
blsKey, err := bls.SecretKeyFromBytes(seed)
|
blsKey, err := bls.SecretKeyFromBytes(seed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate ML-DSA key
|
// Generate ML-DSA key
|
||||||
mldsaKey, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
mldsaKey, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SimpleSigner{
|
return &SimpleSigner{
|
||||||
blsKey: blsKey,
|
blsKey: blsKey,
|
||||||
mldsaKey: mldsaKey,
|
mldsaKey: mldsaKey,
|
||||||
@@ -79,19 +79,19 @@ func (s *SimpleSigner) SignHybrid(message []byte) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("BLS sign failed: %w", err)
|
return nil, fmt.Errorf("BLS sign failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mldsaSig, err := s.SignMLDSA(message)
|
mldsaSig, err := s.SignMLDSA(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ML-DSA sign failed: %w", err)
|
return nil, fmt.Errorf("ML-DSA sign failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine: [2-byte BLS len][BLS sig][ML-DSA sig]
|
// Combine: [2-byte BLS len][BLS sig][ML-DSA sig]
|
||||||
result := make([]byte, 2+len(blsSig)+len(mldsaSig))
|
result := make([]byte, 2+len(blsSig)+len(mldsaSig))
|
||||||
result[0] = byte(len(blsSig) >> 8)
|
result[0] = byte(len(blsSig) >> 8)
|
||||||
result[1] = byte(len(blsSig))
|
result[1] = byte(len(blsSig))
|
||||||
copy(result[2:], blsSig)
|
copy(result[2:], blsSig)
|
||||||
copy(result[2+len(blsSig):], mldsaSig)
|
copy(result[2+len(blsSig):], mldsaSig)
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,15 +100,15 @@ func (s *SimpleSigner) VerifyHybrid(message, signature []byte) bool {
|
|||||||
if len(signature) < 2 {
|
if len(signature) < 2 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
blsLen := int(signature[0])<<8 | int(signature[1])
|
blsLen := int(signature[0])<<8 | int(signature[1])
|
||||||
if len(signature) < 2+blsLen {
|
if len(signature) < 2+blsLen {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
blsSig := signature[2 : 2+blsLen]
|
blsSig := signature[2 : 2+blsLen]
|
||||||
mldsaSig := signature[2+blsLen:]
|
mldsaSig := signature[2+blsLen:]
|
||||||
|
|
||||||
return s.VerifyBLS(message, blsSig) && s.VerifyMLDSA(message, mldsaSig)
|
return s.VerifyBLS(message, blsSig) && s.VerifyMLDSA(message, mldsaSig)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,4 +121,4 @@ func (s *SimpleSigner) GetBLSPublicKey() []byte {
|
|||||||
// GetMLDSAPublicKey returns the ML-DSA public key
|
// GetMLDSAPublicKey returns the ML-DSA public key
|
||||||
func (s *SimpleSigner) GetMLDSAPublicKey() []byte {
|
func (s *SimpleSigner) GetMLDSAPublicKey() []byte {
|
||||||
return s.mldsaKey.PublicKey.Bytes()
|
return s.mldsaKey.PublicKey.Bytes()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,59 +9,59 @@ func TestSimpleSigner(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create signer: %v", err)
|
t.Fatalf("Failed to create signer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
message := []byte("Test message for unified signing")
|
message := []byte("Test message for unified signing")
|
||||||
|
|
||||||
t.Run("BLS", func(t *testing.T) {
|
t.Run("BLS", func(t *testing.T) {
|
||||||
sig, err := signer.SignBLS(message)
|
sig, err := signer.SignBLS(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("BLS sign failed: %v", err)
|
t.Fatalf("BLS sign failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !signer.VerifyBLS(message, sig) {
|
if !signer.VerifyBLS(message, sig) {
|
||||||
t.Fatalf("BLS verification failed")
|
t.Fatalf("BLS verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if signer.VerifyBLS([]byte("wrong"), sig) {
|
if signer.VerifyBLS([]byte("wrong"), sig) {
|
||||||
t.Fatalf("BLS should not verify wrong message")
|
t.Fatalf("BLS should not verify wrong message")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("BLS signature size: %d bytes", len(sig))
|
t.Logf("BLS signature size: %d bytes", len(sig))
|
||||||
t.Logf("BLS public key size: %d bytes", len(signer.GetBLSPublicKey()))
|
t.Logf("BLS public key size: %d bytes", len(signer.GetBLSPublicKey()))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ML-DSA", func(t *testing.T) {
|
t.Run("ML-DSA", func(t *testing.T) {
|
||||||
sig, err := signer.SignMLDSA(message)
|
sig, err := signer.SignMLDSA(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ML-DSA sign failed: %v", err)
|
t.Fatalf("ML-DSA sign failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !signer.VerifyMLDSA(message, sig) {
|
if !signer.VerifyMLDSA(message, sig) {
|
||||||
t.Fatalf("ML-DSA verification failed")
|
t.Fatalf("ML-DSA verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if signer.VerifyMLDSA([]byte("wrong"), sig) {
|
if signer.VerifyMLDSA([]byte("wrong"), sig) {
|
||||||
t.Fatalf("ML-DSA should not verify wrong message")
|
t.Fatalf("ML-DSA should not verify wrong message")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("ML-DSA signature size: %d bytes", len(sig))
|
t.Logf("ML-DSA signature size: %d bytes", len(sig))
|
||||||
t.Logf("ML-DSA public key size: %d bytes", len(signer.GetMLDSAPublicKey()))
|
t.Logf("ML-DSA public key size: %d bytes", len(signer.GetMLDSAPublicKey()))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Hybrid", func(t *testing.T) {
|
t.Run("Hybrid", func(t *testing.T) {
|
||||||
sig, err := signer.SignHybrid(message)
|
sig, err := signer.SignHybrid(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Hybrid sign failed: %v", err)
|
t.Fatalf("Hybrid sign failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !signer.VerifyHybrid(message, sig) {
|
if !signer.VerifyHybrid(message, sig) {
|
||||||
t.Fatalf("Hybrid verification failed")
|
t.Fatalf("Hybrid verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if signer.VerifyHybrid([]byte("wrong"), sig) {
|
if signer.VerifyHybrid([]byte("wrong"), sig) {
|
||||||
t.Fatalf("Hybrid should not verify wrong message")
|
t.Fatalf("Hybrid should not verify wrong message")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Hybrid signature size: %d bytes", len(sig))
|
t.Logf("Hybrid signature size: %d bytes", len(sig))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -71,9 +71,9 @@ func BenchmarkSimpleSigner(b *testing.B) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
message := []byte("Benchmark message for performance testing")
|
message := []byte("Benchmark message for performance testing")
|
||||||
|
|
||||||
b.Run("BLS_Sign", func(b *testing.B) {
|
b.Run("BLS_Sign", func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -83,7 +83,7 @@ func BenchmarkSimpleSigner(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
blsSig, _ := signer.SignBLS(message)
|
blsSig, _ := signer.SignBLS(message)
|
||||||
b.Run("BLS_Verify", func(b *testing.B) {
|
b.Run("BLS_Verify", func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -93,7 +93,7 @@ func BenchmarkSimpleSigner(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("MLDSA_Sign", func(b *testing.B) {
|
b.Run("MLDSA_Sign", func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -103,7 +103,7 @@ func BenchmarkSimpleSigner(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
mldsaSig, _ := signer.SignMLDSA(message)
|
mldsaSig, _ := signer.SignMLDSA(message)
|
||||||
b.Run("MLDSA_Verify", func(b *testing.B) {
|
b.Run("MLDSA_Verify", func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -113,7 +113,7 @@ func BenchmarkSimpleSigner(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("Hybrid_Sign", func(b *testing.B) {
|
b.Run("Hybrid_Sign", func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -123,7 +123,7 @@ func BenchmarkSimpleSigner(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
hybridSig, _ := signer.SignHybrid(message)
|
hybridSig, _ := signer.SignHybrid(message)
|
||||||
b.Run("Hybrid_Verify", func(b *testing.B) {
|
b.Run("Hybrid_Verify", func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -133,4 +133,4 @@ func BenchmarkSimpleSigner(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
package bloom
|
package bloom
|
||||||
|
|
||||||
|
|
||||||
// BloomFilter is the interface for bloom filter implementations that support
|
// BloomFilter is the interface for bloom filter implementations that support
|
||||||
// adding and checking raw byte slices
|
// adding and checking raw byte slices
|
||||||
type BloomFilter interface {
|
type BloomFilter interface {
|
||||||
|
|||||||
@@ -69,4 +69,3 @@ func getIndex(entries []byte, hash, seed uint64) uint64 {
|
|||||||
}
|
}
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
package bloom
|
package bloom
|
||||||
|
|
||||||
|
|
||||||
import "math"
|
import "math"
|
||||||
|
|
||||||
const ln2Squared = math.Ln2 * math.Ln2
|
const ln2Squared = math.Ln2 * math.Ln2
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ const (
|
|||||||
UnitTestID uint32 = 369
|
UnitTestID uint32 = 369
|
||||||
|
|
||||||
// Lux-specific network IDs
|
// Lux-specific network IDs
|
||||||
LuxMainnetID uint32 = 96369 // Lux mainnet
|
LuxMainnetID uint32 = 96369 // Lux mainnet
|
||||||
LuxTestnetID uint32 = 96370 // Lux testnet
|
LuxTestnetID uint32 = 96370 // Lux testnet
|
||||||
QChainMainnetID uint32 = 96380 // Q-Chain mainnet
|
QChainMainnetID uint32 = 96380 // Q-Chain mainnet
|
||||||
QChainTestnetID uint32 = 96381 // Q-Chain testnet
|
QChainTestnetID uint32 = 96381 // Q-Chain testnet
|
||||||
|
|
||||||
LocalName = "local"
|
LocalName = "local"
|
||||||
MainnetName = "mainnet"
|
MainnetName = "mainnet"
|
||||||
@@ -42,7 +42,7 @@ const (
|
|||||||
var (
|
var (
|
||||||
PrimaryNetworkID = ids.Empty
|
PrimaryNetworkID = ids.Empty
|
||||||
PlatformChainID = ids.Empty
|
PlatformChainID = ids.Empty
|
||||||
|
|
||||||
// Q-Chain specific IDs
|
// Q-Chain specific IDs
|
||||||
QChainID = ids.ID{'q', 'c', 'h', 'a', 'i', 'n'}
|
QChainID = ids.ID{'q', 'c', 'h', 'a', 'i', 'n'}
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -6,19 +6,19 @@ package constants
|
|||||||
import "github.com/luxfi/ids"
|
import "github.com/luxfi/ids"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PlatformVMName = "platformvm"
|
PlatformVMName = "platformvm"
|
||||||
XVMName = "xvm"
|
XVMName = "xvm"
|
||||||
EVMName = "evm"
|
EVMName = "evm"
|
||||||
XSVMName = "xsvm"
|
XSVMName = "xsvm"
|
||||||
QVMName = "qvm"
|
QVMName = "qvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
PlatformVMID = ids.ID{'p', 'l', 'a', 't', 'f', 'o', 'r', 'm', 'v', 'm'}
|
PlatformVMID = ids.ID{'p', 'l', 'a', 't', 'f', 'o', 'r', 'm', 'v', 'm'}
|
||||||
XVMID = ids.ID{'a', 'v', 'm'}
|
XVMID = ids.ID{'a', 'v', 'm'}
|
||||||
EVMID = ids.ID{'e', 'v', 'm'}
|
EVMID = ids.ID{'e', 'v', 'm'}
|
||||||
XSVMID = ids.ID{'x', 's', 'v', 'm'}
|
XSVMID = ids.ID{'x', 's', 'v', 'm'}
|
||||||
QVMID = ids.ID{'q', 'v', 'm'}
|
QVMID = ids.ID{'q', 'v', 'm'}
|
||||||
)
|
)
|
||||||
|
|
||||||
// VMName returns the name of the VM with the provided ID. If a human readable
|
// VMName returns the name of the VM with the provided ID. If a human readable
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/luxfi/log"
|
||||||
luxlog "github.com/luxfi/log"
|
luxlog "github.com/luxfi/log"
|
||||||
"github.com/luxfi/node/utils"
|
"github.com/luxfi/node/utils"
|
||||||
"github.com/luxfi/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const ipResolutionTimeout = 10 * time.Second
|
const ipResolutionTimeout = 10 * time.Second
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func NewClaimedIPPort(
|
|||||||
Raw: cert.Raw,
|
Raw: cert.Raw,
|
||||||
PublicKey: cert.PublicKey,
|
PublicKey: cert.PublicKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := &ClaimedIPPort{
|
ip := &ClaimedIPPort{
|
||||||
Cert: cert,
|
Cert: cert,
|
||||||
AddrPort: ipPort,
|
AddrPort: ipPort,
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ func NewAveragerWithErrs(name, desc string, registry metric.Registry, errs *wrap
|
|||||||
a := averager{
|
a := averager{
|
||||||
count: metricsInstance.NewCounter(
|
count: metricsInstance.NewCounter(
|
||||||
AppendNamespace(name, "count"),
|
AppendNamespace(name, "count"),
|
||||||
"Total # of observations of " + desc,
|
"Total # of observations of "+desc,
|
||||||
),
|
),
|
||||||
sum: metricsInstance.NewGauge(
|
sum: metricsInstance.NewGauge(
|
||||||
AppendNamespace(name, "sum"),
|
AppendNamespace(name, "sum"),
|
||||||
"Sum of " + desc,
|
"Sum of "+desc,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ func newMetrics(registerer metric.Registerer) (*metricsImpl, error) {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
err := errors.Join(
|
err := errors.Join(
|
||||||
// registerer.Register(m.numCPUCycles),
|
// registerer.Register(m.numCPUCycles),
|
||||||
// registerer.Register(m.numDiskReads),
|
// registerer.Register(m.numDiskReads),
|
||||||
// registerer.Register(m.numDiskReadBytes),
|
// registerer.Register(m.numDiskReadBytes),
|
||||||
// registerer.Register(m.numDiskWrites),
|
// registerer.Register(m.numDiskWrites),
|
||||||
// registerer.Register(m.numDiskWritesBytes),
|
// registerer.Register(m.numDiskWritesBytes),
|
||||||
)
|
)
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type mockReadCloser struct {
|
type mockReadCloser struct {
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
closed bool
|
closed bool
|
||||||
readAll bool
|
readAll bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockReadCloser) Read(p []byte) (n int, err error) {
|
func (m *mockReadCloser) Read(p []byte) (n int, err error) {
|
||||||
@@ -131,7 +131,7 @@ func TestCleanlyCloseBody_PartiallyReadBody(t *testing.T) {
|
|||||||
|
|
||||||
func BenchmarkCleanlyCloseBody_Small(b *testing.B) {
|
func BenchmarkCleanlyCloseBody_Small(b *testing.B) {
|
||||||
data := []byte("small response body")
|
data := []byte("small response body")
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
mock := &mockReadCloser{
|
mock := &mockReadCloser{
|
||||||
@@ -144,7 +144,7 @@ func BenchmarkCleanlyCloseBody_Small(b *testing.B) {
|
|||||||
func BenchmarkCleanlyCloseBody_Large(b *testing.B) {
|
func BenchmarkCleanlyCloseBody_Large(b *testing.B) {
|
||||||
// 1MB response
|
// 1MB response
|
||||||
data := bytes.Repeat([]byte("x"), 1024*1024)
|
data := bytes.Repeat([]byte("x"), 1024*1024)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
mock := &mockReadCloser{
|
mock := &mockReadCloser{
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
consensustest "github.com/luxfi/consensus/test/helpers"
|
|
||||||
"github.com/luxfi/consensus/engine/chain/chaintest"
|
"github.com/luxfi/consensus/engine/chain/chaintest"
|
||||||
|
consensustest "github.com/luxfi/consensus/test/helpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAcceptSingleBlock(t *testing.T) {
|
func TestAcceptSingleBlock(t *testing.T) {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
|
||||||
"github.com/luxfi/log"
|
"github.com/luxfi/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user