mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
Final identifier purge for the node repo. After this commit zero
'Corona' / 'CORONA' references remain in any Go file.
consensus/quasar:
CoronaWork/Valid/Time/SizeMismatch → CoronaWork/Valid/Time/SizeMismatch
SignatureTypeCorona → SignatureTypeCorona
CoronaConfig/Stats/Signature → CoronaConfig/Stats/Signature
NewCoronaSignature → NewCoronaSignature
CoronaRound1/Round2/RoundData → CoronaRound1/Round2/RoundData
CoronaGroupKey/KeyShare/Coordinator → CoronaGroupKey/KeyShare/Coordinator
CoronaLatency/Parties/Threshold → CoronaLatency/Parties/Threshold
ConnectCorona/InitializeCorona → ConnectCorona/InitializeCorona
ErrCoronaNotConnected/Failed → ErrCoronaNotConnected/Failed
CoronaSigners/Stats → CoronaSigners/Stats
GetCorona() → GetCorona()
{gpu,cpu}CoronaVerify → {gpu,cpu}CoronaVerify
set/teardown test helpers + dozens of compound identifiers all renamed
vms/platformvm:
SigTypeCorona → SigTypeCorona
wallet/keychain:
KeyTypeCorona → KeyTypeRingSig (this is the LSAG ring-sig key
kind — descriptive, not lemur-named)
AddCorona → AddRingSig
Test*Corona* → Test*RingSig*
Parallel Ring+Module Double-Lattice PQ posture intact: Corona
(Ring-LWE) + Pulsar (Module-LWE) supported in parallel as
independent threshold finality kernels.
Tests: consensus/quasar 45s, wallet/keychain 3.5s — both green.
753 lines
20 KiB
Go
753 lines
20 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package keychain
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/luxfi/crypto/bls"
|
|
"github.com/luxfi/crypto/mldsa"
|
|
"github.com/luxfi/crypto/mlkem"
|
|
"github.com/luxfi/crypto/ring"
|
|
"github.com/luxfi/crypto/secp256k1"
|
|
"github.com/luxfi/crypto/slhdsa"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/math/set"
|
|
)
|
|
|
|
var (
|
|
ErrInvalidKeyType = errors.New("invalid key type")
|
|
ErrKeyNotFound = errors.New("key not found")
|
|
)
|
|
|
|
// KeyType represents the type of cryptographic key
|
|
type KeyType uint8
|
|
|
|
const (
|
|
// Classical cryptography
|
|
KeyTypeSecp256k1 KeyType = iota
|
|
KeyTypeBLS // BLS signatures for consensus
|
|
|
|
// Post-quantum cryptography (NIST FIPS standards)
|
|
KeyTypeMLDSA44 // FIPS 204 - ML-DSA-44
|
|
KeyTypeMLDSA65 // FIPS 204 - ML-DSA-65
|
|
KeyTypeMLDSA87 // FIPS 204 - ML-DSA-87
|
|
KeyTypeSLHDSA128 // FIPS 205 - SLH-DSA-128
|
|
KeyTypeSLHDSA192 // FIPS 205 - SLH-DSA-192
|
|
KeyTypeSLHDSA256 // FIPS 205 - SLH-DSA-256
|
|
|
|
// Key encapsulation (FIPS 203)
|
|
KeyTypeMLKEM512 // ML-KEM-512
|
|
KeyTypeMLKEM768 // ML-KEM-768
|
|
KeyTypeMLKEM1024 // ML-KEM-1024
|
|
|
|
// Privacy-preserving
|
|
KeyTypeRingSig // Ring signatures
|
|
|
|
// Hybrid modes (classical + post-quantum)
|
|
KeyTypeHybridSecp256k1MLDSA44
|
|
KeyTypeHybridSecp256k1SLHDSA128
|
|
KeyTypeHybridBLSMLDSA44
|
|
)
|
|
|
|
// PQSigner implements Signer with post-quantum support
|
|
type PQSigner struct {
|
|
keyType KeyType
|
|
address ids.ShortID
|
|
|
|
// Classical keys
|
|
secp256k1Key *secp256k1.PrivateKey
|
|
|
|
// BLS keys (for consensus)
|
|
blsKey *bls.SecretKey
|
|
|
|
// Post-quantum signature keys
|
|
mldsaKey interface{} // Can be *mldsa.PrivateKey44/65/87
|
|
slhdsaKey interface{} // Can be *slhdsa.PrivateKey128/192/256
|
|
|
|
// Post-quantum key encapsulation (ML-KEM)
|
|
mlkemKey *mlkem.PrivateKey
|
|
mlkemPubKey *mlkem.PublicKey
|
|
mlkemMode mlkem.Mode
|
|
|
|
// Ring signatures (privacy-preserving)
|
|
ringSigner ring.Signer
|
|
ringScheme ring.Scheme
|
|
|
|
// For hybrid modes, we store both
|
|
hybridClassical *secp256k1.PrivateKey
|
|
hybridPQ interface{}
|
|
hybridBLS *bls.SecretKey
|
|
}
|
|
|
|
// SignHash signs a hash with the appropriate algorithm
|
|
func (s *PQSigner) SignHash(hash []byte) ([]byte, error) {
|
|
switch s.keyType {
|
|
case KeyTypeSecp256k1:
|
|
if s.secp256k1Key == nil {
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
return s.secp256k1Key.SignHash(hash)
|
|
|
|
case KeyTypeBLS:
|
|
if s.blsKey == nil {
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
sig, err := s.blsKey.Sign(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bls.SignatureToBytes(sig), nil
|
|
|
|
case KeyTypeMLDSA44, KeyTypeMLDSA65, KeyTypeMLDSA87:
|
|
if key, ok := s.mldsaKey.(*mldsa.PrivateKey); ok {
|
|
sig, err := key.Sign(rand.Reader, hash, crypto.Hash(0))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return sig, nil
|
|
}
|
|
return nil, ErrInvalidKeyType
|
|
|
|
case KeyTypeSLHDSA128, KeyTypeSLHDSA192, KeyTypeSLHDSA256:
|
|
if key, ok := s.slhdsaKey.(*slhdsa.PrivateKey); ok {
|
|
sig, err := key.Sign(rand.Reader, hash, crypto.Hash(0))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return sig, nil
|
|
}
|
|
return nil, ErrInvalidKeyType
|
|
|
|
case KeyTypeRingSig:
|
|
// Ring signatures require a ring of public keys
|
|
// For SignHash without a ring, we return an error
|
|
// Use SignRing for ring signature operations
|
|
return nil, errors.New("corona requires ring members - use SignRing method")
|
|
|
|
case KeyTypeHybridSecp256k1MLDSA44:
|
|
// Hybrid mode: concatenate both signatures
|
|
if s.hybridClassical == nil || s.hybridPQ == nil {
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
|
|
classicalSig, err := s.hybridClassical.SignHash(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if key, ok := s.hybridPQ.(*mldsa.PrivateKey); ok {
|
|
pqSig, err := key.Sign(rand.Reader, hash, crypto.Hash(0))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Concatenate signatures with length prefixes
|
|
result := make([]byte, 0, 2+len(classicalSig)+2+len(pqSig))
|
|
result = append(result, byte(len(classicalSig)>>8), byte(len(classicalSig)))
|
|
result = append(result, classicalSig...)
|
|
result = append(result, byte(len(pqSig)>>8), byte(len(pqSig)))
|
|
result = append(result, pqSig...)
|
|
return result, nil
|
|
}
|
|
return nil, ErrInvalidKeyType
|
|
|
|
case KeyTypeHybridBLSMLDSA44:
|
|
// Hybrid BLS + ML-DSA mode
|
|
if s.hybridBLS == nil || s.hybridPQ == nil {
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
|
|
blsSig, err := s.hybridBLS.Sign(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
blsSigBytes := bls.SignatureToBytes(blsSig)
|
|
|
|
if key, ok := s.hybridPQ.(*mldsa.PrivateKey); ok {
|
|
pqSig, err := key.Sign(rand.Reader, hash, crypto.Hash(0))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Concatenate signatures with length prefixes
|
|
result := make([]byte, 0, 2+len(blsSigBytes)+2+len(pqSig))
|
|
result = append(result, byte(len(blsSigBytes)>>8), byte(len(blsSigBytes)))
|
|
result = append(result, blsSigBytes...)
|
|
result = append(result, byte(len(pqSig)>>8), byte(len(pqSig)))
|
|
result = append(result, pqSig...)
|
|
return result, nil
|
|
}
|
|
return nil, ErrInvalidKeyType
|
|
|
|
default:
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
}
|
|
|
|
// Sign signs a message with the appropriate algorithm
|
|
func (s *PQSigner) Sign(msg []byte) ([]byte, error) {
|
|
switch s.keyType {
|
|
case KeyTypeSecp256k1:
|
|
// secp256k1 needs a 32-byte hash
|
|
if s.secp256k1Key == nil {
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
hash := sha256.New()
|
|
hash.Write(msg)
|
|
return s.secp256k1Key.SignHash(hash.Sum(nil))
|
|
|
|
case KeyTypeBLS:
|
|
// BLS signs message directly
|
|
if s.blsKey == nil {
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
sig, err := s.blsKey.Sign(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bls.SignatureToBytes(sig), nil
|
|
|
|
case KeyTypeRingSig:
|
|
// Ring signatures require a ring of public keys
|
|
return nil, errors.New("corona requires ring members - use SignRing method")
|
|
|
|
case KeyTypeHybridSecp256k1MLDSA44, KeyTypeHybridSecp256k1SLHDSA128:
|
|
// For hybrid, we need to hash for the classical part
|
|
hash := sha256.New()
|
|
hash.Write(msg)
|
|
hashBytes := hash.Sum(nil)
|
|
|
|
// Sign with both algorithms
|
|
if s.hybridClassical == nil || s.hybridPQ == nil {
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
|
|
classicalSig, err := s.hybridClassical.SignHash(hashBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var pqSig []byte
|
|
switch pq := s.hybridPQ.(type) {
|
|
case *mldsa.PrivateKey:
|
|
pqSig, err = pq.Sign(rand.Reader, msg, crypto.Hash(0))
|
|
case *slhdsa.PrivateKey:
|
|
pqSig, err = pq.Sign(rand.Reader, msg, crypto.Hash(0))
|
|
default:
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Concatenate signatures with length prefixes
|
|
result := make([]byte, 0, 2+len(classicalSig)+2+len(pqSig))
|
|
result = append(result, byte(len(classicalSig)>>8), byte(len(classicalSig)))
|
|
result = append(result, classicalSig...)
|
|
result = append(result, byte(len(pqSig)>>8), byte(len(pqSig)))
|
|
result = append(result, pqSig...)
|
|
return result, nil
|
|
|
|
case KeyTypeHybridBLSMLDSA44:
|
|
// Hybrid BLS + ML-DSA mode
|
|
if s.hybridBLS == nil || s.hybridPQ == nil {
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
|
|
blsSig, err := s.hybridBLS.Sign(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
blsSigBytes := bls.SignatureToBytes(blsSig)
|
|
|
|
if key, ok := s.hybridPQ.(*mldsa.PrivateKey); ok {
|
|
pqSig, err := key.Sign(rand.Reader, msg, crypto.Hash(0))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Concatenate signatures with length prefixes
|
|
result := make([]byte, 0, 2+len(blsSigBytes)+2+len(pqSig))
|
|
result = append(result, byte(len(blsSigBytes)>>8), byte(len(blsSigBytes)))
|
|
result = append(result, blsSigBytes...)
|
|
result = append(result, byte(len(pqSig)>>8), byte(len(pqSig)))
|
|
result = append(result, pqSig...)
|
|
return result, nil
|
|
}
|
|
return nil, ErrInvalidKeyType
|
|
|
|
default:
|
|
// PQ algorithms sign the message directly
|
|
return s.SignHash(msg)
|
|
}
|
|
}
|
|
|
|
// Address returns the address associated with this signer
|
|
func (s *PQSigner) Address() ids.ShortID {
|
|
return s.address
|
|
}
|
|
|
|
// SignRing creates a ring signature for the given message using the provided ring of public keys.
|
|
// The signer's public key must be included in the ring at signerIndex.
|
|
func (s *PQSigner) SignRing(message []byte, ringPubKeys [][]byte, signerIndex int) (ring.RingSignature, error) {
|
|
if s.keyType != KeyTypeRingSig {
|
|
return nil, errors.New("SignRing only supported for Corona key type")
|
|
}
|
|
if s.ringSigner == nil {
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
|
|
return s.ringSigner.Sign(message, ringPubKeys, signerIndex)
|
|
}
|
|
|
|
// KeyImage returns the key image for linkability (ring signatures only).
|
|
// Returns nil for non-ring signature key types.
|
|
func (s *PQSigner) KeyImage() []byte {
|
|
if s.ringSigner != nil {
|
|
return s.ringSigner.KeyImage()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RingScheme returns the ring signature scheme used (for Corona keys).
|
|
func (s *PQSigner) RingScheme() ring.Scheme {
|
|
return s.ringScheme
|
|
}
|
|
|
|
// PublicKey returns the public key bytes for this signer.
|
|
func (s *PQSigner) PublicKey() []byte {
|
|
switch s.keyType {
|
|
case KeyTypeSecp256k1:
|
|
if s.secp256k1Key != nil {
|
|
return s.secp256k1Key.PublicKey().CompressedBytes()
|
|
}
|
|
case KeyTypeBLS:
|
|
if s.blsKey != nil {
|
|
return bls.PublicKeyToCompressedBytes(s.blsKey.PublicKey())
|
|
}
|
|
case KeyTypeMLDSA44, KeyTypeMLDSA65, KeyTypeMLDSA87:
|
|
if key, ok := s.mldsaKey.(*mldsa.PrivateKey); ok {
|
|
return key.PublicKey.Bytes()
|
|
}
|
|
case KeyTypeSLHDSA128, KeyTypeSLHDSA192, KeyTypeSLHDSA256:
|
|
if key, ok := s.slhdsaKey.(*slhdsa.PrivateKey); ok {
|
|
return key.PublicKey.Bytes()
|
|
}
|
|
case KeyTypeMLKEM512, KeyTypeMLKEM768, KeyTypeMLKEM1024:
|
|
if s.mlkemPubKey != nil {
|
|
return s.mlkemPubKey.Bytes()
|
|
}
|
|
case KeyTypeRingSig:
|
|
if s.ringSigner != nil {
|
|
return s.ringSigner.PublicKey()
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Encapsulate generates a shared secret and ciphertext for the given public key.
|
|
// Only valid for ML-KEM key types.
|
|
func (s *PQSigner) Encapsulate(recipientPubKey *mlkem.PublicKey) (ciphertext, sharedSecret []byte, err error) {
|
|
if s.keyType != KeyTypeMLKEM512 && s.keyType != KeyTypeMLKEM768 && s.keyType != KeyTypeMLKEM1024 {
|
|
return nil, nil, errors.New("Encapsulate only supported for ML-KEM key types")
|
|
}
|
|
return recipientPubKey.Encapsulate()
|
|
}
|
|
|
|
// Decapsulate recovers the shared secret from a ciphertext.
|
|
// Only valid for ML-KEM key types.
|
|
func (s *PQSigner) Decapsulate(ciphertext []byte) (sharedSecret []byte, err error) {
|
|
if s.keyType != KeyTypeMLKEM512 && s.keyType != KeyTypeMLKEM768 && s.keyType != KeyTypeMLKEM1024 {
|
|
return nil, errors.New("Decapsulate only supported for ML-KEM key types")
|
|
}
|
|
if s.mlkemKey == nil {
|
|
return nil, ErrInvalidKeyType
|
|
}
|
|
return s.mlkemKey.Decapsulate(ciphertext)
|
|
}
|
|
|
|
// BLSPublicKey returns the BLS public key (for BLS or hybrid BLS key types).
|
|
func (s *PQSigner) BLSPublicKey() *bls.PublicKey {
|
|
switch s.keyType {
|
|
case KeyTypeBLS:
|
|
if s.blsKey != nil {
|
|
return s.blsKey.PublicKey()
|
|
}
|
|
case KeyTypeHybridBLSMLDSA44:
|
|
if s.hybridBLS != nil {
|
|
return s.hybridBLS.PublicKey()
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// KeyType returns the key type of this signer.
|
|
func (s *PQSigner) KeyType() KeyType {
|
|
return s.keyType
|
|
}
|
|
|
|
// PQKeychain implements Keychain with post-quantum support
|
|
type PQKeychain struct {
|
|
keysByAddress map[ids.ShortID]*PQSigner
|
|
addressSet set.Set[ids.ShortID]
|
|
defaultType KeyType
|
|
}
|
|
|
|
// NewPQKeychain creates a new post-quantum keychain
|
|
func NewPQKeychain(defaultType KeyType) *PQKeychain {
|
|
return &PQKeychain{
|
|
keysByAddress: make(map[ids.ShortID]*PQSigner),
|
|
addressSet: set.NewSet[ids.ShortID](0),
|
|
defaultType: defaultType,
|
|
}
|
|
}
|
|
|
|
// AddSecp256k1 adds a secp256k1 key to the keychain
|
|
func (kc *PQKeychain) AddSecp256k1(key *secp256k1.PrivateKey) ids.ShortID {
|
|
pk := key.PublicKey()
|
|
addr := pk.Address()
|
|
shortAddr, _ := ids.ToShortID(addr[:])
|
|
|
|
signer := &PQSigner{
|
|
keyType: KeyTypeSecp256k1,
|
|
address: shortAddr,
|
|
secp256k1Key: key,
|
|
}
|
|
|
|
kc.keysByAddress[shortAddr] = signer
|
|
kc.addressSet.Add(shortAddr)
|
|
return shortAddr
|
|
}
|
|
|
|
// AddMLDSA adds an ML-DSA key to the keychain
|
|
func (kc *PQKeychain) AddMLDSA(key *mldsa.PrivateKey, keyType KeyType) ids.ShortID {
|
|
// Generate address from public key bytes
|
|
pubKeyBytes := key.Bytes()
|
|
addrBytes := ids.ShortID{}
|
|
copy(addrBytes[:], pubKeyBytes[:20]) // Use first 20 bytes as address
|
|
|
|
signer := &PQSigner{
|
|
keyType: keyType,
|
|
address: addrBytes,
|
|
mldsaKey: key,
|
|
}
|
|
|
|
kc.keysByAddress[addrBytes] = signer
|
|
kc.addressSet.Add(addrBytes)
|
|
return addrBytes
|
|
}
|
|
|
|
// AddSLHDSA adds an SLH-DSA key to the keychain
|
|
func (kc *PQKeychain) AddSLHDSA(key *slhdsa.PrivateKey, keyType KeyType) ids.ShortID {
|
|
pubKeyBytes := key.Bytes()
|
|
addrBytes := ids.ShortID{}
|
|
copy(addrBytes[:], pubKeyBytes[:20])
|
|
|
|
signer := &PQSigner{
|
|
keyType: keyType,
|
|
address: addrBytes,
|
|
slhdsaKey: key,
|
|
}
|
|
|
|
kc.keysByAddress[addrBytes] = signer
|
|
kc.addressSet.Add(addrBytes)
|
|
return addrBytes
|
|
}
|
|
|
|
// AddBLS adds a BLS key to the keychain
|
|
func (kc *PQKeychain) AddBLS(key *bls.SecretKey) ids.ShortID {
|
|
pubKey := key.PublicKey()
|
|
pubKeyBytes := bls.PublicKeyToCompressedBytes(pubKey)
|
|
|
|
// Generate address from public key bytes (first 20 bytes of hash)
|
|
hash := sha256.Sum256(pubKeyBytes)
|
|
addrBytes := ids.ShortID{}
|
|
copy(addrBytes[:], hash[:20])
|
|
|
|
signer := &PQSigner{
|
|
keyType: KeyTypeBLS,
|
|
address: addrBytes,
|
|
blsKey: key,
|
|
}
|
|
|
|
kc.keysByAddress[addrBytes] = signer
|
|
kc.addressSet.Add(addrBytes)
|
|
return addrBytes
|
|
}
|
|
|
|
// AddMLKEM adds an ML-KEM key pair to the keychain for key encapsulation
|
|
func (kc *PQKeychain) AddMLKEM(pubKey *mlkem.PublicKey, privKey *mlkem.PrivateKey, mode mlkem.Mode) ids.ShortID {
|
|
pubKeyBytes := pubKey.Bytes()
|
|
|
|
// Generate address from public key bytes
|
|
hash := sha256.Sum256(pubKeyBytes)
|
|
addrBytes := ids.ShortID{}
|
|
copy(addrBytes[:], hash[:20])
|
|
|
|
var keyType KeyType
|
|
switch mode {
|
|
case mlkem.MLKEM512:
|
|
keyType = KeyTypeMLKEM512
|
|
case mlkem.MLKEM768:
|
|
keyType = KeyTypeMLKEM768
|
|
case mlkem.MLKEM1024:
|
|
keyType = KeyTypeMLKEM1024
|
|
default:
|
|
keyType = KeyTypeMLKEM768 // Default to MLKEM768
|
|
}
|
|
|
|
signer := &PQSigner{
|
|
keyType: keyType,
|
|
address: addrBytes,
|
|
mlkemKey: privKey,
|
|
mlkemPubKey: pubKey,
|
|
mlkemMode: mode,
|
|
}
|
|
|
|
kc.keysByAddress[addrBytes] = signer
|
|
kc.addressSet.Add(addrBytes)
|
|
return addrBytes
|
|
}
|
|
|
|
// AddRingSig adds a ring signature key to the keychain
|
|
// scheme specifies which ring signature scheme to use (LSAG or LatticeLSAG)
|
|
func (kc *PQKeychain) AddRingSig(signer ring.Signer, scheme ring.Scheme) ids.ShortID {
|
|
pubKeyBytes := signer.PublicKey()
|
|
|
|
// Generate address from public key bytes
|
|
hash := sha256.Sum256(pubKeyBytes)
|
|
addrBytes := ids.ShortID{}
|
|
copy(addrBytes[:], hash[:20])
|
|
|
|
pqSigner := &PQSigner{
|
|
keyType: KeyTypeRingSig,
|
|
address: addrBytes,
|
|
ringSigner: signer,
|
|
ringScheme: scheme,
|
|
}
|
|
|
|
kc.keysByAddress[addrBytes] = pqSigner
|
|
kc.addressSet.Add(addrBytes)
|
|
return addrBytes
|
|
}
|
|
|
|
// AddHybrid adds a hybrid classical+PQ key pair
|
|
func (kc *PQKeychain) AddHybrid(classical *secp256k1.PrivateKey, pq interface{}) ids.ShortID {
|
|
// Generate address from classical key for compatibility
|
|
pk := classical.PublicKey()
|
|
addr := pk.Address()
|
|
shortAddr, _ := ids.ToShortID(addr[:])
|
|
|
|
var keyType KeyType
|
|
switch pq.(type) {
|
|
case *mldsa.PrivateKey:
|
|
keyType = KeyTypeHybridSecp256k1MLDSA44
|
|
case *slhdsa.PrivateKey:
|
|
keyType = KeyTypeHybridSecp256k1SLHDSA128
|
|
default:
|
|
return ids.ShortEmpty
|
|
}
|
|
|
|
signer := &PQSigner{
|
|
keyType: keyType,
|
|
address: shortAddr,
|
|
hybridClassical: classical,
|
|
hybridPQ: pq,
|
|
}
|
|
|
|
kc.keysByAddress[shortAddr] = signer
|
|
kc.addressSet.Add(shortAddr)
|
|
return shortAddr
|
|
}
|
|
|
|
// AddHybridBLS adds a hybrid BLS + ML-DSA key pair
|
|
// This combines BLS for aggregatable consensus signatures with ML-DSA for post-quantum security
|
|
func (kc *PQKeychain) AddHybridBLS(blsKey *bls.SecretKey, pqKey *mldsa.PrivateKey) ids.ShortID {
|
|
// Generate address from BLS public key
|
|
pubKey := blsKey.PublicKey()
|
|
pubKeyBytes := bls.PublicKeyToCompressedBytes(pubKey)
|
|
hash := sha256.Sum256(pubKeyBytes)
|
|
addrBytes := ids.ShortID{}
|
|
copy(addrBytes[:], hash[:20])
|
|
|
|
signer := &PQSigner{
|
|
keyType: KeyTypeHybridBLSMLDSA44,
|
|
address: addrBytes,
|
|
hybridBLS: blsKey,
|
|
hybridPQ: pqKey,
|
|
}
|
|
|
|
kc.keysByAddress[addrBytes] = signer
|
|
kc.addressSet.Add(addrBytes)
|
|
return addrBytes
|
|
}
|
|
|
|
// GenerateKey generates a new key of the default type
|
|
func (kc *PQKeychain) GenerateKey() (ids.ShortID, error) {
|
|
switch kc.defaultType {
|
|
case KeyTypeSecp256k1:
|
|
key, err := secp256k1.NewPrivateKey()
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddSecp256k1(key), nil
|
|
|
|
case KeyTypeMLDSA44:
|
|
key, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddMLDSA(key, KeyTypeMLDSA44), nil
|
|
|
|
case KeyTypeMLDSA65:
|
|
key, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddMLDSA(key, KeyTypeMLDSA65), nil
|
|
|
|
case KeyTypeMLDSA87:
|
|
key, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA87)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddMLDSA(key, KeyTypeMLDSA87), nil
|
|
|
|
case KeyTypeSLHDSA128:
|
|
key, err := slhdsa.GenerateKey(rand.Reader, slhdsa.SHA2_128s)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddSLHDSA(key, KeyTypeSLHDSA128), nil
|
|
|
|
case KeyTypeSLHDSA192:
|
|
key, err := slhdsa.GenerateKey(rand.Reader, slhdsa.SHA2_192s)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddSLHDSA(key, KeyTypeSLHDSA192), nil
|
|
|
|
case KeyTypeSLHDSA256:
|
|
key, err := slhdsa.GenerateKey(rand.Reader, slhdsa.SHA2_256s)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddSLHDSA(key, KeyTypeSLHDSA256), nil
|
|
|
|
case KeyTypeBLS:
|
|
key, err := bls.NewSecretKey()
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddBLS(key), nil
|
|
|
|
case KeyTypeMLKEM512:
|
|
pubKey, privKey, err := mlkem.GenerateKey(mlkem.MLKEM512)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddMLKEM(pubKey, privKey, mlkem.MLKEM512), nil
|
|
|
|
case KeyTypeMLKEM768:
|
|
pubKey, privKey, err := mlkem.GenerateKey(mlkem.MLKEM768)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddMLKEM(pubKey, privKey, mlkem.MLKEM768), nil
|
|
|
|
case KeyTypeMLKEM1024:
|
|
pubKey, privKey, err := mlkem.GenerateKey(mlkem.MLKEM1024)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddMLKEM(pubKey, privKey, mlkem.MLKEM1024), nil
|
|
|
|
case KeyTypeRingSig:
|
|
// Default to LSAG (secp256k1-based) ring signatures
|
|
// Use GenerateCoronaKey with specific scheme if needed
|
|
signer, err := ring.NewSigner(ring.LSAG)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddRingSig(signer, ring.LSAG), nil
|
|
|
|
case KeyTypeHybridSecp256k1MLDSA44:
|
|
classical, err := secp256k1.NewPrivateKey()
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
pq, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddHybrid(classical, pq), nil
|
|
|
|
case KeyTypeHybridSecp256k1SLHDSA128:
|
|
classical, err := secp256k1.NewPrivateKey()
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
pq, err := slhdsa.GenerateKey(rand.Reader, slhdsa.SHA2_128s)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddHybrid(classical, pq), nil
|
|
|
|
case KeyTypeHybridBLSMLDSA44:
|
|
blsKey, err := bls.NewSecretKey()
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
pqKey, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddHybridBLS(blsKey, pqKey), nil
|
|
|
|
default:
|
|
return ids.ShortEmpty, fmt.Errorf("unsupported key type: %v", kc.defaultType)
|
|
}
|
|
}
|
|
|
|
// GenerateCoronaKey generates a new ring signature key with a specific scheme.
|
|
// scheme can be ring.LSAG (secp256k1-based) or ring.LatticeLSAG (post-quantum).
|
|
func (kc *PQKeychain) GenerateCoronaKey(scheme ring.Scheme) (ids.ShortID, error) {
|
|
signer, err := ring.NewSigner(scheme)
|
|
if err != nil {
|
|
return ids.ShortEmpty, err
|
|
}
|
|
return kc.AddRingSig(signer, scheme), nil
|
|
}
|
|
|
|
// Addresses returns all addresses in the keychain
|
|
func (kc *PQKeychain) Addresses() []ids.ShortID {
|
|
addrs := make([]ids.ShortID, 0, kc.addressSet.Len())
|
|
for addr := range kc.addressSet {
|
|
addrs = append(addrs, addr)
|
|
}
|
|
return addrs
|
|
}
|
|
|
|
// Get returns the signer for the given address
|
|
func (kc *PQKeychain) Get(addr ids.ShortID) (Signer, bool) {
|
|
signer, exists := kc.keysByAddress[addr]
|
|
if !exists {
|
|
return nil, false
|
|
}
|
|
return signer, true
|
|
}
|
|
|
|
// GetPQSigner returns the PQ signer for advanced operations
|
|
func (kc *PQKeychain) GetPQSigner(addr ids.ShortID) (*PQSigner, bool) {
|
|
signer, exists := kc.keysByAddress[addr]
|
|
return signer, exists
|
|
}
|
|
|
|
// SetDefaultType sets the default key type for new keys
|
|
func (kc *PQKeychain) SetDefaultType(keyType KeyType) {
|
|
kc.defaultType = keyType
|
|
}
|