Skip to content

soatok/frost

Repository files navigation

Go Implementation of FROST (RFC 9591)

Build Status

This Go module implements RFC 9591 (FROST) for threshold signing with the Ed25519 ciphersuite.

This was developed in part to make FREON possible. Learn more about FREON.

Security Notice

Warning

This package has not been audited by a third party.

Install

go install github.com/soatok/frost@latest

Usage

Key Generation

Trusted Dealer

import (
	"github.com/soatok/trusteddealer"
	"github.com/soatok/frost"
)

func main() {
	// We are using the default ciphersuite:
	c := frost.DefaultCiphersuite()
	dealer := trusteddealer.NewTrustedDealer(c)

	// This is a 3-of-4 threshold:
	participants := uint32(4)
	threshold := uint32(3)
	keygen, err := td.Keygen(participants, threshold)

	// Group public key:
	publicKey := keygen.GroupPublicKey.Bytes()

	// Commitments:
	commitments := keygen.Commitments()

	// Send shares to each party:
	for i := range keygen.Count() {
		id := keygen.Participants[i].Identifier.Bytes()
		secret := keygen.ParticipantPrivateKeys[i].Bytes()
		// Send {id, secret, publicKey, commitments} to party {i}
	}
}

Signing

We will explain it usage inline with an example Go program:

import (
	"github.com/soatok/frost"
)

func main() {
	// We are using the default ciphersuite:
	ciphersuite := frost.DefaultCiphersuite()

	// Initialize a list of participants
	participants = []*frost.Participant{}
	for _, member := range membersDefinedElsewhere {
		id := member.Identifier
		pks := member.PublicKeyShare
		// id []byte length 32
		// pk []byte length 32
		party, err := frost.NewParticipant(id, pks)
		if err != nil {
			panic(err)
		}
		// Append to list:
		participants = append(participants, party)
	}

	// Supply the group Ed25519 public key here:
	gk, err := frost.GroupKeyFromBytes([]byte{/* ... */})
	if err != nil {
		panic(err)
	}

	// Message to sign:
	message := []byte("the message you are signing goes here")

	// Initialize secret share
	mySecretShare, err := frost.SecretShareFromBytes([]byte{/* ... */}, []byte{/* ... */})
	if err != nil {
		panic(err)
	}

	// Define a State object for the signing ceremony
	state := frost.NewState(ciphersuite, participants, message, mySecretShare)

	// First round: commit to the message
	round1, err := state.Commit()
	if err != nil {
		panic(err)
	}

	// Send your commitment from round1 to the other participants here
	send1, err := round1.EncodeJSON()
	if err != nil {
		panic(err)
	}
	// YOU MUST DEFINE THE TRANSPORT OF `send1` YOURSELF

	// networking happens here

	// Load commitments from other parties
	recv1, err := getMessagesFromAbroad() // [][]byte -- YOU MUST DEFINE THIS YOURSELF
	if err != nil {
		panic(err)
	}
	commitments := []*Commitment{}
	for _, r := range recv1 {
		c, err := CommitmentFromJSON(r)
		if err != nil {
			panic(err)
		}
		commitments = append(commitments, c)
	}

	// Perform round 2: get signature shares
	round2, err := state.Sign(commitments)
	if err != nil {
		panic(err)
	}

	// Send your commitment from round1 to the other participants here
	send2, err := round2.EncodeJSON()
	// YOU MUST DEFINE THE TRANSPORT OF `send2` YOURSELF
	
	// networking happens here

	// Load signature shares from other parties
	recv2, err := getMessagesFromAbroad() // [][]byte -- YOU MUST DEFINE THIS YOURSELF
	if err != nil {
		panic(err)
	}
	shares := []*SignatureShare{}
	for _, r := range recv2 {
		s, err := SignatureShareFromJSON(r)
		if err != nil {
			panic(err)
		}

		// Validate this share before including it:
		valid, err := state.VerifySignatureShare(s)
		if err != nil {
			panic(err)
		}
		if valid {
			shares = append(shares, s)
		}
	}

	// Finally, aggregate the shares to calculate the final signature
	finalSig, err := state.Aggregate(shares)
}

About

Go Implementation of FROST (RFC 9591)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages