Files
Hanzo AI 5d465c1da5 node: kill all remaining Corona identifiers across consensus, vms, wallet
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.
2026-05-12 09:54:58 -07:00

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
}