├── .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 | 12 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/copyright/Binance.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 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 | --------------------------------------------------------------------------------