├── .github
└── workflows
│ ├── gofmt.yml
│ └── test.yml
├── .gitignore
├── .gitlab-ci.yml
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
└── copyright
│ ├── Binance.xml
│ └── profiles_settings.xml
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── cmd
├── go.mod
├── go.sum
├── tss-benchgen
│ └── main.go
└── tss-benchsign
│ └── main.go
├── common
├── hash.go
├── hash_utils.go
├── hash_utils_test.go
├── int.go
├── logger.go
├── pbpoint.go
├── random.go
├── random_test.go
├── safe_prime.go
├── safe_prime_test.go
├── shared.pb.go
├── signature.pb.go
└── slice.go
├── crypto
├── ckd
│ ├── child_key_derivation.go
│ └── child_key_derivation_test.go
├── commitments
│ ├── builder.go
│ ├── builder_test.go
│ ├── hashed.go
│ └── hashed_test.go
├── dlnp
│ └── proof.go
├── ecpoint.go
├── ecpoint_test.go
├── mta
│ ├── proofs.go
│ ├── range_proof.go
│ ├── share_protocol.go
│ └── share_protocol_test.go
├── paillier
│ ├── paillier.go
│ └── paillier_test.go
├── utils.go
├── vss
│ ├── feldman_vss.go
│ └── feldman_vss_test.go
└── zkp
│ ├── affg
│ ├── affg.go
│ └── affg_test.go
│ ├── dec
│ ├── dec.go
│ └── dec_test.go
│ ├── dlog_proof.go
│ ├── dlog_proof_test.go
│ ├── enc
│ ├── enc.go
│ └── enc_test.go
│ ├── fac
│ ├── fac.go
│ └── fac_test.go
│ ├── hv_proofs.go
│ ├── logstar
│ ├── logstar.go
│ └── logstar_test.go
│ ├── mod
│ ├── mod.go
│ └── mod_test.go
│ ├── mul
│ ├── mul.go
│ └── mul_test.go
│ ├── pdl_w_slack_proof.go
│ ├── prm
│ ├── prm.go
│ └── prm_test.go
│ └── sch
│ ├── sch.go
│ └── sch_test.go
├── ecdsa
├── keygen
│ ├── ecdsa-keygen.pb.go
│ ├── local_party.go
│ ├── local_party_test.go
│ ├── messages.go
│ ├── prepare.go
│ ├── round_1.go
│ ├── round_2.go
│ ├── round_3.go
│ ├── round_4.go
│ ├── round_out.go
│ ├── rounds.go
│ ├── save_data.go
│ └── test_utils.go
├── resharing
│ ├── ecdsa-resharing.pb.go
│ ├── local_party.go
│ ├── local_party_test.go
│ ├── messages.go
│ ├── round_1_old_step_1.go
│ ├── round_2_new_step_1.go
│ ├── round_3_old_step_2.go
│ ├── round_4_new_step_2.go
│ ├── round_5_new_step_3.go
│ └── rounds.go
├── signing
│ ├── ecdsa-signature.pb.go
│ ├── ecdsa-signing.pb.go
│ ├── identification_6.go
│ ├── identification_7.go
│ ├── identification_prep.go
│ ├── key_derivation_test.go
│ ├── key_derivation_util.go
│ ├── local_party.go
│ ├── local_party_test.go
│ ├── messages.go
│ ├── mta.go
│ ├── mta_test.go
│ ├── prepare.go
│ ├── presign_1.go
│ ├── presign_2.go
│ ├── presign_3.go
│ ├── rounds.go
│ ├── sign_4.go
│ └── sign_out.go
└── utils.go
├── eddsa
├── keygen
│ ├── eddsa-keygen.pb.go
│ ├── local_party.go
│ ├── local_party_test.go
│ ├── messages.go
│ ├── round_1.go
│ ├── round_2.go
│ ├── round_3.go
│ ├── rounds.go
│ ├── save_data.go
│ └── test_utils.go
├── resharing
│ ├── eddsa-resharing.pb.go
│ ├── local_party.go
│ ├── local_party_test.go
│ ├── messages.go
│ ├── round_1_old_step_1.go
│ ├── round_2_new_step_1.go
│ ├── round_3_old_step_2.go
│ ├── round_4_new_step_2.go
│ ├── round_5_new_step_3.go
│ └── rounds.go
└── signing
│ ├── eddsa-signature.pb.go
│ ├── eddsa-signing.pb.go
│ ├── finalize.go
│ ├── local_party.go
│ ├── local_party_test.go
│ ├── messages.go
│ ├── prepare.go
│ ├── round_1.go
│ ├── round_2.go
│ ├── round_3.go
│ ├── rounds.go
│ └── utils.go
├── go.mod
├── go.sum
├── protob
├── ecdsa-keygen.proto
├── ecdsa-resharing.proto
├── ecdsa-signature.proto
├── ecdsa-signing.proto
├── eddsa-keygen.proto
├── eddsa-resharing.proto
├── eddsa-signature.proto
├── eddsa-signing.proto
├── message.proto
├── shared.proto
└── signature.proto
├── test
├── _ecdsa_fixtures
│ ├── keygen_data_0.json
│ ├── keygen_data_1.json
│ ├── keygen_data_2.json
│ ├── keygen_data_3.json
│ └── keygen_data_4.json
├── _eddsa_fixtures
│ ├── keygen_data_0.json
│ ├── keygen_data_1.json
│ ├── keygen_data_2.json
│ ├── keygen_data_3.json
│ └── keygen_data_4.json
├── config.go
└── utils.go
└── tss
├── curve.go
├── error.go
├── message.go
├── message.pb.go
├── params.go
├── party.go
├── party_id.go
├── peers.go
├── round.go
└── wire.go
/.github/workflows/gofmt.yml:
--------------------------------------------------------------------------------
1 | name: Go-fmt
2 | on: push
3 | jobs:
4 | gofmt:
5 | name: Go fmt project
6 | runs-on: ubuntu-latest
7 | steps:
8 | - name: check out
9 | uses: actions/checkout@v2
10 | - name: go fmt project
11 | uses: Jerome1337/gofmt-action@v1.0.4
12 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Go Test
2 | on:
3 | push:
4 | branches:
5 | - master
6 | - release/*
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | build:
13 | name: Test
14 | runs-on: macOS-latest
15 | steps:
16 |
17 | - name: Set up Go 1.16
18 | uses: actions/setup-go@v1
19 | with:
20 | go-version: 1.16
21 | id: go
22 |
23 | - name: Check out code into the Go module directory
24 | uses: actions/checkout@v1
25 |
26 | - name: Get dependencies
27 | run: go get -v -t -d ./...
28 |
29 | - name: Run Tests
30 | run: make test_unit
31 |
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | vendor
3 | benchdata
4 | .DS_Store
5 |
6 | # Binaries for programs and plugins
7 | *.exe
8 | *.exe~
9 | *.dll
10 | *.so
11 | *.dylib
12 |
13 | # Test binary, build with `go test -c`
14 | *.test
15 |
16 | # Output of the go coverage tool, specifically when used with LiteIDE
17 | *.out
18 |
19 | # IDE
20 | .idea/*
21 | !.idea/copyright/
22 | !.idea/codeStyles/
23 | *.swp
24 | .vscode/*.json
25 |
26 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | image: golang:latest
2 |
3 | variables:
4 | # Please edit to your GitLab project
5 | REPO_NAME: gitlab.com/thorchaintss/tss-lib
6 |
7 | # The problem is that to be able to use go get, one needs to put
8 | # the repository in the $GOPATH. So for example if your gitlab domain
9 | # is gitlab.com, and that your repository is namespace/project, and
10 | # the default GOPATH being /go, then you'd need to have your
11 | # repository in /go/src/gitlab.com/namespace/project
12 | # Thus, making a symbolic link corrects this.
13 | before_script:
14 | - mkdir -p $GOPATH/src/$(dirname $REPO_NAME)
15 | - ln -svf $CI_PROJECT_DIR $GOPATH/src/$REPO_NAME
16 | - cd $GOPATH/src/$REPO_NAME
17 |
18 | stages:
19 | - test
20 |
21 |
22 | unit_tests:
23 | stage: test
24 | coverage: '/total:\s+\(statements\)\s+(\d+.\d+\%)/'
25 | script:
26 | - go test -timeout 60m -race -v -coverprofile=coverage.out ./...
27 | - go tool cover -func=coverage.out
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/copyright/Binance.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Binance
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | MODULE = github.com/binance-chain/tss-lib
2 | PACKAGES = $(shell go list ./... | grep -v '/vendor/')
3 |
4 | all: protob test
5 |
6 | ########################################
7 | ### Protocol Buffers
8 |
9 | protob:
10 | @echo "--> Building Protocol Buffers"
11 | @for protocol in message signature ecdsa-keygen ecdsa-signing ecdsa-resharing eddsa-keygen eddsa-signing eddsa-resharing; do \
12 | echo "Generating $$protocol.pb.go" ; \
13 | protoc --go_out=. ./protob/$$protocol.proto ; \
14 | done
15 |
16 | ########################################
17 | ### Format
18 |
19 | fmt:
20 | @go fmt ./...
21 |
22 | lint:
23 | @golangci-lint run
24 |
25 | build: protob
26 | go fmt ./...
27 |
28 | ########################################
29 | ### Benchmarking
30 |
31 | benchgen: fmt
32 | cd cmd && go run ./tss-benchgen benchdata
33 |
34 | benchsign: fmt
35 | cd cmd && go run ./tss-benchsign benchdata
36 |
37 | ########################################
38 | ### Testing
39 |
40 | test_unit:
41 | @echo "--> Running Unit Tests"
42 | @echo "!!! WARNING: This will take a long time :)"
43 | go test -timeout 60m $(PACKAGES)
44 |
45 | test_unit_race:
46 | @echo "--> Running Unit Tests (with Race Detection)"
47 | @echo "!!! WARNING: This will take a long time :)"
48 | # go clean -testcache
49 | go test -timeout 60m -race $(PACKAGES)
50 |
51 | test:
52 | make test_unit_race
53 |
54 | ########################################
55 | ### Pre Commit
56 |
57 | pre_commit: build test
58 |
59 | ########################################
60 |
61 | # To avoid unintended conflicts with file names, always add to .PHONY
62 | # # unless there is a reason not to.
63 | # # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
64 | .PHONY: protob build test_unit test_unit_race test benchgen benchsign
65 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Bug or Vulnerability
4 |
5 | For non-security problems please open an issue in this GitHub repository.
6 |
7 | If you find any security issues please send a report confidentially to security@binance.com.
8 |
9 | Please include notes about the impact of the issue and a walkthrough on how it can be exploited.
10 |
11 | For severe security issues that completely breach the safety of the scheme or leak the secret shares we would be happy to reward you with a bounty based on the security impact and severity. Please include a link to this notice in your email.
12 |
--------------------------------------------------------------------------------
/cmd/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/binance-chain/tss-lib/cmd
2 |
3 | go 1.17
4 |
5 | require (
6 | github.com/binance-chain/tss-lib v1.3.3
7 | github.com/btcsuite/btcd/btcec/v2 v2.0.0
8 | github.com/ipfs/go-log v1.0.5
9 | github.com/olekukonko/tablewriter v0.0.5
10 | github.com/pkg/errors v0.9.1
11 | golang.org/x/text v0.3.7
12 | )
13 |
14 | require (
15 | github.com/agl/ed25519 v0.0.0-20200305024217-f36fc4b53d43 // indirect
16 | github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c // indirect
17 | github.com/btcsuite/btcutil v1.0.3-0.20211129182920-9c4bbabe7acd // indirect
18 | github.com/decred/dcrd/dcrec/edwards/v2 v2.0.2 // indirect
19 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
20 | github.com/gogo/protobuf v1.3.2 // indirect
21 | github.com/ipfs/go-log/v2 v2.5.0 // indirect
22 | github.com/mattn/go-isatty v0.0.14 // indirect
23 | github.com/mattn/go-runewidth v0.0.13 // indirect
24 | github.com/opentracing/opentracing-go v1.2.0 // indirect
25 | github.com/otiai10/primes v0.0.0-20210501021515-f1b2be525a11 // indirect
26 | github.com/rivo/uniseg v0.2.0 // indirect
27 | go.uber.org/atomic v1.9.0 // indirect
28 | go.uber.org/multierr v1.7.0 // indirect
29 | go.uber.org/zap v1.20.0 // indirect
30 | golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed // indirect
31 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect
32 | google.golang.org/protobuf v1.27.1 // indirect
33 | )
34 |
35 | replace github.com/binance-chain/tss-lib => github.com/SwingbyProtocol/tss-lib v1.5.1-0.20220129135114-1e9891f47740
36 |
37 | replace github.com/agl/ed25519 => github.com/SwingbyProtocol/edwards25519 v0.0.0-20200305024217-f36fc4b53d43
38 |
39 | replace github.com/btcsuite/btcd => github.com/Roasbeef/btcd v0.0.0-20220128222530-5a59e7c0ddfb
40 |
41 | replace github.com/btcsuite/btcd/btcec/v2 => github.com/Roasbeef/btcd/btcec/v2 v2.0.0-20220128222530-5a59e7c0ddfb
42 |
--------------------------------------------------------------------------------
/common/hash.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package common
8 |
9 | import (
10 | "crypto"
11 | _ "crypto/sha512"
12 | "encoding/binary"
13 | "math/big"
14 | )
15 |
16 | const (
17 | hashInputDelimiter = byte('$')
18 | )
19 |
20 | // SHA-512/256 is protected against length extension attacks and is more performant than SHA-256 on 64-bit architectures.
21 | // https://en.wikipedia.org/wiki/Template:Comparison_of_SHA_functions
22 | func SHA512_256(in ...[]byte) []byte {
23 | var data []byte
24 | state := crypto.SHA512_256.New()
25 | inLen := len(in)
26 | if inLen == 0 {
27 | return nil
28 | }
29 | bzSize := 0
30 | // prevent hash collisions with this prefix containing the block count
31 | inLenBz := make([]byte, 64/8)
32 | // converting between int and uint64 doesn't change the sign bit, but it may be interpreted as a larger value.
33 | // this prefix is never read/interpreted, so that doesn't matter.
34 | binary.LittleEndian.PutUint64(inLenBz, uint64(inLen))
35 | for _, bz := range in {
36 | bzSize += len(bz)
37 | }
38 | data = make([]byte, 0, len(inLenBz)+bzSize+inLen)
39 | data = append(data, inLenBz...)
40 | for _, bz := range in {
41 | data = append(data, bz...)
42 | data = append(data, hashInputDelimiter) // safety delimiter
43 | }
44 | // n < len(data) or an error will never happen.
45 | // see: https://golang.org/pkg/hash/#Hash and https://github.com/golang/go/wiki/Hashing#the-hashhash-interface
46 | if _, err := state.Write(data); err != nil {
47 | Logger.Errorf("SHA512_256 Write() failed: %v", err)
48 | return nil
49 | }
50 | return state.Sum(nil)
51 | }
52 |
53 | func SHA512_256i(in ...*big.Int) *big.Int {
54 | var data []byte
55 | state := crypto.SHA512_256.New()
56 | inLen := len(in)
57 | if inLen == 0 {
58 | return nil
59 | }
60 | bzSize := 0
61 | // prevent hash collisions with this prefix containing the block count
62 | inLenBz := make([]byte, 64/8)
63 | // converting between int and uint64 doesn't change the sign bit, but it may be interpreted as a larger value.
64 | // this prefix is never read/interpreted, so that doesn't matter.
65 | binary.LittleEndian.PutUint64(inLenBz, uint64(inLen))
66 | ptrs := make([][]byte, inLen)
67 | for i, n := range in {
68 | ptrs[i] = n.Bytes()
69 | bzSize += len(ptrs[i])
70 | }
71 | data = make([]byte, 0, len(inLenBz)+bzSize+inLen)
72 | data = append(data, inLenBz...)
73 | for i := range in {
74 | data = append(data, ptrs[i]...)
75 | data = append(data, hashInputDelimiter) // safety delimiter
76 | }
77 | // n < len(data) or an error will never happen.
78 | // see: https://golang.org/pkg/hash/#Hash and https://github.com/golang/go/wiki/Hashing#the-hashhash-interface
79 | if _, err := state.Write(data); err != nil {
80 | Logger.Errorf("SHA512_256i Write() failed: %v", err)
81 | return nil
82 | }
83 | return new(big.Int).SetBytes(state.Sum(nil))
84 | }
85 |
86 | func SHA512_256iOne(in *big.Int) *big.Int {
87 | var data []byte
88 | state := crypto.SHA512_256.New()
89 | if in == nil {
90 | return nil
91 | }
92 | data = in.Bytes()
93 | // n < len(data) or an error will never happen.
94 | // see: https://golang.org/pkg/hash/#Hash and https://github.com/golang/go/wiki/Hashing#the-hashhash-interface
95 | if _, err := state.Write(data); err != nil {
96 | Logger.Errorf("SHA512_256iOne Write() failed: %v", err)
97 | return nil
98 | }
99 | return new(big.Int).SetBytes(state.Sum(nil))
100 | }
101 |
--------------------------------------------------------------------------------
/common/hash_utils.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package common
8 |
9 | import (
10 | "math/big"
11 | )
12 |
13 | // RejectionSample implements the rejection sampling logic for converting a
14 | // SHA512/256 hash to a value between 0-q
15 | func RejectionSample(q *big.Int, eHash *big.Int) *big.Int { // e' = eHash
16 | e := eHash.Mod(eHash, q)
17 | return e
18 | }
19 |
--------------------------------------------------------------------------------
/common/hash_utils_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package common_test
8 |
9 | import (
10 | "math/big"
11 | "reflect"
12 | "testing"
13 |
14 | "github.com/binance-chain/tss-lib/common"
15 | )
16 |
17 | func TestRejectionSample(t *testing.T) {
18 | curveQ := common.GetRandomPrimeInt(256)
19 | randomQ := common.MustGetRandomInt(64)
20 | hash := common.SHA512_256iOne(big.NewInt(123))
21 | rs1 := common.RejectionSample(curveQ, hash)
22 | rs2 := common.RejectionSample(randomQ, hash)
23 | rs3 := common.RejectionSample(common.MustGetRandomInt(64), hash)
24 | type args struct {
25 | q *big.Int
26 | eHash *big.Int
27 | }
28 | tests := []struct {
29 | name string
30 | args args
31 | want *big.Int
32 | wantBitLen int
33 | notEqual bool
34 | }{{
35 | name: "happy path with curve order",
36 | args: args{curveQ, hash},
37 | want: rs1,
38 | wantBitLen: 256,
39 | }, {
40 | name: "happy path with random 64-bit int",
41 | args: args{randomQ, hash},
42 | want: rs2,
43 | wantBitLen: 64,
44 | }, {
45 | name: "inequality with different input",
46 | args: args{randomQ, hash},
47 | want: rs3,
48 | wantBitLen: 64,
49 | notEqual: true,
50 | }}
51 | for _, tt := range tests {
52 | t.Run(tt.name, func(t *testing.T) {
53 | got := common.RejectionSample(tt.args.q, tt.args.eHash)
54 | if !tt.notEqual && !reflect.DeepEqual(got, tt.want) {
55 | t.Errorf("RejectionSample() = %v, want %v", got, tt.want)
56 | }
57 | if tt.wantBitLen < got.BitLen() { // leading zeros not counted
58 | t.Errorf("RejectionSample() = bitlen %d, want %d", got.BitLen(), tt.wantBitLen)
59 | }
60 | })
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/common/int.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package common
8 |
9 | import (
10 | "math/big"
11 | )
12 |
13 | // modInt is a *big.Int that performs all of its arithmetic with modular reduction.
14 | type modInt big.Int
15 |
16 | var (
17 | zero = big.NewInt(0)
18 | one = big.NewInt(1)
19 | two = big.NewInt(2)
20 | )
21 |
22 | func ModInt(mod *big.Int) *modInt {
23 | return (*modInt)(mod)
24 | }
25 |
26 | func (mi *modInt) Add(x, y *big.Int) *big.Int {
27 | i := new(big.Int)
28 | i.Add(x, y)
29 | return i.Mod(i, mi.i())
30 | }
31 |
32 | func (mi *modInt) Sub(x, y *big.Int) *big.Int {
33 | i := new(big.Int)
34 | i.Sub(x, y)
35 | return i.Mod(i, mi.i())
36 | }
37 |
38 | func (mi *modInt) Div(x, y *big.Int) *big.Int {
39 | i := new(big.Int)
40 | i.Div(x, y)
41 | return i.Mod(i, mi.i())
42 | }
43 |
44 | func (mi *modInt) Mul(x, y *big.Int) *big.Int {
45 | i := new(big.Int)
46 | i.Mul(x, y)
47 | return i.Mod(i, mi.i())
48 | }
49 |
50 | func (mi *modInt) Exp(x, y *big.Int) *big.Int {
51 | return new(big.Int).Exp(x, y, mi.i())
52 | }
53 |
54 | func (mi *modInt) Neg(x *big.Int) *big.Int {
55 | i := new(big.Int)
56 | i.Neg(x)
57 | return i.Mod(i, mi.i())
58 | }
59 |
60 | func (mi *modInt) Inverse(g *big.Int) *big.Int {
61 | return new(big.Int).ModInverse(g, mi.i())
62 | }
63 |
64 | func (mi *modInt) Sqrt(x *big.Int) *big.Int {
65 | return new(big.Int).ModSqrt(x, mi.i())
66 | }
67 |
68 | func (mi *modInt) i() *big.Int {
69 | return (*big.Int)(mi)
70 | }
71 |
--------------------------------------------------------------------------------
/common/logger.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package common
8 |
9 | import (
10 | "math/big"
11 |
12 | "github.com/ipfs/go-log"
13 | )
14 |
15 | var Logger = log.Logger("tss-lib")
16 |
17 | func FormatBigInt(a *big.Int) string {
18 | if a == nil {
19 | return ""
20 | }
21 | var aux = new(big.Int).SetInt64(0xFFFFFFFF)
22 | return func(i *big.Int) string {
23 | return new(big.Int).And(i, aux).Text(16)
24 | }(a)
25 | }
26 |
--------------------------------------------------------------------------------
/common/pbpoint.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package common
8 |
9 | func (x *ECPoint) ValidateBasic() bool {
10 | return x != nil && NonEmptyBytes(x.GetX()) && NonEmptyBytes(x.GetY())
11 | }
12 |
--------------------------------------------------------------------------------
/common/random.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package common
8 |
9 | import (
10 | "crypto/rand"
11 | "fmt"
12 | "math/big"
13 |
14 | "github.com/pkg/errors"
15 | )
16 |
17 | const (
18 | mustGetRandomIntMaxBits = 5000
19 | )
20 |
21 | // MustGetRandomInt panics if it is unable to gather entropy from `rand.Reader` or when `bits` is <= 0
22 | func MustGetRandomInt(bits int) *big.Int {
23 | if bits <= 0 || mustGetRandomIntMaxBits < bits {
24 | panic(fmt.Errorf("MustGetRandomInt: bits should be positive, non-zero and less than %d", mustGetRandomIntMaxBits))
25 | }
26 | // Max random value e.g. 2^256 - 1
27 | max := new(big.Int)
28 | max = max.Exp(two, big.NewInt(int64(bits)), nil).Sub(max, one)
29 |
30 | // Generate cryptographically strong pseudo-random int between 0 - max
31 | n, err := rand.Int(rand.Reader, max)
32 | if err != nil {
33 | panic(errors.Wrap(err, "rand.Int failure in MustGetRandomInt!"))
34 | }
35 | return n
36 | }
37 |
38 | func GetRandomPositiveInt(upper *big.Int) *big.Int {
39 | if upper == nil || zero.Cmp(upper) != -1 {
40 | return nil
41 | }
42 | var try *big.Int
43 | for {
44 | try = MustGetRandomInt(upper.BitLen())
45 | if try.Cmp(upper) < 0 && try.Cmp(zero) >= 0 {
46 | break
47 | }
48 | }
49 | return try
50 | }
51 |
52 | func GetRandomPrimeInt(bits int) *big.Int {
53 | if bits <= 0 {
54 | return nil
55 | }
56 | try, err := rand.Prime(rand.Reader, bits)
57 | if err != nil ||
58 | try.Cmp(zero) == 0 {
59 | // fallback to older method
60 | for {
61 | try = MustGetRandomInt(bits)
62 | if probablyPrime(try) {
63 | break
64 | }
65 | }
66 | }
67 | return try
68 | }
69 |
70 | // Generate a random element in the group of all the elements in Z/nZ that
71 | // has a multiplicative inverse.
72 | func GetRandomPositiveRelativelyPrimeInt(n *big.Int) *big.Int {
73 | if n == nil || zero.Cmp(n) != -1 {
74 | return nil
75 | }
76 | var try *big.Int
77 | for {
78 | try = MustGetRandomInt(n.BitLen())
79 | if IsNumberInMultiplicativeGroup(n, try) {
80 | break
81 | }
82 | }
83 | return try
84 | }
85 |
86 | func IsNumberInMultiplicativeGroup(n, v *big.Int) bool {
87 | if n == nil || v == nil || zero.Cmp(n) != -1 {
88 | return false
89 | }
90 | gcd := big.NewInt(0)
91 | return v.Cmp(n) < 0 && v.Cmp(one) >= 0 &&
92 | gcd.GCD(nil, nil, v, n).Cmp(one) == 0
93 | }
94 |
95 | // Return a random generator of RQn with high probability.
96 | // THIS METHOD ONLY WORKS IF N IS THE PRODUCT OF TWO SAFE PRIMES!
97 | // https://github.com/didiercrunch/paillier/blob/d03e8850a8e4c53d04e8016a2ce8762af3278b71/utils.go#L39
98 | func GetRandomGeneratorOfTheQuadraticResidue(n *big.Int) *big.Int {
99 | f := GetRandomPositiveRelativelyPrimeInt(n)
100 | fSq := new(big.Int).Mul(f, f)
101 | return fSq.Mod(fSq, n)
102 | }
103 |
104 | func GetRandomQuandraticNonResidue(n *big.Int) *big.Int {
105 | for {
106 | w := GetRandomPositiveInt(n)
107 | if big.Jacobi(w, n) == -1 {
108 | return w
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/common/random_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package common_test
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 |
13 | "github.com/stretchr/testify/assert"
14 |
15 | "github.com/binance-chain/tss-lib/common"
16 | )
17 |
18 | const (
19 | randomIntBitLen = 1024
20 | )
21 |
22 | func TestGetRandomInt(t *testing.T) {
23 | rnd := common.MustGetRandomInt(randomIntBitLen)
24 | assert.NotZero(t, rnd, "rand int should not be zero")
25 | }
26 |
27 | func TestGetRandomPositiveInt(t *testing.T) {
28 | rnd := common.MustGetRandomInt(randomIntBitLen)
29 | rndPos := common.GetRandomPositiveInt(rnd)
30 | assert.NotZero(t, rndPos, "rand int should not be zero")
31 | assert.True(t, rndPos.Cmp(big.NewInt(0)) == 1, "rand int should be positive")
32 | }
33 |
34 | func TestGetRandomPositiveRelativelyPrimeInt(t *testing.T) {
35 | rnd := common.MustGetRandomInt(randomIntBitLen)
36 | rndPosRP := common.GetRandomPositiveRelativelyPrimeInt(rnd)
37 | assert.NotZero(t, rndPosRP, "rand int should not be zero")
38 | assert.True(t, common.IsNumberInMultiplicativeGroup(rnd, rndPosRP))
39 | assert.True(t, rndPosRP.Cmp(big.NewInt(0)) == 1, "rand int should be positive")
40 | // TODO test for relative primeness
41 | }
42 |
43 | func TestGetRandomPrimeInt(t *testing.T) {
44 | prime := common.GetRandomPrimeInt(randomIntBitLen)
45 | assert.NotZero(t, prime, "rand prime should not be zero")
46 | assert.True(t, prime.ProbablyPrime(50), "rand prime should be prime")
47 | }
48 |
49 | func TestGetRandomQuadraticNonResidue(t *testing.T) {
50 | rnd := common.MustGetRandomInt(randomIntBitLen)
51 | N := common.GetRandomPositiveRelativelyPrimeInt(rnd)
52 | // ensure N is odd
53 | for N.Bit(0) == 0 {
54 | N = common.GetRandomPositiveRelativelyPrimeInt(rnd)
55 | }
56 | w := common.GetRandomQuandraticNonResidue(N)
57 | assert.Equal(t, big.Jacobi(w, N), -1, "must get quandratic non residue")
58 | }
59 |
--------------------------------------------------------------------------------
/common/safe_prime_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package common
8 |
9 | import (
10 | "math/big"
11 | "runtime"
12 | "testing"
13 | "time"
14 |
15 | "github.com/stretchr/testify/assert"
16 | )
17 |
18 | func Test_getSafePrime(t *testing.T) {
19 | prime := new(big.Int).SetInt64(5)
20 | sPrime := PrimeToSafePrime(prime)
21 | assert.True(t, sPrime.ProbablyPrime(50))
22 | }
23 |
24 | func Test_getSafePrime_Bad(t *testing.T) {
25 | prime := new(big.Int).SetInt64(12)
26 | sPrime := PrimeToSafePrime(prime)
27 | assert.False(t, sPrime.ProbablyPrime(50))
28 | }
29 |
30 | func Test_Validate(t *testing.T) {
31 | prime := new(big.Int).SetInt64(5)
32 | sPrime := PrimeToSafePrime(prime)
33 | sgp := &GermainSafePrime{prime, sPrime}
34 | assert.True(t, sgp.Validate())
35 | }
36 |
37 | func Test_Validate_Bad(t *testing.T) {
38 | prime := new(big.Int).SetInt64(12)
39 | sPrime := PrimeToSafePrime(prime)
40 | sgp := &GermainSafePrime{prime, sPrime}
41 | assert.False(t, sgp.Validate())
42 | }
43 |
44 | func TestGetRandomGermainPrimeConcurrent(t *testing.T) {
45 | sgps, err := GetRandomSafePrimesConcurrent(1024, 2, 20*time.Minute, runtime.NumCPU())
46 | assert.NoError(t, err)
47 | assert.Equal(t, 2, len(sgps))
48 | for _, sgp := range sgps {
49 | assert.NotNil(t, sgp)
50 | assert.True(t, sgp.Validate())
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/common/slice.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package common
8 |
9 | import (
10 | "math/big"
11 | )
12 |
13 | func BigIntsToBytes(bigInts []*big.Int) [][]byte {
14 | bzs := make([][]byte, len(bigInts))
15 | for i := range bzs {
16 | if bigInts[i] == nil {
17 | continue
18 | }
19 | bzs[i] = bigInts[i].Bytes()
20 | }
21 | return bzs
22 | }
23 |
24 | func ByteSlicesToBigInts(bytes [][]byte) []*big.Int {
25 | ints := make([]*big.Int, len(bytes))
26 | for i := range ints {
27 | ints[i] = new(big.Int).SetBytes(bytes[i])
28 | }
29 | return ints
30 | }
31 |
32 | // Returns true when the byte slice is non-nil and non-empty
33 | func NonEmptyBytes(bz []byte, minByteLen ...int) bool {
34 | if 0 == len(bz) {
35 | return false
36 | }
37 | allZero := true
38 | for _, b := range bz {
39 | if b != 0 {
40 | allZero = false
41 | break
42 | }
43 | }
44 | return !allZero &&
45 | (len(minByteLen) == 0 || minByteLen[0] <= len(bz))
46 | }
47 |
48 | // Returns true when all of the slices in the multi-dimensional byte slice are non-nil and non-empty
49 | func NonEmptyMultiBytes(bzs [][]byte, expectLen ...int) bool {
50 | if len(bzs) == 0 {
51 | return false
52 | }
53 | // variadic (optional) arg test
54 | if 0 < len(expectLen) && expectLen[0] != len(bzs) {
55 | return false
56 | }
57 | for _, bz := range bzs {
58 | if !NonEmptyBytes(bz) {
59 | return false
60 | }
61 | }
62 | return true
63 | }
64 |
65 | // Returns true when at least one of the slices in the multi-dimensional byte slice are non-nil and non-empty
66 | func AnyNonEmptyMultiByte(bzs [][]byte, expectLen ...int) bool {
67 | if len(bzs) == 0 {
68 | return false
69 | }
70 | // variadic (optional) arg test
71 | if 0 < len(expectLen) && expectLen[0] != len(bzs) {
72 | return false
73 | }
74 | for _, bz := range bzs {
75 | if NonEmptyBytes(bz) {
76 | return true
77 | }
78 | }
79 | return false
80 | }
81 |
--------------------------------------------------------------------------------
/crypto/commitments/builder.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package commitments
8 |
9 | import (
10 | "errors"
11 | "fmt"
12 | "math/big"
13 | )
14 |
15 | const (
16 | PartsCap = 3
17 | MaxPartSize = int64(1 * 1024 * 128) // ~128 KB
18 | )
19 |
20 | type builder struct {
21 | parts [][]*big.Int
22 | }
23 |
24 | func NewBuilder() *builder {
25 | b := new(builder)
26 | b.parts = make([][]*big.Int, 0, PartsCap)
27 | return b
28 | }
29 |
30 | func (b *builder) Parts() [][]*big.Int {
31 | return b.parts[:]
32 | }
33 |
34 | func (b *builder) AddPart(part ...*big.Int) *builder {
35 | b.parts = append(b.parts, part[:])
36 | return b
37 | }
38 |
39 | func (b *builder) Secrets() ([]*big.Int, error) {
40 | secretsLen := 0
41 | if len(b.parts) > PartsCap {
42 | return nil, fmt.Errorf("builder.Secrets: too many commitment parts provided: got %d, max %d", len(b.parts), PartsCap)
43 | }
44 | for _, p := range b.parts {
45 | secretsLen += 1 + len(p) // +1 to accommodate length prefix element
46 | }
47 | secrets := make([]*big.Int, 0, secretsLen)
48 | for i, p := range b.parts {
49 | partLen := int64(len(p))
50 | if MaxPartSize < partLen {
51 | return nil, fmt.Errorf("builder.Secrets: commitment part too large: part %d, size %d", i, partLen)
52 | }
53 | secrets = append(secrets, big.NewInt(partLen))
54 | secrets = append(secrets, p...)
55 | }
56 | return secrets, nil
57 | }
58 |
59 | func ParseSecrets(secrets []*big.Int) ([][]*big.Int, error) {
60 | if secrets == nil || len(secrets) < 2 {
61 | return nil, errors.New("ParseSecrets: secrets == nil or is too small")
62 | }
63 | var el, nextPartLen int64
64 | parts := make([][]*big.Int, 0, PartsCap)
65 | isLenEl := true // are we looking at a length prefix element? (first one is)
66 | inLen := int64(len(secrets))
67 | for el < inLen {
68 | if el < 0 {
69 | return nil, errors.New("ParseSecrets: `el` overflow")
70 | }
71 | if isLenEl {
72 | nextPartLen = secrets[el].Int64()
73 | if nextPartLen <= 0 {
74 | return nil, fmt.Errorf("ParseSecrets: commitment part len is 0 or negative: part %d, len %d", len(parts), nextPartLen)
75 | }
76 | if MaxPartSize < nextPartLen {
77 | return nil, fmt.Errorf("ParseSecrets: commitment part too large: part %d, size %d", len(parts), nextPartLen)
78 | }
79 | el += 1
80 | } else {
81 | if PartsCap <= len(parts) {
82 | return nil, fmt.Errorf("ParseSecrets: commitment has too many parts: part %d, max %d", len(parts), PartsCap)
83 | }
84 | if inLen < el+nextPartLen {
85 | return nil, errors.New("ParseSecrets: not enough data to consume stated data length")
86 | }
87 | part := secrets[el : el+nextPartLen]
88 | parts = append(parts, part)
89 | el += nextPartLen
90 | }
91 | isLenEl = !isLenEl
92 | }
93 | return parts, nil
94 | }
95 |
--------------------------------------------------------------------------------
/crypto/commitments/builder_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package commitments
8 |
9 | import (
10 | "math/big"
11 | "reflect"
12 | "testing"
13 | )
14 |
15 | var (
16 | one = big.NewInt(1)
17 | two = big.NewInt(2)
18 | three = big.NewInt(3)
19 | )
20 |
21 | func Test_builder_Secrets(t *testing.T) {
22 | type fields struct {
23 | parts [][]*big.Int
24 | }
25 | tests := []struct {
26 | name string
27 | fields fields
28 | want []*big.Int
29 | wantErr bool
30 | }{{
31 | name: "Happy path: Single part",
32 | fields: fields{[][]*big.Int{
33 | {one},
34 | }},
35 | want: []*big.Int{
36 | one, one,
37 | },
38 | }, {
39 | name: "Happy path: Multiple parts",
40 | fields: fields{[][]*big.Int{
41 | {one},
42 | {one, two},
43 | {one, two, three},
44 | }},
45 | want: []*big.Int{
46 | one, one,
47 | two, one, two,
48 | three, one, two, three,
49 | },
50 | }, {
51 | name: "Errors: Too many parts - max is 3",
52 | fields: fields{[][]*big.Int{
53 | {one},
54 | {one},
55 | {one, two},
56 | {one, two, three},
57 | }},
58 | wantErr: true,
59 | }}
60 | for _, tt := range tests {
61 | t.Run(tt.name, func(t *testing.T) {
62 | b := &builder{
63 | parts: tt.fields.parts,
64 | }
65 | got, err := b.Secrets()
66 | if (err != nil) != tt.wantErr {
67 | t.Errorf("builder.Secrets() error = %v, wantErr %v", err, tt.wantErr)
68 | return
69 | }
70 | if !reflect.DeepEqual(got, tt.want) {
71 | t.Errorf("builder.Secrets() = %v, want %v", got, tt.want)
72 | }
73 | })
74 | }
75 | }
76 |
77 | func TestParseSecrets(t *testing.T) {
78 | type args struct {
79 | secrets []*big.Int
80 | }
81 | tests := []struct {
82 | name string
83 | args args
84 | want [][]*big.Int
85 | wantErr bool
86 | }{{
87 | name: "Happy path: Single part",
88 | args: args{
89 | []*big.Int{
90 | one, one,
91 | },
92 | },
93 | want: [][]*big.Int{
94 | {one},
95 | },
96 | }, {
97 | name: "Happy path: Multiple parts",
98 | args: args{
99 | []*big.Int{
100 | // one element: one
101 | one, one,
102 | // two elements: one, two
103 | two, one, two,
104 | // three elements: one, two three
105 | three, one, two, three,
106 | },
107 | },
108 | want: [][]*big.Int{
109 | {one},
110 | {one, two},
111 | {one, two, three},
112 | },
113 | }, {
114 | name: "Errors: Invalid input - too short",
115 | args: args{
116 | []*big.Int{
117 | one, // just the length prefix, no content!
118 | },
119 | },
120 | wantErr: true,
121 | }, {
122 | name: "Errors: Invalid input - insufficient data",
123 | args: args{
124 | []*big.Int{
125 | one, one,
126 | two, one, // one element is missing
127 | },
128 | },
129 | wantErr: true,
130 | }, {
131 | name: "Errors: Too many parts - max is 3",
132 | args: args{
133 | []*big.Int{
134 | one, one,
135 | one, one,
136 | one, one,
137 | one, one,
138 | },
139 | },
140 | wantErr: true,
141 | }}
142 | for _, tt := range tests {
143 | t.Run(tt.name, func(t *testing.T) {
144 | got, err := ParseSecrets(tt.args.secrets)
145 | if (err != nil) != tt.wantErr {
146 | t.Errorf("ParseSecrets() error = %v, wantErr %v", err, tt.wantErr)
147 | return
148 | }
149 | if !reflect.DeepEqual(got, tt.want) {
150 | t.Errorf("ParseSecrets() = %v, want %v", got, tt.want)
151 | }
152 | })
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/crypto/commitments/hashed.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | // partly ported from:
8 | // https://github.com/KZen-networks/curv/blob/78a70f43f5eda376e5888ce33aec18962f572bbe/src/cryptographic_primitives/commitments/hash_commitment.rs
9 |
10 | package commitments
11 |
12 | import (
13 | "math/big"
14 |
15 | "github.com/binance-chain/tss-lib/common"
16 | )
17 |
18 | const (
19 | HashLength = 256
20 | )
21 |
22 | type (
23 | HashCommitment = *big.Int
24 | HashDeCommitment = []*big.Int
25 |
26 | HashCommitDecommit struct {
27 | C HashCommitment
28 | D HashDeCommitment
29 | }
30 | )
31 |
32 | func NewHashCommitmentWithRandomness(r *big.Int, secrets ...*big.Int) *HashCommitDecommit {
33 | parts := make([]*big.Int, len(secrets)+1)
34 | parts[0] = r
35 | for i := 1; i < len(parts); i++ {
36 | parts[i] = secrets[i-1]
37 | }
38 | hash := common.SHA512_256i(parts...)
39 |
40 | cmt := &HashCommitDecommit{}
41 | cmt.C = hash
42 | cmt.D = parts
43 | return cmt
44 | }
45 |
46 | func NewHashCommitment(secrets ...*big.Int) *HashCommitDecommit {
47 | r := common.MustGetRandomInt(HashLength) // r
48 | return NewHashCommitmentWithRandomness(r, secrets...)
49 | }
50 |
51 | func NewHashDeCommitmentFromBytes(marshalled [][]byte) HashDeCommitment {
52 | return common.ByteSlicesToBigInts(marshalled)
53 | }
54 |
55 | func (cmt *HashCommitDecommit) Verify() bool {
56 | C, D := cmt.C, cmt.D
57 | if C == nil || D == nil {
58 | return false
59 | }
60 | hash := common.SHA512_256i(D...)
61 | return hash.Cmp(C) == 0
62 | }
63 |
64 | func (cmt *HashCommitDecommit) DeCommit() (bool, HashDeCommitment) {
65 | if cmt.Verify() {
66 | // [1:] skips random element r in D
67 | return true, cmt.D[1:]
68 | } else {
69 | return false, nil
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/crypto/commitments/hashed_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package commitments_test
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 |
13 | "github.com/stretchr/testify/assert"
14 |
15 | . "github.com/binance-chain/tss-lib/crypto/commitments"
16 | )
17 |
18 | func TestCreateVerify(t *testing.T) {
19 | one := big.NewInt(1)
20 | zero := big.NewInt(0)
21 |
22 | commitment := NewHashCommitment(zero, one)
23 | pass := commitment.Verify()
24 |
25 | assert.True(t, pass, "must pass")
26 | }
27 |
28 | func TestDeCommit(t *testing.T) {
29 | one := big.NewInt(1)
30 | zero := big.NewInt(0)
31 |
32 | commitment := NewHashCommitment(zero, one)
33 | pass, secrets := commitment.DeCommit()
34 |
35 | assert.True(t, pass, "must pass")
36 |
37 | assert.NotZero(t, len(secrets), "len(secrets) must be non-zero")
38 | }
39 |
--------------------------------------------------------------------------------
/crypto/dlnp/proof.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | // Zero-knowledge proof of knowledge of the discrete logarithm over safe prime product
8 |
9 | // A proof of knowledge of the discrete log of an element h2 = hx1 with respect to h1.
10 | // In our protocol, we will run two of these in parallel to prove that two elements h1,h2 generate the same group modN.
11 |
12 | package dlnp
13 |
14 | import (
15 | "fmt"
16 | "math/big"
17 |
18 | "github.com/binance-chain/tss-lib/common"
19 | cmts "github.com/binance-chain/tss-lib/crypto/commitments"
20 | )
21 |
22 | const Iterations = 128
23 |
24 | type (
25 | Proof struct {
26 | Alpha,
27 | T [Iterations]*big.Int
28 | }
29 | )
30 |
31 | func NewProof(h1, h2, x, p, q, N *big.Int) *Proof {
32 | pMulQ := new(big.Int).Mul(p, q)
33 | modN, modPQ := common.ModInt(N), common.ModInt(pMulQ)
34 | a := make([]*big.Int, Iterations)
35 | alpha := [Iterations]*big.Int{}
36 | for i := range alpha {
37 | a[i] = common.GetRandomPositiveInt(pMulQ)
38 | alpha[i] = modN.Exp(h1, a[i])
39 | }
40 | msg := append([]*big.Int{h1, h2, N}, alpha[:]...)
41 | c := common.SHA512_256i(msg...)
42 | t := [Iterations]*big.Int{}
43 | cIBI := new(big.Int)
44 | for i := range t {
45 | cI := c.Bit(i)
46 | cIBI = cIBI.SetInt64(int64(cI))
47 | t[i] = modPQ.Add(a[i], modPQ.Mul(cIBI, x))
48 | }
49 | return &Proof{alpha, t}
50 | }
51 |
52 | func (p *Proof) Verify(h1, h2, N *big.Int) bool {
53 | if p == nil {
54 | return false
55 | }
56 | modN := common.ModInt(N)
57 | msg := append([]*big.Int{h1, h2, N}, p.Alpha[:]...)
58 | c := common.SHA512_256i(msg...)
59 | cIBI := new(big.Int)
60 | for i := 0; i < Iterations; i++ {
61 | if p.Alpha[i] == nil || p.T[i] == nil {
62 | return false
63 | }
64 | cI := c.Bit(i)
65 | cIBI = cIBI.SetInt64(int64(cI))
66 | h1ExpTi := modN.Exp(h1, p.T[i])
67 | h2ExpCi := modN.Exp(h2, cIBI)
68 | alphaIMulH2ExpCi := modN.Mul(p.Alpha[i], h2ExpCi)
69 | if h1ExpTi.Cmp(alphaIMulH2ExpCi) != 0 {
70 | return false
71 | }
72 | }
73 | return true
74 | }
75 |
76 | func (p *Proof) Marshal() ([][]byte, error) {
77 | cb := cmts.NewBuilder()
78 | cb = cb.AddPart(p.Alpha[:]...)
79 | cb = cb.AddPart(p.T[:]...)
80 | ints, err := cb.Secrets()
81 | if err != nil {
82 | return nil, err
83 | }
84 | bzs := make([][]byte, len(ints))
85 | for i, part := range ints {
86 | if part == nil {
87 | bzs[i] = []byte{}
88 | continue
89 | }
90 | bzs[i] = part.Bytes()
91 | }
92 | return bzs, nil
93 | }
94 |
95 | func UnmarshalProof(bzs [][]byte) (*Proof, error) {
96 | bis := make([]*big.Int, len(bzs))
97 | for i := range bis {
98 | bis[i] = new(big.Int).SetBytes(bzs[i])
99 | }
100 | parsed, err := cmts.ParseSecrets(bis)
101 | if err != nil {
102 | return nil, err
103 | }
104 | expParts := 2
105 | if len(parsed) != expParts {
106 | return nil, fmt.Errorf("dlnp.UnmarshalProof expected %d parts but got %d", expParts, len(parsed))
107 | }
108 | pf := new(Proof)
109 | if len1 := copy(pf.Alpha[:], parsed[0]); len1 != Iterations {
110 | return nil, fmt.Errorf("dlnp.UnmarshalProof expected %d but copied %d", Iterations, len1)
111 | }
112 | if len2 := copy(pf.T[:], parsed[1]); len2 != Iterations {
113 | return nil, fmt.Errorf("dlnp.UnmarshalProof expected %d but copied %d", Iterations, len2)
114 | }
115 | return pf, nil
116 | }
117 |
--------------------------------------------------------------------------------
/crypto/mta/share_protocol.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package mta
8 |
9 | import (
10 | "errors"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/common"
14 | "github.com/binance-chain/tss-lib/crypto"
15 | "github.com/binance-chain/tss-lib/crypto/paillier"
16 | "github.com/binance-chain/tss-lib/tss"
17 | )
18 |
19 | func AliceInit(
20 | pkA *paillier.PublicKey,
21 | a, cA, rA, NTildeB, h1B, h2B *big.Int,
22 | ) (pf *RangeProofAlice, err error) {
23 | return ProveRangeAlice(pkA, cA, NTildeB, h1B, h2B, a, rA)
24 | }
25 |
26 | func BobMid(
27 | pkA *paillier.PublicKey,
28 | pf *RangeProofAlice,
29 | b, cA, NTildeA, h1A, h2A, NTildeB, h1B, h2B *big.Int,
30 | ) (beta, cB, betaPrm *big.Int, piB *ProofBob, err error) {
31 | if !pf.Verify(pkA, NTildeB, h1B, h2B, cA) {
32 | err = errors.New("RangeProofAlice.Verify() returned false")
33 | return
34 | }
35 | q := tss.EC().Params().N
36 | betaPrm = common.GetRandomPositiveInt(pkA.N)
37 | cBetaPrm, cRand, err := pkA.EncryptAndReturnRandomness(betaPrm)
38 | if err != nil {
39 | return
40 | }
41 | if cB, err = pkA.HomoMult(b, cA); err != nil {
42 | return
43 | }
44 | if cB, err = pkA.HomoAdd(cB, cBetaPrm); err != nil {
45 | return
46 | }
47 | beta = common.ModInt(q).Sub(zero, betaPrm)
48 | piB, err = ProveBob(pkA, NTildeA, h1A, h2A, cA, cB, b, betaPrm, cRand)
49 | return
50 | }
51 |
52 | func BobMidWC(
53 | pkA *paillier.PublicKey,
54 | pf *RangeProofAlice,
55 | b, cA, NTildeA, h1A, h2A, NTildeB, h1B, h2B *big.Int,
56 | B *crypto.ECPoint,
57 | ) (betaPrm, cB *big.Int, piB *ProofBobWC, err error) {
58 | if !pf.Verify(pkA, NTildeB, h1B, h2B, cA) {
59 | err = errors.New("RangeProofAlice.Verify() returned false")
60 | return
61 | }
62 | betaPrm = common.GetRandomPositiveInt(pkA.N)
63 | cBetaPrm, cRand, err := pkA.EncryptAndReturnRandomness(betaPrm)
64 | if err != nil {
65 | return
66 | }
67 | cB, err = pkA.HomoMult(b, cA)
68 | if err != nil {
69 | return
70 | }
71 | cB, err = pkA.HomoAdd(cB, cBetaPrm)
72 | if err != nil {
73 | return
74 | }
75 | piB, err = ProveBobWC(pkA, NTildeA, h1A, h2A, cA, cB, b, betaPrm, cRand, B)
76 | return
77 | }
78 |
79 | func AliceEnd(
80 | pkA *paillier.PublicKey,
81 | pf *ProofBob,
82 | h1A, h2A, cA, cB, NTildeA *big.Int,
83 | sk *paillier.PrivateKey,
84 | ) (alphaIJ *big.Int, err error) {
85 | if !pf.Verify(pkA, NTildeA, h1A, h2A, cA, cB) {
86 | err = errors.New("ProofBob.Verify() returned false")
87 | return
88 | }
89 | if alphaIJ, err = sk.Decrypt(cB); err != nil {
90 | return
91 | }
92 | q := tss.EC().Params().N
93 | alphaIJ.Mod(alphaIJ, q)
94 | return
95 | }
96 |
97 | func AliceEndWC(
98 | pkA *paillier.PublicKey,
99 | pf *ProofBobWC,
100 | B *crypto.ECPoint,
101 | cA, cB, NTildeA, h1A, h2A *big.Int,
102 | sk *paillier.PrivateKey,
103 | ) (muIJ, muIJRec, muIJRand *big.Int, err error) {
104 | if !pf.Verify(pkA, NTildeA, h1A, h2A, cA, cB, B) {
105 | err = errors.New("ProofBobWC.Verify() returned false")
106 | return
107 | }
108 | if muIJRec, muIJRand, err = sk.DecryptAndRecoverRandomness(cB); err != nil {
109 | return
110 | }
111 | q := tss.EC().Params().N
112 | muIJ = new(big.Int).Mod(muIJRec, q)
113 | return
114 | }
115 |
--------------------------------------------------------------------------------
/crypto/mta/share_protocol_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package mta
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 | "time"
13 |
14 | "github.com/stretchr/testify/assert"
15 |
16 | "github.com/binance-chain/tss-lib/common"
17 | "github.com/binance-chain/tss-lib/crypto"
18 | "github.com/binance-chain/tss-lib/crypto/paillier"
19 | "github.com/binance-chain/tss-lib/ecdsa/keygen"
20 | "github.com/binance-chain/tss-lib/tss"
21 | )
22 |
23 | // Using a modulus length of 2048 is recommended in the GG18 spec
24 | const (
25 | testPaillierKeyLength = 2048
26 | )
27 |
28 | func TestShareProtocol(t *testing.T) {
29 | q := tss.EC().Params().N
30 |
31 | sk, pk, err := paillier.GenerateKeyPair(testPaillierKeyLength, 10*time.Minute)
32 | assert.NoError(t, err)
33 |
34 | a := common.GetRandomPositiveInt(q)
35 | b := common.GetRandomPositiveInt(q)
36 |
37 | NTildei, h1i, h2i, err := keygen.LoadNTildeH1H2FromTestFixture(0)
38 | assert.NoError(t, err)
39 | NTildej, h1j, h2j, err := keygen.LoadNTildeH1H2FromTestFixture(1)
40 | assert.NoError(t, err)
41 |
42 | cA, rA, err := pk.EncryptAndReturnRandomness(a)
43 | assert.NoError(t, err)
44 | pf, err := AliceInit(pk, a, cA, rA, NTildej, h1j, h2j)
45 | assert.NoError(t, err)
46 |
47 | _, cB, betaPrm, pfB, err := BobMid(pk, pf, b, cA, NTildei, h1i, h2i, NTildej, h1j, h2j)
48 | assert.NoError(t, err)
49 |
50 | alpha, err := AliceEnd(pk, pfB, h1i, h2i, cA, cB, NTildei, sk)
51 | assert.NoError(t, err)
52 |
53 | // expect: alpha = ab + betaPrm
54 | aTimesB := new(big.Int).Mul(a, b)
55 | aTimesBPlusBeta := new(big.Int).Add(aTimesB, betaPrm)
56 | aTimesBPlusBetaModQ := new(big.Int).Mod(aTimesBPlusBeta, q)
57 | assert.Equal(t, 0, alpha.Cmp(aTimesBPlusBetaModQ))
58 | }
59 |
60 | func TestShareProtocolWC(t *testing.T) {
61 | q := tss.EC().Params().N
62 |
63 | sk, pk, err := paillier.GenerateKeyPair(testPaillierKeyLength, 10*time.Minute)
64 | assert.NoError(t, err)
65 |
66 | a := common.GetRandomPositiveInt(q)
67 | b := common.GetRandomPositiveInt(q)
68 | gBX, gBY := tss.EC().ScalarBaseMult(b.Bytes())
69 |
70 | NTildei, h1i, h2i, err := keygen.LoadNTildeH1H2FromTestFixture(0)
71 | assert.NoError(t, err)
72 | NTildej, h1j, h2j, err := keygen.LoadNTildeH1H2FromTestFixture(1)
73 | assert.NoError(t, err)
74 |
75 | cA, rA, err := pk.EncryptAndReturnRandomness(a)
76 | assert.NoError(t, err)
77 | pf, err := AliceInit(pk, a, cA, rA, NTildej, h1j, h2j)
78 | assert.NoError(t, err)
79 |
80 | gBPoint, err := crypto.NewECPoint(tss.EC(), gBX, gBY)
81 | assert.NoError(t, err)
82 | betaPrm, cB, pfB, err := BobMidWC(pk, pf, b, cA, NTildei, h1i, h2i, NTildej, h1j, h2j, gBPoint)
83 | assert.NoError(t, err)
84 |
85 | muIJ, _, muRandIJ, err := AliceEndWC(pk, pfB, gBPoint, cA, cB, NTildei, h1i, h2i, sk)
86 | assert.NoError(t, err)
87 | assert.NotNil(t, muRandIJ)
88 |
89 | // expect: muIJ = ab + betaPrm
90 | aTimesB := new(big.Int).Mul(a, b)
91 | aTimesBPlusBeta := new(big.Int).Add(aTimesB, betaPrm)
92 | aTimesBPlusBetaModQ := new(big.Int).Mod(aTimesBPlusBeta, q)
93 | assert.Equal(t, 0, muIJ.Cmp(aTimesBPlusBetaModQ))
94 | }
95 |
--------------------------------------------------------------------------------
/crypto/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package crypto
8 |
9 | import (
10 | "fmt"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/common"
14 | )
15 |
16 | func GenerateNTildei(safePrimes [2]*big.Int) (NTildei, h1i, h2i *big.Int, err error) {
17 | if safePrimes[0] == nil || safePrimes[1] == nil {
18 | return nil, nil, nil, fmt.Errorf("GenerateNTildei: needs two primes, got %v", safePrimes)
19 | }
20 | if !safePrimes[0].ProbablyPrime(30) || !safePrimes[1].ProbablyPrime(30) {
21 | return nil, nil, nil, fmt.Errorf("GenerateNTildei: expected two primes")
22 | }
23 | NTildei = new(big.Int).Mul(safePrimes[0], safePrimes[1])
24 | h1 := common.GetRandomGeneratorOfTheQuadraticResidue(NTildei)
25 | h2 := common.GetRandomGeneratorOfTheQuadraticResidue(NTildei)
26 | return NTildei, h1, h2, nil
27 | }
28 |
29 | func FormatECPoint(p *ECPoint) string {
30 | x := common.FormatBigInt(p.X())
31 | y := common.FormatBigInt(p.Y())
32 | return "(" + x + "," + y + ")"
33 | }
34 |
--------------------------------------------------------------------------------
/crypto/vss/feldman_vss_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package vss_test
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 |
13 | "github.com/stretchr/testify/assert"
14 |
15 | "github.com/binance-chain/tss-lib/common"
16 | . "github.com/binance-chain/tss-lib/crypto/vss"
17 | "github.com/binance-chain/tss-lib/tss"
18 | )
19 |
20 | func TestCheckIndexesDup(t *testing.T) {
21 | indexes := make([]*big.Int, 0)
22 | for i := 0; i < 1000; i++ {
23 | indexes = append(indexes, common.GetRandomPositiveInt(tss.EC().Params().N))
24 | }
25 | _, e := CheckIndexes(tss.EC(), indexes)
26 | assert.NoError(t, e)
27 |
28 | indexes = append(indexes, indexes[99])
29 | _, e = CheckIndexes(tss.EC(), indexes)
30 | assert.Error(t, e)
31 | }
32 |
33 | func TestCheckIndexesZero(t *testing.T) {
34 | indexes := make([]*big.Int, 0)
35 | for i := 0; i < 1000; i++ {
36 | indexes = append(indexes, common.GetRandomPositiveInt(tss.EC().Params().N))
37 | }
38 | _, e := CheckIndexes(tss.EC(), indexes)
39 | assert.NoError(t, e)
40 |
41 | indexes = append(indexes, tss.EC().Params().N)
42 | _, e = CheckIndexes(tss.EC(), indexes)
43 | assert.Error(t, e)
44 | }
45 |
46 | func TestCreate(t *testing.T) {
47 | num, threshold := 5, 3
48 |
49 | secret := common.GetRandomPositiveInt(tss.EC().Params().N)
50 |
51 | ids := make([]*big.Int, 0)
52 | for i := 0; i < num; i++ {
53 | ids = append(ids, common.GetRandomPositiveInt(tss.EC().Params().N))
54 | }
55 |
56 | vs, _, err := Create(tss.EC(), threshold, secret, ids)
57 | assert.Nil(t, err)
58 |
59 | assert.Equal(t, threshold+1, len(vs))
60 | // assert.Equal(t, num, params.NumShares)
61 |
62 | assert.Equal(t, threshold+1, len(vs))
63 |
64 | // ensure that each vs has two points on the curve
65 | for i, pg := range vs {
66 | assert.NotZero(t, pg.X())
67 | assert.NotZero(t, pg.Y())
68 | assert.True(t, pg.IsOnCurve())
69 | assert.NotZero(t, vs[i].X())
70 | assert.NotZero(t, vs[i].Y())
71 | }
72 | }
73 |
74 | func TestVerify(t *testing.T) {
75 | num, threshold := 5, 3
76 |
77 | secret := common.GetRandomPositiveInt(tss.EC().Params().N)
78 |
79 | ids := make([]*big.Int, 0)
80 | for i := 0; i < num; i++ {
81 | ids = append(ids, common.GetRandomPositiveInt(tss.EC().Params().N))
82 | }
83 |
84 | vs, shares, err := Create(tss.EC(), threshold, secret, ids)
85 | assert.NoError(t, err)
86 |
87 | for i := 0; i < num; i++ {
88 | assert.True(t, shares[i].Verify(tss.EC(), threshold, vs))
89 | }
90 | }
91 |
92 | func TestReconstruct(t *testing.T) {
93 | num, threshold := 5, 3
94 |
95 | secret := common.GetRandomPositiveInt(tss.EC().Params().N)
96 |
97 | ids := make([]*big.Int, 0)
98 | for i := 0; i < num; i++ {
99 | ids = append(ids, common.GetRandomPositiveInt(tss.EC().Params().N))
100 | }
101 |
102 | _, shares, err := Create(tss.EC(), threshold, secret, ids)
103 | assert.NoError(t, err)
104 |
105 | secret2, err2 := shares[:threshold-1].ReConstruct(tss.EC())
106 | assert.Error(t, err2) // not enough shares to satisfy the threshold
107 | assert.Nil(t, secret2)
108 |
109 | secret3, err3 := shares[:threshold].ReConstruct(tss.EC())
110 | assert.NoError(t, err3)
111 | assert.NotZero(t, secret3)
112 |
113 | secret4, err4 := shares[:num].ReConstruct(tss.EC())
114 | assert.NoError(t, err4)
115 | assert.NotZero(t, secret4)
116 | }
117 |
--------------------------------------------------------------------------------
/crypto/zkp/affg/affg_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkpaffg
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 | "time"
13 |
14 | "github.com/stretchr/testify/assert"
15 |
16 | "github.com/binance-chain/tss-lib/common"
17 | "github.com/binance-chain/tss-lib/crypto"
18 | "github.com/binance-chain/tss-lib/crypto/paillier"
19 | "github.com/binance-chain/tss-lib/ecdsa/keygen"
20 | "github.com/binance-chain/tss-lib/tss"
21 | )
22 |
23 | // Using a modulus length of 2048 is recommended in the GG18 spec
24 | const (
25 | testPaillierKeyLength = 2048
26 | )
27 |
28 | func TestAffg(test *testing.T) {
29 | ec := tss.EC()
30 | q := ec.Params().N
31 | q3 := new(big.Int).Mul(q, q)
32 | q3 = new(big.Int).Mul(q, q3)
33 | // q6 := new(big.Int).Mul(q3, q3)
34 |
35 | _, pk0, err := paillier.GenerateKeyPair(testPaillierKeyLength, 10*time.Minute)
36 | assert.NoError(test, err)
37 | _, pk1, err := paillier.GenerateKeyPair(testPaillierKeyLength, 10*time.Minute)
38 | assert.NoError(test, err)
39 |
40 | // a*b+w
41 | a := common.GetRandomPositiveInt(q)
42 | x := common.GetRandomPositiveInt(q)
43 | // x := q6
44 | y := common.GetRandomPositiveInt(q3)
45 |
46 | X := crypto.ScalarBaseMult(ec, x)
47 | assert.NoError(test, err)
48 |
49 | Y, rhoy, err := pk1.EncryptAndReturnRandomness(y)
50 | assert.NoError(test, err)
51 |
52 | NCap, s, t, err := keygen.LoadNTildeH1H2FromTestFixture(1)
53 | assert.NoError(test, err)
54 |
55 | C, _, err := pk0.EncryptAndReturnRandomness(a)
56 | assert.NoError(test, err)
57 |
58 | cw, rho, err := pk0.EncryptAndReturnRandomness(y)
59 | assert.NoError(test, err)
60 |
61 | D, err := pk0.HomoMult(x, C)
62 | assert.NoError(test, err)
63 | D, err = pk0.HomoAdd(D, cw)
64 | assert.NoError(test, err)
65 |
66 | proof, err := NewProof(ec, pk0, pk1, NCap, s, t, C, D, Y, X, x, y, rho, rhoy)
67 | assert.NoError(test, err)
68 |
69 | ok := proof.Verify(ec, pk0, pk1, NCap, s, t, C, D, Y, X)
70 | assert.True(test, ok, "proof must verify")
71 |
72 | x = q3
73 | proof, err = NewProof(ec, pk0, pk1, NCap, s, t, C, D, Y, X, x, y, rho, rhoy)
74 | assert.NoError(test, err)
75 |
76 | ok = proof.Verify(ec, pk0, pk1, NCap, s, t, C, D, Y, X)
77 | assert.False(test, ok, "proof must verify")
78 |
79 | x = common.GetRandomPositiveInt(q)
80 | y = q3
81 | proof, err = NewProof(ec, pk0, pk1, NCap, s, t, C, D, Y, X, x, y, rho, rhoy)
82 | assert.NoError(test, err)
83 |
84 | ok = proof.Verify(ec, pk0, pk1, NCap, s, t, C, D, Y, X)
85 | assert.False(test, ok, "proof must verify")
86 | }
87 |
--------------------------------------------------------------------------------
/crypto/zkp/dec/dec_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkpdec
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 | "time"
13 |
14 | "github.com/stretchr/testify/assert"
15 |
16 | "github.com/binance-chain/tss-lib/common"
17 | "github.com/binance-chain/tss-lib/crypto"
18 | "github.com/binance-chain/tss-lib/crypto/paillier"
19 | "github.com/binance-chain/tss-lib/tss"
20 | )
21 |
22 | // Using a modulus length of 2048 is recommended in the GG18 spec
23 | const (
24 | testSafePrimeBits = 1024
25 | )
26 |
27 | func TestDec(test *testing.T) {
28 | ec := tss.EC()
29 | q := ec.Params().N
30 |
31 | primes := [2]*big.Int{common.GetRandomPrimeInt(testSafePrimeBits), common.GetRandomPrimeInt(testSafePrimeBits)}
32 | NCap, s, t, err := crypto.GenerateNTildei(primes)
33 | assert.NoError(test, err)
34 |
35 | sk, pk, err := paillier.GenerateKeyPair(testSafePrimeBits*2, time.Minute*10)
36 | assert.NoError(test, err)
37 |
38 | x := common.GetRandomPositiveInt(q)
39 | y := new(big.Int).Add(x, q)
40 | C, rho, err := sk.EncryptAndReturnRandomness(y)
41 | assert.NoError(test, err)
42 |
43 | proof, err := NewProof(ec, pk, C, x, NCap, s, t, y, rho)
44 | assert.NoError(test, err)
45 |
46 | ok := proof.Verify(ec, pk, C, x, NCap, s, t)
47 | assert.True(test, ok, "proof must verify")
48 | }
49 |
50 | func TestDecWithCompositions(test *testing.T) {
51 | ec := tss.EC()
52 | q := ec.Params().N
53 | q3 := new(big.Int).Mul(q, new(big.Int).Mul(q, q))
54 | // modQ3 := common.ModInt(q3)
55 | modN := common.ModInt(q)
56 | zero := big.NewInt(0)
57 |
58 | primes := [2]*big.Int{common.GetRandomPrimeInt(testSafePrimeBits), common.GetRandomPrimeInt(testSafePrimeBits)}
59 | NCap, s, t, err := crypto.GenerateNTildei(primes)
60 | assert.NoError(test, err)
61 |
62 | _, pk, err := paillier.GenerateKeyPair(testSafePrimeBits*2, time.Minute*10)
63 | assert.NoError(test, err)
64 | N2 := pk.NSquare()
65 |
66 | // Ki = enc(ki,𝜌i)
67 | 𝛾i := common.GetRandomPositiveInt(q)
68 | ki := common.GetRandomPositiveInt(q)
69 | Ki, 𝜌i, err := pk.EncryptAndReturnRandomness(ki)
70 |
71 | proof1, err := NewProof(ec, pk, Ki, modN.Add(zero, ki), NCap, s, t, ki, 𝜌i)
72 | assert.NoError(test, err)
73 | ok1 := proof1.Verify(ec, pk, Ki, modN.Add(zero, ki), NCap, s, t)
74 | assert.True(test, ok1, "proof must verify")
75 |
76 | // 𝛾K = (𝛾i ⊗ Ki)
77 | 𝛾K, err := pk.HomoMult(𝛾i, Ki)
78 | 𝜌ʹ := big.NewInt(1).Exp(𝜌i, 𝛾i, N2)
79 | yʹ := q3.Mul(𝛾i, ki)
80 | proof2, err := NewProof(ec, pk, 𝛾K, modN.Add(zero, yʹ), NCap, s, t, yʹ, 𝜌ʹ)
81 | assert.NoError(test, err)
82 | ok2 := proof2.Verify(ec, pk, 𝛾K, modN.Add(zero, yʹ), NCap, s, t)
83 | assert.True(test, ok2, "proof must verify")
84 |
85 | // Di = (𝛾i ⊗ Ki) ⊕ enc(-𝛽,si)
86 | x := common.GetRandomPositiveInt(q)
87 | 𝛽ʹ := new(big.Int).Add(x, q)
88 | T, si, err := pk.EncryptAndReturnRandomness(𝛽ʹ)
89 | assert.NoError(test, err)
90 | Di, err := pk.HomoAdd(𝛾K, T)
91 |
92 | 𝜌ʺ := N2.Mul(big.NewInt(1).Exp(𝜌i, 𝛾i, N2), si)
93 | yʺ := q3.Add(𝛽ʹ, q3.Mul(𝛾i, ki))
94 | proof3, err := NewProof(ec, pk, Di, modN.Add(zero, yʺ), NCap, s, t, yʺ, 𝜌ʺ)
95 | assert.NoError(test, err)
96 |
97 | ok3 := proof3.Verify(ec, pk, Di, modN.Add(zero, yʺ), NCap, s, t)
98 | assert.True(test, ok3, "proof must verify")
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/crypto/zkp/dlog_proof.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkp
8 |
9 | import (
10 | "errors"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/common"
14 | "github.com/binance-chain/tss-lib/crypto"
15 | "github.com/binance-chain/tss-lib/tss"
16 | )
17 |
18 | type (
19 | // Schnorr ZK of the discrete logarithm of pho_i such that A = g^pho (GG18)
20 | DLogProof struct {
21 | Alpha *crypto.ECPoint
22 | T *big.Int
23 | }
24 | )
25 |
26 | // NewDLogProof constructs a new Schnorr ZK of the discrete logarithm of pho_i such that A = g^pho (GG18)
27 | func NewDLogProof(x *big.Int, X *crypto.ECPoint) (*DLogProof, error) {
28 | if x == nil || X == nil || !X.ValidateBasic() {
29 | return nil, errors.New("NewDLogProof received nil or invalid value(s)")
30 | }
31 | ecParams := tss.EC().Params()
32 | q := ecParams.N
33 | g := crypto.NewECPointNoCurveCheck(tss.EC(), ecParams.Gx, ecParams.Gy) // already on the curve.
34 |
35 | a := common.GetRandomPositiveInt(q)
36 | alpha := crypto.ScalarBaseMult(tss.EC(), a)
37 |
38 | var c *big.Int
39 | {
40 | cHash := common.SHA512_256i(X.X(), X.Y(), g.X(), g.Y(), alpha.X(), alpha.Y())
41 | c = common.RejectionSample(q, cHash)
42 | }
43 | t := new(big.Int).Mul(c, x)
44 | t = common.ModInt(q).Add(a, t)
45 |
46 | return &DLogProof{Alpha: alpha, T: t}, nil
47 | }
48 |
49 | // NewDLogProof verifies a new Schnorr ZK proof of knowledge of the discrete logarithm (GG18Spec Fig. 16)
50 | func (pf *DLogProof) Verify(X *crypto.ECPoint) bool {
51 | if pf == nil || !pf.ValidateBasic() {
52 | return false
53 | }
54 | ecParams := tss.EC().Params()
55 | q := ecParams.N
56 | g := crypto.NewECPointNoCurveCheck(tss.EC(), ecParams.Gx, ecParams.Gy)
57 |
58 | var c *big.Int
59 | {
60 | cHash := common.SHA512_256i(X.X(), X.Y(), g.X(), g.Y(), pf.Alpha.X(), pf.Alpha.Y())
61 | c = common.RejectionSample(q, cHash)
62 | }
63 | tG := crypto.ScalarBaseMult(tss.EC(), pf.T)
64 | Xc := X.ScalarMult(c)
65 | aXc, err := pf.Alpha.Add(Xc)
66 | if err != nil {
67 | return false
68 | }
69 | if aXc.X().Cmp(tG.X()) != 0 || aXc.Y().Cmp(tG.Y()) != 0 {
70 | return false
71 | }
72 | return true
73 | }
74 |
75 | func (pf *DLogProof) ValidateBasic() bool {
76 | return pf.T != nil && pf.Alpha != nil && pf.Alpha.ValidateBasic()
77 | }
78 |
--------------------------------------------------------------------------------
/crypto/zkp/dlog_proof_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkp_test
8 |
9 | import (
10 | "testing"
11 |
12 | "github.com/stretchr/testify/assert"
13 |
14 | "github.com/binance-chain/tss-lib/common"
15 | "github.com/binance-chain/tss-lib/crypto"
16 | . "github.com/binance-chain/tss-lib/crypto/zkp"
17 | "github.com/binance-chain/tss-lib/tss"
18 | )
19 |
20 | func TestSchnorrProof(t *testing.T) {
21 | q := tss.EC().Params().N
22 | u := common.GetRandomPositiveInt(q)
23 | uG := crypto.ScalarBaseMult(tss.EC(), u)
24 | proof, _ := NewDLogProof(u, uG)
25 |
26 | assert.True(t, proof.Alpha.IsOnCurve())
27 | assert.NotZero(t, proof.Alpha.X())
28 | assert.NotZero(t, proof.Alpha.Y())
29 | assert.NotZero(t, proof.T)
30 | }
31 |
32 | func TestSchnorrProofVerify(t *testing.T) {
33 | q := tss.EC().Params().N
34 | u := common.GetRandomPositiveInt(q)
35 | X := crypto.ScalarBaseMult(tss.EC(), u)
36 |
37 | proof, _ := NewDLogProof(u, X)
38 | res := proof.Verify(X)
39 |
40 | assert.True(t, res, "verify result must be true")
41 | }
42 |
43 | func TestSchnorrProofVerifyBadX(t *testing.T) {
44 | q := tss.EC().Params().N
45 | u := common.GetRandomPositiveInt(q)
46 | u2 := common.GetRandomPositiveInt(q)
47 | X := crypto.ScalarBaseMult(tss.EC(), u)
48 | X2 := crypto.ScalarBaseMult(tss.EC(), u2)
49 |
50 | proof, _ := NewDLogProof(u2, X2)
51 | res := proof.Verify(X)
52 |
53 | assert.False(t, res, "verify result must be false")
54 | }
55 |
--------------------------------------------------------------------------------
/crypto/zkp/enc/enc_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkpenc
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 | "time"
13 |
14 | "github.com/stretchr/testify/assert"
15 |
16 | "github.com/binance-chain/tss-lib/common"
17 | "github.com/binance-chain/tss-lib/crypto"
18 | "github.com/binance-chain/tss-lib/crypto/paillier"
19 | "github.com/binance-chain/tss-lib/tss"
20 | )
21 |
22 | // Using a modulus length of 2048 is recommended in the GG18 spec
23 | const (
24 | testSafePrimeBits = 1024
25 | )
26 |
27 | func TestEnc(test *testing.T) {
28 | ec := tss.EC()
29 | q := ec.Params().N
30 |
31 | sk, pk, err := paillier.GenerateKeyPair(testSafePrimeBits*2, time.Minute*10)
32 | assert.NoError(test, err)
33 |
34 | k := common.GetRandomPositiveInt(q)
35 | K, rho, err := sk.EncryptAndReturnRandomness(k)
36 | assert.NoError(test, err)
37 |
38 | primes := [2]*big.Int{common.GetRandomPrimeInt(testSafePrimeBits), common.GetRandomPrimeInt(testSafePrimeBits)}
39 | NCap, s, t, err := crypto.GenerateNTildei(primes)
40 | assert.NoError(test, err)
41 | proof, err := NewProof(ec, pk, K, NCap, s, t, k, rho)
42 | assert.NoError(test, err)
43 |
44 | ok := proof.Verify(ec, pk, NCap, s, t, K)
45 | assert.True(test, ok, "proof must verify")
46 | }
47 |
--------------------------------------------------------------------------------
/crypto/zkp/fac/fac_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2021 Swingby
2 |
3 | package zkpfac
4 |
5 | import (
6 | "math/big"
7 | "testing"
8 | "time"
9 |
10 | "github.com/binance-chain/tss-lib/crypto"
11 | "github.com/binance-chain/tss-lib/crypto/paillier"
12 | "github.com/stretchr/testify/assert"
13 |
14 | "github.com/binance-chain/tss-lib/common"
15 | "github.com/binance-chain/tss-lib/tss"
16 | )
17 |
18 | // Using a modulus length of 2048 is recommended in the GG18 spec
19 | const (
20 | testSafePrimeBits = 1024
21 | )
22 |
23 | func TestFacPQNoSmallFactor(test *testing.T) {
24 | ec := tss.EC()
25 | Twol := ec.Params().N
26 |
27 | primes := [2]*big.Int{common.GetRandomPrimeInt(testSafePrimeBits), common.GetRandomPrimeInt(testSafePrimeBits)}
28 | NCap, s, t, err := crypto.GenerateNTildei(primes)
29 | modNCap := common.ModInt(NCap)
30 |
31 | pqOk := false
32 |
33 | var p, q *big.Int
34 | var pk *paillier.PublicKey
35 |
36 | for !pqOk {
37 | var err2 error
38 | _, pk, p, q, err2 = paillier.GenerateKeyPairAndPQ(testSafePrimeBits*2, time.Minute*10)
39 | assert.NoError(test, err2)
40 | sqrtNo := new(big.Int).Sqrt(pk.N)
41 | sqrtNoTwol := modNCap.Mul(sqrtNo, Twol)
42 | pUpperBound := p.Cmp(sqrtNoTwol) == -1
43 | qUpperBound := q.Cmp(sqrtNoTwol) == -1
44 | pLowerBound := p.Cmp(Twol) == +1
45 | qLowerBound := q.Cmp(Twol) == +1
46 | pqOk = pUpperBound && qUpperBound && pLowerBound && qLowerBound
47 | }
48 |
49 | proof, err := NewProof(ec, pk, NCap, s, t, p, q)
50 | assert.NoError(test, err)
51 |
52 | ok := proof.Verify(ec, pk, NCap, s, t)
53 | assert.True(test, ok, "proof must verify")
54 | }
55 |
--------------------------------------------------------------------------------
/crypto/zkp/logstar/logstar_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkplogstar
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 | "time"
13 |
14 | "github.com/stretchr/testify/assert"
15 |
16 | "github.com/binance-chain/tss-lib/common"
17 | "github.com/binance-chain/tss-lib/crypto"
18 | "github.com/binance-chain/tss-lib/crypto/paillier"
19 | "github.com/binance-chain/tss-lib/tss"
20 | )
21 |
22 | // Using a modulus length of 2048 is recommended in the GG18 spec
23 | const (
24 | testSafePrimeBits = 1024
25 | )
26 |
27 | func TestLogstar(test *testing.T) {
28 | ec := tss.EC()
29 | q := ec.Params().N
30 |
31 | sk, pk, err := paillier.GenerateKeyPair(testSafePrimeBits*2, time.Minute*10)
32 | assert.NoError(test, err)
33 |
34 | x := common.GetRandomPositiveInt(q)
35 | C, rho, err := sk.EncryptAndReturnRandomness(x)
36 | assert.NoError(test, err)
37 | X := crypto.ScalarBaseMult(ec, x)
38 |
39 | primes := [2]*big.Int{common.GetRandomPrimeInt(testSafePrimeBits), common.GetRandomPrimeInt(testSafePrimeBits)}
40 | NCap, s, t, err := crypto.GenerateNTildei(primes)
41 | assert.NoError(test, err)
42 |
43 | g := crypto.ScalarBaseMult(ec, big.NewInt(1))
44 | proof, err := NewProof(ec, pk, C, X, g, NCap, s, t, x, rho)
45 | assert.NoError(test, err)
46 |
47 | ok := proof.Verify(ec, pk, C, X, g, NCap, s, t)
48 | assert.True(test, ok, "proof must verify")
49 | }
50 |
--------------------------------------------------------------------------------
/crypto/zkp/mod/mod_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkpmod_test
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 | "time"
13 |
14 | . "github.com/binance-chain/tss-lib/crypto/zkp/mod"
15 | "github.com/binance-chain/tss-lib/ecdsa/keygen"
16 | "github.com/stretchr/testify/assert"
17 | )
18 |
19 | func TestMod(test *testing.T) {
20 | preParams, err := keygen.GeneratePreParams(time.Minute*10, 8)
21 | assert.NoError(test, err)
22 |
23 | p, q, N := preParams.P, preParams.Q, preParams.NTildei
24 | // p2, q2 := new(big.Int).Mul(p, big.NewInt(2)), new(big.Int).Mul(q, big.NewInt(2))
25 | p2, q2 := new(big.Int).Lsh(p, 1), new(big.Int).Lsh(q, 1)
26 | P, Q := new(big.Int).Add(p2, big.NewInt(1)), new(big.Int).Add(q2, big.NewInt(1))
27 |
28 | proof, err := NewProof(N, P, Q)
29 | assert.NoError(test, err)
30 |
31 | proofBzs := proof.Bytes()
32 | proof, err = NewProofFromBytes(proofBzs[:])
33 | assert.NoError(test, err)
34 |
35 | ok := proof.Verify(N)
36 | assert.True(test, ok, "proof must verify")
37 | }
38 |
--------------------------------------------------------------------------------
/crypto/zkp/mul/mul.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkpmul
8 |
9 | import (
10 | "crypto/elliptic"
11 | "errors"
12 | "fmt"
13 | "math/big"
14 |
15 | "github.com/binance-chain/tss-lib/common"
16 | "github.com/binance-chain/tss-lib/crypto/paillier"
17 | )
18 |
19 | const (
20 | ProofMulBytesParts = 5
21 | )
22 |
23 | type (
24 | ProofMul struct {
25 | A, B, Z, U, V *big.Int
26 | }
27 | )
28 |
29 | // NewProof implements proofenc
30 | func NewProof(ec elliptic.Curve, pk *paillier.PublicKey, X, Y, C, x, rhox *big.Int) (*ProofMul, error) {
31 | if pk == nil || X == nil || Y == nil || C == nil || rhox == nil {
32 | return nil, errors.New("ProveMul constructor received nil value(s)")
33 | }
34 | q := ec.Params().N
35 |
36 | // Fig 28.1 sample
37 | alpha := common.GetRandomPositiveRelativelyPrimeInt(pk.N)
38 | r := common.GetRandomPositiveRelativelyPrimeInt(pk.N)
39 | s := common.GetRandomPositiveRelativelyPrimeInt(pk.N)
40 |
41 | modNSquared := common.ModInt(pk.NSquare())
42 | A := modNSquared.Exp(Y, alpha)
43 | A = modNSquared.Mul(A, modNSquared.Exp(r, pk.N))
44 |
45 | B := modNSquared.Exp(pk.Gamma(), alpha)
46 | B = modNSquared.Mul(B, modNSquared.Exp(s, pk.N))
47 |
48 | // Fig 28.2 e
49 | var e *big.Int
50 | {
51 | eHash := common.SHA512_256i(append(pk.AsInts(), X, Y, C, A, B)...)
52 | e = common.RejectionSample(q, eHash)
53 | }
54 |
55 | // Fig 14.3
56 | z := new(big.Int).Mul(e, x)
57 | z = new(big.Int).Add(z, alpha)
58 |
59 | modN := common.ModInt(pk.N)
60 | // u := modN.Exp(rho, e)
61 | // u = modN.Mul(u, r)
62 |
63 | v := modN.Exp(rhox, e)
64 | v = modN.Mul(v, s)
65 |
66 | return &ProofMul{A: A, B: B, Z: z, U: r, V: v}, nil
67 | }
68 |
69 | func NewProofFromBytes(bzs [][]byte) (*ProofMul, error) {
70 | if !common.NonEmptyMultiBytes(bzs, ProofMulBytesParts) {
71 | return nil, fmt.Errorf("expected %d byte parts to construct ProofMul", ProofMulBytesParts)
72 | }
73 | return &ProofMul{
74 | A: new(big.Int).SetBytes(bzs[0]),
75 | B: new(big.Int).SetBytes(bzs[1]),
76 | Z: new(big.Int).SetBytes(bzs[2]),
77 | U: new(big.Int).SetBytes(bzs[3]),
78 | V: new(big.Int).SetBytes(bzs[4]),
79 | }, nil
80 | }
81 |
82 | func (pf *ProofMul) Verify(ec elliptic.Curve, pk *paillier.PublicKey, X, Y, C *big.Int) bool {
83 | if pf == nil || !pf.ValidateBasic() || ec == nil || pk == nil || X == nil || Y == nil || C == nil {
84 | return false
85 | }
86 |
87 | q := ec.Params().N
88 |
89 | var e *big.Int
90 | {
91 | eHash := common.SHA512_256i(append(pk.AsInts(), X, Y, C, pf.A, pf.B)...)
92 | e = common.RejectionSample(q, eHash)
93 | }
94 |
95 | // Fig 14. Equality Check
96 | modNSquare := common.ModInt(pk.NSquare())
97 | {
98 | YEXPz := modNSquare.Exp(Y, pf.Z)
99 | uEXPN := modNSquare.Exp(pf.U, pk.N)
100 | left := modNSquare.Mul(YEXPz, uEXPN)
101 |
102 | CEXPe := modNSquare.Exp(C, e)
103 | right := modNSquare.Mul(pf.A, CEXPe)
104 |
105 | if left.Cmp(right) != 0 {
106 | return false
107 | }
108 | }
109 |
110 | {
111 | Np1EXPz := modNSquare.Exp(pk.Gamma(), pf.Z)
112 | CEXPN := modNSquare.Exp(pf.V, pk.N)
113 | left := modNSquare.Mul(Np1EXPz, CEXPN)
114 |
115 | XEXPe := modNSquare.Exp(X, e)
116 | right := modNSquare.Mul(pf.B, XEXPe)
117 | if left.Cmp(right) != 0 {
118 | return false
119 | }
120 | }
121 | return true
122 | }
123 |
124 | func (pf *ProofMul) ValidateBasic() bool {
125 | return pf.A != nil &&
126 | pf.B != nil &&
127 | pf.Z != nil &&
128 | pf.U != nil &&
129 | pf.V != nil
130 | }
131 |
132 | func (pf *ProofMul) Bytes() [ProofMulBytesParts][]byte {
133 | return [...][]byte{
134 | pf.A.Bytes(),
135 | pf.B.Bytes(),
136 | pf.Z.Bytes(),
137 | pf.U.Bytes(),
138 | pf.V.Bytes(),
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/crypto/zkp/mul/mul_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkpmul
8 |
9 | import (
10 | "testing"
11 | "time"
12 |
13 | "github.com/stretchr/testify/assert"
14 |
15 | "github.com/binance-chain/tss-lib/common"
16 | "github.com/binance-chain/tss-lib/crypto/paillier"
17 | "github.com/binance-chain/tss-lib/tss"
18 | )
19 |
20 | // Using a modulus length of 2048 is recommended in the GG18 spec
21 | const (
22 | testSafePrimeBits = 1024
23 | )
24 |
25 | func TestMul(test *testing.T) {
26 | ec := tss.EC()
27 | q := ec.Params().N
28 |
29 | sk, pk, err := paillier.GenerateKeyPair(testSafePrimeBits*2, time.Minute*10)
30 | assert.NoError(test, err)
31 |
32 | x := common.GetRandomPositiveInt(q)
33 | X, rhox, err := sk.EncryptAndReturnRandomness(x)
34 | assert.NoError(test, err)
35 |
36 | y := common.GetRandomPositiveInt(q)
37 | Y, _, err := sk.EncryptAndReturnRandomness(y)
38 | // rho := big.NewInt(1)
39 | assert.NoError(test, err)
40 |
41 | C, err := pk.HomoMult(x, Y)
42 | assert.NoError(test, err)
43 |
44 | proof, err := NewProof(ec, pk, X, Y, C, x, rhox)
45 | assert.NoError(test, err)
46 |
47 | ok := proof.Verify(ec, pk, X, Y, C)
48 | assert.True(test, ok, "proof must verify")
49 | }
50 |
--------------------------------------------------------------------------------
/crypto/zkp/prm/prm.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019-2020 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkpprm
8 |
9 | import (
10 | "fmt"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/common"
14 | )
15 |
16 | const (
17 | Iterations = 64
18 | ProofPrmBytesParts = Iterations * 2
19 | )
20 |
21 | type (
22 | ProofPrm struct {
23 | A [Iterations]*big.Int
24 | Z [Iterations]*big.Int
25 | }
26 | )
27 |
28 | func NewProof(s, t, N, Phi, lambda *big.Int) (*ProofPrm, error) {
29 | modN, modPhi := common.ModInt(N), common.ModInt(Phi)
30 |
31 | // Fig 17.1
32 | a := make([]*big.Int, Iterations)
33 | A := [Iterations]*big.Int{}
34 | for i := range A {
35 | a[i] = common.GetRandomPositiveInt(Phi)
36 | A[i] = modN.Exp(t, a[i])
37 | }
38 |
39 | // Fig 17.2
40 | e := common.SHA512_256i(append([]*big.Int{s, t, N}, A[:]...)...)
41 |
42 | // Fig 17.3
43 | Z := [Iterations]*big.Int{}
44 | for i := range Z {
45 | ei := big.NewInt(int64(e.Bit(i)))
46 | Z[i] = modPhi.Add(a[i], modPhi.Mul(ei, lambda))
47 | }
48 | return &ProofPrm{A: A, Z: Z}, nil
49 | }
50 |
51 | func NewProofFromBytes(bzs [][]byte) (*ProofPrm, error) {
52 | if !common.NonEmptyMultiBytes(bzs, ProofPrmBytesParts) {
53 | return nil, fmt.Errorf("expected %d byte parts to construct ProofPrm", ProofPrmBytesParts)
54 | }
55 | bis := make([]*big.Int, len(bzs))
56 | for i := range bis {
57 | bis[i] = new(big.Int).SetBytes(bzs[i])
58 | }
59 | A := [Iterations]*big.Int{}
60 | copy(A[:], bis[:Iterations])
61 |
62 | Z := [Iterations]*big.Int{}
63 | copy(Z[:], bis[Iterations:])
64 |
65 | return &ProofPrm{
66 | A: A,
67 | Z: Z,
68 | }, nil
69 | }
70 |
71 | func (pf *ProofPrm) Verify(s, t, N *big.Int) bool {
72 | if pf == nil || !pf.ValidateBasic() {
73 | return false
74 | }
75 | modN := common.ModInt(N)
76 | e := common.SHA512_256i(append([]*big.Int{s, t, N}, pf.A[:]...)...)
77 |
78 | // Fig 17. Verification
79 | for i := 0; i < Iterations; i++ {
80 | ei := big.NewInt(int64(e.Bit(i)))
81 | left := modN.Exp(t, pf.Z[i])
82 | right := modN.Exp(s, ei)
83 | right = modN.Mul(pf.A[i], right)
84 | if left.Cmp(right) != 0 {
85 | return false
86 | }
87 | }
88 | return true
89 | }
90 |
91 | func (pf *ProofPrm) ValidateBasic() bool {
92 | for i := range pf.A {
93 | if pf.A[i] == nil {
94 | return false
95 | }
96 | }
97 | for i := range pf.Z {
98 | if pf.Z[i] == nil {
99 | return false
100 | }
101 | }
102 | return true
103 | }
104 |
105 | func (pf *ProofPrm) Bytes() [ProofPrmBytesParts][]byte {
106 | bzs := [ProofPrmBytesParts][]byte{}
107 | for i := range pf.A {
108 | bzs[i] = pf.A[i].Bytes()
109 | }
110 | for i := range pf.Z {
111 | bzs[i+Iterations] = pf.Z[i].Bytes()
112 | }
113 | return bzs
114 | }
115 |
--------------------------------------------------------------------------------
/crypto/zkp/prm/prm_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkpprm_test
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 | "time"
13 |
14 | "github.com/stretchr/testify/assert"
15 |
16 | . "github.com/binance-chain/tss-lib/crypto/zkp/prm"
17 | keygen "github.com/binance-chain/tss-lib/ecdsa/keygen"
18 | )
19 |
20 | func TestPrm(test *testing.T) {
21 | preParams, err := keygen.GeneratePreParams(time.Minute*10, 8)
22 | assert.NoError(test, err)
23 |
24 | s, t, lambda, P, Q, N := preParams.H1i, preParams.H2i, preParams.Beta, preParams.P, preParams.Q, preParams.NTildei
25 | P2, Q2 := new(big.Int).Lsh(P, 1), new(big.Int).Lsh(Q, 1)
26 | Phi := new(big.Int).Mul(P2, Q2)
27 |
28 | proof, err := NewProof(s, t, N, Phi, lambda)
29 | assert.NoError(test, err)
30 |
31 | proofBzs := proof.Bytes()
32 | proof, err = NewProofFromBytes(proofBzs[:])
33 | assert.NoError(test, err)
34 |
35 | ok := proof.Verify(s, t, N)
36 | assert.True(test, ok, "proof must verify")
37 | }
38 |
--------------------------------------------------------------------------------
/crypto/zkp/sch/sch_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package zkpsch
8 |
9 | import (
10 | "testing"
11 |
12 | "github.com/stretchr/testify/assert"
13 |
14 | "github.com/binance-chain/tss-lib/common"
15 | "github.com/binance-chain/tss-lib/crypto"
16 | "github.com/binance-chain/tss-lib/tss"
17 | )
18 |
19 | func TestSchnorrProof(t *testing.T) {
20 | q := tss.EC().Params().N
21 | u := common.GetRandomPositiveInt(q)
22 | uG := crypto.ScalarBaseMult(tss.EC(), u)
23 | proof, _ := NewProof(uG, u)
24 |
25 | assert.True(t, proof.A.IsOnCurve())
26 | assert.NotZero(t, proof.A.X())
27 | assert.NotZero(t, proof.A.Y())
28 | assert.NotZero(t, proof.Z)
29 | }
30 |
31 | func TestSchnorrProofVerify(t *testing.T) {
32 | q := tss.EC().Params().N
33 | u := common.GetRandomPositiveInt(q)
34 | X := crypto.ScalarBaseMult(tss.EC(), u)
35 |
36 | proof, _ := NewProof(X, u)
37 | res := proof.Verify(X)
38 |
39 | assert.True(t, res, "verify result must be true")
40 | }
41 |
42 | func TestSchnorrProofVerifyBadX(t *testing.T) {
43 | q := tss.EC().Params().N
44 | u := common.GetRandomPositiveInt(q)
45 | u2 := common.GetRandomPositiveInt(q)
46 | X := crypto.ScalarBaseMult(tss.EC(), u)
47 | X2 := crypto.ScalarBaseMult(tss.EC(), u2)
48 |
49 | proof, _ := NewProof(X2, u2)
50 | res := proof.Verify(X)
51 |
52 | assert.False(t, res, "verify result must be false")
53 | }
54 |
--------------------------------------------------------------------------------
/ecdsa/keygen/prepare.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "errors"
11 | "math/big"
12 | "runtime"
13 | "time"
14 |
15 | "github.com/binance-chain/tss-lib/common"
16 | "github.com/binance-chain/tss-lib/crypto/paillier"
17 | )
18 |
19 | const (
20 | safePrimeBitLen = 1024
21 | )
22 |
23 | var (
24 | one = big.NewInt(1)
25 | )
26 |
27 | // GeneratePreParams finds two safe primes and computes the Paillier secret required for the protocol.
28 | // This can be a time consuming process so it is recommended to do it out-of-band.
29 | // If not specified, a concurrency value equal to the number of available CPU cores will be used.
30 | func GeneratePreParams(timeout time.Duration, optionalConcurrency ...int) (*LocalPreParams, error) {
31 | var concurrency int
32 | if 0 < len(optionalConcurrency) {
33 | if 1 < len(optionalConcurrency) {
34 | panic(errors.New("GeneratePreParams: expected 0 or 1 item in `optionalConcurrency`"))
35 | }
36 | concurrency = optionalConcurrency[0]
37 | } else {
38 | concurrency = runtime.NumCPU()
39 | }
40 | if concurrency /= 3; concurrency < 1 {
41 | concurrency = 1
42 | }
43 |
44 | common.Logger.Info("generating the safe primes for the signing proofs, please wait...")
45 | start := time.Now()
46 | sgps, err := common.GetRandomSafePrimesConcurrent(safePrimeBitLen, 2, timeout, concurrency)
47 | if err != nil {
48 | // ch <- nil
49 | return nil, err
50 | }
51 | common.Logger.Infof("safe primes generated. took %s\n", time.Since(start))
52 |
53 | if sgps == nil || sgps[0] == nil || sgps[1] == nil ||
54 | !sgps[0].Prime().ProbablyPrime(30) || !sgps[1].Prime().ProbablyPrime(30) ||
55 | !sgps[0].SafePrime().ProbablyPrime(30) || !sgps[1].SafePrime().ProbablyPrime(30) {
56 | return nil, errors.New("error while generating the safe primes")
57 | }
58 |
59 | P, Q := sgps[0].SafePrime(), sgps[1].SafePrime()
60 | paiPK := &paillier.PublicKey{N: new(big.Int).Mul(P, Q)}
61 | // phiN = P-1 * Q-1
62 | PMinus1, QMinus1 := new(big.Int).Sub(P, one), new(big.Int).Sub(Q, one)
63 | phiN := new(big.Int).Mul(PMinus1, QMinus1)
64 | // lambdaN = lcm(P−1, Q−1)
65 | gcd := new(big.Int).GCD(nil, nil, PMinus1, QMinus1)
66 | lambdaN := new(big.Int).Div(phiN, gcd)
67 | paiSK := &paillier.PrivateKey{PublicKey: *paiPK, LambdaN: lambdaN, PhiN: phiN}
68 | NTildei := new(big.Int).Mul(P, Q)
69 | modNTildeI := common.ModInt(NTildei)
70 |
71 | p, q := sgps[0].Prime(), sgps[1].Prime()
72 | modPQ := common.ModInt(new(big.Int).Mul(p, q))
73 | f1 := common.GetRandomPositiveRelativelyPrimeInt(NTildei)
74 | alpha := common.GetRandomPositiveRelativelyPrimeInt(NTildei)
75 | beta := modPQ.Inverse(alpha)
76 | h1i := modNTildeI.Mul(f1, f1)
77 | h2i := modNTildeI.Exp(h1i, alpha)
78 |
79 | preParams := &LocalPreParams{
80 | PaillierSK: paiSK,
81 | NTildei: NTildei,
82 | H1i: h1i,
83 | H2i: h2i,
84 | Alpha: alpha,
85 | Beta: beta,
86 | P: p,
87 | Q: q,
88 | }
89 | return preParams, nil
90 | }
91 |
--------------------------------------------------------------------------------
/ecdsa/keygen/round_1.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "errors"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/common"
14 | "github.com/binance-chain/tss-lib/crypto"
15 | "github.com/binance-chain/tss-lib/crypto/vss"
16 | zkpprm "github.com/binance-chain/tss-lib/crypto/zkp/prm"
17 | zkpsch "github.com/binance-chain/tss-lib/crypto/zkp/sch"
18 | "github.com/binance-chain/tss-lib/tss"
19 | )
20 |
21 | var (
22 | zero = big.NewInt(0)
23 | )
24 |
25 | func newRound1(params *tss.Parameters, save *LocalPartySaveData, temp *localTempData, out chan<- tss.Message, end chan<- LocalPartySaveData) tss.Round {
26 | return &round1{
27 | &base{params, save, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 1}}
28 | }
29 |
30 | func (round *round1) Start() *tss.Error {
31 | if round.started {
32 | return round.WrapError(errors.New("round already started"))
33 | }
34 | round.number = 1
35 | round.started = true
36 | round.resetOK()
37 |
38 | Pi := round.PartyID()
39 | i := Pi.Index
40 | round.ok[i] = true
41 |
42 | // Fig 5. Round 1. private key part
43 | ridi := common.GetRandomPositiveInt(round.EC().Params().N)
44 | ui := common.GetRandomPositiveInt(round.EC().Params().N)
45 |
46 | // Fig 5. Round 1. pub key part, vss shares
47 | ids := round.Parties().IDs().Keys()
48 | vs, shares, err := vss.Create(round.Params().EC(), round.Threshold(), ui, ids)
49 | if err != nil {
50 | return round.WrapError(err, Pi)
51 | }
52 | xi := new(big.Int).Set(shares[i].Share)
53 | Xi := crypto.ScalarBaseMult(round.EC(), xi)
54 | Ai, τ, err := zkpsch.NewProofCommitment(Xi, xi)
55 | if err != nil {
56 | return round.WrapError(err, Pi)
57 | }
58 |
59 | // Fig 6. Round 1.
60 | var preParams *LocalPreParams
61 | if round.save.LocalPreParams.Validate() {
62 | preParams = &round.save.LocalPreParams
63 | } else {
64 | preParams, err = GeneratePreParams(round.SafePrimeGenTimeout())
65 | if err != nil {
66 | return round.WrapError(errors.New("pre-params generation failed"), Pi)
67 | }
68 | }
69 |
70 | P2, Q2 := new(big.Int).Lsh(preParams.P, 1), new(big.Int).Lsh(preParams.Q, 1)
71 | 𝜑 := new(big.Int).Mul(P2, Q2)
72 | 𝜓i, err := zkpprm.NewProof(preParams.H1i, preParams.H2i, preParams.NTildei, 𝜑, preParams.Beta)
73 | listToHash, err := crypto.FlattenECPoints(vs)
74 | if err != nil {
75 | return round.WrapError(err, Pi)
76 | }
77 | listToHash = append(listToHash, preParams.PaillierSK.PublicKey.N, ridi, Xi.X(), Xi.Y(), Ai.X(), Ai.Y(), preParams.NTildei, preParams.H1i, preParams.H2i)
78 | for _, a := range 𝜓i.A {
79 | listToHash = append(listToHash, a)
80 | }
81 | for _, z := range 𝜓i.Z {
82 | listToHash = append(listToHash, z)
83 | }
84 | VHash := common.SHA512_256i(listToHash...)
85 | {
86 | msg := NewKGRound1Message(round.PartyID(), VHash)
87 | round.out <- msg
88 | }
89 |
90 | round.temp.𝜓i = 𝜓i
91 | round.temp.vs = vs
92 | round.temp.ridi = ridi
93 | round.temp.ui = ui
94 | round.temp.Ai = Ai
95 | round.temp.τ = τ
96 | round.save.Ks = ids
97 | round.save.LocalPreParams = *preParams
98 | round.save.NTildej[i] = preParams.NTildei
99 | round.save.H1j[i], round.save.H2j[i] = preParams.H1i, preParams.H2i
100 | round.save.ShareID = ids[i]
101 | round.temp.shares = shares
102 | round.save.PaillierSK = preParams.PaillierSK
103 | round.save.PaillierPKs[i] = &preParams.PaillierSK.PublicKey
104 |
105 | return nil
106 | }
107 |
108 | func (round *round1) CanAccept(msg tss.ParsedMessage) bool {
109 | if _, ok := msg.Content().(*KGRound1Message); ok {
110 | return msg.IsBroadcast()
111 | }
112 | return false
113 | }
114 |
115 | func (round *round1) Update() (bool, *tss.Error) {
116 | for j, msg := range round.temp.r1msgVHashs {
117 | if round.ok[j] {
118 | continue
119 | }
120 | if msg == nil {
121 | return false, nil
122 | }
123 | round.ok[j] = true
124 | }
125 | return true, nil
126 | }
127 |
128 | func (round *round1) NextRound() tss.Round {
129 | round.started = false
130 | return &round2{round}
131 | }
132 |
--------------------------------------------------------------------------------
/ecdsa/keygen/round_2.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "errors"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/crypto"
14 | "github.com/binance-chain/tss-lib/tss"
15 | )
16 |
17 | func (round *round2) Start() *tss.Error {
18 | if round.started {
19 | return round.WrapError(errors.New("round already started"))
20 | }
21 | round.number = 2
22 | round.started = true
23 | round.resetOK()
24 |
25 | Pi := round.PartyID()
26 | i := Pi.Index
27 | round.ok[i] = true
28 |
29 | // Fig 5. Round 2. / Fig 6. Round 2.
30 | {
31 | xi := new(big.Int).Set(round.temp.shares[i].Share)
32 | Xi := crypto.ScalarBaseMult(round.EC(), xi)
33 | msg := NewKGRound2Message(round.PartyID(), round.temp.vs, &round.save.PaillierSK.PublicKey, round.save.NTildei,
34 | round.save.H1i, round.save.H2i, round.temp.ridi, round.temp.Ai, Xi, round.temp.𝜓i)
35 | round.out <- msg
36 | }
37 |
38 | return nil
39 | }
40 |
41 | func (round *round2) CanAccept(msg tss.ParsedMessage) bool {
42 | if _, ok := msg.Content().(*KGRound2Message); ok {
43 | return msg.IsBroadcast()
44 | }
45 | return false
46 | }
47 |
48 | func (round *round2) Update() (bool, *tss.Error) {
49 | for j, msg := range round.temp.r2msgVss {
50 | if round.ok[j] {
51 | continue
52 | }
53 | if msg == nil {
54 | return false, nil
55 | }
56 | round.ok[j] = true
57 | }
58 | return true, nil
59 | }
60 |
61 | func (round *round2) NextRound() tss.Round {
62 | round.started = false
63 | return &round3{round}
64 | }
65 |
--------------------------------------------------------------------------------
/ecdsa/keygen/round_out.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "errors"
11 | "sync"
12 |
13 | "github.com/binance-chain/tss-lib/tss"
14 | )
15 |
16 | func (round *roundout) Start() *tss.Error {
17 | if round.started {
18 | return round.WrapError(errors.New("round already started"))
19 | }
20 | round.number = 5
21 | round.started = true
22 | round.resetOK()
23 |
24 | i := round.PartyID().Index
25 | round.ok[i] = true
26 |
27 | wg := sync.WaitGroup{}
28 | errChs := make(chan *tss.Error, len(round.Parties().IDs())-1)
29 | for j, Pj := range round.Parties().IDs() {
30 | if j == i {
31 | continue
32 | }
33 |
34 | wg.Add(1)
35 | go func(j int, Pj *tss.PartyID) {
36 | defer wg.Done()
37 | if ok := round.temp.r4msgpf[j].Verify(round.save.BigXj[j]); !ok {
38 | errChs <- round.WrapError(errors.New("proof sch verify failed"), Pj)
39 | }
40 | }(j, Pj)
41 | }
42 | wg.Wait()
43 | close(errChs)
44 | culprits := make([]*tss.PartyID, 0)
45 | for err := range errChs {
46 | culprits = append(culprits, err.Culprits()...)
47 | }
48 | if len(culprits) > 0 {
49 | return round.WrapError(errors.New("round_out: proof sch verify failed"), culprits...)
50 | }
51 |
52 | round.end <- *round.save
53 |
54 | return nil
55 | }
56 |
57 | func (round *roundout) CanAccept(msg tss.ParsedMessage) bool {
58 | // not expecting any incoming messages in this round
59 | return false
60 | }
61 |
62 | func (round *roundout) Update() (bool, *tss.Error) {
63 | // not expecting any incoming messages in this round
64 | return false, nil
65 | }
66 |
67 | func (round *roundout) NextRound() tss.Round {
68 | return nil // finished!
69 | }
70 |
--------------------------------------------------------------------------------
/ecdsa/keygen/rounds.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "errors"
11 |
12 | "github.com/binance-chain/tss-lib/tss"
13 | )
14 |
15 | const (
16 | TaskName = "ecdsa-keygen"
17 | )
18 |
19 | type (
20 | base struct {
21 | *tss.Parameters
22 | save *LocalPartySaveData
23 | temp *localTempData
24 | out chan<- tss.Message
25 | end chan<- LocalPartySaveData
26 | ok []bool // `ok` tracks parties which have been verified by Update()
27 | started bool
28 | number int
29 | }
30 | round1 struct {
31 | *base
32 | }
33 | round2 struct {
34 | *round1
35 | }
36 | round3 struct {
37 | *round2
38 | }
39 | round4 struct {
40 | *round3
41 | }
42 | roundout struct {
43 | *round4
44 | }
45 | )
46 |
47 | var (
48 | _ tss.Round = (*round1)(nil)
49 | _ tss.Round = (*round2)(nil)
50 | _ tss.Round = (*round3)(nil)
51 | _ tss.Round = (*round4)(nil)
52 | _ tss.Round = (*roundout)(nil)
53 | )
54 |
55 | // ----- //
56 |
57 | func (round *base) Params() *tss.Parameters {
58 | return round.Parameters
59 | }
60 |
61 | func (round *base) ValidateParams() *error {
62 | if round.Threshold() >= round.PartyCount() {
63 | err := errors.New("t new committee
66 | for j, msg1 := range round.temp.dgRound3Message1s {
67 | if round.oldOK[j] {
68 | continue
69 | }
70 | if msg1 == nil || !round.CanAccept(msg1) {
71 | return false, nil
72 | }
73 | msg2 := round.temp.dgRound3Message2s[j]
74 | if msg2 == nil || !round.CanAccept(msg2) {
75 | return false, nil
76 | }
77 | round.oldOK[j] = true
78 | }
79 | return true, nil
80 | }
81 |
82 | func (round *round3) NextRound() tss.Round {
83 | round.started = false
84 | return &round4{round}
85 | }
86 |
--------------------------------------------------------------------------------
/ecdsa/resharing/round_5_new_step_3.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package resharing
8 |
9 | import (
10 | "errors"
11 |
12 | "github.com/binance-chain/tss-lib/tss"
13 | )
14 |
15 | func (round *round5) Start() *tss.Error {
16 | if round.started {
17 | return round.WrapError(errors.New("round already started"))
18 | }
19 | round.number = 5
20 | round.started = true
21 |
22 | round.allOldOK()
23 | round.allNewOK()
24 |
25 | Pi := round.PartyID()
26 | i := Pi.Index
27 |
28 | if round.IsNewCommittee() {
29 | // 21.
30 | // for this P: SAVE data
31 | round.save.BigXj = round.temp.newBigXjs
32 | round.save.ShareID = round.PartyID().KeyInt()
33 | round.save.Xi = round.temp.newXi
34 | round.save.Ks = round.temp.newKs
35 |
36 | // misc: build list of paillier public keys to save
37 | for j, msg := range round.temp.dgRound2Message1s {
38 | if j == i {
39 | continue
40 | }
41 | r2msg1 := msg.Content().(*DGRound2Message1)
42 | round.save.PaillierPKs[j] = r2msg1.UnmarshalPaillierPK()
43 | }
44 | } else if round.IsOldCommittee() {
45 | round.input.Xi.SetInt64(0)
46 | }
47 |
48 | round.end <- *round.save
49 | return nil
50 | }
51 |
52 | func (round *round5) CanAccept(msg tss.ParsedMessage) bool {
53 | return false
54 | }
55 |
56 | func (round *round5) Update() (bool, *tss.Error) {
57 | return false, nil
58 | }
59 |
60 | func (round *round5) NextRound() tss.Round {
61 | return nil // both committees are finished!
62 | }
63 |
--------------------------------------------------------------------------------
/ecdsa/resharing/rounds.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package resharing
8 |
9 | import (
10 | ecdsautils "github.com/binance-chain/tss-lib/ecdsa"
11 | "github.com/binance-chain/tss-lib/ecdsa/keygen"
12 | "github.com/binance-chain/tss-lib/tss"
13 | )
14 |
15 | const (
16 | TaskName = "ecdsa-resharing"
17 | )
18 |
19 | type (
20 | base struct {
21 | *tss.ReSharingParameters
22 | temp *localTempData
23 | input, save *keygen.LocalPartySaveData
24 | out chan<- tss.Message
25 | end chan<- keygen.LocalPartySaveData
26 | oldOK, // old committee "ok" tracker
27 | newOK []bool // `ok` tracks parties which have been verified by Update(); this one is for the new committee
28 | started bool
29 | number int
30 | }
31 | round1 struct {
32 | *base
33 | }
34 | round2 struct {
35 | *round1
36 | }
37 | round3 struct {
38 | *round2
39 | }
40 | round4 struct {
41 | *round3
42 | }
43 | round5 struct {
44 | *round4
45 | }
46 | )
47 |
48 | var (
49 | _ tss.Round = (*round1)(nil)
50 | _ tss.Round = (*round2)(nil)
51 | _ tss.Round = (*round3)(nil)
52 | _ tss.Round = (*round4)(nil)
53 | _ tss.Round = (*round5)(nil)
54 | )
55 |
56 | // ----- //
57 |
58 | func (round *base) Params() *tss.Parameters {
59 | return round.ReSharingParameters.Parameters
60 | }
61 |
62 | func (round *base) ReSharingParams() *tss.ReSharingParameters {
63 | return round.ReSharingParameters
64 | }
65 |
66 | func (round *base) RoundNumber() int {
67 | return round.number
68 | }
69 |
70 | // CanProceed is inherited by other rounds
71 | func (round *base) CanProceed() bool {
72 | if !round.started {
73 | return false
74 | }
75 | for _, ok := range append(round.oldOK, round.newOK...) {
76 | if !ok {
77 | return false
78 | }
79 | }
80 | return true
81 | }
82 |
83 | // WaitingFor is called by a Party for reporting back to the caller
84 | func (round *base) WaitingFor() []*tss.PartyID {
85 | oldPs := round.OldParties().IDs()
86 | newPs := round.NewParties().IDs()
87 | idsMap := make(map[*tss.PartyID]bool)
88 | ids := make([]*tss.PartyID, 0, len(round.oldOK))
89 | for j, ok := range round.oldOK {
90 | if ok {
91 | continue
92 | }
93 | idsMap[oldPs[j]] = true
94 | }
95 | for j, ok := range round.newOK {
96 | if ok {
97 | continue
98 | }
99 | idsMap[newPs[j]] = true
100 | }
101 | // consolidate into the list
102 | for id := range idsMap {
103 | ids = append(ids, id)
104 | }
105 | return ids
106 | }
107 |
108 | func (round *base) WrapError(err error, culprits ...*tss.PartyID) *tss.Error {
109 | return tss.NewError(err, TaskName, round.number, round.PartyID(), culprits...)
110 | }
111 |
112 | func (round *base) WrapMultiError(err error, victim *tss.PartyID, culprits ...*tss.PartyID) *tss.Error {
113 | return tss.NewError(err, TaskName, round.number, victim, culprits...)
114 | }
115 |
116 | // ----- //
117 |
118 | // `oldOK` tracks parties which have been verified by Update()
119 | func (round *base) resetOK() {
120 | for j := range round.oldOK {
121 | round.oldOK[j] = false
122 | }
123 | for j := range round.newOK {
124 | round.newOK[j] = false
125 | }
126 | }
127 |
128 | // sets all pairings in `oldOK` to true
129 | func (round *base) allOldOK() {
130 | for j := range round.oldOK {
131 | round.oldOK[j] = true
132 | }
133 | }
134 |
135 | // sets all pairings in `newOK` to true
136 | func (round *base) allNewOK() {
137 | for j := range round.newOK {
138 | round.newOK[j] = true
139 | }
140 | }
141 |
142 | func (round *base) shouldTriggerAbort(trigger ecdsautils.AbortTrigger) bool {
143 | if len(round.temp.abortTriggers) == 0 {
144 | return false
145 | }
146 | for _, t := range round.temp.abortTriggers {
147 | if trigger == t {
148 | return true
149 | }
150 | }
151 | return false
152 | }
153 |
--------------------------------------------------------------------------------
/ecdsa/signing/identification_7.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "errors"
11 | "math/big"
12 | sync "sync"
13 |
14 | "github.com/binance-chain/tss-lib/common"
15 | "github.com/binance-chain/tss-lib/ecdsa/keygen"
16 | "github.com/binance-chain/tss-lib/tss"
17 | )
18 |
19 | func newRound7(params *tss.Parameters, key *keygen.LocalPartySaveData, data *common.SignatureData, temp *localTempData, out chan<- tss.Message, end chan<- common.SignatureData) tss.Round {
20 | return &identification7{&identification6{&identificationPrep{&sign4{&presign3{&presign2{&presign1{
21 | &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 3}}}}, false}}}}
22 | }
23 |
24 | func (round *identification7) Start() *tss.Error {
25 | if round.started {
26 | return round.WrapError(errors.New("round already started"))
27 | }
28 | round.number = 7
29 | round.started = true
30 | round.resetOK()
31 |
32 | i := round.PartyID().Index
33 | round.ok[i] = true
34 | modN := common.ModInt(round.EC().Params().N)
35 |
36 | // Fig 7. Output.2
37 | errChs := make(chan *tss.Error, len(round.Parties().IDs())-1)
38 |
39 | wg := sync.WaitGroup{}
40 | for j, Pj := range round.Parties().IDs() {
41 | if j == i {
42 | continue
43 | }
44 |
45 | wg.Add(1)
46 | go func(j int, Pj *tss.PartyID) {
47 | defer wg.Done()
48 |
49 | proofMul := round.temp.r6msgProofMul[j]
50 | ok := proofMul.Verify(round.EC(), round.key.PaillierPKs[j], round.temp.r1msgK[j], round.temp.r1msgG[j], round.temp.r6msgH[j])
51 | if !ok {
52 | common.Logger.Errorf("round7: proofmul verify failed. Current party(i): %v, culprit(j): %v", round.PartyID(), Pj)
53 | errChs <- round.WrapError(errors.New("round7: proofmul verify failed"), Pj)
54 | return
55 | }
56 | }(j, Pj)
57 |
58 | wg.Add(1)
59 | go func(j int, Pj *tss.PartyID) {
60 | defer wg.Done()
61 | proofDec := round.temp.r6msgProofDec[j]
62 | okDec := proofDec.Verify(round.EC(), round.key.PaillierPKs[j], round.temp.r6msgDeltaShareEnc[j],
63 | modN.Add(zero, round.temp.r6msgEncryptedValueSum[j]), round.key.NTildej[j], round.key.H1j[j], round.key.H2j[j])
64 | if !okDec {
65 | common.Logger.Errorf("round7: proofdec verify failed. Current party(i): %v, culprit(j): %v", round.PartyID(), Pj)
66 | errChs <- round.WrapError(errors.New("round7: proofdec verify failed"), Pj)
67 | return
68 | }
69 | }(j, Pj)
70 |
71 | }
72 | wg.Wait()
73 | close(errChs)
74 | culprits := make([]*tss.PartyID, 0)
75 | for err := range errChs {
76 | culprits = append(culprits, err.Culprits()...)
77 | }
78 | if len(culprits) > 0 {
79 | return round.WrapError(errors.New("round7: identification verify failed"), culprits...)
80 | } else {
81 | common.Logger.Errorf("party %v - abort triggered but no culprit was identified", round.PartyID())
82 | // or when running a unit test where messages are tainted, the current party (i) may be the culprit
83 | }
84 |
85 | // retire unused variables
86 | round.temp.𝛾i = nil
87 | round.temp.DeltaShareBetas = nil
88 | round.temp.DeltaShareBetaNegs = nil
89 |
90 | round.temp.DeltaMtASij = make([]*big.Int, round.PartyCount())
91 | round.temp.DeltaMtARij = make([]*big.Int, round.PartyCount())
92 | round.temp.DeltaMtAFji = make([]*big.Int, round.PartyCount())
93 | round.temp.r1msgG = make([]*big.Int, round.PartyCount())
94 | round.temp.r1msgK = make([]*big.Int, round.PartyCount())
95 | round.temp.r3msg𝛿j = make([]*big.Int, round.PartyCount())
96 | return nil
97 | }
98 |
99 | func (round *identification7) Update() (bool, *tss.Error) {
100 | return true, nil
101 | }
102 |
103 | func (round *identification7) CanAccept(msg tss.ParsedMessage) bool {
104 | return true
105 | }
106 |
107 | func (round *identification7) NextRound() tss.Round {
108 | round.started = false
109 | return nil
110 | }
111 |
--------------------------------------------------------------------------------
/ecdsa/signing/identification_prep.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2021 Swingby
2 |
3 | package signing
4 |
5 | import (
6 | "errors"
7 |
8 | "github.com/binance-chain/tss-lib/common"
9 | "github.com/binance-chain/tss-lib/tss"
10 | )
11 |
12 | func (round *identificationPrep) Start() *tss.Error {
13 | if round.started {
14 | return round.WrapError(errors.New("round already started"))
15 | }
16 | round.number = 5
17 | common.Logger.Debugf("party %v, identificationPrep Start", round.PartyID())
18 | round.started = true
19 | round.AbortingSigning = true
20 | round.resetOK()
21 | i := round.PartyID().Index
22 | round.ok[i] = true
23 | for j, Pj := range round.Parties().IDs() {
24 | if j == i {
25 | continue
26 | }
27 | r5msg := NewIdentificationPrepRound5Message(Pj, round.PartyID(), round.temp.𝛾i, round.temp.DeltaMtASij[j], round.temp.DeltaShareBetaNegs[j])
28 | round.out <- r5msg
29 |
30 | }
31 |
32 | return nil
33 | }
34 |
35 | func (round *identificationPrep) CanAccept(msg tss.ParsedMessage) bool {
36 | if _, ok := msg.Content().(*IdentificationPrepRound5Message); ok {
37 | return !msg.IsBroadcast()
38 | }
39 | return false
40 | }
41 |
42 | func (round *identificationPrep) NextRound() tss.Round {
43 | round.started = false
44 | return &identification6{round}
45 | }
46 |
47 | func (round *identificationPrep) Update() (bool, *tss.Error) {
48 | for j, msg := range round.temp.r5msg𝛾j {
49 | if round.ok[j] {
50 | continue
51 | }
52 | if msg == nil {
53 | return false, nil
54 | }
55 | round.ok[j] = true
56 | }
57 | return true, nil
58 | }
59 |
60 | func (round *identificationPrep) setOK() {
61 | for j := range round.ok {
62 | round.ok[j] = true
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/ecdsa/signing/key_derivation_util.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2021 Swingby
2 |
3 | package signing
4 |
5 | import (
6 | "crypto/ecdsa"
7 | "crypto/elliptic"
8 | "math/big"
9 |
10 | "github.com/binance-chain/tss-lib/common"
11 | "github.com/binance-chain/tss-lib/crypto"
12 | "github.com/binance-chain/tss-lib/crypto/ckd"
13 | "github.com/binance-chain/tss-lib/ecdsa/keygen"
14 | "github.com/btcsuite/btcd/chaincfg"
15 | )
16 |
17 | func UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta *big.Int, keys []keygen.LocalPartySaveData,
18 | extendedChildPk *ecdsa.PublicKey, ec elliptic.Curve) error {
19 | var err error
20 | gDelta := crypto.ScalarBaseMult(ec, keyDerivationDelta)
21 | for k := range keys {
22 | keys[k].ECDSAPub, err = crypto.NewECPoint(ec, extendedChildPk.X, extendedChildPk.Y)
23 | if err != nil {
24 | common.Logger.Errorf("error creating new extended child public key")
25 | return err
26 | }
27 | // Suppose X_j has shamir shares X_j0, X_j1, ..., X_jn
28 | // So X_j + D has shamir shares X_j0 + D, X_j1 + D, ..., X_jn + D
29 | for j := range keys[k].BigXj {
30 | keys[k].BigXj[j], err = keys[k].BigXj[j].Add(gDelta)
31 | if err != nil {
32 | common.Logger.Errorf("error in delta operation")
33 | return err
34 | }
35 | }
36 | }
37 | return nil
38 | }
39 |
40 | func derivingPubkeyFromPath(masterPub *crypto.ECPoint, chainCode []byte, path []uint32, ec elliptic.Curve) (*big.Int, *ckd.ExtendedKey, error) {
41 | // build ecdsa key pair
42 | pk := ecdsa.PublicKey{
43 | Curve: ec,
44 | X: masterPub.X(),
45 | Y: masterPub.Y(),
46 | }
47 |
48 | net := &chaincfg.MainNetParams
49 | extendedParentPk := &ckd.ExtendedKey{
50 | PublicKey: pk,
51 | Depth: 0,
52 | ChildIndex: 0,
53 | ChainCode: chainCode[:],
54 | ParentFP: []byte{0x00, 0x00, 0x00, 0x00},
55 | Version: net.HDPrivateKeyID[:],
56 | }
57 |
58 | return ckd.DeriveChildKeyFromHierarchy(path, extendedParentPk, ec.Params().N, ec)
59 | }
60 |
--------------------------------------------------------------------------------
/ecdsa/signing/mta.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "crypto/elliptic"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/common"
14 | "github.com/binance-chain/tss-lib/crypto"
15 | "github.com/binance-chain/tss-lib/crypto/paillier"
16 | zkpaffg "github.com/binance-chain/tss-lib/crypto/zkp/affg"
17 | )
18 |
19 | type MtAOut struct {
20 | Dji *big.Int
21 | Fji *big.Int
22 | Sij *big.Int
23 | Rij *big.Int
24 | Beta *big.Int
25 | BetaNeg *big.Int
26 | Proofji *zkpaffg.ProofAffg
27 | }
28 |
29 | func NewMtA(ec elliptic.Curve, Kj *big.Int, gammai *big.Int, BigGammai *crypto.ECPoint, pkj *paillier.PublicKey, pki *paillier.PublicKey, NCap, s, t *big.Int) (*MtAOut, error) {
30 | q := ec.Params().N
31 | q3 := new(big.Int).Mul(q, q)
32 | q3 = new(big.Int).Mul(q, q3)
33 |
34 | betaNeg := common.GetRandomPositiveInt(q3)
35 |
36 | gammaK, err := pkj.HomoMult(gammai, Kj)
37 | if err != nil {
38 | return nil, err
39 | }
40 | Dji, sij, err := pkj.EncryptAndReturnRandomness(betaNeg)
41 | if err != nil {
42 | return nil, err
43 | }
44 | Dji, err = pkj.HomoAdd(gammaK, Dji)
45 | if err != nil {
46 | return nil, err
47 | }
48 |
49 | Fji, rij, err := pki.EncryptAndReturnRandomness(betaNeg)
50 | if err != nil {
51 | return nil, err
52 | }
53 |
54 | // q := ec.Params().N
55 | beta := common.ModInt(q).Sub(zero, betaNeg)
56 |
57 | Psiji, err := zkpaffg.NewProof(ec, pkj, pki, NCap, s, t, Kj, Dji, Fji, BigGammai, gammai, betaNeg, sij, rij)
58 | if err != nil {
59 | return nil, err
60 | }
61 |
62 | return &MtAOut{
63 | Dji: Dji,
64 | Fji: Fji,
65 | Sij: sij,
66 | Rij: rij,
67 | Beta: beta,
68 | BetaNeg: betaNeg,
69 | Proofji: Psiji,
70 | }, nil
71 | }
72 |
--------------------------------------------------------------------------------
/ecdsa/signing/mta_test.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "math/big"
11 | "testing"
12 | "time"
13 |
14 | zkpdec "github.com/binance-chain/tss-lib/crypto/zkp/dec"
15 | "github.com/stretchr/testify/assert"
16 |
17 | "github.com/binance-chain/tss-lib/common"
18 | "github.com/binance-chain/tss-lib/crypto"
19 | "github.com/binance-chain/tss-lib/crypto/paillier"
20 | "github.com/binance-chain/tss-lib/ecdsa/keygen"
21 | "github.com/binance-chain/tss-lib/tss"
22 | )
23 |
24 | // Using a modulus length of 2048 is recommended in the GG18 spec
25 | const (
26 | testPaillierKeyLength = 2048
27 | )
28 |
29 | func TestAffg(test *testing.T) {
30 | ec := tss.EC()
31 | q := ec.Params().N
32 | // q3 := new(big.Int).Mul(q, q)
33 | // q3 = new(big.Int).Mul(q, q3)
34 | // q6 := new(big.Int).Mul(q3, q3)
35 |
36 | _, pki, err := paillier.GenerateKeyPair(testPaillierKeyLength, 10*time.Minute)
37 | assert.NoError(test, err)
38 | skj, pkj, err := paillier.GenerateKeyPair(testPaillierKeyLength, 10*time.Minute)
39 | assert.NoError(test, err)
40 |
41 | // gammai * kj == betai + alphaj
42 | kj := common.GetRandomPositiveInt(q)
43 | Kj, err := pkj.Encrypt(kj)
44 | assert.NoError(test, err)
45 |
46 | gammai := common.GetRandomPositiveInt(q)
47 | BigGammai := crypto.ScalarBaseMult(ec, gammai)
48 |
49 | NCap, s, t, err := keygen.LoadNTildeH1H2FromTestFixture(1)
50 | assert.NoError(test, err)
51 |
52 | MtaOut, err := NewMtA(ec, Kj, gammai, BigGammai, pkj, pki, NCap, s, t)
53 | assert.NoError(test, err)
54 |
55 | alphaj, err := skj.Decrypt(MtaOut.Dji)
56 | assert.NoError(test, err)
57 | betai := MtaOut.Beta
58 |
59 | modN := common.ModInt(ec.Params().N)
60 | lhs := modN.Add(alphaj, betai)
61 | rhs := modN.Mul(kj, gammai)
62 | test.Log(lhs, rhs)
63 | assert.Equal(test, 0, lhs.Cmp(rhs))
64 | ok := MtaOut.Proofji.Verify(ec, pkj, pki, NCap, s, t, Kj, MtaOut.Dji, MtaOut.Fji, BigGammai)
65 | assert.True(test, ok)
66 | }
67 |
68 | func TestDec(test *testing.T) {
69 | ec := tss.EC()
70 | q := ec.Params().N
71 | q3 := new(big.Int).Mul(q, new(big.Int).Mul(q, q))
72 | modN := common.ModInt(ec.Params().N)
73 |
74 | _, pki, err := paillier.GenerateKeyPair(testPaillierKeyLength, 10*time.Minute)
75 | assert.NoError(test, err)
76 | _, pkj, err := paillier.GenerateKeyPair(testPaillierKeyLength, 10*time.Minute)
77 | assert.NoError(test, err)
78 |
79 | kj := common.GetRandomPositiveInt(q)
80 | Kj, 𝜌j, err := pkj.EncryptAndReturnRandomness(kj)
81 | assert.NoError(test, err)
82 |
83 | 𝛾i := common.GetRandomPositiveInt(q)
84 | Γi := crypto.ScalarBaseMult(ec, 𝛾i)
85 |
86 | NCap, s, t, err := keygen.LoadNTildeH1H2FromTestFixture(1)
87 | assert.NoError(test, err)
88 |
89 | N2 := pkj.NSquare()
90 |
91 | MtaOut, err := NewMtA(ec, Kj, 𝛾i, Γi, pkj, pki, NCap, s, t)
92 | assert.NoError(test, err)
93 |
94 | 𝜌𝛾s := N2.Mul(big.NewInt(1).Exp(𝜌j, 𝛾i, N2), MtaOut.Sij)
95 | 𝛾k𝛽ʹ := q3.Add(MtaOut.BetaNeg, q3.Mul(𝛾i, kj))
96 |
97 | proofD, err := zkpdec.NewProof(ec, pkj, MtaOut.Dji, modN.Add(zero, 𝛾k𝛽ʹ), NCap, s, t, 𝛾k𝛽ʹ, 𝜌𝛾s)
98 | assert.NoError(test, err)
99 | okD := proofD.Verify(ec, pkj, MtaOut.Dji, modN.Add(zero, 𝛾k𝛽ʹ), NCap, s, t)
100 | assert.True(test, okD, "proof must verify")
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/ecdsa/signing/prepare.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "crypto/elliptic"
11 | "fmt"
12 | "math/big"
13 |
14 | "github.com/binance-chain/tss-lib/common"
15 | "github.com/binance-chain/tss-lib/crypto"
16 | "github.com/binance-chain/tss-lib/tss"
17 | )
18 |
19 | // PrepareForSigning(), GG18Spec (11) Fig. 14
20 | func PrepareForSigning(ec elliptic.Curve, i, pax int, xi *big.Int, ks []*big.Int, bigXs []*crypto.ECPoint) (wi *big.Int, bigWs []*crypto.ECPoint, err error) {
21 | modQ := common.ModInt(ec.Params().N)
22 | if len(ks) != len(bigXs) {
23 | panic(fmt.Errorf("PrepareForSigning: len(ks) != len(bigXs) (%d != %d)", len(ks), len(bigXs)))
24 | }
25 | if len(ks) != pax {
26 | panic(fmt.Errorf("PrepareForSigning: len(ks) != pax (%d != %d)", len(ks), pax))
27 | }
28 | if len(ks) <= i {
29 | panic(fmt.Errorf("PrepareForSigning: len(ks) <= i (%d <= %d)", len(ks), i))
30 | }
31 |
32 | // 2-4.
33 | wi = new(big.Int).Set(xi)
34 | for j := 0; j < pax; j++ {
35 | if j == i {
36 | continue
37 | }
38 | ksj := ks[j]
39 | ksi := ks[i]
40 | if ksj.Cmp(ksi) == 0 {
41 | panic(fmt.Errorf("index of two parties are equal"))
42 | }
43 | // big.Int Div is calculated as: a/b = a * modInv(b,q)
44 | coef := modQ.Mul(ks[j], modQ.Inverse(new(big.Int).Sub(ksj, ksi)))
45 | wi = modQ.Mul(wi, coef)
46 | }
47 |
48 | // 5-10.
49 | bigWs = make([]*crypto.ECPoint, len(ks))
50 | for j := 0; j < pax; j++ {
51 | bigWj := bigXs[j]
52 | for c := 0; c < pax; c++ {
53 | if j == c {
54 | continue
55 | }
56 | ksc, ksj := modQ.Add(ks[c], zero), modQ.Add(ks[j], zero)
57 | if ksj.Cmp(ksc) == 0 {
58 | err = fmt.Errorf("the indices of two parties are equal")
59 | return
60 | }
61 | // big.Int Div is calculated as: a/b = a * modInv(b,q)
62 | iota := modQ.Mul(ksc, modQ.Inverse(new(big.Int).Sub(ksc, ksj)))
63 | bigWj = bigWj.ScalarMult(iota)
64 | }
65 | bigWs[j] = bigWj
66 | }
67 |
68 | // assertion: g^w_i == W_i
69 | if !crypto.ScalarBaseMult(tss.EC(), wi).Equals(bigWs[i]) {
70 | err = fmt.Errorf("assertion failed: g^w_i == W_i")
71 | return
72 | }
73 | return
74 | }
75 |
--------------------------------------------------------------------------------
/ecdsa/signing/rounds.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "github.com/binance-chain/tss-lib/common"
11 | "github.com/binance-chain/tss-lib/ecdsa/keygen"
12 | "github.com/binance-chain/tss-lib/tss"
13 | )
14 |
15 | const (
16 | TaskName = "signing"
17 | )
18 |
19 | type (
20 | base struct {
21 | *tss.Parameters
22 | key *keygen.LocalPartySaveData
23 | data *common.SignatureData
24 | temp *localTempData
25 | out chan<- tss.Message
26 | end chan<- common.SignatureData
27 | ok []bool // `ok` tracks parties which have been verified by Update()
28 | started bool
29 | number int
30 | }
31 | presign1 struct {
32 | *base
33 | }
34 | presign2 struct {
35 | *presign1
36 | }
37 | presign3 struct {
38 | *presign2
39 | }
40 | sign4 struct {
41 | *presign3
42 | AbortingSigning bool
43 | }
44 | identificationPrep struct {
45 | *sign4
46 | }
47 | signout struct {
48 | *sign4
49 | }
50 |
51 | // identification rounds
52 | identification6 struct {
53 | *identificationPrep
54 | }
55 | identification7 struct {
56 | *identification6
57 | }
58 | )
59 |
60 | var (
61 | _ tss.Round = (*presign1)(nil)
62 | _ tss.Round = (*presign2)(nil)
63 | _ tss.Round = (*presign3)(nil)
64 | _ tss.Round = (*sign4)(nil)
65 | _ tss.Round = (*signout)(nil)
66 | _ tss.Round = (*identificationPrep)(nil)
67 | _ tss.Round = (*identification6)(nil)
68 | _ tss.Round = (*identification7)(nil)
69 | )
70 |
71 | // ----- //
72 |
73 | func (round *base) Params() *tss.Parameters {
74 | return round.Parameters
75 | }
76 |
77 | func (round *base) RoundNumber() int {
78 | return round.number
79 | }
80 |
81 | // CanProceed is inherited by other rounds
82 | func (round *base) CanProceed() bool {
83 | if !round.started {
84 | return false
85 | }
86 | for _, ok := range round.ok {
87 | if !ok {
88 | return false
89 | }
90 | }
91 | return true
92 | }
93 |
94 | // WaitingFor is called by a Party for reporting back to the caller
95 | func (round *base) WaitingFor() []*tss.PartyID {
96 | Ps := round.Parties().IDs()
97 | ids := make([]*tss.PartyID, 0, len(round.ok))
98 | for j, ok := range round.ok {
99 | if ok {
100 | continue
101 | }
102 | ids = append(ids, Ps[j])
103 | }
104 | return ids
105 | }
106 |
107 | func (round *base) WrapError(err error, culprits ...*tss.PartyID) *tss.Error {
108 | return tss.NewError(err, TaskName, round.number, round.PartyID(), culprits...)
109 | }
110 |
111 | // ----- //
112 |
113 | // `ok` tracks parties which have been verified by Update()
114 | func (round *base) resetOK() {
115 | for j := range round.ok {
116 | round.ok[j] = false
117 | }
118 | }
119 |
120 | func (round *base) setOK() {
121 | for j := range round.ok {
122 | round.ok[j] = true
123 | }
124 | }
125 |
126 | func (round *base) Dump(dumpCh chan tss.ParsedMessage) {
127 | DumpMsg := NewTempDataDumpMessage(round.PartyID(), *round.temp, round.number)
128 | dumpCh <- DumpMsg
129 | }
130 |
--------------------------------------------------------------------------------
/ecdsa/signing/sign_out.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "crypto/ecdsa"
11 | "crypto/elliptic"
12 | "errors"
13 | "fmt"
14 | "math/big"
15 |
16 | "github.com/binance-chain/tss-lib/common"
17 | "github.com/binance-chain/tss-lib/crypto"
18 | "github.com/binance-chain/tss-lib/ecdsa/keygen"
19 | "github.com/binance-chain/tss-lib/tss"
20 | )
21 |
22 | func VerirySig(ec elliptic.Curve, R *crypto.ECPoint, S *big.Int, m *big.Int, PK *crypto.ECPoint) bool {
23 | modN := common.ModInt(ec.Params().N)
24 | SInv := modN.Inverse(S)
25 | mG := crypto.ScalarBaseMult(ec, m)
26 | rx := R.X()
27 | rxPK := PK.ScalarMult(rx)
28 | R2, _ := mG.Add(rxPK)
29 | R2 = R2.ScalarMult(SInv)
30 | return R2.Equals(R)
31 | }
32 |
33 | func newRound5(params *tss.Parameters, key *keygen.LocalPartySaveData, data *common.SignatureData, temp *localTempData, out chan<- tss.Message, end chan<- common.SignatureData) tss.Round {
34 | return &signout{&sign4{&presign3{&presign2{&presign1{
35 | &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 5}}}}, false}}
36 | }
37 |
38 | func (round *signout) Start() *tss.Error {
39 | if round.started {
40 | return round.WrapError(errors.New("round already started"))
41 | }
42 | round.number = 5
43 | round.started = true
44 | round.resetOK()
45 |
46 | // Fig 8. Output. combine signature shares verify and output
47 | Sigma := round.temp.SigmaShare
48 | modN := common.ModInt(round.Params().EC().Params().N)
49 | for j := range round.Parties().IDs() {
50 | round.ok[j] = true
51 | if j == round.PartyID().Index {
52 | continue
53 | }
54 | Sigma = modN.Add(Sigma, round.temp.r4msg𝜎j[j])
55 | }
56 | recid := 0
57 | // byte v = if(R.X > curve.N) then 2 else 0) | (if R.Y.IsEven then 0 else 1);
58 | if round.temp.Rx.Cmp(round.Params().EC().Params().N) > 0 {
59 | recid = 2
60 | }
61 | if round.temp.BigR.Y().Bit(0) != 0 {
62 | recid |= 1
63 | }
64 |
65 | // This is copied from:
66 | // https://github.com/btcsuite/btcd/blob/c26ffa870fd817666a857af1bf6498fabba1ffe3/btcec/signature.go#L442-L444
67 | // This is needed because of tendermint checks here:
68 | // https://github.com/tendermint/tendermint/blob/d9481e3648450cb99e15c6a070c1fb69aa0c255b/crypto/secp256k1/secp256k1_nocgo.go#L43-L47
69 | halfN := new(big.Int).Rsh(round.Params().EC().Params().N, 1)
70 | if Sigma.Cmp(halfN) > 0 {
71 | Sigma.Sub(round.Params().EC().Params().N, Sigma)
72 | recid ^= 1
73 | }
74 |
75 | // save the signature for final output
76 | bitSizeInBytes := round.Params().EC().Params().BitSize / 8
77 | round.data.R = padToLengthBytesInPlace(round.temp.Rx.Bytes(), bitSizeInBytes)
78 | round.data.S = padToLengthBytesInPlace(Sigma.Bytes(), bitSizeInBytes)
79 | round.data.Signature = append(round.data.R, round.data.S...)
80 | round.data.SignatureRecovery = []byte{byte(recid)}
81 | round.data.M = round.temp.m.Bytes()
82 |
83 | pk := ecdsa.PublicKey{
84 | Curve: round.Params().EC(),
85 | X: round.key.ECDSAPub.X(),
86 | Y: round.key.ECDSAPub.Y(),
87 | }
88 | ok := ecdsa.Verify(&pk, round.temp.m.Bytes(), round.temp.Rx, Sigma)
89 | if !ok {
90 | return round.WrapError(fmt.Errorf("signature verification failed"))
91 | }
92 |
93 | round.end <- *round.data
94 | round.temp.G = nil
95 | round.temp.𝜈i = nil
96 | return nil
97 | }
98 |
99 | func (round *signout) CanAccept(msg tss.ParsedMessage) bool {
100 | // not expecting any incoming messages in this round
101 | return false
102 | }
103 |
104 | func (round *signout) Update() (bool, *tss.Error) {
105 | // not expecting any incoming messages in this round
106 | return false, nil
107 | }
108 |
109 | func (round *signout) NextRound() tss.Round {
110 | return nil // finished!
111 | }
112 |
113 | func padToLengthBytesInPlace(src []byte, length int) []byte {
114 | oriLen := len(src)
115 | if oriLen < length {
116 | for i := 0; i < length-oriLen; i++ {
117 | src = append([]byte{0}, src...)
118 | }
119 | }
120 | return src
121 | }
122 |
--------------------------------------------------------------------------------
/ecdsa/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2021 Swingby
2 |
3 | package ecdsautils
4 |
5 | import (
6 | "crypto/ecdsa"
7 | "encoding/json"
8 | "math/big"
9 |
10 | "github.com/binance-chain/tss-lib/common"
11 | "github.com/binance-chain/tss-lib/crypto/paillier"
12 | "github.com/binance-chain/tss-lib/crypto/vss"
13 | "github.com/binance-chain/tss-lib/tss"
14 | )
15 |
16 | type ECDSASignature struct {
17 | R, S *big.Int
18 | }
19 |
20 | type AbortTrigger int
21 |
22 | func HashShare(share *vss.Share) (hash []byte) {
23 | hash = append(share.ID.Bytes(), share.Share.Bytes()...)
24 | hash = append(hash, big.NewInt(int64(share.Threshold)).Bytes()...)
25 | hash = common.SHA512_256(hash)
26 | return
27 | }
28 |
29 | func NewECDSASignature(r, s *big.Int) *ECDSASignature {
30 | return &ECDSASignature{R: r, S: s}
31 | }
32 |
33 | func HashPaillierKey(pk *paillier.PublicKey) (hash []byte) {
34 | hash = common.SHA512_256i(pk.AsInts()...).Bytes()
35 | return
36 | }
37 |
38 | func (k MarshallableEcdsaPrivateKey) MarshalJSON() ([]byte, error) {
39 | return json.Marshal(struct {
40 | PublicKey MarshallableEcdsaPublicKey
41 | D *big.Int
42 | }{
43 | PublicKey: (MarshallableEcdsaPublicKey)(k.PublicKey),
44 | D: k.D,
45 | })
46 | }
47 |
48 | func (k *MarshallableEcdsaPrivateKey) UnmarshalJSON(b []byte) error {
49 | // PrivateKey represents an ECDSA private key.
50 | newKey := new(struct {
51 | PublicKey MarshallableEcdsaPublicKey
52 | D *big.Int
53 | })
54 | if err := json.Unmarshal(b, &newKey); err != nil {
55 | return err
56 | }
57 | k.D = newKey.D
58 | k.PublicKey = (ecdsa.PublicKey)(newKey.PublicKey)
59 |
60 | return nil
61 | }
62 |
63 | func (k MarshallableEcdsaPublicKey) MarshalJSON() ([]byte, error) {
64 | return json.Marshal(struct {
65 | X, Y *big.Int
66 | }{
67 | X: k.X,
68 | Y: k.Y,
69 | })
70 | }
71 |
72 | func (k *MarshallableEcdsaPublicKey) UnmarshalJSON(b []byte) error {
73 | newKey := new(struct {
74 | X, Y *big.Int
75 | })
76 | if err := json.Unmarshal(b, &newKey); err != nil {
77 | return err
78 | }
79 | k.X = newKey.X
80 | k.Y = newKey.Y
81 | k.Curve = tss.EC()
82 |
83 | return nil
84 | }
85 |
86 | // We will customize the Json serialization of the public key
87 | // used for party authentication.
88 | // The serialization of the Koblitz curve showed problems,
89 | // as the type does not expose a number of attributes.
90 | type MarshallableEcdsaPublicKey ecdsa.PublicKey
91 |
92 | type MarshallableEcdsaPrivateKey ecdsa.PrivateKey
93 |
94 | func ProofNSquareFree(NTildei *big.Int, p *big.Int, q *big.Int) (*big.Int, *big.Int) {
95 | randIntProofNSquareFreei := common.GetRandomPositiveInt(NTildei)
96 |
97 | // Using Euler's totient function: phi(N)=phi(P)(Q)=(P-1)(Q-1)=2p2q
98 | phiNTildei := new(big.Int).Mul(new(big.Int).Mul(big.NewInt(4), p), q)
99 | bigM := new(big.Int).ModInverse(NTildei, phiNTildei)
100 | proofNSquareFree := common.ModInt(NTildei).Exp(randIntProofNSquareFreei, bigM)
101 | return randIntProofNSquareFreei, proofNSquareFree
102 | }
103 |
--------------------------------------------------------------------------------
/eddsa/keygen/messages.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "crypto/elliptic"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/common"
14 | cmt "github.com/binance-chain/tss-lib/crypto/commitments"
15 | "github.com/binance-chain/tss-lib/crypto/vss"
16 | zkpsch "github.com/binance-chain/tss-lib/crypto/zkp/sch"
17 | "github.com/binance-chain/tss-lib/tss"
18 | )
19 |
20 | // These messages were generated from Protocol Buffers definitions into eddsa-keygen.pb.go
21 | // The following messages are registered on the Protocol Buffers "wire"
22 |
23 | var (
24 | // Ensure that keygen messages implement ValidateBasic
25 | _ = []tss.MessageContent{
26 | (*KGRound1Message)(nil),
27 | (*KGRound2Message1)(nil),
28 | (*KGRound2Message2)(nil),
29 | }
30 | )
31 |
32 | // ----- //
33 |
34 | func NewKGRound1Message(from *tss.PartyID, ct cmt.HashCommitment) tss.ParsedMessage {
35 | meta := tss.MessageRouting{
36 | From: from,
37 | IsBroadcast: true,
38 | }
39 | content := &KGRound1Message{
40 | Commitment: ct.Bytes(),
41 | }
42 | msg := tss.NewMessageWrapper(meta, content)
43 | return tss.NewMessage(meta, content, msg)
44 | }
45 |
46 | func (m *KGRound1Message) ValidateBasic() bool {
47 | return m != nil && common.NonEmptyBytes(m.GetCommitment())
48 | }
49 |
50 | func (m *KGRound1Message) UnmarshalCommitment() *big.Int {
51 | return new(big.Int).SetBytes(m.GetCommitment())
52 | }
53 |
54 | // ----- //
55 |
56 | func NewKGRound2Message1(
57 | to, from *tss.PartyID,
58 | share *vss.Share,
59 | ) tss.ParsedMessage {
60 | meta := tss.MessageRouting{
61 | From: from,
62 | To: []*tss.PartyID{to},
63 | IsBroadcast: false,
64 | }
65 | content := &KGRound2Message1{
66 | Share: share.Share.Bytes(),
67 | }
68 | msg := tss.NewMessageWrapper(meta, content)
69 | return tss.NewMessage(meta, content, msg)
70 | }
71 |
72 | func (m *KGRound2Message1) ValidateBasic() bool {
73 | return m != nil &&
74 | common.NonEmptyBytes(m.GetShare())
75 | }
76 |
77 | func (m *KGRound2Message1) UnmarshalShare() *big.Int {
78 | return new(big.Int).SetBytes(m.Share)
79 | }
80 |
81 | // ----- //
82 |
83 | func NewKGRound2Message2(
84 | from *tss.PartyID,
85 | deCommitment cmt.HashDeCommitment,
86 | proof *zkpsch.ProofSch,
87 | ) tss.ParsedMessage {
88 | meta := tss.MessageRouting{
89 | From: from,
90 | IsBroadcast: true,
91 | }
92 | dcBzs := common.BigIntsToBytes(deCommitment)
93 | proofBzs := proof.Bytes()
94 | content := &KGRound2Message2{
95 | DeCommitment: dcBzs,
96 | Proof: proofBzs[:],
97 | }
98 | msg := tss.NewMessageWrapper(meta, content)
99 | return tss.NewMessage(meta, content, msg)
100 | }
101 |
102 | func (m *KGRound2Message2) ValidateBasic() bool {
103 | return m != nil &&
104 | common.NonEmptyMultiBytes(m.GetDeCommitment()) &&
105 | common.NonEmptyMultiBytes(m.Proof, zkpsch.ProofSchBytesParts)
106 | }
107 |
108 | func (m *KGRound2Message2) UnmarshalDeCommitment() []*big.Int {
109 | deComBzs := m.GetDeCommitment()
110 | return cmt.NewHashDeCommitmentFromBytes(deComBzs)
111 | }
112 |
113 | func (m *KGRound2Message2) UnmarshalZKProof(ec elliptic.Curve) (*zkpsch.ProofSch, error) {
114 | return zkpsch.NewProofFromBytes(ec, m.GetProof())
115 | }
116 |
--------------------------------------------------------------------------------
/eddsa/keygen/round_1.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "errors"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/common"
14 | "github.com/binance-chain/tss-lib/crypto"
15 | cmts "github.com/binance-chain/tss-lib/crypto/commitments"
16 | "github.com/binance-chain/tss-lib/crypto/vss"
17 | "github.com/binance-chain/tss-lib/tss"
18 | )
19 |
20 | var (
21 | zero = big.NewInt(0)
22 | )
23 |
24 | // round 1 represents round 1 of the keygen part of the EDDSA TSS spec
25 | func newRound1(params *tss.Parameters, save *LocalPartySaveData, temp *localTempData, out chan<- tss.Message, end chan<- LocalPartySaveData) tss.Round {
26 | return &round1{
27 | &base{params, save, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 1}}
28 | }
29 |
30 | func (round *round1) Start() *tss.Error {
31 | if round.started {
32 | return round.WrapError(errors.New("round already started"))
33 | }
34 | round.number = 1
35 | round.started = true
36 | round.resetOK()
37 |
38 | Pi := round.PartyID()
39 | i := Pi.Index
40 |
41 | // 1. calculate "partial" key share ui
42 | ui := common.GetRandomPositiveInt(round.Params().EC().Params().N)
43 | round.temp.ui = ui
44 |
45 | // 2. compute the vss shares
46 | ids := round.Parties().IDs().Keys()
47 | vs, shares, err := vss.Create(round.Params().EC(), round.Threshold(), ui, ids)
48 | if err != nil {
49 | return round.WrapError(err, Pi)
50 | }
51 | round.save.Ks = ids
52 |
53 | // security: the original u_i may be discarded
54 | ui = zero // clears the secret data from memory
55 | _ = ui // silences a linter warning
56 |
57 | // 3. make commitment -> (C, D)
58 | pGFlat, err := crypto.FlattenECPoints(vs)
59 | if err != nil {
60 | return round.WrapError(err, Pi)
61 | }
62 | cmt := cmts.NewHashCommitment(pGFlat...)
63 |
64 | // for this P: SAVE
65 | // - shareID
66 | // and keep in temporary storage:
67 | // - VSS Vs
68 | // - our set of Shamir shares
69 | round.save.ShareID = ids[i]
70 | round.temp.vs = vs
71 | round.temp.shares = shares
72 |
73 | round.temp.deCommitPolyG = cmt.D
74 |
75 | // BROADCAST commitments
76 | {
77 | msg := NewKGRound1Message(round.PartyID(), cmt.C)
78 | round.temp.kgRound1Messages[i] = msg
79 | round.out <- msg
80 | }
81 | return nil
82 | }
83 |
84 | func (round *round1) CanAccept(msg tss.ParsedMessage) bool {
85 | if _, ok := msg.Content().(*KGRound1Message); ok {
86 | return msg.IsBroadcast()
87 | }
88 | return false
89 | }
90 |
91 | func (round *round1) Update() (bool, *tss.Error) {
92 | for j, msg := range round.temp.kgRound1Messages {
93 | if round.ok[j] {
94 | continue
95 | }
96 | if msg == nil || !round.CanAccept(msg) {
97 | return false, nil
98 | }
99 | // vss check is in round 2
100 | round.ok[j] = true
101 | }
102 | return true, nil
103 | }
104 |
105 | func (round *round1) NextRound() tss.Round {
106 | round.started = false
107 | return &round2{round}
108 | }
109 |
--------------------------------------------------------------------------------
/eddsa/keygen/round_2.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "errors"
11 |
12 | errors2 "github.com/pkg/errors"
13 |
14 | zkpsch "github.com/binance-chain/tss-lib/crypto/zkp/sch"
15 | "github.com/binance-chain/tss-lib/tss"
16 | )
17 |
18 | func (round *round2) Start() *tss.Error {
19 | if round.started {
20 | return round.WrapError(errors.New("round already started"))
21 | }
22 | round.number = 2
23 | round.started = true
24 | round.resetOK()
25 |
26 | i := round.PartyID().Index
27 |
28 | // 4. store r1 message pieces
29 | for j, msg := range round.temp.kgRound1Messages {
30 | r1msg := msg.Content().(*KGRound1Message)
31 | round.temp.KGCs[j] = r1msg.UnmarshalCommitment()
32 | }
33 |
34 | // 3. p2p send share ij to Pj
35 | shares := round.temp.shares
36 | for j, Pj := range round.Parties().IDs() {
37 | r2msg1 := NewKGRound2Message1(Pj, round.PartyID(), shares[j])
38 | // do not send to this Pj, but store for round 3
39 | if j == i {
40 | round.temp.kgRound2Message1s[j] = r2msg1
41 | continue
42 | }
43 | round.temp.kgRound2Message1s[i] = r2msg1
44 | round.out <- r2msg1
45 | }
46 |
47 | // 5. compute Schnorr prove
48 | pii, err := zkpsch.NewProof(round.temp.vs[0], round.temp.ui)
49 | if err != nil {
50 | return round.WrapError(errors2.Wrapf(err, "NewZKProof(ui, vi0)"))
51 | }
52 |
53 | // 5. BROADCAST de-commitments of Shamir poly*G and Schnorr prove
54 | r2msg2 := NewKGRound2Message2(round.PartyID(), round.temp.deCommitPolyG, pii)
55 | round.temp.kgRound2Message2s[i] = r2msg2
56 | round.out <- r2msg2
57 |
58 | return nil
59 | }
60 |
61 | func (round *round2) CanAccept(msg tss.ParsedMessage) bool {
62 | if _, ok := msg.Content().(*KGRound2Message1); ok {
63 | return !msg.IsBroadcast()
64 | }
65 | if _, ok := msg.Content().(*KGRound2Message2); ok {
66 | return msg.IsBroadcast()
67 | }
68 | return false
69 | }
70 |
71 | func (round *round2) Update() (bool, *tss.Error) {
72 | // guard - VERIFY de-commit for all Pj
73 | for j, msg := range round.temp.kgRound2Message1s {
74 | if round.ok[j] {
75 | continue
76 | }
77 | if msg == nil || !round.CanAccept(msg) {
78 | return false, nil
79 | }
80 | msg2 := round.temp.kgRound2Message2s[j]
81 | if msg2 == nil || !round.CanAccept(msg2) {
82 | return false, nil
83 | }
84 | round.ok[j] = true
85 | }
86 | return true, nil
87 | }
88 |
89 | func (round *round2) NextRound() tss.Round {
90 | round.started = false
91 | return &round3{round}
92 | }
93 |
--------------------------------------------------------------------------------
/eddsa/keygen/rounds.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "github.com/binance-chain/tss-lib/tss"
11 | )
12 |
13 | const (
14 | TaskName = "eddsa-keygen"
15 | )
16 |
17 | type (
18 | base struct {
19 | *tss.Parameters
20 | save *LocalPartySaveData
21 | temp *localTempData
22 | out chan<- tss.Message
23 | end chan<- LocalPartySaveData
24 | ok []bool // `ok` tracks parties which have been verified by Update()
25 | started bool
26 | number int
27 | }
28 | round1 struct {
29 | *base
30 | }
31 | round2 struct {
32 | *round1
33 | }
34 | round3 struct {
35 | *round2
36 | }
37 | )
38 |
39 | func (round *base) Params() *tss.Parameters {
40 | return round.Parameters
41 | }
42 |
43 | func (round *base) RoundNumber() int {
44 | return round.number
45 | }
46 |
47 | // CanProceed is inherited by other rounds
48 | func (round *base) CanProceed() bool {
49 | if !round.started {
50 | return false
51 | }
52 | for _, ok := range round.ok {
53 | if !ok {
54 | return false
55 | }
56 | }
57 | return true
58 | }
59 |
60 | // WaitingFor is called by a Party for reporting back to the caller
61 | func (round *base) WaitingFor() []*tss.PartyID {
62 | Ps := round.Parties().IDs()
63 | ids := make([]*tss.PartyID, 0, len(round.ok))
64 | for j, ok := range round.ok {
65 | if ok {
66 | continue
67 | }
68 | ids = append(ids, Ps[j])
69 | }
70 | return ids
71 | }
72 |
73 | func (round *base) WrapError(err error, culprits ...*tss.PartyID) *tss.Error {
74 | return tss.NewError(err, TaskName, round.number, round.PartyID(), culprits...)
75 | }
76 |
77 | // ----- //
78 |
79 | // `ok` tracks parties which have been verified by Update()
80 | func (round *base) resetOK() {
81 | for j := range round.ok {
82 | round.ok[j] = false
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/eddsa/keygen/save_data.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "encoding/hex"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/crypto"
14 | "github.com/binance-chain/tss-lib/tss"
15 | )
16 |
17 | type (
18 | LocalSecrets struct {
19 | // secret fields (not shared, but stored locally)
20 | Xi, ShareID *big.Int // xi, kj
21 | }
22 |
23 | // Everything in LocalPartySaveData is saved locally to user's HD when done
24 | LocalPartySaveData struct {
25 | LocalSecrets
26 |
27 | // original indexes (ki in signing preparation phase)
28 | Ks []*big.Int
29 |
30 | // public keys (Xj = uj*G for each Pj)
31 | BigXj []*crypto.ECPoint // Xj
32 |
33 | // the EdDSA public key
34 | EDDSAPub *crypto.ECPoint // y
35 | }
36 | )
37 |
38 | func NewLocalPartySaveData(partyCount int) (saveData LocalPartySaveData) {
39 | saveData.Ks = make([]*big.Int, partyCount)
40 | saveData.BigXj = make([]*crypto.ECPoint, partyCount)
41 | return
42 | }
43 |
44 | // BuildLocalSaveDataSubset re-creates the LocalPartySaveData to contain data for only the list of signing parties.
45 | func BuildLocalSaveDataSubset(sourceData LocalPartySaveData, sortedIDs tss.SortedPartyIDs) LocalPartySaveData {
46 | keysToIndices := make(map[string]int, len(sourceData.Ks))
47 | for j, kj := range sourceData.Ks {
48 | keysToIndices[hex.EncodeToString(kj.Bytes())] = j
49 | }
50 | newData := NewLocalPartySaveData(sortedIDs.Len())
51 | newData.LocalSecrets = sourceData.LocalSecrets
52 | newData.EDDSAPub = sourceData.EDDSAPub
53 | for j, id := range sortedIDs {
54 | savedIdx, ok := keysToIndices[hex.EncodeToString(id.Key)]
55 | if !ok {
56 | panic("BuildLocalSaveDataSubset: unable to find a signer party in the local save data")
57 | }
58 | newData.Ks[j] = sourceData.Ks[savedIdx]
59 | newData.BigXj[j] = sourceData.BigXj[savedIdx]
60 | }
61 | return newData
62 | }
63 |
--------------------------------------------------------------------------------
/eddsa/keygen/test_utils.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package keygen
8 |
9 | import (
10 | "encoding/json"
11 | "fmt"
12 | "io/ioutil"
13 | "math/rand"
14 | "path/filepath"
15 | "runtime"
16 | "sort"
17 |
18 | "github.com/pkg/errors"
19 |
20 | "github.com/binance-chain/tss-lib/test"
21 | "github.com/binance-chain/tss-lib/tss"
22 | )
23 |
24 | const (
25 | // To change these parameters, you must first delete the text fixture files in test/_fixtures/ and then run the keygen test alone.
26 | // Then the signing and resharing tests will work with the new n, t configuration using the newly written fixture files.
27 | TestParticipants = test.TestParticipants
28 | TestThreshold = test.TestParticipants / 2
29 | )
30 | const (
31 | testFixtureDirFormat = "%s/../../test/_eddsa_fixtures"
32 | testFixtureFileFormat = "keygen_data_%d.json"
33 | )
34 |
35 | func LoadKeygenTestFixtures(qty int, optionalStart ...int) ([]LocalPartySaveData, tss.SortedPartyIDs, error) {
36 | keys := make([]LocalPartySaveData, 0, qty)
37 | start := 0
38 | if 0 < len(optionalStart) {
39 | start = optionalStart[0]
40 | }
41 | for i := start; i < qty; i++ {
42 | fixtureFilePath := makeTestFixtureFilePath(i)
43 | bz, err := ioutil.ReadFile(fixtureFilePath)
44 | if err != nil {
45 | return nil, nil, errors.Wrapf(err,
46 | "could not open the test fixture for party %d in the expected location: %s. run keygen tests first.",
47 | i, fixtureFilePath)
48 | }
49 | var key LocalPartySaveData
50 | if err = json.Unmarshal(bz, &key); err != nil {
51 | return nil, nil, errors.Wrapf(err,
52 | "could not unmarshal fixture data for party %d located at: %s",
53 | i, fixtureFilePath)
54 | }
55 | for _, kbxj := range key.BigXj {
56 | kbxj.SetCurve(tss.Edwards())
57 | }
58 | key.EDDSAPub.SetCurve(tss.Edwards())
59 | keys = append(keys, key)
60 | }
61 | partyIDs := make(tss.UnSortedPartyIDs, len(keys))
62 | for i, key := range keys {
63 | pMoniker := fmt.Sprintf("%d", i+start+1)
64 | partyIDs[i] = tss.NewPartyID(pMoniker, pMoniker, key.ShareID)
65 | }
66 | sortedPIDs := tss.SortPartyIDs(partyIDs)
67 | return keys, sortedPIDs, nil
68 | }
69 |
70 | func LoadKeygenTestFixturesRandomSet(qty, fixtureCount int) ([]LocalPartySaveData, tss.SortedPartyIDs, error) {
71 | keys := make([]LocalPartySaveData, 0, qty)
72 | plucked := make(map[int]interface{}, qty)
73 | for i := 0; len(plucked) < qty; i = (i + 1) % fixtureCount {
74 | _, have := plucked[i]
75 | if pluck := rand.Float32() < 0.5; !have && pluck {
76 | plucked[i] = new(struct{})
77 | }
78 | }
79 | for i := range plucked {
80 | fixtureFilePath := makeTestFixtureFilePath(i)
81 | bz, err := ioutil.ReadFile(fixtureFilePath)
82 | if err != nil {
83 | return nil, nil, errors.Wrapf(err,
84 | "could not open the test fixture for party %d in the expected location: %s. run keygen tests first.",
85 | i, fixtureFilePath)
86 | }
87 | var key LocalPartySaveData
88 | if err = json.Unmarshal(bz, &key); err != nil {
89 | return nil, nil, errors.Wrapf(err,
90 | "could not unmarshal fixture data for party %d located at: %s",
91 | i, fixtureFilePath)
92 | }
93 | for _, kbxj := range key.BigXj {
94 | kbxj.SetCurve(tss.Edwards())
95 | }
96 | key.EDDSAPub.SetCurve(tss.Edwards())
97 | keys = append(keys, key)
98 | }
99 | partyIDs := make(tss.UnSortedPartyIDs, len(keys))
100 | j := 0
101 | for i := range plucked {
102 | key := keys[j]
103 | pMoniker := fmt.Sprintf("%d", i+1)
104 | partyIDs[j] = tss.NewPartyID(pMoniker, pMoniker, key.ShareID)
105 | j++
106 | }
107 | sortedPIDs := tss.SortPartyIDs(partyIDs)
108 | sort.Slice(keys, func(i, j int) bool { return keys[i].ShareID.Cmp(keys[j].ShareID) == -1 })
109 | return keys, sortedPIDs, nil
110 | }
111 |
112 | func makeTestFixtureFilePath(partyIndex int) string {
113 | _, callerFileName, _, _ := runtime.Caller(0)
114 | srcDirName := filepath.Dir(callerFileName)
115 | fixtureDirName := fmt.Sprintf(testFixtureDirFormat, srcDirName)
116 | return fmt.Sprintf("%s/"+testFixtureFileFormat, fixtureDirName, partyIndex)
117 | }
118 |
--------------------------------------------------------------------------------
/eddsa/resharing/round_1_old_step_1.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package resharing
8 |
9 | import (
10 | "errors"
11 | "fmt"
12 |
13 | "github.com/binance-chain/tss-lib/crypto"
14 | "github.com/binance-chain/tss-lib/crypto/commitments"
15 | "github.com/binance-chain/tss-lib/crypto/vss"
16 | "github.com/binance-chain/tss-lib/eddsa/keygen"
17 | "github.com/binance-chain/tss-lib/eddsa/signing"
18 | "github.com/binance-chain/tss-lib/tss"
19 | )
20 |
21 | // round 1 represents round 1 of the keygen part of the EDDSA TSS spec
22 | func newRound1(params *tss.ReSharingParameters, input, save *keygen.LocalPartySaveData, temp *localTempData, out chan<- tss.Message, end chan<- keygen.LocalPartySaveData) tss.Round {
23 | return &round1{
24 | &base{params, temp, input, save, out, end, make([]bool, len(params.OldParties().IDs())), make([]bool, len(params.NewParties().IDs())), false, 1}}
25 | }
26 |
27 | func (round *round1) Start() *tss.Error {
28 | if round.started {
29 | return round.WrapError(errors.New("round already started"))
30 | }
31 | round.number = 1
32 | round.started = true
33 | round.resetOK() // resets both round.oldOK and round.newOK
34 | round.allNewOK()
35 |
36 | if !round.ReSharingParams().IsOldCommittee() {
37 | return nil
38 | }
39 | round.allOldOK()
40 |
41 | Pi := round.PartyID()
42 | i := Pi.Index
43 |
44 | // 1. PrepareForSigning() -> w_i
45 | xi, ks := round.input.Xi, round.input.Ks
46 | if round.Threshold()+1 > len(ks) {
47 | return round.WrapError(fmt.Errorf("t+1=%d is not satisfied by the key count of %d", round.Threshold()+1, len(ks)), round.PartyID())
48 | }
49 | newKs := round.NewParties().IDs().Keys()
50 | wi := signing.PrepareForSigning(round.Params().EC(), i, len(round.OldParties().IDs()), xi, ks)
51 |
52 | // 2.
53 | vi, shares, err := vss.Create(round.Params().EC(), round.NewThreshold(), wi, newKs)
54 | if err != nil {
55 | return round.WrapError(err, round.PartyID())
56 | }
57 |
58 | // 3.
59 | flatVis, err := crypto.FlattenECPoints(vi)
60 | if err != nil {
61 | return round.WrapError(err, round.PartyID())
62 | }
63 | vCmt := commitments.NewHashCommitment(flatVis...)
64 |
65 | // 4. populate temp data
66 | round.temp.VD = vCmt.D
67 | round.temp.NewShares = shares
68 |
69 | // 5. "broadcast" C_i to members of the NEW committee
70 | r1msg := NewDGRound1Message(
71 | round.NewParties().IDs().Exclude(round.PartyID()), round.PartyID(),
72 | round.input.EDDSAPub, vCmt.C)
73 | round.temp.dgRound1Messages[i] = r1msg
74 | round.out <- r1msg
75 |
76 | return nil
77 | }
78 |
79 | func (round *round1) CanAccept(msg tss.ParsedMessage) bool {
80 | // accept messages from old -> new committee
81 | if _, ok := msg.Content().(*DGRound1Message); ok {
82 | return msg.IsBroadcast()
83 | }
84 | return false
85 | }
86 |
87 | func (round *round1) Update() (bool, *tss.Error) {
88 | // only the new committee receive in this round
89 | if !round.ReSharingParameters.IsNewCommittee() {
90 | return true, nil
91 | }
92 | // accept messages from old -> new committee
93 | for j, msg := range round.temp.dgRound1Messages {
94 | if round.oldOK[j] {
95 | continue
96 | }
97 | if msg == nil || !round.CanAccept(msg) {
98 | return false, nil
99 | }
100 | round.oldOK[j] = true
101 |
102 | // save the eddsa pub received from the old committee
103 | r1msg := round.temp.dgRound1Messages[0].Content().(*DGRound1Message)
104 | candidate, err := r1msg.UnmarshalEDDSAPub(round.Params().EC())
105 | if err != nil {
106 | return false, round.WrapError(errors.New("unable to unmarshal the eddsa pub key"), msg.GetFrom())
107 | }
108 | if round.save.EDDSAPub != nil &&
109 | !candidate.Equals(round.save.EDDSAPub) {
110 | // uh oh - anomaly!
111 | return false, round.WrapError(errors.New("eddsa pub key did not match what we received previously"), msg.GetFrom())
112 | }
113 | round.save.EDDSAPub = candidate
114 | }
115 | return true, nil
116 | }
117 |
118 | func (round *round1) NextRound() tss.Round {
119 | round.started = false
120 | return &round2{round}
121 | }
122 |
--------------------------------------------------------------------------------
/eddsa/resharing/round_2_new_step_1.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package resharing
8 |
9 | import (
10 | "errors"
11 |
12 | "github.com/binance-chain/tss-lib/tss"
13 | )
14 |
15 | func (round *round2) Start() *tss.Error {
16 | if round.started {
17 | return round.WrapError(errors.New("round already started"))
18 | }
19 | round.number = 2
20 | round.started = true
21 | round.resetOK() // resets both round.oldOK and round.newOK
22 | round.allOldOK()
23 |
24 | if !round.ReSharingParams().IsNewCommittee() {
25 | return nil
26 | }
27 | round.allNewOK()
28 |
29 | Pi := round.PartyID()
30 | i := Pi.Index
31 |
32 | // 1. "broadcast" "ACK" members of the OLD committee
33 | r2msg := NewDGRound2Message(round.OldParties().IDs(), Pi)
34 | round.temp.dgRound2Messages[i] = r2msg
35 | round.out <- r2msg
36 |
37 | return nil
38 | }
39 |
40 | func (round *round2) CanAccept(msg tss.ParsedMessage) bool {
41 | if _, ok := msg.Content().(*DGRound2Message); ok {
42 | return msg.IsBroadcast()
43 | }
44 | return false
45 | }
46 |
47 | func (round *round2) Update() (bool, *tss.Error) {
48 | // only the old committee receive in this round
49 | if !round.ReSharingParams().IsOldCommittee() {
50 | return true, nil
51 | }
52 |
53 | // accept messages from new -> old committee
54 | for j, msg := range round.temp.dgRound2Messages {
55 | if round.newOK[j] {
56 | continue
57 | }
58 | if msg == nil || !round.CanAccept(msg) {
59 | return false, nil
60 | }
61 | round.newOK[j] = true
62 | }
63 |
64 | return true, nil
65 | }
66 |
67 | func (round *round2) NextRound() tss.Round {
68 | round.started = false
69 | return &round3{round}
70 | }
71 |
--------------------------------------------------------------------------------
/eddsa/resharing/round_3_old_step_2.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package resharing
8 |
9 | import (
10 | "errors"
11 |
12 | "github.com/binance-chain/tss-lib/tss"
13 | )
14 |
15 | func (round *round3) Start() *tss.Error {
16 | if round.started {
17 | return round.WrapError(errors.New("round already started"))
18 | }
19 | round.number = 3
20 | round.started = true
21 | round.resetOK() // resets both round.oldOK and round.newOK
22 | round.allNewOK()
23 |
24 | if !round.ReSharingParams().IsOldCommittee() {
25 | return nil
26 | }
27 | round.allOldOK()
28 |
29 | Pi := round.PartyID()
30 | i := Pi.Index
31 |
32 | // 1-2. send share to Pj from the new committee
33 | for j, Pj := range round.NewParties().IDs() {
34 | share := round.temp.NewShares[j]
35 | r3msg1 := NewDGRound3Message1(Pj, round.PartyID(), share)
36 | round.temp.dgRound3Message1s[i] = r3msg1
37 | round.out <- r3msg1
38 | }
39 |
40 | // 3. broadcast de-commitment to new committees
41 | vDeCmt := round.temp.VD
42 | r3msg2 := NewDGRound3Message2(
43 | round.NewParties().IDs().Exclude(round.PartyID()), round.PartyID(),
44 | vDeCmt)
45 | round.temp.dgRound3Message2s[i] = r3msg2
46 | round.out <- r3msg2
47 |
48 | return nil
49 | }
50 |
51 | func (round *round3) CanAccept(msg tss.ParsedMessage) bool {
52 | if _, ok := msg.Content().(*DGRound3Message1); ok {
53 | return !msg.IsBroadcast()
54 | }
55 | if _, ok := msg.Content().(*DGRound3Message2); ok {
56 | return msg.IsBroadcast()
57 | }
58 | return false
59 | }
60 |
61 | func (round *round3) Update() (bool, *tss.Error) {
62 | // only the new committee receive in this round
63 | if !round.ReSharingParams().IsNewCommittee() {
64 | return true, nil
65 | }
66 |
67 | // accept messages from old -> new committee
68 | for j, msg1 := range round.temp.dgRound3Message1s {
69 | if round.oldOK[j] {
70 | continue
71 | }
72 | if msg1 == nil || !round.CanAccept(msg1) {
73 | return false, nil
74 | }
75 | msg2 := round.temp.dgRound3Message2s[j]
76 | if msg2 == nil || !round.CanAccept(msg2) {
77 | return false, nil
78 | }
79 | round.oldOK[j] = true
80 | }
81 | return true, nil
82 | }
83 |
84 | func (round *round3) NextRound() tss.Round {
85 | round.started = false
86 | return &round4{round}
87 | }
88 |
--------------------------------------------------------------------------------
/eddsa/resharing/round_5_new_step_3.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package resharing
8 |
9 | import (
10 | "errors"
11 |
12 | "github.com/binance-chain/tss-lib/tss"
13 | )
14 |
15 | func (round *round5) Start() *tss.Error {
16 | if round.started {
17 | return round.WrapError(errors.New("round already started"))
18 | }
19 | round.number = 5
20 | round.started = true
21 |
22 | round.allOldOK()
23 | round.allNewOK()
24 |
25 | if round.IsNewCommittee() {
26 | // for this P: SAVE data
27 | round.save.BigXj = round.temp.newBigXjs
28 | round.save.ShareID = round.PartyID().KeyInt()
29 | round.save.Xi = round.temp.newXi
30 | round.save.Ks = round.temp.newKs
31 |
32 | } else if round.IsOldCommittee() {
33 | round.input.Xi.SetInt64(0)
34 | }
35 |
36 | round.end <- *round.save
37 | return nil
38 | }
39 |
40 | func (round *round5) CanAccept(msg tss.ParsedMessage) bool {
41 | return false
42 | }
43 |
44 | func (round *round5) Update() (bool, *tss.Error) {
45 | return false, nil
46 | }
47 |
48 | func (round *round5) NextRound() tss.Round {
49 | return nil // both committees are finished!
50 | }
51 |
--------------------------------------------------------------------------------
/eddsa/resharing/rounds.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package resharing
8 |
9 | import (
10 | "github.com/binance-chain/tss-lib/eddsa/keygen"
11 | "github.com/binance-chain/tss-lib/tss"
12 | )
13 |
14 | const (
15 | TaskName = "eddsa-resharing"
16 | )
17 |
18 | type (
19 | base struct {
20 | *tss.ReSharingParameters
21 | temp *localTempData
22 | input, save *keygen.LocalPartySaveData
23 | out chan<- tss.Message
24 | end chan<- keygen.LocalPartySaveData
25 | oldOK, // old committee "ok" tracker
26 | newOK []bool // `ok` tracks parties which have been verified by Update(); this one is for the new committee
27 | started bool
28 | number int
29 | }
30 | round1 struct {
31 | *base
32 | }
33 | round2 struct {
34 | *round1
35 | }
36 | round3 struct {
37 | *round2
38 | }
39 | round4 struct {
40 | *round3
41 | }
42 | round5 struct {
43 | *round4
44 | }
45 | )
46 |
47 | var (
48 | _ tss.Round = (*round1)(nil)
49 | _ tss.Round = (*round2)(nil)
50 | _ tss.Round = (*round3)(nil)
51 | _ tss.Round = (*round4)(nil)
52 | _ tss.Round = (*round5)(nil)
53 | )
54 |
55 | // ----- //
56 |
57 | func (round *base) Params() *tss.Parameters {
58 | return round.ReSharingParameters.Parameters
59 | }
60 |
61 | func (round *base) ReSharingParams() *tss.ReSharingParameters {
62 | return round.ReSharingParameters
63 | }
64 |
65 | func (round *base) RoundNumber() int {
66 | return round.number
67 | }
68 |
69 | // CanProceed is inherited by other rounds
70 | func (round *base) CanProceed() bool {
71 | if !round.started {
72 | return false
73 | }
74 | for _, ok := range append(round.oldOK, round.newOK...) {
75 | if !ok {
76 | return false
77 | }
78 | }
79 | return true
80 | }
81 |
82 | // WaitingFor is called by a Party for reporting back to the caller
83 | func (round *base) WaitingFor() []*tss.PartyID {
84 | oldPs := round.OldParties().IDs()
85 | newPs := round.NewParties().IDs()
86 | idsMap := make(map[*tss.PartyID]bool)
87 | ids := make([]*tss.PartyID, 0, len(round.oldOK))
88 | for j, ok := range round.oldOK {
89 | if ok {
90 | continue
91 | }
92 | idsMap[oldPs[j]] = true
93 | }
94 | for j, ok := range round.newOK {
95 | if ok {
96 | continue
97 | }
98 | idsMap[newPs[j]] = true
99 | }
100 | // consolidate into the list
101 | for id := range idsMap {
102 | ids = append(ids, id)
103 | }
104 | return ids
105 | }
106 |
107 | func (round *base) WrapError(err error, culprits ...*tss.PartyID) *tss.Error {
108 | return tss.NewError(err, TaskName, round.number, round.PartyID(), culprits...)
109 | }
110 |
111 | // ----- //
112 |
113 | // `oldOK` tracks parties which have been verified by Update()
114 | func (round *base) resetOK() {
115 | for j := range round.oldOK {
116 | round.oldOK[j] = false
117 | }
118 | for j := range round.newOK {
119 | round.newOK[j] = false
120 | }
121 | }
122 |
123 | // sets all pairings in `oldOK` to true
124 | func (round *base) allOldOK() {
125 | for j := range round.oldOK {
126 | round.oldOK[j] = true
127 | }
128 | }
129 |
130 | // sets all pairings in `newOK` to true
131 | func (round *base) allNewOK() {
132 | for j := range round.newOK {
133 | round.newOK[j] = true
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/eddsa/signing/finalize.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "errors"
11 | "fmt"
12 | "math/big"
13 |
14 | "github.com/agl/ed25519/edwards25519"
15 | "github.com/decred/dcrd/dcrec/edwards/v2"
16 |
17 | "github.com/binance-chain/tss-lib/common"
18 | "github.com/binance-chain/tss-lib/tss"
19 | )
20 |
21 | func (round *finalization) Start() *tss.Error {
22 | if round.started {
23 | return round.WrapError(errors.New("round already started"))
24 | }
25 | round.number = 4
26 | round.started = true
27 | round.resetOK()
28 |
29 | sumS := round.temp.si
30 | for j := range round.Parties().IDs() {
31 | round.ok[j] = true
32 | if j == round.PartyID().Index {
33 | continue
34 | }
35 | r3msg := round.temp.signRound3Messages[j].Content().(*SignRound3Message)
36 | sjBytes := bigIntToEncodedBytes(r3msg.UnmarshalS())
37 | var tmpSumS [32]byte
38 | edwards25519.ScMulAdd(&tmpSumS, sumS, bigIntToEncodedBytes(big.NewInt(1)), sjBytes)
39 | sumS = &tmpSumS
40 | }
41 | s := encodedBytesToBigInt(sumS)
42 |
43 | // save the signature for final output
44 | signature := new(common.ECSignature)
45 | signature.Signature = append(bigIntToEncodedBytes(round.temp.r)[:], sumS[:]...)
46 | signature.R = bigIntToEncodedBytes(round.temp.r)[:]
47 | signature.S = bigIntToEncodedBytes(s)[:]
48 | signature.M = round.temp.m.Bytes()
49 |
50 | round.data.R = signature.R
51 | round.data.S = signature.S
52 | round.data.Signature = append(round.data.R, round.data.S...)
53 |
54 | pk := edwards.PublicKey{
55 | Curve: round.Params().EC(),
56 | X: round.key.EDDSAPub.X(),
57 | Y: round.key.EDDSAPub.Y(),
58 | }
59 |
60 | ok := edwards.Verify(&pk, round.temp.m.Bytes(), round.temp.r, s)
61 | if !ok {
62 | return round.WrapError(fmt.Errorf("signature verification failed"))
63 | }
64 | round.end <- *round.data
65 |
66 | return nil
67 | }
68 |
69 | func (round *finalization) CanAccept(msg tss.ParsedMessage) bool {
70 | // not expecting any incoming messages in this round
71 | return false
72 | }
73 |
74 | func (round *finalization) Update() (bool, *tss.Error) {
75 | // not expecting any incoming messages in this round
76 | return false, nil
77 | }
78 |
79 | func (round *finalization) NextRound() tss.Round {
80 | return nil // finished!
81 | }
82 |
83 | func padToLengthBytesInPlace(src []byte, length int) []byte {
84 | oriLen := len(src)
85 | if oriLen < length {
86 | for i := 0; i < length-oriLen; i++ {
87 | src = append([]byte{0}, src...)
88 | }
89 | }
90 | return src
91 | }
92 |
--------------------------------------------------------------------------------
/eddsa/signing/messages.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "crypto/elliptic"
11 | "math/big"
12 |
13 | "github.com/binance-chain/tss-lib/common"
14 | cmt "github.com/binance-chain/tss-lib/crypto/commitments"
15 | zkpsch "github.com/binance-chain/tss-lib/crypto/zkp/sch"
16 | "github.com/binance-chain/tss-lib/tss"
17 | )
18 |
19 | // These messages were generated from Protocol Buffers definitions into eddsa-signing.pb.go
20 | // The following messages are registered on the Protocol Buffers "wire"
21 |
22 | var (
23 | // Ensure that signing messages implement ValidateBasic
24 | _ = []tss.MessageContent{
25 | (*SignRound1Message)(nil),
26 | (*SignRound2Message)(nil),
27 | (*SignRound3Message)(nil),
28 | }
29 | )
30 |
31 | // ----- //
32 |
33 | func NewSignRound1Message(
34 | from *tss.PartyID,
35 | commitment cmt.HashCommitment,
36 | ) tss.ParsedMessage {
37 | meta := tss.MessageRouting{
38 | From: from,
39 | IsBroadcast: true,
40 | }
41 | content := &SignRound1Message{
42 | Commitment: commitment.Bytes(),
43 | }
44 | msg := tss.NewMessageWrapper(meta, content)
45 | return tss.NewMessage(meta, content, msg)
46 | }
47 |
48 | func (m *SignRound1Message) ValidateBasic() bool {
49 | return m.Commitment != nil &&
50 | common.NonEmptyBytes(m.GetCommitment())
51 | }
52 |
53 | func (m *SignRound1Message) UnmarshalCommitment() *big.Int {
54 | return new(big.Int).SetBytes(m.GetCommitment())
55 | }
56 |
57 | // ----- //
58 |
59 | func NewSignRound2Message(
60 | from *tss.PartyID,
61 | deCommitment cmt.HashDeCommitment,
62 | proof *zkpsch.ProofSch,
63 | ) tss.ParsedMessage {
64 | meta := tss.MessageRouting{
65 | From: from,
66 | IsBroadcast: true,
67 | }
68 | dcBzs := common.BigIntsToBytes(deCommitment)
69 | proofBzs := proof.Bytes()
70 | content := &SignRound2Message{
71 | DeCommitment: dcBzs,
72 | Proof: proofBzs[:],
73 | }
74 | msg := tss.NewMessageWrapper(meta, content)
75 | return tss.NewMessage(meta, content, msg)
76 | }
77 |
78 | func (m *SignRound2Message) ValidateBasic() bool {
79 | return m != nil &&
80 | common.NonEmptyMultiBytes(m.DeCommitment, 3) &&
81 | common.NonEmptyMultiBytes(m.Proof, zkpsch.ProofSchBytesParts)
82 | }
83 |
84 | func (m *SignRound2Message) UnmarshalDeCommitment() []*big.Int {
85 | deComBzs := m.GetDeCommitment()
86 | return cmt.NewHashDeCommitmentFromBytes(deComBzs)
87 | }
88 |
89 | func (m *SignRound2Message) UnmarshalZKProof(ec elliptic.Curve) (*zkpsch.ProofSch, error) {
90 | return zkpsch.NewProofFromBytes(ec, m.GetProof())
91 | }
92 |
93 | // ----- //
94 |
95 | func NewSignRound3Message(
96 | from *tss.PartyID,
97 | si *big.Int,
98 | ) tss.ParsedMessage {
99 | meta := tss.MessageRouting{
100 | From: from,
101 | IsBroadcast: true,
102 | }
103 | content := &SignRound3Message{
104 | S: si.Bytes(),
105 | }
106 | msg := tss.NewMessageWrapper(meta, content)
107 | return tss.NewMessage(meta, content, msg)
108 | }
109 |
110 | func (m *SignRound3Message) ValidateBasic() bool {
111 | return m != nil &&
112 | common.NonEmptyBytes(m.S)
113 | }
114 |
115 | func (m *SignRound3Message) UnmarshalS() *big.Int {
116 | return new(big.Int).SetBytes(m.S)
117 | }
118 |
--------------------------------------------------------------------------------
/eddsa/signing/prepare.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "crypto/elliptic"
11 | "fmt"
12 | "math/big"
13 |
14 | "github.com/binance-chain/tss-lib/common"
15 | )
16 |
17 | // PrepareForSigning(), Fig. 7
18 | func PrepareForSigning(ec elliptic.Curve, i, pax int, xi *big.Int, ks []*big.Int) (wi *big.Int) {
19 | modQ := common.ModInt(ec.Params().N)
20 | if len(ks) != pax {
21 | panic(fmt.Errorf("PrepareForSigning: len(ks) != pax (%d != %d)", len(ks), pax))
22 | }
23 | if len(ks) <= i {
24 | panic(fmt.Errorf("PrepareForSigning: len(ks) <= i (%d <= %d)", len(ks), i))
25 | }
26 |
27 | // 1-4.
28 | wi = xi
29 | for j := 0; j < pax; j++ {
30 | if j == i {
31 | continue
32 | }
33 | ksj := ks[j]
34 | ksi := ks[i]
35 | if ksj.Cmp(ksi) == 0 {
36 | panic(fmt.Errorf("index of two parties are equal"))
37 | }
38 | // big.Int Div is calculated as: a/b = a * modInv(b,q)
39 | coef := modQ.Mul(ks[j], modQ.Inverse(new(big.Int).Sub(ksj, ksi)))
40 | wi = modQ.Mul(wi, coef)
41 | }
42 |
43 | return
44 | }
45 |
--------------------------------------------------------------------------------
/eddsa/signing/round_1.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "errors"
11 | "fmt"
12 | "math/big"
13 |
14 | "github.com/binance-chain/tss-lib/common"
15 | "github.com/binance-chain/tss-lib/crypto"
16 | "github.com/binance-chain/tss-lib/crypto/commitments"
17 | "github.com/binance-chain/tss-lib/eddsa/keygen"
18 | "github.com/binance-chain/tss-lib/tss"
19 | )
20 |
21 | var (
22 | zero = big.NewInt(0)
23 | )
24 |
25 | // round 1 represents round 1 of the signing part of the EDDSA TSS spec
26 | func newRound1(params *tss.Parameters, key *keygen.LocalPartySaveData, data *common.SignatureData, temp *localTempData, out chan<- tss.Message, end chan<- common.SignatureData) tss.Round {
27 | return &round1{
28 | &base{params, key, data, temp, out, end, make([]bool, len(params.Parties().IDs())), false, 1}}
29 | }
30 |
31 | func (round *round1) Start() *tss.Error {
32 | if round.started {
33 | return round.WrapError(errors.New("round already started"))
34 | }
35 |
36 | round.number = 1
37 | round.started = true
38 | round.resetOK()
39 |
40 | // 1. select ri
41 | ri := common.GetRandomPositiveInt(round.Params().EC().Params().N)
42 |
43 | // 2. make commitment
44 | pointRi := crypto.ScalarBaseMult(round.Params().EC(), ri)
45 | cmt := commitments.NewHashCommitment(pointRi.X(), pointRi.Y())
46 |
47 | // 3. store r1 message pieces
48 | round.temp.ri = ri
49 | round.temp.pointRi = pointRi
50 | round.temp.deCommit = cmt.D
51 |
52 | i := round.PartyID().Index
53 | round.ok[i] = true
54 |
55 | // 4. broadcast commitment
56 | r1msg2 := NewSignRound1Message(round.PartyID(), cmt.C)
57 | round.temp.signRound1Messages[i] = r1msg2
58 | round.out <- r1msg2
59 |
60 | return nil
61 | }
62 |
63 | func (round *round1) Update() (bool, *tss.Error) {
64 | for j, msg := range round.temp.signRound1Messages {
65 | if round.ok[j] {
66 | continue
67 | }
68 | if msg == nil || !round.CanAccept(msg) {
69 | return false, nil
70 | }
71 | round.ok[j] = true
72 | }
73 | return true, nil
74 | }
75 |
76 | func (round *round1) CanAccept(msg tss.ParsedMessage) bool {
77 | if _, ok := msg.Content().(*SignRound1Message); ok {
78 | return msg.IsBroadcast()
79 | }
80 | return false
81 | }
82 |
83 | func (round *round1) NextRound() tss.Round {
84 | round.started = false
85 | return &round2{round}
86 | }
87 |
88 | // ----- //
89 |
90 | // helper to call into PrepareForSigning()
91 | func (round *round1) prepare() error {
92 | i := round.PartyID().Index
93 |
94 | xi := round.key.Xi
95 | ks := round.key.Ks
96 |
97 | if round.Threshold()+1 > len(ks) {
98 | // TODO: this should not panic
99 | return fmt.Errorf("t+1=%d is not consistent with the key count %d", round.Threshold()+1, len(ks))
100 | }
101 | wi := PrepareForSigning(round.Params().EC(), i, len(ks), xi, ks)
102 |
103 | round.temp.wi = wi
104 | return nil
105 | }
106 |
--------------------------------------------------------------------------------
/eddsa/signing/round_2.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "errors"
11 |
12 | errors2 "github.com/pkg/errors"
13 |
14 | zkpsch "github.com/binance-chain/tss-lib/crypto/zkp/sch"
15 | "github.com/binance-chain/tss-lib/tss"
16 | )
17 |
18 | func (round *round2) Start() *tss.Error {
19 | if round.started {
20 | return round.WrapError(errors.New("round already started"))
21 | }
22 | round.number = 2
23 | round.started = true
24 | round.resetOK()
25 |
26 | i := round.PartyID().Index
27 |
28 | // 1. store r1 message pieces
29 | for j, msg := range round.temp.signRound1Messages {
30 | r1msg := msg.Content().(*SignRound1Message)
31 | round.temp.cjs[j] = r1msg.UnmarshalCommitment()
32 | }
33 |
34 | // 2. compute Schnorr prove
35 | pir, err := zkpsch.NewProof(round.temp.pointRi, round.temp.ri)
36 | if err != nil {
37 | return round.WrapError(errors2.Wrapf(err, "NewZKProof(ri, pointRi)"))
38 | }
39 |
40 | // 3. BROADCAST de-commitments of Shamir poly*G and Schnorr prove
41 | r2msg2 := NewSignRound2Message(round.PartyID(), round.temp.deCommit, pir)
42 | round.temp.signRound2Messages[i] = r2msg2
43 | round.out <- r2msg2
44 |
45 | return nil
46 | }
47 |
48 | func (round *round2) CanAccept(msg tss.ParsedMessage) bool {
49 | if _, ok := msg.Content().(*SignRound2Message); ok {
50 | return msg.IsBroadcast()
51 | }
52 | return false
53 | }
54 |
55 | func (round *round2) Update() (bool, *tss.Error) {
56 | for j, msg := range round.temp.signRound2Messages {
57 | if round.ok[j] {
58 | continue
59 | }
60 | if msg == nil || !round.CanAccept(msg) {
61 | return false, nil
62 | }
63 | round.ok[j] = true
64 | }
65 | return true, nil
66 | }
67 |
68 | func (round *round2) NextRound() tss.Round {
69 | round.started = false
70 | return &round3{round}
71 | }
72 |
--------------------------------------------------------------------------------
/eddsa/signing/round_3.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "crypto/sha512"
11 |
12 | "github.com/agl/ed25519/edwards25519"
13 | "github.com/pkg/errors"
14 |
15 | "github.com/binance-chain/tss-lib/crypto"
16 | "github.com/binance-chain/tss-lib/crypto/commitments"
17 | "github.com/binance-chain/tss-lib/tss"
18 | )
19 |
20 | func (round *round3) Start() *tss.Error {
21 | if round.started {
22 | return round.WrapError(errors.New("round already started"))
23 | }
24 |
25 | round.number = 3
26 | round.started = true
27 | round.resetOK()
28 |
29 | // 1. init R
30 | var R edwards25519.ExtendedGroupElement
31 | riBytes := bigIntToEncodedBytes(round.temp.ri)
32 | edwards25519.GeScalarMultBase(&R, riBytes)
33 |
34 | // 2-6. compute R
35 | i := round.PartyID().Index
36 | for j, Pj := range round.Parties().IDs() {
37 | if j == i {
38 | continue
39 | }
40 |
41 | msg := round.temp.signRound2Messages[j]
42 | r2msg := msg.Content().(*SignRound2Message)
43 | cmtDeCmt := commitments.HashCommitDecommit{C: round.temp.cjs[j], D: r2msg.UnmarshalDeCommitment()}
44 | ok, coordinates := cmtDeCmt.DeCommit()
45 | if !ok {
46 | return round.WrapError(errors.New("de-commitment verify failed"))
47 | }
48 | if len(coordinates) != 2 {
49 | return round.WrapError(errors.New("length of de-commitment should be 2"))
50 | }
51 |
52 | Rj, err := crypto.NewECPoint(round.Params().EC(), coordinates[0], coordinates[1])
53 | if err != nil {
54 | return round.WrapError(errors.Wrapf(err, "NewECPoint(Rj)"), Pj)
55 | }
56 | proof, err := r2msg.UnmarshalZKProof(round.Params().EC())
57 | if err != nil {
58 | return round.WrapError(errors.New("failed to unmarshal Rj proof"), Pj)
59 | }
60 | ok = proof.Verify(Rj)
61 | if !ok {
62 | return round.WrapError(errors.New("failed to prove Rj"), Pj)
63 | }
64 |
65 | extendedRj := ecPointToExtendedElement(round.Params().EC(), Rj.X(), Rj.Y())
66 | R = addExtendedElements(R, extendedRj)
67 | }
68 |
69 | // 7. compute lambda
70 | var encodedR [32]byte
71 | R.ToBytes(&encodedR)
72 | encodedPubKey := ecPointToEncodedBytes(round.key.EDDSAPub.X(), round.key.EDDSAPub.Y())
73 |
74 | // h = hash512(k || A || M)
75 | h := sha512.New()
76 | h.Reset()
77 | h.Write(encodedR[:])
78 | h.Write(encodedPubKey[:])
79 | h.Write(round.temp.m.Bytes())
80 |
81 | var lambda [64]byte
82 | h.Sum(lambda[:0])
83 | var lambdaReduced [32]byte
84 | edwards25519.ScReduce(&lambdaReduced, &lambda)
85 |
86 | // 8. compute si
87 | var localS [32]byte
88 | edwards25519.ScMulAdd(&localS, &lambdaReduced, bigIntToEncodedBytes(round.temp.wi), riBytes)
89 |
90 | // 9. store r3 message pieces
91 | round.temp.si = &localS
92 | round.temp.r = encodedBytesToBigInt(&encodedR)
93 |
94 | // 10. broadcast si to other parties
95 | r3msg := NewSignRound3Message(round.PartyID(), encodedBytesToBigInt(&localS))
96 | round.temp.signRound3Messages[round.PartyID().Index] = r3msg
97 | round.out <- r3msg
98 |
99 | return nil
100 | }
101 |
102 | func (round *round3) Update() (bool, *tss.Error) {
103 | for j, msg := range round.temp.signRound3Messages {
104 | if round.ok[j] {
105 | continue
106 | }
107 | if msg == nil || !round.CanAccept(msg) {
108 | return false, nil
109 | }
110 | round.ok[j] = true
111 | }
112 | return true, nil
113 | }
114 |
115 | func (round *round3) CanAccept(msg tss.ParsedMessage) bool {
116 | if _, ok := msg.Content().(*SignRound3Message); ok {
117 | return msg.IsBroadcast()
118 | }
119 | return false
120 | }
121 |
122 | func (round *round3) NextRound() tss.Round {
123 | round.started = false
124 | return &finalization{round}
125 | }
126 |
--------------------------------------------------------------------------------
/eddsa/signing/rounds.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "github.com/binance-chain/tss-lib/common"
11 | "github.com/binance-chain/tss-lib/eddsa/keygen"
12 | "github.com/binance-chain/tss-lib/tss"
13 | )
14 |
15 | const (
16 | TaskName = "eddsa-signing"
17 | )
18 |
19 | type (
20 | base struct {
21 | *tss.Parameters
22 | key *keygen.LocalPartySaveData
23 | data *common.SignatureData
24 | temp *localTempData
25 | out chan<- tss.Message
26 | end chan<- common.SignatureData
27 | ok []bool // `ok` tracks parties which have been verified by Update()
28 | started bool
29 | number int
30 | }
31 | round1 struct {
32 | *base
33 | }
34 | round2 struct {
35 | *round1
36 | }
37 | round3 struct {
38 | *round2
39 | }
40 | finalization struct {
41 | *round3
42 | }
43 | )
44 |
45 | var (
46 | _ tss.Round = (*round1)(nil)
47 | _ tss.Round = (*round2)(nil)
48 | _ tss.Round = (*round3)(nil)
49 | _ tss.Round = (*finalization)(nil)
50 | )
51 |
52 | // ----- //
53 |
54 | func (round *base) Params() *tss.Parameters {
55 | return round.Parameters
56 | }
57 |
58 | func (round *base) RoundNumber() int {
59 | return round.number
60 | }
61 |
62 | // CanProceed is inherited by other rounds
63 | func (round *base) CanProceed() bool {
64 | if !round.started {
65 | return false
66 | }
67 | for _, ok := range round.ok {
68 | if !ok {
69 | return false
70 | }
71 | }
72 | return true
73 | }
74 |
75 | // WaitingFor is called by a Party for reporting back to the caller
76 | func (round *base) WaitingFor() []*tss.PartyID {
77 | Ps := round.Parties().IDs()
78 | ids := make([]*tss.PartyID, 0, len(round.ok))
79 | for j, ok := range round.ok {
80 | if ok {
81 | continue
82 | }
83 | ids = append(ids, Ps[j])
84 | }
85 | return ids
86 | }
87 |
88 | func (round *base) WrapError(err error, culprits ...*tss.PartyID) *tss.Error {
89 | return tss.NewError(err, TaskName, round.number, round.PartyID(), culprits...)
90 | }
91 |
92 | // ----- //
93 |
94 | // `ok` tracks parties which have been verified by Update()
95 | func (round *base) resetOK() {
96 | for j := range round.ok {
97 | round.ok[j] = false
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/eddsa/signing/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package signing
8 |
9 | import (
10 | "crypto/elliptic"
11 | "math/big"
12 |
13 | "github.com/agl/ed25519/edwards25519"
14 |
15 | "github.com/binance-chain/tss-lib/common"
16 | )
17 |
18 | func encodedBytesToBigInt(s *[32]byte) *big.Int {
19 | // Use a copy so we don't screw up our original
20 | // memory.
21 | sCopy := new([32]byte)
22 | for i := 0; i < 32; i++ {
23 | sCopy[i] = s[i]
24 | }
25 | reverse(sCopy)
26 |
27 | bi := new(big.Int).SetBytes(sCopy[:])
28 |
29 | return bi
30 | }
31 |
32 | func bigIntToEncodedBytes(a *big.Int) *[32]byte {
33 | s := new([32]byte)
34 | if a == nil {
35 | return s
36 | }
37 |
38 | // Caveat: a can be longer than 32 bytes.
39 | s = copyBytes(a.Bytes())
40 |
41 | // Reverse the byte string --> little endian after
42 | // encoding.
43 | reverse(s)
44 |
45 | return s
46 | }
47 |
48 | func copyBytes(aB []byte) *[32]byte {
49 | if aB == nil {
50 | return nil
51 | }
52 | s := new([32]byte)
53 |
54 | // If we have a short byte string, expand
55 | // it so that it's long enough.
56 | aBLen := len(aB)
57 | if aBLen < 32 {
58 | diff := 32 - aBLen
59 | for i := 0; i < diff; i++ {
60 | aB = append([]byte{0x00}, aB...)
61 | }
62 | }
63 |
64 | for i := 0; i < 32; i++ {
65 | s[i] = aB[i]
66 | }
67 |
68 | return s
69 | }
70 |
71 | func ecPointToEncodedBytes(x *big.Int, y *big.Int) *[32]byte {
72 | s := bigIntToEncodedBytes(y)
73 | xB := bigIntToEncodedBytes(x)
74 | xFE := new(edwards25519.FieldElement)
75 | edwards25519.FeFromBytes(xFE, xB)
76 | isNegative := edwards25519.FeIsNegative(xFE) == 1
77 |
78 | if isNegative {
79 | s[31] |= (1 << 7)
80 | } else {
81 | s[31] &^= (1 << 7)
82 | }
83 |
84 | return s
85 | }
86 |
87 | func reverse(s *[32]byte) {
88 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
89 | s[i], s[j] = s[j], s[i]
90 | }
91 | }
92 |
93 | func addExtendedElements(p, q edwards25519.ExtendedGroupElement) edwards25519.ExtendedGroupElement {
94 | var r edwards25519.CompletedGroupElement
95 | var qCached edwards25519.CachedGroupElement
96 | q.ToCached(&qCached)
97 | edwards25519.GeAdd(&r, &p, &qCached)
98 | var result edwards25519.ExtendedGroupElement
99 | r.ToExtended(&result)
100 | return result
101 | }
102 |
103 | func ecPointToExtendedElement(ec elliptic.Curve, x *big.Int, y *big.Int) edwards25519.ExtendedGroupElement {
104 | encodedXBytes := bigIntToEncodedBytes(x)
105 | encodedYBytes := bigIntToEncodedBytes(y)
106 |
107 | z := common.GetRandomPositiveInt(ec.Params().N)
108 | encodedZBytes := bigIntToEncodedBytes(z)
109 |
110 | var fx, fy, fxy edwards25519.FieldElement
111 | edwards25519.FeFromBytes(&fx, encodedXBytes)
112 | edwards25519.FeFromBytes(&fy, encodedYBytes)
113 |
114 | var X, Y, Z, T edwards25519.FieldElement
115 | edwards25519.FeFromBytes(&Z, encodedZBytes)
116 |
117 | edwards25519.FeMul(&X, &fx, &Z)
118 | edwards25519.FeMul(&Y, &fy, &Z)
119 | edwards25519.FeMul(&fxy, &fx, &fy)
120 | edwards25519.FeMul(&T, &fxy, &Z)
121 |
122 | return edwards25519.ExtendedGroupElement{
123 | X: X,
124 | Y: Y,
125 | Z: Z,
126 | T: T,
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/binance-chain/tss-lib
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/agl/ed25519 v0.0.0-20200225211852-fd4d107ace12
7 | github.com/btcsuite/btcd v0.22.0-beta
8 | github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
9 | github.com/decred/dcrd/dcrec/edwards/v2 v2.0.2
10 | github.com/golang/protobuf v1.5.2
11 | github.com/hashicorp/errwrap v1.1.0 // indirect
12 | github.com/hashicorp/go-multierror v1.1.1
13 | github.com/ipfs/go-log/v2 v2.5.0 // indirect
14 | github.com/kr/text v0.2.0 // indirect
15 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
16 | github.com/otiai10/primes v0.0.0-20210501021515-f1b2be525a11
17 | github.com/pkg/errors v0.9.1
18 | github.com/stretchr/testify v1.7.0
19 | golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed
20 | google.golang.org/protobuf v1.27.1
21 | )
22 |
23 | require (
24 | github.com/ipfs/go-log v1.0.5
25 | go.uber.org/atomic v1.9.0 // indirect
26 | go.uber.org/multierr v1.7.0 // indirect
27 | go.uber.org/zap v1.20.0 // indirect
28 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
29 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect
30 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
31 | )
32 |
33 | replace github.com/agl/ed25519 => github.com/SwingbyProtocol/edwards25519 v0.0.0-20200305024217-f36fc4b53d43
34 |
--------------------------------------------------------------------------------
/protob/ecdsa-keygen.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 | package binance.tsslib.ecdsa.keygen;
9 | option go_package = "ecdsa/keygen";
10 |
11 | /*
12 | * Represents a BROADCAST message sent during Round 1 of the ECDSA TSS keygen protocol.
13 | */
14 | message KGRound1Message {
15 | bytes VHash = 1;
16 | }
17 |
18 | /*
19 | * Represents a BROADCAST message sent during Round 1 of the ECDSA TSS keygen protocol.
20 | */
21 | message KGRound2Message {
22 | repeated bytes vs = 1;
23 | bytes paillier_n = 2;
24 | bytes n_tilde = 3;
25 | bytes h1 = 4;
26 | bytes h2 = 5;
27 | bytes ridi = 6;
28 | repeated bytes Ai = 7;
29 | repeated bytes Xi = 8;
30 | repeated bytes PrmProof = 9;
31 | }
32 |
33 | /*
34 | * Represents a P2P message sent to each party during Round 2 of the ECDSA TSS keygen protocol.
35 | */
36 | message KGRound3Message {
37 | bytes share = 1;
38 | repeated bytes mod_proof = 2;
39 | repeated bytes fac_proof = 3;
40 | repeated bytes psii_proof = 4;
41 | }
42 |
43 | /*
44 | * Represents a BROADCAST message sent to each party during Round 3 of the ECDSA TSS keygen protocol.
45 | */
46 | message KGRound4Message {
47 | repeated bytes proof = 1;
48 | }
49 |
--------------------------------------------------------------------------------
/protob/ecdsa-resharing.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 | package binance.tsslib.ecdsa.resharing;
9 | option go_package = "ecdsa/resharing";
10 |
11 | /*
12 | * The Round 1 data is broadcast to peers of the New Committee in this message.
13 | */
14 | message DGRound1Message {
15 | bytes ecdsa_pub_x = 1;
16 | bytes ecdsa_pub_y = 2;
17 | bytes v_commitment = 3;
18 | }
19 |
20 | /*
21 | * The Round 2 data is broadcast to other peers of the New Committee in this message.
22 | */
23 | message DGRound2Message1 {
24 | bytes paillier_n = 1;
25 | repeated bytes paillier_proof = 2;
26 | bytes n_tilde = 3;
27 | bytes h1 = 4;
28 | bytes h2 = 5;
29 | }
30 |
31 | /*
32 | * The Round 2 "ACK" is broadcast to peers of the Old Committee in this message.
33 | */
34 | message DGRound2Message2 {
35 | }
36 |
37 | /*
38 | * The Round 3 data is sent to peers of the New Committee in this message.
39 | */
40 | message DGRound3Message1 {
41 | bytes share = 1;
42 | }
43 |
44 | /*
45 | * The Round 3 data is broadcast to peers of the New Committee in this message.
46 | */
47 | message DGRound3Message2 {
48 | repeated bytes v_decommitment = 1;
49 | }
50 |
51 | /*
52 | * The Round 4 "ACK" is broadcast to peers of the Old and New Committees from the New Committee in this message.
53 | */
54 | message DGRound4Message {
55 | }
56 |
--------------------------------------------------------------------------------
/protob/ecdsa-signature.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 |
9 | option go_package = "github.com/binance-chain/tss-lib/ecdsa/signing";
10 |
11 | import "protob/shared.proto";
12 |
13 | /*
14 | * State object for signatures, either partial (for offline/async "one round" signing) or full (contains the final ECDSA signature).
15 | */
16 | message SignatureData {
17 | message OneRoundData {
18 | // Sanity check in FinalizeGetAndVerifyFinalSig
19 | int32 t = 1;
20 |
21 | // Components to produce s = sum(s_i)
22 | bytes k_i = 2;
23 | bytes r_sigma_i = 3;
24 | ECPoint big_r = 4;
25 |
26 | // Components for identifiable aborts during the final phase
27 | map big_r_bar_j = 5;
28 | map big_s_j = 6;
29 | }
30 | ECSignature signature = 10;
31 | OneRoundData one_round_data = 11;
32 | }
33 |
--------------------------------------------------------------------------------
/protob/ecdsa-signing.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 | package binance.tsslib.ecdsa.signing;
9 | option go_package = "ecdsa/signing";
10 |
11 | /*
12 | * Represents a P2P message sent to each party during Round 1 of the ECDSA TSS signing protocol.
13 | */
14 | message PreSignRound1Message {
15 | bytes K = 1;
16 | bytes G = 2;
17 | repeated bytes EncProof = 3;
18 | }
19 |
20 | /*
21 | * Represents a P2P message sent to each party during Round 2 of the ECDSA TSS signing protocol.
22 | */
23 | message PreSignRound2Message {
24 | repeated bytes BigGammaShare = 1;
25 | bytes DjiDelta = 2;
26 | bytes FjiDelta = 3;
27 | bytes DjiChi = 4;
28 | bytes FjiChi = 5;
29 | repeated bytes AffgProofDelta = 6;
30 | repeated bytes AffgProofChi = 7;
31 | repeated bytes LogstarProof = 8;
32 | }
33 |
34 | /*
35 | * Represents a P2P message sent to all parties during Round 3 of the ECDSA TSS signing protocol.
36 | */
37 | message PreSignRound3Message {
38 | bytes DeltaShare = 1;
39 | repeated bytes BigDeltaShare = 2;
40 | repeated bytes ProofLogstar = 3;
41 | }
42 |
43 | /*
44 | * Represents a BROADCAST message sent to all parties during Round 4 of the ECDSA TSS signing protocol.
45 | */
46 | message SignRound4Message {
47 | bytes SigmaShare = 1;
48 | }
49 |
50 | message SignRound4AbortingMessage {
51 | }
52 |
53 | message IdentificationPrepRound5Message {
54 | bytes Gamma = 1;
55 | bytes Sji = 2;
56 | bytes BetaNegji = 3;
57 | }
58 |
59 | message IdentificationRound6Message {
60 | bytes H = 1;
61 | repeated bytes MulProof = 2;
62 | bytes DeltaShareEnc = 3;
63 | bytes EncryptedValueSum = 4;
64 | repeated bytes DecProof = 5;
65 | }
66 |
67 | message TempDataDumpMessage {
68 | bytes DataDump = 1;
69 | int32 RoundNum = 2;
70 | }
--------------------------------------------------------------------------------
/protob/eddsa-keygen.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 | package binance.tsslib.eddsa.keygen;
9 | option go_package = "eddsa/keygen";
10 |
11 | /*
12 | * Represents a BROADCAST message sent during Round 1 of the EDDSA TSS keygen protocol.
13 | */
14 | message KGRound1Message {
15 | bytes commitment = 1;
16 | }
17 |
18 | /*
19 | * Represents a P2P message sent to each party during Round 2 of the EDDSA TSS keygen protocol.
20 | */
21 | message KGRound2Message1 {
22 | bytes share = 1;
23 | }
24 |
25 | /*
26 | * Represents a BROADCAST message sent to each party during Round 2 of the EDDSA TSS keygen protocol.
27 | */
28 | message KGRound2Message2 {
29 | repeated bytes de_commitment = 1;
30 | repeated bytes proof = 2;
31 | }
32 |
--------------------------------------------------------------------------------
/protob/eddsa-resharing.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 | package binance.tsslib.eddsa.resharing;
9 | option go_package = "eddsa/resharing";
10 |
11 | /*
12 | * The Round 1 data is broadcast to peers of the New Committee in this message.
13 | */
14 | message DGRound1Message {
15 | bytes eddsa_pub_x = 1;
16 | bytes eddsa_pub_y = 2;
17 | bytes v_commitment = 3;
18 | }
19 |
20 | /*
21 | * The Round 2 "ACK" is broadcast to peers of the Old Committee in this message.
22 | */
23 | message DGRound2Message {
24 | }
25 |
26 | /*
27 | * The Round 3 data is sent to peers of the New Committee in this message.
28 | */
29 | message DGRound3Message1 {
30 | bytes share = 1;
31 | }
32 |
33 | /*
34 | * The Round 3 data is broadcast to peers of the New Committee in this message.
35 | */
36 | message DGRound3Message2 {
37 | repeated bytes v_decommitment = 1;
38 | }
39 |
40 | /*
41 | * The Round 4 "ACK" is broadcast to peers of the Old and New Committees from the New Committee in this message.
42 | */
43 | message DGRound4Message {
44 | }
45 |
--------------------------------------------------------------------------------
/protob/eddsa-signature.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 |
9 | option go_package = "github.com/binance-chain/tss-lib/eddsa/signing";
10 |
11 | import "protob/shared.proto";
12 |
13 | /*
14 | * State object for signatures, contains the final EdDSA signature.
15 | */
16 | message SignatureData {
17 | ECSignature signature = 10;
18 | }
19 |
--------------------------------------------------------------------------------
/protob/eddsa-signing.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 | package binance.tsslib.eddsa.signing;
9 | option go_package = "eddsa/signing";
10 |
11 | /*
12 | * Represents a BROADCAST message sent to all parties during Round 1 of the EDDSA TSS signing protocol.
13 | */
14 | message SignRound1Message {
15 | bytes commitment = 1;
16 | }
17 |
18 | /*
19 | * Represents a BROADCAST message sent to all parties during Round 2 of the EDDSA TSS signing protocol.
20 | */
21 | message SignRound2Message {
22 | repeated bytes de_commitment = 1;
23 | repeated bytes proof = 2;
24 | }
25 |
26 | /*
27 | * Represents a BROADCAST message sent to all parties during Round 3 of the EDDSA TSS signing protocol.
28 | */
29 | message SignRound3Message {
30 | bytes s = 1;
31 | }
32 |
--------------------------------------------------------------------------------
/protob/message.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 | package binance.tsslib;
9 | option go_package = "./tss";
10 |
11 | import "google/protobuf/any.proto";
12 |
13 | /*
14 | * Wrapper for TSS messages, often read by the transport layer and not itself sent over the wire
15 | */
16 | message MessageWrapper {
17 | // PartyID represents a participant in the TSS protocol rounds.
18 | // Note: The `id` and `moniker` are provided for convenience to allow you to track participants easier.
19 | // The `id` is intended to be a unique string representation of `key` and `moniker` can be anything (even left blank).
20 | message PartyID {
21 | string id = 1;
22 | string moniker = 2;
23 | bytes key = 3;
24 | }
25 |
26 | // Metadata optionally un-marshalled and used by the transport to route this message.
27 | bool is_broadcast = 1;
28 | // Metadata optionally un-marshalled and used by the transport to route this message.
29 | bool is_to_old_committee = 2; // used only in certain resharing messages
30 | // Metadata optionally un-marshalled and used by the transport to route this message.
31 | bool is_to_old_and_new_committees = 5; // used only in certain resharing messages
32 |
33 | // Metadata optionally un-marshalled and used by the transport to route this message.
34 | PartyID from = 3;
35 | // Metadata optionally un-marshalled and used by the transport to route this message.
36 | repeated PartyID to = 4;
37 |
38 | // This field is actually what is sent through the wire and consumed on the other end by UpdateFromBytes.
39 | // An Any contains an arbitrary serialized message as bytes, along with a URL that
40 | // acts as a globally unique identifier for and resolves to that message's type.
41 | google.protobuf.Any message = 10;
42 | }
43 |
--------------------------------------------------------------------------------
/protob/shared.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 |
9 | option go_package = "github.com/binance-chain/tss-lib/common";
10 |
11 | message ECPoint {
12 | bytes x = 1;
13 | bytes y = 2;
14 | }
15 |
16 | message ECSignature {
17 | bytes signature = 1;
18 |
19 | // Ethereum-style Recovery ID: Used to enable extracting the public key from the signature.
20 | bytes signature_recovery = 2;
21 |
22 | // Signature components R, S
23 | bytes r = 3;
24 | bytes s = 4;
25 |
26 | // M represents the original message digest that was signed M
27 | bytes m = 5;
28 | }
29 |
30 | message VSSShareWithAuthSigMessage {
31 | uint32 vss_threshold = 1;
32 | bytes vss_id = 2;
33 | bytes vss_sigma = 3;
34 | uint32 accused_party = 4;
35 | ECPoint auth_sig_pk = 5;
36 | bytes authEcdsaSignature_r = 6;
37 | bytes authEcdsaSignature_s = 7;
38 | repeated bytes Dj = 8;
39 | bytes Cj = 9;
40 | }
41 |
--------------------------------------------------------------------------------
/protob/signature.proto:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | syntax = "proto3";
8 | package binance.tsslib;
9 | option go_package = "./common";
10 |
11 | /*
12 | * Container for output signatures, mostly used for marshalling this data structure to a mobile app
13 | */
14 | message SignatureData {
15 | bytes signature = 1;
16 |
17 | // Ethereum-style recovery byte; only the first byte is relevant
18 | bytes signature_recovery = 2;
19 |
20 | // Signature components R, S
21 | bytes r = 3;
22 | bytes s = 4;
23 |
24 | // M represents the original message digest that was signed M
25 | bytes m = 5;
26 | }
27 |
--------------------------------------------------------------------------------
/test/_eddsa_fixtures/keygen_data_0.json:
--------------------------------------------------------------------------------
1 | {"Xi":4035558533062709819999066175720808528201062157116959842237332813932238616128,"ShareID":4331412928177670560630249357713645140224222675389722735738732090810250798211,"Ks":[4331412928177670560630249357713645140224222675389722735738732090810250798211,4331412928177670560630249357713645140224222675389722735738732090810250798212,4331412928177670560630249357713645140224222675389722735738732090810250798213,4331412928177670560630249357713645140224222675389722735738732090810250798214,4331412928177670560630249357713645140224222675389722735738732090810250798215],"BigXj":[{"Curve":"ed25519","Coords":[43310099667191579169521946934030711066313578772454024333166342656953690707905,38262908547499520970138465680473622143445318034940413117238454107324155485797]},{"Curve":"ed25519","Coords":[24927223220985169141357864268825305163035760221331262595481037667448513230521,54543586647335403608940330836676147882229516532084699385623699026578670835490]},{"Curve":"ed25519","Coords":[19616187786153771277930658236501515378058310865337414616528052406614836915381,5716086794073894084115757926314451153376043864681595081544933294419482836932]},{"Curve":"ed25519","Coords":[54894815670576274903749323651006626882715390257891571131934873815604647096151,6261975146537300228952779313756496780554610579250869076285924817563901726620]},{"Curve":"ed25519","Coords":[56199043572031726140854435832515077152685912346629787788574432437354212337803,20932642671481609624660147804972444129208387391842241879798619789962570609578]}],"EDDSAPub":{"Curve":"ed25519","Coords":[19086049614490163056889859244855881816030591019773605655349500169700108043042,41654918635428963527745188055885714592211199796738262868910566706065491471139]}}
--------------------------------------------------------------------------------
/test/_eddsa_fixtures/keygen_data_1.json:
--------------------------------------------------------------------------------
1 | {"Xi":3043586451770172198202994337291909300581990387576065449561143874105554349662,"ShareID":4331412928177670560630249357713645140224222675389722735738732090810250798212,"Ks":[4331412928177670560630249357713645140224222675389722735738732090810250798211,4331412928177670560630249357713645140224222675389722735738732090810250798212,4331412928177670560630249357713645140224222675389722735738732090810250798213,4331412928177670560630249357713645140224222675389722735738732090810250798214,4331412928177670560630249357713645140224222675389722735738732090810250798215],"BigXj":[{"Curve":"ed25519","Coords":[43310099667191579169521946934030711066313578772454024333166342656953690707905,38262908547499520970138465680473622143445318034940413117238454107324155485797]},{"Curve":"ed25519","Coords":[24927223220985169141357864268825305163035760221331262595481037667448513230521,54543586647335403608940330836676147882229516532084699385623699026578670835490]},{"Curve":"ed25519","Coords":[19616187786153771277930658236501515378058310865337414616528052406614836915381,5716086794073894084115757926314451153376043864681595081544933294419482836932]},{"Curve":"ed25519","Coords":[54894815670576274903749323651006626882715390257891571131934873815604647096151,6261975146537300228952779313756496780554610579250869076285924817563901726620]},{"Curve":"ed25519","Coords":[56199043572031726140854435832515077152685912346629787788574432437354212337803,20932642671481609624660147804972444129208387391842241879798619789962570609578]}],"EDDSAPub":{"Curve":"ed25519","Coords":[19086049614490163056889859244855881816030591019773605655349500169700108043042,41654918635428963527745188055885714592211199796738262868910566706065491471139]}}
--------------------------------------------------------------------------------
/test/_eddsa_fixtures/keygen_data_2.json:
--------------------------------------------------------------------------------
1 | {"Xi":3816246280213061394677789505107161438228093145050056017416883247327953924913,"ShareID":4331412928177670560630249357713645140224222675389722735738732090810250798213,"Ks":[4331412928177670560630249357713645140224222675389722735738732090810250798211,4331412928177670560630249357713645140224222675389722735738732090810250798212,4331412928177670560630249357713645140224222675389722735738732090810250798213,4331412928177670560630249357713645140224222675389722735738732090810250798214,4331412928177670560630249357713645140224222675389722735738732090810250798215],"BigXj":[{"Curve":"ed25519","Coords":[43310099667191579169521946934030711066313578772454024333166342656953690707905,38262908547499520970138465680473622143445318034940413117238454107324155485797]},{"Curve":"ed25519","Coords":[24927223220985169141357864268825305163035760221331262595481037667448513230521,54543586647335403608940330836676147882229516532084699385623699026578670835490]},{"Curve":"ed25519","Coords":[19616187786153771277930658236501515378058310865337414616528052406614836915381,5716086794073894084115757926314451153376043864681595081544933294419482836932]},{"Curve":"ed25519","Coords":[54894815670576274903749323651006626882715390257891571131934873815604647096151,6261975146537300228952779313756496780554610579250869076285924817563901726620]},{"Curve":"ed25519","Coords":[56199043572031726140854435832515077152685912346629787788574432437354212337803,20932642671481609624660147804972444129208387391842241879798619789962570609578]}],"EDDSAPub":{"Curve":"ed25519","Coords":[19086049614490163056889859244855881816030591019773605655349500169700108043042,41654918635428963527745188055885714592211199796738262868910566706065491471139]}}
--------------------------------------------------------------------------------
/test/_eddsa_fixtures/keygen_data_3.json:
--------------------------------------------------------------------------------
1 | {"Xi":6353538018391377409423451679166564941139370429538931545804550933599437341881,"ShareID":4331412928177670560630249357713645140224222675389722735738732090810250798214,"Ks":[4331412928177670560630249357713645140224222675389722735738732090810250798211,4331412928177670560630249357713645140224222675389722735738732090810250798212,4331412928177670560630249357713645140224222675389722735738732090810250798213,4331412928177670560630249357713645140224222675389722735738732090810250798214,4331412928177670560630249357713645140224222675389722735738732090810250798215],"BigXj":[{"Curve":"ed25519","Coords":[43310099667191579169521946934030711066313578772454024333166342656953690707905,38262908547499520970138465680473622143445318034940413117238454107324155485797]},{"Curve":"ed25519","Coords":[24927223220985169141357864268825305163035760221331262595481037667448513230521,54543586647335403608940330836676147882229516532084699385623699026578670835490]},{"Curve":"ed25519","Coords":[19616187786153771277930658236501515378058310865337414616528052406614836915381,5716086794073894084115757926314451153376043864681595081544933294419482836932]},{"Curve":"ed25519","Coords":[54894815670576274903749323651006626882715390257891571131934873815604647096151,6261975146537300228952779313756496780554610579250869076285924817563901726620]},{"Curve":"ed25519","Coords":[56199043572031726140854435832515077152685912346629787788574432437354212337803,20932642671481609624660147804972444129208387391842241879798619789962570609578]}],"EDDSAPub":{"Curve":"ed25519","Coords":[19086049614490163056889859244855881816030591019773605655349500169700108043042,41654918635428963527745188055885714592211199796738262868910566706065491471139]}}
--------------------------------------------------------------------------------
/test/_eddsa_fixtures/keygen_data_4.json:
--------------------------------------------------------------------------------
1 | {"Xi":3418456088972858028466794296427125568458705881662784428722195994634550349577,"ShareID":4331412928177670560630249357713645140224222675389722735738732090810250798215,"Ks":[4331412928177670560630249357713645140224222675389722735738732090810250798211,4331412928177670560630249357713645140224222675389722735738732090810250798212,4331412928177670560630249357713645140224222675389722735738732090810250798213,4331412928177670560630249357713645140224222675389722735738732090810250798214,4331412928177670560630249357713645140224222675389722735738732090810250798215],"BigXj":[{"Curve":"ed25519","Coords":[43310099667191579169521946934030711066313578772454024333166342656953690707905,38262908547499520970138465680473622143445318034940413117238454107324155485797]},{"Curve":"ed25519","Coords":[24927223220985169141357864268825305163035760221331262595481037667448513230521,54543586647335403608940330836676147882229516532084699385623699026578670835490]},{"Curve":"ed25519","Coords":[19616187786153771277930658236501515378058310865337414616528052406614836915381,5716086794073894084115757926314451153376043864681595081544933294419482836932]},{"Curve":"ed25519","Coords":[54894815670576274903749323651006626882715390257891571131934873815604647096151,6261975146537300228952779313756496780554610579250869076285924817563901726620]},{"Curve":"ed25519","Coords":[56199043572031726140854435832515077152685912346629787788574432437354212337803,20932642671481609624660147804972444129208387391842241879798619789962570609578]}],"EDDSAPub":{"Curve":"ed25519","Coords":[19086049614490163056889859244855881816030591019773605655349500169700108043042,41654918635428963527745188055885714592211199796738262868910566706065491471139]}}
--------------------------------------------------------------------------------
/test/config.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package test
8 |
9 | const (
10 | // To change these parameters, you must first delete the text fixture files in test/_fixtures/ and then run the keygen test alone.
11 | // Then the signing and resharing tests will work with the new n, t configuration using the newly written fixture files.
12 | TestParticipants = 5
13 | TestThreshold = TestParticipants / 2
14 | )
15 |
--------------------------------------------------------------------------------
/test/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package test
8 |
9 | import (
10 | "github.com/binance-chain/tss-lib/tss"
11 | )
12 |
13 | func SharedPartyUpdater(party tss.Party, msg tss.Message, errCh chan<- *tss.Error) {
14 | // do not send a message from this party back to itself
15 | if party.PartyID() == msg.GetFrom() {
16 | return
17 | }
18 | bz, _, err := msg.WireBytes()
19 | if err != nil {
20 | errCh <- party.WrapError(err)
21 | return
22 | }
23 | pMsg, err := tss.ParseWireMessage(bz, msg.GetFrom(), msg.IsBroadcast())
24 | if err != nil {
25 | errCh <- party.WrapError(err)
26 | return
27 | }
28 | if _, err := party.Update(pMsg); err != nil {
29 | errCh <- err
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tss/curve.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package tss
8 |
9 | import (
10 | "crypto/elliptic"
11 | "errors"
12 | "reflect"
13 |
14 | s256k1 "github.com/btcsuite/btcd/btcec"
15 | "github.com/decred/dcrd/dcrec/edwards/v2"
16 | )
17 |
18 | type CurveName string
19 |
20 | const (
21 | Secp256k1 CurveName = "secp256k1"
22 | Nist256p1 CurveName = "nist256p1" // a.k.a secp256r1
23 | Ed25519 CurveName = "ed25519"
24 | )
25 |
26 | var (
27 | ec elliptic.Curve
28 | registry map[CurveName]elliptic.Curve
29 | )
30 |
31 | // Init default curve (secp256k1)
32 | func init() {
33 | ec = s256k1.S256()
34 |
35 | registry = make(map[CurveName]elliptic.Curve)
36 | registry[Secp256k1] = s256k1.S256()
37 | registry[Nist256p1] = elliptic.P256()
38 | registry[Ed25519] = edwards.Edwards()
39 | }
40 |
41 | func RegisterCurve(name CurveName, curve elliptic.Curve) {
42 | registry[name] = curve
43 | }
44 |
45 | // return curve, exist(bool)
46 | func GetCurveByName(name CurveName) (elliptic.Curve, bool) {
47 | if val, exist := registry[name]; exist {
48 | return val, true
49 | }
50 |
51 | return nil, false
52 | }
53 |
54 | // return name, exist(bool)
55 | func GetCurveName(curve elliptic.Curve) (CurveName, bool) {
56 | for name, e := range registry {
57 | if reflect.TypeOf(curve) == reflect.TypeOf(e) {
58 | return name, true
59 | }
60 | }
61 |
62 | return "", false
63 | }
64 |
65 | // EC returns the current elliptic curve in use. The default is secp256k1
66 | func EC() elliptic.Curve {
67 | return ec
68 | }
69 |
70 | // SetCurve sets the curve used by TSS. Must be called before Start. The default is secp256k1
71 | // Deprecated
72 | func SetCurve(curve elliptic.Curve) {
73 | if curve == nil {
74 | panic(errors.New("SetCurve received a nil curve"))
75 | }
76 | ec = curve
77 | }
78 |
79 | // secp256k1
80 | func S256() elliptic.Curve {
81 | return s256k1.S256()
82 | }
83 |
84 | func Edwards() elliptic.Curve {
85 | return edwards.Edwards()
86 | }
87 |
--------------------------------------------------------------------------------
/tss/error.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package tss
8 |
9 | import (
10 | "fmt"
11 | )
12 |
13 | // Represents an error that occurred during execution of the TSS protocol rounds.
14 | type Error struct {
15 | cause error
16 | task string
17 | round int
18 | victim *PartyID
19 | culprits []*PartyID
20 | }
21 |
22 | type VictimAndCulprit struct {
23 | Victim *PartyID
24 | Culprit *PartyID
25 | Message string
26 | }
27 |
28 | func NewError(err error, task string, round int, victim *PartyID, culprits ...*PartyID) *Error {
29 | return &Error{cause: err, task: task, round: round, victim: victim, culprits: culprits}
30 | }
31 |
32 | func (err *Error) Unwrap() error { return err.cause }
33 |
34 | func (err *Error) Cause() error { return err.cause }
35 |
36 | func (err *Error) Task() string { return err.task }
37 |
38 | func (err *Error) Round() int { return err.round }
39 |
40 | func (err *Error) Victim() *PartyID { return err.victim }
41 |
42 | func (err *Error) Culprits() []*PartyID { return err.culprits }
43 |
44 | func (err *Error) SelfCaused() bool {
45 | return len(err.culprits) == 0 || (len(err.culprits) == 1 && err.culprits[0] == err.victim)
46 | }
47 |
48 | func (err *Error) Error() string {
49 | if err == nil || err.cause == nil {
50 | return "Error is nil"
51 | }
52 | if err.culprits != nil && len(err.culprits) > 0 {
53 | return fmt.Sprintf("task %s, party %v, round %d, culprits %s: %s",
54 | err.task, err.victim, err.round, err.culprits, err.cause.Error())
55 | }
56 | if err.victim != nil {
57 | return fmt.Sprintf("task %s, party %v, round %d: %s",
58 | err.task, err.victim, err.round, err.cause.Error())
59 | }
60 | return fmt.Sprintf("task %s, round %d: %s",
61 | err.task, err.round, err.cause.Error())
62 | }
63 |
64 | func (vc *VictimAndCulprit) Error() string {
65 | message := ""
66 | if vc.Culprit != nil {
67 | message = fmt.Sprintf("culprit party: %s", vc.Culprit)
68 | }
69 | if vc.Victim != nil {
70 | message = message + fmt.Sprintf(" victim party: %s", vc.Victim)
71 | }
72 | if len(vc.Message) > 0 {
73 | message = message + " " + vc.Message
74 | }
75 | return message
76 | }
77 |
--------------------------------------------------------------------------------
/tss/peers.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package tss
8 |
9 | type (
10 | PeerContext struct {
11 | partyIDs SortedPartyIDs
12 | }
13 | )
14 |
15 | func NewPeerContext(parties SortedPartyIDs) *PeerContext {
16 | return &PeerContext{partyIDs: parties}
17 | }
18 |
19 | func (p2pCtx *PeerContext) IDs() SortedPartyIDs {
20 | return p2pCtx.partyIDs
21 | }
22 |
23 | func (p2pCtx *PeerContext) SetIDs(ids SortedPartyIDs) {
24 | p2pCtx.partyIDs = ids
25 | }
26 |
--------------------------------------------------------------------------------
/tss/round.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package tss
8 |
9 | type Round interface {
10 | Params() *Parameters
11 | Start() *Error
12 | Update() (bool, *Error)
13 | RoundNumber() int
14 | CanAccept(msg ParsedMessage) bool
15 | CanProceed() bool
16 | NextRound() Round
17 | WaitingFor() []*PartyID
18 | WrapError(err error, culprits ...*PartyID) *Error
19 | }
20 |
--------------------------------------------------------------------------------
/tss/wire.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Binance
2 | //
3 | // This file is part of Binance. The full Binance copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | package tss
8 |
9 | import (
10 | "errors"
11 |
12 | "github.com/golang/protobuf/proto"
13 | "github.com/golang/protobuf/ptypes"
14 | "github.com/golang/protobuf/ptypes/any"
15 | )
16 |
17 | const (
18 | ECDSAProtoNamePrefix = "binance.tss-lib.ecdsa."
19 | EDDSAProtoNamePrefix = "binance.tss-lib.eddsa."
20 | )
21 |
22 | // Used externally to update a LocalParty with a valid ParsedMessage
23 | func ParseWireMessage(wireBytes []byte, from *PartyID, isBroadcast bool) (ParsedMessage, error) {
24 | wire := new(MessageWrapper)
25 | wire.Message = new(any.Any)
26 | wire.From = from.MessageWrapper_PartyID
27 | wire.IsBroadcast = isBroadcast
28 | if err := proto.Unmarshal(wireBytes, wire.Message); err != nil {
29 | return nil, err
30 | }
31 | return parseWrappedMessage(wire, from)
32 | }
33 |
34 | func parseWrappedMessage(wire *MessageWrapper, from *PartyID) (ParsedMessage, error) {
35 | var any ptypes.DynamicAny
36 | meta := MessageRouting{
37 | From: from,
38 | IsBroadcast: wire.IsBroadcast,
39 | }
40 | if err := ptypes.UnmarshalAny(wire.Message, &any); err != nil {
41 | return nil, err
42 | }
43 | if content, ok := any.Message.(MessageContent); ok {
44 | return NewMessage(meta, content, wire), nil
45 | }
46 | return nil, errors.New("ParseWireMessage: the message contained unknown content")
47 | }
48 |
--------------------------------------------------------------------------------