Skip to content

Commit

Permalink
Ecdsa proof session byte (#256)
Browse files Browse the repository at this point in the history
* Add modProof in ECDSA-keygen

* Reduce test param to reduce github workflow load

* Add test fixtures

* Fix EDDSA keygen example

* Restore test fixtures

* Compress modproof

* use warning instead of fatal in logger to prevent panic

* Add more checks in range proof

* Regenerate preparams for older version

* Reset test params and regenerate fixtures

* Update ecdsa-resharing with Paillier key proofs

* Add a switch to optional turn off Pai key proofs

* Update README for caution of preparams updated

* add: ecdsa proof session byte

---------

Co-authored-by: ycen <yycen@live.com>
Co-authored-by: ZhAnGeek <lecky.z@nodereal.io>
  • Loading branch information
3 people authored Aug 23, 2023
1 parent 3d95e54 commit 1a14f3a
Show file tree
Hide file tree
Showing 105 changed files with 1,685 additions and 6,385 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/gofmt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: check out
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: go fmt project
uses: Jerome1337/gofmt-action@v1.0.2
uses: Jerome1337/gofmt-action@v1.0.4
30 changes: 16 additions & 14 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,32 @@ name: Go Test
on:
push:
branches:
- master
- release/*
- master
- release/*
pull_request:
branches:
- master
- master

jobs:
build:
name: Test
runs-on: macOS-latest
steps:

- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Set up Go 1.20
uses: actions/setup-go@v3
with:
go-version: 1.20.3
id: go

- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Check out code into the Go module directory
uses: actions/checkout@v3

- name: Get dependencies
run: go get -v -t -d ./...
- name: Clean dependencies
run: go clean --modcache

- name: Run Tests
run: make test_unit_race
- name: Get dependencies
run: go mod tidy

- name: Run Tests
run: make test_unit_race
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ build: protob
test_unit:
@echo "--> Running Unit Tests"
@echo "!!! WARNING: This will take a long time :)"
go clean -testcache
go test -timeout 60m $(PACKAGES)

test_unit_race:
@echo "--> Running Unit Tests (with Race Detection)"
@echo "!!! WARNING: This will take a long time :)"
go clean -testcache
go test -timeout 60m -race $(PACKAGES)

test:
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ In a typical use case, it is expected that a transport implementation will consu

This way there is no need to deal with Marshal/Unmarshalling Protocol Buffers to implement a transport.

## Changes of Preparams of ECDSA in v2.0

Two fields PaillierSK.P and PaillierSK.Q is added in version 2.0. They are used to generate Paillier key proofs. Key valuts generated from versions before 2.0 need to regenerate(resharing) the key valuts to update the praparams with the necessary fileds filled.

## How to use this securely

⚠️ This section is important. Be sure to read it!
Expand Down
41 changes: 41 additions & 0 deletions common/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,47 @@ func SHA512_256i(in ...*big.Int) *big.Int {
return new(big.Int).SetBytes(state.Sum(nil))
}

// SHA512_256i_TAGGED tagged version of SHA512_256i
func SHA512_256i_TAGGED(tag []byte, in ...*big.Int) *big.Int {
tagBz := SHA512_256(tag)
var data []byte
state := crypto.SHA512_256.New()
state.Write(tagBz)
state.Write(tagBz)
inLen := len(in)
if inLen == 0 {
return nil
}
bzSize := 0
// prevent hash collisions with this prefix containing the block count
inLenBz := make([]byte, 64/8)
// converting between int and uint64 doesn't change the sign bit, but it may be interpreted as a larger value.
// this prefix is never read/interpreted, so that doesn't matter.
binary.LittleEndian.PutUint64(inLenBz, uint64(inLen))
ptrs := make([][]byte, inLen)
for i, n := range in {
if n == nil {
ptrs[i] = zero.Bytes()
} else {
ptrs[i] = n.Bytes()
}
bzSize += len(ptrs[i])
}
data = make([]byte, 0, len(inLenBz)+bzSize+inLen)
data = append(data, inLenBz...)
for i := range in {
data = append(data, ptrs[i]...)
data = append(data, hashInputDelimiter) // safety delimiter
}
// n < len(data) or an error will never happen.
// see: https://golang.org/pkg/hash/#Hash and https://github.com/golang/go/wiki/Hashing#the-hashhash-interface
if _, err := state.Write(data); err != nil {
Logger.Error(err)
return nil
}
return new(big.Int).SetBytes(state.Sum(nil))
}

func SHA512_256iOne(in *big.Int) *big.Int {
var data []byte
state := crypto.SHA512_256.New()
Expand Down
7 changes: 7 additions & 0 deletions common/int.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,10 @@ func (mi *modInt) i() *big.Int {
func IsInInterval(b *big.Int, bound *big.Int) bool {
return b.Cmp(bound) == -1 && b.Cmp(zero) >= 0
}

func AppendBigIntToBytesSlice(commonBytes []byte, appended *big.Int) []byte {
resultBytes := make([]byte, len(commonBytes), len(commonBytes)+len(appended.Bytes()))
copy(resultBytes, commonBytes)
resultBytes = append(resultBytes, appended.Bytes()...)
return resultBytes
}
31 changes: 29 additions & 2 deletions common/random.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,38 @@ func IsNumberInMultiplicativeGroup(n, v *big.Int) bool {
gcd.GCD(nil, nil, v, n).Cmp(one) == 0
}

// Return a random generator of RQn with high probability.
// THIS METHOD ONLY WORKS IF N IS THE PRODUCT OF TWO SAFE PRIMES!
// Return a random generator of RQn with high probability.
// THIS METHOD ONLY WORKS IF N IS THE PRODUCT OF TWO SAFE PRIMES!
//
// https://github.com/didiercrunch/paillier/blob/d03e8850a8e4c53d04e8016a2ce8762af3278b71/utils.go#L39
func GetRandomGeneratorOfTheQuadraticResidue(n *big.Int) *big.Int {
f := GetRandomPositiveRelativelyPrimeInt(n)
fSq := new(big.Int).Mul(f, f)
return fSq.Mod(fSq, n)
}

// GetRandomQuadraticNonResidue returns a quadratic non residue of odd n.
func GetRandomQuadraticNonResidue(n *big.Int) *big.Int {
for {
w := GetRandomPositiveInt(n)
if big.Jacobi(w, n) == -1 {
return w
}
}
}

// GetRandomBytes returns random bytes of length.
func GetRandomBytes(length int) ([]byte, error) {
// Per [BIP32], the seed must be in range [MinSeedBytes, MaxSeedBytes].
if length <= 0 {
return nil, errors.New("invalid length")
}

buf := make([]byte, length)
_, err := rand.Read(buf)
if err != nil {
return nil, err
}

return buf, nil
}
12 changes: 12 additions & 0 deletions common/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,15 @@ func NonEmptyMultiBytes(bzs [][]byte, expectLen ...int) bool {
}
return true
}

// PadToLengthBytesInPlace pad {0, ...} to the front of src if len(src) < length
// output length is equal to the parameter length
func PadToLengthBytesInPlace(src []byte, length int) []byte {
oriLen := len(src)
if oriLen < length {
for i := 0; i < length-oriLen; i++ {
src = append([]byte{0}, src...)
}
}
return src
}
10 changes: 8 additions & 2 deletions crypto/ecpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ func (p *ECPoint) Add(p1 *ECPoint) (*ECPoint, error) {

func (p *ECPoint) ScalarMult(k *big.Int) *ECPoint {
x, y := p.curve.ScalarMult(p.X(), p.Y(), k.Bytes())
newP, _ := NewECPoint(p.curve, x, y) // it must be on the curve, no need to check.
newP, err := NewECPoint(p.curve, x, y) // it must be on the curve, no need to check.
if err != nil {
panic(fmt.Errorf("scalar mult to an ecpoint %s", err.Error()))
}
return newP
}

Expand Down Expand Up @@ -103,7 +106,10 @@ func (p *ECPoint) EightInvEight() *ECPoint {

func ScalarBaseMult(curve elliptic.Curve, k *big.Int) *ECPoint {
x, y := curve.ScalarBaseMult(k.Bytes())
p, _ := NewECPoint(curve, x, y) // it must be on the curve, no need to check.
p, err := NewECPoint(curve, x, y) // it must be on the curve, no need to check.
if err != nil {
panic(fmt.Errorf("scalar mult to an ecpoint %s", err.Error()))
}
return p
}

Expand Down
103 changes: 27 additions & 76 deletions crypto/facproof/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import (
"crypto/elliptic"
"errors"
"fmt"
"github.com/bnb-chain/tss-lib/common"
"math/big"

"github.com/bnb-chain/tss-lib/common"
)

const (
Expand All @@ -30,31 +31,31 @@ var (
one = big.NewInt(1)
)

// NewProof implements proofFac
func NewProof(ec elliptic.Curve, N0, NCap, s, t, N0p, N0q *big.Int) (*ProofFac, error) {
// NewProof implements prooffac
func NewProof(Session []byte, ec elliptic.Curve, N0, NCap, s, t, N0p, N0q *big.Int) (*ProofFac, error) {
if ec == nil || N0 == nil || NCap == nil || s == nil || t == nil || N0p == nil || N0q == nil {
return nil, errors.New("ProveFac constructor received nil value(s)")
}

q := ec.Params().N
q3 := new(big.Int).Mul(q, q)
q3 = new(big.Int).Mul(q, q3)
qNCap := new(big.Int).Mul(q, NCap)
qN0NCap := new(big.Int).Mul(qNCap, N0)
q3NCap := new(big.Int).Mul(q3, NCap)
q3N0NCap := new(big.Int).Mul(q3NCap, N0)
sqrtN0 := new(big.Int).Sqrt(N0)

leSqrtN0 := new(big.Int).Mul(rangeParameter, q)
leSqrtN0 = new(big.Int).Mul(leSqrtN0, sqrtN0)
lNCap := new(big.Int).Mul(rangeParameter, NCap)
lN0NCap := new(big.Int).Mul(lNCap, N0)
leN0NCap := new(big.Int).Mul(lN0NCap, q)
leNCap := new(big.Int).Mul(lNCap, q)
q3SqrtN0 := new(big.Int).Mul(q3, sqrtN0)

// Fig 28.1 sample
alpha := common.GetRandomPositiveInt(leSqrtN0)
beta := common.GetRandomPositiveInt(leSqrtN0)
mu := common.GetRandomPositiveInt(lNCap)
nu := common.GetRandomPositiveInt(lNCap)
sigma := common.GetRandomPositiveInt(lN0NCap)
r := common.GetRandomPositiveRelativelyPrimeInt(leN0NCap)
x := common.GetRandomPositiveInt(leNCap)
y := common.GetRandomPositiveInt(leNCap)
alpha := common.GetRandomPositiveInt(q3SqrtN0)
beta := common.GetRandomPositiveInt(q3SqrtN0)
mu := common.GetRandomPositiveInt(qNCap)
nu := common.GetRandomPositiveInt(qNCap)
sigma := common.GetRandomPositiveInt(qN0NCap)
r := common.GetRandomPositiveRelativelyPrimeInt(q3N0NCap)
x := common.GetRandomPositiveInt(q3NCap)
y := common.GetRandomPositiveInt(q3NCap)

// Fig 28.1 compute
modNCap := common.ModInt(NCap)
Expand All @@ -76,7 +77,7 @@ func NewProof(ec elliptic.Curve, N0, NCap, s, t, N0p, N0q *big.Int) (*ProofFac,
// Fig 28.2 e
var e *big.Int
{
eHash := common.SHA512_256i(N0, NCap, s, t, P, Q, A, B, T, sigma)
eHash := common.SHA512_256i_TAGGED(Session, N0, NCap, s, t, P, Q, A, B, T, sigma)
e = common.RejectionSample(q, eHash)
}

Expand Down Expand Up @@ -120,82 +121,32 @@ func NewProofFromBytes(bzs [][]byte) (*ProofFac, error) {
}, nil
}

func (pf *ProofFac) Verify(ec elliptic.Curve, N0, NCap, s, t *big.Int) bool {
func (pf *ProofFac) Verify(Session []byte, ec elliptic.Curve, N0, NCap, s, t *big.Int) bool {
if pf == nil || !pf.ValidateBasic() || ec == nil || N0 == nil || NCap == nil || s == nil || t == nil {
return false
}
if N0.Sign() != 1 {
return false
}
if NCap.Sign() != 1 {
return false
}

q := ec.Params().N
q3 := new(big.Int).Mul(q, q)
q3 = new(big.Int).Mul(q, q3)
sqrtN0 := new(big.Int).Sqrt(N0)

leSqrtN0 := new(big.Int).Mul(rangeParameter, q)
leSqrtN0 = new(big.Int).Mul(leSqrtN0, sqrtN0)
lNCap := new(big.Int).Mul(rangeParameter, NCap)
lN0NCap := new(big.Int).Mul(lNCap, N0)
leN0NCap2 := new(big.Int).Lsh(new(big.Int).Mul(lN0NCap, q), 1)
leNCap2 := new(big.Int).Lsh(new(big.Int).Mul(lNCap, q), 1)

if !common.IsInInterval(pf.P, NCap) {
return false
}
if !common.IsInInterval(pf.Q, NCap) {
return false
}
if !common.IsInInterval(pf.A, NCap) {
return false
}
if !common.IsInInterval(pf.B, NCap) {
return false
}
if !common.IsInInterval(pf.T, NCap) {
return false
}
if !common.IsInInterval(pf.Sigma, lN0NCap) {
return false
}
if new(big.Int).GCD(nil, nil, pf.P, NCap).Cmp(one) != 0 {
return false
}
if new(big.Int).GCD(nil, nil, pf.Q, NCap).Cmp(one) != 0 {
return false
}
if new(big.Int).GCD(nil, nil, pf.A, NCap).Cmp(one) != 0 {
return false
}
if new(big.Int).GCD(nil, nil, pf.B, NCap).Cmp(one) != 0 {
return false
}
if new(big.Int).GCD(nil, nil, pf.T, NCap).Cmp(one) != 0 {
return false
}
if !common.IsInInterval(pf.W1, leNCap2) {
return false
}
if !common.IsInInterval(pf.W2, leNCap2) {
return false
}
if !common.IsInInterval(pf.V, leN0NCap2) {
return false
}
q3SqrtN0 := new(big.Int).Mul(q3, sqrtN0)

// Fig 28. Range Check
if !common.IsInInterval(pf.Z1, leSqrtN0) {
if !common.IsInInterval(pf.Z1, q3SqrtN0) {
return false
}

if !common.IsInInterval(pf.Z2, leSqrtN0) {
if !common.IsInInterval(pf.Z2, q3SqrtN0) {
return false
}

var e *big.Int
{
eHash := common.SHA512_256i(N0, NCap, s, t, pf.P, pf.Q, pf.A, pf.B, pf.T, pf.Sigma)
eHash := common.SHA512_256i_TAGGED(Session, N0, NCap, s, t, pf.P, pf.Q, pf.A, pf.B, pf.T, pf.Sigma)
e = common.RejectionSample(q, eHash)
}

Expand Down
Loading

0 comments on commit 1a14f3a

Please sign in to comment.