Skip to content

Commit

Permalink
some refactor and clean up
Browse files Browse the repository at this point in the history
Signed-off-by: bytemare <[email protected]>
  • Loading branch information
bytemare committed Aug 30, 2024
1 parent f145a58 commit 59b560e
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 64 deletions.
35 changes: 2 additions & 33 deletions commitment.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func (c *Configuration) ValidateCommitmentList(commitments CommitmentList) error
}

// List must be sorted, compare with the next commitment.
if uint64(i) < length-2 {
if uint64(i) <= length-2 {
if cmpID(commitment, commitments[i+1]) > 0 {
return fmt.Errorf("commitment list is not sorted by signer identifiers")

Check warning on line 223 in commitment.go

View check run for this annotation

Codecov / codecov/patch

commitment.go#L223

Added line #L223 was not covered by tests
}
Expand All @@ -237,37 +237,6 @@ func (c *Configuration) ValidateCommitmentList(commitments CommitmentList) error
return nil
}

// Validate checks for the Commitment list's integrity.
// - list is returned sorted
// - no signer identifier in commitments is 0
// - no
func (c CommitmentList) Validate(g group.Group) error {
// set to detect duplication
set := make(map[uint64]struct{}, len(c))

for _, com := range c {

// Check for duplicate participant entries.
if _, exists := set[com.SignerID]; exists {
return fmt.Errorf("commitment list contains multiple commitments of participant %d", com.SignerID)
}

set[com.SignerID] = struct{}{}

// Check general validity of the commitment.
if err := com.Validate(g); err != nil {
return err
}
}

// Ensure the list is sorted
if !c.IsSorted() {
c.Sort()
}

return nil
}

func (c CommitmentList) Encode() []byte {
n := len(c)
if n == 0 {
Expand Down Expand Up @@ -359,7 +328,7 @@ func encodeCommitmentList(g group.Group, commitments []*commitmentWithEncodedID)
return encoded
}

// BindingFactors is a map of participant identifier to BindingFactors.
// BindingFactors is a map of participant identifiers to BindingFactors.
type BindingFactors map[uint64]*group.Scalar

func (c CommitmentList) bindingFactors(publicKey *group.Element, message []byte) BindingFactors {
Expand Down
59 changes: 47 additions & 12 deletions coordinator.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ type Signature struct {
// The coordinator should verify this signature using the group public key before publishing or releasing the signature.
// This aggregate signature will verify if and only if all signature shares are valid. If an invalid share is identified
// a reasonable approach is to remove the signer from the set of allowed participants in future runs of FROST. If verify
// is set to true, AggregateSignatures will automatically verify the signature shares and produced signatures, and will
// is set to true, AggregateSignatures will automatically verify the signature shares and the output signature, and will
// return an error with the first encountered invalid signature share.
func (c *Configuration) AggregateSignatures(
message []byte,
sigShares []*SignatureShare,
commitments CommitmentList,
verify bool,
) (*Signature, error) {
groupCommitment, bindingFactors, participants, err := c.PrepareVerifySignatureShare(message, commitments)
groupCommitment, bindingFactors, participants, err := c.PrepareSignatureShareVerification(message, commitments)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -85,15 +85,15 @@ func (c *Configuration) VerifySignatureShare(
message []byte,
commitments CommitmentList,
) error {
groupCommitment, bindingFactors, participants, err := c.PrepareVerifySignatureShare(message, commitments)
groupCommitment, bindingFactors, participants, err := c.PrepareSignatureShareVerification(message, commitments)
if err != nil {
return err
}

return c.verifySignatureShare(sigShare, message, commitments, participants, groupCommitment, bindingFactors)
}

func (c *Configuration) PrepareVerifySignatureShare(message []byte,
func (c *Configuration) PrepareSignatureShareVerification(message []byte,
commitments CommitmentList,
) (*group.Element, BindingFactors, []*group.Scalar, error) {
if !c.verified {
Expand Down Expand Up @@ -125,6 +125,46 @@ func (c *Configuration) getSignerPubKey(id uint64) *group.Element {
return nil

Check warning on line 125 in coordinator.go

View check run for this annotation

Codecov / codecov/patch

coordinator.go#L125

Added line #L125 was not covered by tests
}

func (c *Configuration) validateSignatureShareLight(sigShare *SignatureShare) error {
if sigShare == nil {
return errors.New("nil signature share")

Check warning on line 130 in coordinator.go

View check run for this annotation

Codecov / codecov/patch

coordinator.go#L130

Added line #L130 was not covered by tests
}

if sigShare.SignatureShare == nil || sigShare.SignatureShare.IsZero() {
return errors.New("invalid signature share (nil or zero)")

Check warning on line 134 in coordinator.go

View check run for this annotation

Codecov / codecov/patch

coordinator.go#L134

Added line #L134 was not covered by tests
}

return nil
}

func (c *Configuration) validateSignatureShareExtensive(sigShare *SignatureShare) error {
if err := c.validateSignatureShareLight(sigShare); err != nil {
return err

Check warning on line 142 in coordinator.go

View check run for this annotation

Codecov / codecov/patch

coordinator.go#L142

Added line #L142 was not covered by tests
}

if sigShare.SignerIdentifier == 0 {
return errors.New("signature share's signer identifier is 0 (invalid)")

Check warning on line 146 in coordinator.go

View check run for this annotation

Codecov / codecov/patch

coordinator.go#L146

Added line #L146 was not covered by tests
}

if sigShare.SignerIdentifier > c.MaxSigners {
return fmt.Errorf(
"signature share has invalid ID %d, above authorized range [1:%d]",
sigShare.SignerIdentifier,
c.MaxSigners,
)

Check warning on line 154 in coordinator.go

View check run for this annotation

Codecov / codecov/patch

coordinator.go#L150-L154

Added lines #L150 - L154 were not covered by tests
}

if sigShare.Group != c.group {
return fmt.Errorf("signature share has invalid group parameter, want %s got %s", c.group, sigShare.Group)

Check warning on line 158 in coordinator.go

View check run for this annotation

Codecov / codecov/patch

coordinator.go#L158

Added line #L158 was not covered by tests
}

if c.getSignerPubKey(sigShare.SignerIdentifier) == nil {
return fmt.Errorf("no public key registered for signer %d", sigShare.SignerIdentifier)

Check warning on line 162 in coordinator.go

View check run for this annotation

Codecov / codecov/patch

coordinator.go#L162

Added line #L162 was not covered by tests
}

return nil
}

func (c *Configuration) verifySignatureShare(
sigShare *SignatureShare,
message []byte,
Expand All @@ -133,9 +173,8 @@ func (c *Configuration) verifySignatureShare(
groupCommitment *group.Element,
bindingFactors BindingFactors,
) error {
// Due diligence check that no signer id == 0.
if sigShare.SignerIdentifier == 0 {
return errors.New("signer identifier is 0 (invalid)")
if err := c.validateSignatureShareExtensive(sigShare); err != nil {
return err

Check warning on line 177 in coordinator.go

View check run for this annotation

Codecov / codecov/patch

coordinator.go#L177

Added line #L177 was not covered by tests
}

com := commitments.Get(sigShare.SignerIdentifier)
Expand All @@ -144,11 +183,7 @@ func (c *Configuration) verifySignatureShare(
}

pk := c.getSignerPubKey(sigShare.SignerIdentifier)
if pk == nil {
return fmt.Errorf("public key not registered for signer %d", sigShare.SignerIdentifier)
}

lambda := internal.Lambda2(c.group, sigShare.SignerIdentifier, participants)
lambda := internal.Lambda(c.group, sigShare.SignerIdentifier, participants)
lambdaChall := c.challenge(lambda, message, groupCommitment)

// Commitment KeyShare: r = g(h + b*f + l*s)
Expand Down
67 changes: 65 additions & 2 deletions frost.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ var (
)

func (c *Configuration) verifySignerPublicKeys() error {
if uint64(len(c.SignerPublicKeys)) < c.Threshold ||
uint64(len(c.SignerPublicKeys)) > c.MaxSigners {
length := uint64(len(c.SignerPublicKeys))
if length < c.Threshold || length > c.MaxSigners {
return errInvalidNumberOfPublicKeys
}

Expand Down Expand Up @@ -216,6 +216,65 @@ func (c *Configuration) Init() error {
return nil
}

func (c *Configuration) ValidateKeyShare(keyShare *KeyShare) error {
if !c.verified {
if err := c.Init(); err != nil {
return err

Check warning on line 222 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L221-L222

Added lines #L221 - L222 were not covered by tests
}
}

if keyShare == nil {
return errors.New("provided key share is nil")

Check warning on line 227 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L227

Added line #L227 was not covered by tests
}

if keyShare.ID == 0 {
return errors.New("provided key share has invalid ID 0")

Check warning on line 231 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L231

Added line #L231 was not covered by tests
}

if keyShare.ID > c.MaxSigners {
return fmt.Errorf(
"provided key share has invalid ID %d, above authorized range [1:%d]",
keyShare.ID,
c.MaxSigners,
)

Check warning on line 239 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L235-L239

Added lines #L235 - L239 were not covered by tests
}

if keyShare.Group != c.group {
return fmt.Errorf("provided key share has invalid group parameter, want %s got %s", c.group, keyShare.Group)

Check warning on line 243 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L243

Added line #L243 was not covered by tests
}

if c.GroupPublicKey.Equal(keyShare.GroupPublicKey) != 1 {
return errors.New(
"the group's public key in the provided key share does not match the one in the configuration",
)

Check warning on line 249 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L247-L249

Added lines #L247 - L249 were not covered by tests
}

if keyShare.PublicKey == nil {
return errors.New("provided key share has nil public key")

Check warning on line 253 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L253

Added line #L253 was not covered by tests
}

if keyShare.Secret == nil || keyShare.Secret.IsZero() {
return errors.New("provided key share has invalid secret key")

Check warning on line 257 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L257

Added line #L257 was not covered by tests
}

if c.group.Base().Multiply(keyShare.Secret).Equal(keyShare.PublicKey) != 1 {
return errors.New("provided key share has non-matching secret and public keys")

Check warning on line 261 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L261

Added line #L261 was not covered by tests
}

pk := c.getSignerPubKey(keyShare.ID)
if pk == nil {
return errors.New("provided key share has no registered signer identifier in the configuration")

Check warning on line 266 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L266

Added line #L266 was not covered by tests
}

if pk.Equal(keyShare.PublicKey) != 1 {
return errors.New(
"provided key share has a different public key than the one registered for that signer in the configuration",
)

Check warning on line 272 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L270-L272

Added lines #L270 - L272 were not covered by tests
}

return nil
}

// Signer returns a new participant of the protocol instantiated from the Configuration and the signer's key share.
func (c *Configuration) Signer(keyShare *KeyShare) (*Signer, error) {
if !c.verified {
Expand All @@ -224,6 +283,10 @@ func (c *Configuration) Signer(keyShare *KeyShare) (*Signer, error) {
}
}

if err := c.ValidateKeyShare(keyShare); err != nil {
return nil, err

Check warning on line 287 in frost.go

View check run for this annotation

Codecov / codecov/patch

frost.go#L287

Added line #L287 was not covered by tests
}

return &Signer{
KeyShare: keyShare,
LambdaRegistry: make(internal.LambdaRegistry),
Expand Down
13 changes: 2 additions & 11 deletions internal/lambda.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,12 @@ import (
secretsharing "github.com/bytemare/secret-sharing"
)

func Lambda(g group.Group, id uint64, polynomial secretsharing.Polynomial) (*group.Scalar, error) {
l, err := polynomial.DeriveInterpolatingValue(g, g.NewScalar().SetUInt64(id))
if err != nil {
return nil, fmt.Errorf("anomaly in participant identifiers: %w", err)
}

return l, nil
}

// Lambda derives the interpolating value for id in the polynomial made by the participant identifiers.
// This function assumes that:
// - id is non-nil and != 0
// - every scalar in participants is non-nil and != 0
// - there are no duplicates in participants

Check failure on line 24 in internal/lambda.go

View workflow job for this annotation

GitHub Actions / Lint / GolangCI-Lint

Comment should end in a period (godot)
func Lambda2(g group.Group, id uint64, participants []*group.Scalar) *group.Scalar {
func Lambda(g group.Group, id uint64, participants []*group.Scalar) *group.Scalar {
sid := g.NewScalar().SetUInt64(id)
numerator := g.NewScalar().One()
denominator := g.NewScalar().One()
Expand Down Expand Up @@ -68,7 +59,7 @@ func (l LambdaRegistry) New(g group.Group, id uint64, participants []uint64) *gr
}
*/
lambda := Lambda2(g, id, polynomial)
lambda := Lambda(g, id, polynomial)

l.Set(participants, lambda)

Expand Down
7 changes: 4 additions & 3 deletions signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ func (s *Signer) Identifier() uint64 {
func randomCommitmentID() uint64 {
buf := make([]byte, 8)

_, err := rand.Read(buf)
if err != nil {
if _, err := rand.Read(buf); err != nil {
panic(fmt.Errorf("FATAL: %w", err))

Check warning on line 85 in signer.go

View check run for this annotation

Codecov / codecov/patch

signer.go#L85

Added line #L85 was not covered by tests
}

Expand All @@ -100,6 +99,8 @@ func (s *Signer) generateNonce(secret *group.Scalar, random []byte) *group.Scala
func (s *Signer) genNonceID() uint64 {
cid := randomCommitmentID()

// In the extremely rare and unlikely case the CSPRNG returns an already registered ID, we try again 128 times
// before failing.
for range 128 {
if _, exists := s.NonceCommitments[cid]; !exists {
return cid
Expand All @@ -108,7 +109,7 @@ func (s *Signer) genNonceID() uint64 {
cid = randomCommitmentID()

Check warning on line 109 in signer.go

View check run for this annotation

Codecov / codecov/patch

signer.go#L109

Added line #L109 was not covered by tests
}

panic("FATAL: CSPRNG could not generate a unique nonce over 128 iterations")
panic("FATAL: CSPRNG could not generate unique commitment identifiers over 128 iterations")

Check warning on line 112 in signer.go

View check run for this annotation

Codecov / codecov/patch

signer.go#L112

Added line #L112 was not covered by tests
}

// Commit generates a signer's nonces and commitment, to be used in the second FROST round. The internal nonce must
Expand Down
4 changes: 2 additions & 2 deletions tests/frost_error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ func TestConfiguration_PrepareVerifySignatureShare_BadNonVerifiedConfiguration(t
SignerPublicKeys: publicKeyShares,
}

if _, _, _, err := configuration.PrepareVerifySignatureShare(nil, nil); err == nil ||
if _, _, _, err := configuration.PrepareSignatureShareVerification(nil, nil); err == nil ||
err.Error() != expectedErrorPrefix.Error() {
t.Fatalf("expected %q, got %q", expectedErrorPrefix, err)
}
Expand All @@ -405,7 +405,7 @@ func TestConfiguration_PrepareVerifySignatureShare_InvalidCommitments(t *testing
coms[i] = s.Commit()
}

if _, _, _, err := configuration.PrepareVerifySignatureShare(nil, coms[:1]); err == nil ||
if _, _, _, err := configuration.PrepareSignatureShareVerification(nil, coms[:1]); err == nil ||
!strings.HasPrefix(err.Error(), expectedErrorPrefix) {
t.Fatalf("expected %q, got %q", expectedErrorPrefix, err)
}
Expand Down
2 changes: 1 addition & 1 deletion tests/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ func TestLambda_BadID(t *testing.T) {
}

// todo : what happens if the participant list is not vetted?
fmt.Println(internal.Lambda2(g, 1, polynomial).Hex())
fmt.Println(internal.Lambda(g, 1, polynomial).Hex())
}

func TestLambdaRegistry(t *testing.T) {
Expand Down

0 comments on commit 59b560e

Please sign in to comment.