Skip to content

Commit

Permalink
Update (#4)
Browse files Browse the repository at this point in the history
* lots of refactoring, adding tests, examples, updating latest vectors
  • Loading branch information
bytemare authored Nov 27, 2023
1 parent 3fb825b commit bc6f787
Show file tree
Hide file tree
Showing 22 changed files with 1,037 additions and 694 deletions.
6 changes: 3 additions & 3 deletions .github/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ linters:
- contextcheck
- cyclop
- deadcode
- depguard
#- depguard
- dogsled
- dupl
- durationcheck
Expand Down Expand Up @@ -199,5 +199,5 @@ issues:
run:
tests: false

#output:
# format: github-actions
output:
format: github-actions
30 changes: 15 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
name: FROST
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
branches:
- main

permissions:
contents: read

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # pin@v3
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # pin@master
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@v3
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@master
with:
go-version-file: ./go.mod

# Lint
- name: Linting
uses: golangci/golangci-lint-action@5c56cd6c9dc07901af25baab6f2b0d9f3b7c3018 # pin@5c56cd6c9dc07901af25baab6f2b0d9f3b7c3018
uses: golangci/golangci-lint-action@5c56cd6c9dc07901af25baab6f2b0d9f3b7c3018 # pin@master
with:
version: latest
args: --config=./.github/.golangci.yml ./...
Expand All @@ -34,14 +35,14 @@ jobs:
strategy:
fail-fast: false
matrix:
go: [ '1.20', '1.19' ]
go: [ '1.21' ]
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # pin@v3
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # pin@master
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@v3
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@master
with:
go-version: ${{ matrix.go }}

Expand All @@ -51,15 +52,14 @@ jobs:

analyze:
name: Analyze
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # pin@v3
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # pin@master
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@v3
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@master
with:
go-version-file: ./go.mod

Expand All @@ -69,13 +69,13 @@ jobs:

# Codecov
- name: Codecov
uses: codecov/codecov-action@29386c70ef20e286228c72b668a06fd0e8399192 # pin@v1
uses: codecov/codecov-action@29386c70ef20e286228c72b668a06fd0e8399192 # pin@master
with:
file: .github/coverage.out

# Sonar
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@cb201f3b2d7a38231a8c042dfea4539c8bea180b # pin@master
uses: SonarSource/sonarcloud-github-action@5ee47de3c96f0c1c51b09d2ff1fec0cfeefcf67c # pin@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@dc323e67f16fb5f7663d20ff7941f27f5809e9b6 # pin@v2
uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # pin@master

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@231aa2c8a89117b126725a0e11897209b7118144 # pin@v1
uses: github/codeql-action/init@231aa2c8a89117b126725a0e11897209b7118144 # pin@master
with:
languages: go

- name: Autobuild
uses: github/codeql-action/autobuild@231aa2c8a89117b126725a0e11897209b7118144 # pin@v1
uses: github/codeql-action/autobuild@231aa2c8a89117b126725a0e11897209b7118144 # pin@master

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@231aa2c8a89117b126725a0e11897209b7118144 # pin@v1
uses: github/codeql-action/analyze@231aa2c8a89117b126725a0e11897209b7118144 # pin@master
8 changes: 4 additions & 4 deletions .github/workflows/scorecards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ jobs:

steps:
- name: "Checkout code"
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # pin@master
with:
persist-credentials: false

- name: "Run analysis"
uses: ossf/scorecard-action@c1aec4ac820532bab364f02a81873c555a0ba3a1 # v1.0.4
uses: ossf/scorecard-action@c1aec4ac820532bab364f02a81873c555a0ba3a1 # pin@master
with:
results_file: results.sarif
results_format: sarif
Expand All @@ -42,14 +42,14 @@ jobs:

# Upload the results as artifacts (optional).
- name: "Upload artifact"
uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # v2.3.1
uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # pin@master
with:
name: SARIF file
path: results.sarif
retention-days: 5

# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26
uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # pin@master
with:
sarif_file: results.sarif
130 changes: 130 additions & 0 deletions commitment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: MIT
//
// Copyright (C) 2023 Daniel Bourdrez. All Rights Reserved.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree or at
// https://spdx.org/licenses/MIT.html

package frost

import (
"errors"
"fmt"
"slices"

group "github.com/bytemare/crypto"

"github.com/bytemare/frost/internal"
)

var errDecodeCommitmentLength = errors.New("failed to decode commitment: invalid length")

// Commitment is a participant's one-time commitment holding its identifier, and hiding and binding nonces.
type Commitment struct {
Identifier *group.Scalar
HidingNonce *group.Element
BindingNonce *group.Element
}

// Encode returns the serialized byte encoding of a participant's commitment.
func (c Commitment) Encode() []byte {
id := c.Identifier.Encode()
hNonce := c.HidingNonce.Encode()
bNonce := c.BindingNonce.Encode()

out := make([]byte, len(id)+len(hNonce)+len(bNonce))
copy(out, id)
copy(out[len(id):], hNonce)
copy(out[len(id)+len(hNonce):], bNonce)

return out
}

// DecodeCommitment attempts to deserialize the encoded commitment given as input, and to return it.
func DecodeCommitment(cs Ciphersuite, data []byte) (*Commitment, error) {
g := cs.Configuration().Ciphersuite.Group
scalarLength := g.ScalarLength()
elementLength := g.ElementLength()

if len(data) != scalarLength+2*elementLength {
return nil, errDecodeCommitmentLength
}

c := &Commitment{
Identifier: g.NewScalar(),
HidingNonce: g.NewElement(),
BindingNonce: g.NewElement(),
}

if err := c.Identifier.Decode(data[:scalarLength]); err != nil {
return nil, fmt.Errorf("failed to decode commitment identifier: %w", err)
}

if err := c.HidingNonce.Decode(data[:scalarLength]); err != nil {
return nil, fmt.Errorf("failed to decode commitment hiding nonce: %w", err)
}

if err := c.BindingNonce.Decode(data[:scalarLength]); err != nil {
return nil, fmt.Errorf("failed to decode commitment binding nonce: %w", err)
}

return c, nil
}

// CommitmentList is a sortable list of commitments.
type CommitmentList []*Commitment

func cmpID(a, b *Commitment) int {
switch {
case a.Identifier.Equal(b.Identifier) == 1: // a == b
return 0
case a.Identifier.LessOrEqual(b.Identifier) == 1: // a < b
return -1
default:
return 1
}
}

// Sort sorts the list the ascending order of identifiers.
func (c CommitmentList) Sort() {
slices.SortFunc(c, cmpID)
}

// IsSorted returns whether the list is sorted in ascending order by identifier.
func (c CommitmentList) IsSorted() bool {
return slices.IsSortedFunc(c, cmpID)
}

// Encode serializes a whole commitment list.
func (c CommitmentList) Encode() []byte {
var encoded []byte

for _, l := range c {
e := internal.Concatenate(l.Identifier.Encode(), l.HidingNonce.Encode(), l.BindingNonce.Encode())
encoded = append(encoded, e...)
}

return encoded
}

// Participants returns the list of participants in the commitment list.
func (c CommitmentList) Participants() []*group.Scalar {
identifiers := make([]*group.Scalar, len(c))
for i, l := range c {
identifiers[i] = l.Identifier
}

return identifiers
}

// Get returns the commitment of the participant with the corresponding identifier, or nil if it was not found.
func (c CommitmentList) Get(identifier *group.Scalar) *Commitment {
for _, com := range c {
if com.Identifier.Equal(identifier) == 1 {
return com
}
}

return nil
}
40 changes: 18 additions & 22 deletions coordinator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,39 @@ package frost
import (
group "github.com/bytemare/crypto"
secretsharing "github.com/bytemare/secret-sharing"

"github.com/bytemare/frost/internal"
"github.com/bytemare/frost/internal/schnorr"
)

// Aggregate allows the coordinator to produce the final signature given all signature shares.
//
// Before aggregation, each signature share must be a valid deserialized element. If that validation fails the
// Before aggregation, each signature share must be a valid, deserialized element. If that validation fails the
// coordinator must abort the protocol, as the resulting signature will be invalid.
// The CommitmentList must be sorted in ascending order by identifier.
//
// 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 participant from the set of allowed participants in future runs of FROST.
func (p *Participant) Aggregate(
list internal.CommitmentList,
list CommitmentList,
msg []byte,
sigShares []*group.Scalar,
) *schnorr.Signature {
sigShares []*SignatureShare,
) *Signature {
if !list.IsSorted() {
panic("list not sorted")
}

// Compute binding factors
bindingFactorList, _ := list.ComputeBindingFactors(p.Ciphersuite, msg)
bindingFactorList := p.computeBindingFactors(list, msg)

// Compute group commitment
groupCommitment := list.ComputeGroupCommitment(p.Ciphersuite, bindingFactorList)
groupCommitment := p.computeGroupCommitment(list, bindingFactorList)

// Compute aggregate signature
z := p.Ciphersuite.Group.NewScalar().Zero()
for _, zi := range sigShares {
z.Add(zi)
z := p.Ciphersuite.Group.NewScalar()
for _, share := range sigShares {
z.Add(share.SignatureShare)
}

return &schnorr.Signature{
return &Signature{
R: groupCommitment,
Z: z,
}
Expand All @@ -58,34 +55,33 @@ func (p *Participant) Aggregate(
//
// The CommitmentList must be sorted in ascending order by identifier.
func (p *Participant) VerifySignatureShare(
id *group.Scalar,
commitment *Commitment,
pki *group.Element,
commi [2]*group.Element,
sigShareI *group.Scalar,
coms internal.CommitmentList,
coms CommitmentList,
msg []byte,
) bool {
if !coms.IsSorted() {
panic("list not sorted")
}

// Compute Binding Factor(s)
bindingFactorList, _ := coms.ComputeBindingFactors(p.Ciphersuite, msg)
bindingFactor := bindingFactorList.BindingFactorForParticipant(id)
bindingFactorList := p.computeBindingFactors(coms, msg)
bindingFactor := bindingFactorList.BindingFactorForParticipant(commitment.Identifier)

// Compute Group Commitment
groupCommitment := coms.ComputeGroupCommitment(p.Ciphersuite, bindingFactorList)
groupCommitment := p.computeGroupCommitment(coms, bindingFactorList)

// Commitment KeyShare
commShare := commi[0].Copy().Add(commi[1].Copy().Multiply(bindingFactor))
commShare := commitment.HidingNonce.Copy().Add(commitment.BindingNonce.Copy().Multiply(bindingFactor))

// Compute the challenge
challenge := schnorr.Challenge(p.Ciphersuite, groupCommitment, p.Configuration.GroupPublicKey, msg)
challenge := challenge(p.Ciphersuite, groupCommitment, p.Configuration.GroupPublicKey, msg)

// Compute the interpolating value
participantList := secretsharing.Polynomial(coms.Participants())

lambdaI, err := participantList.DeriveInterpolatingValue(p.Ciphersuite.Group, id)
lambdaI, err := participantList.DeriveInterpolatingValue(p.Ciphersuite.Group, commitment.Identifier)
if err != nil {
panic(err)
}
Expand Down
Loading

0 comments on commit bc6f787

Please sign in to comment.