diff --git a/go.mod b/go.mod index adb38c3e..00bac157 100644 --- a/go.mod +++ b/go.mod @@ -7,11 +7,10 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 github.com/fxamacker/cbor/v2 v2.3.0 - github.com/rs/zerolog v1.23.0 github.com/stretchr/testify v1.7.0 github.com/zeebo/blake3 v0.2.0 - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/crypto v0.1.0 + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index 411169da..8eb26b54 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,31 @@ +github.com/aws/aws-sdk-go v1.44.238 h1:qSWVXr/y/SsYyuvwVHYQpzcMKa2UzOjKgqPp7BTGfbo= +github.com/aws/aws-sdk-go v1.44.238/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go-v2 v1.17.7 h1:CLSjnhJSTSogvqUGhIC6LqFKATMRexcxLZ0i/Nzk9Eg= +github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.19 h1:AqFK6zFNtq4i1EYu+eC7lcKHYnZagMn6SW171la0bGw= +github.com/aws/aws-sdk-go-v2/config v1.18.19/go.mod h1:XvTmGMY8d52ougvakOv1RpiTLPz9dlG/OQHsKU/cMmY= +github.com/aws/aws-sdk-go-v2/credentials v1.13.18 h1:EQMdtHwz0ILTW1hoP+EwuWhwCG1hD6l3+RWFQABET4c= +github.com/aws/aws-sdk-go-v2/credentials v1.13.18/go.mod h1:vnwlwjIe+3XJPBYKu1et30ZPABG3VaXJYr8ryohpIyM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 h1:gt57MN3liKiyGopcqgNzJb2+d9MJaKT/q1OksHNXVE4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1/go.mod h1:lfUx8puBRdM5lVVMQlwt2v+ofiG/X6Ms+dy0UkG/kXw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 h1:sJLYcS+eZn5EeNINGHSCRAwUJMFVqklwkH36Vbyai7M= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25 h1:1mnRASEKnkqsntcxHaysxwgVoUUp5dkiB+l3llKnqyg= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 h1:p5luUImdIqywn6JpQsW3tq5GNOxKmOnEpybzPx+d1lk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25 h1:5LHn8JQ0qvjD9L9JhMtylnkcw7j05GDZqM9Oin6hpr0= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU= +github.com/aws/aws-sdk-go-v2/service/kms v1.20.8 h1:R5f4VOFi3ScTe7TtePyxLqEhNqTJIAxL57MzrXFNs6I= +github.com/aws/aws-sdk-go-v2/service/kms v1.20.8/go.mod h1:OtP3pBOgmJM+acQyQcQXtQHets3yJoVuanCx2T5M7v4= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 h1:5V7DWLBd7wTELVz5bPpwzYy/sikk0gsgZfj40X+l5OI= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.6/go.mod h1:Y1VOmit/Fn6Tz1uFAeCO6Q7M2fmfXSCLeL5INVYsLuY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 h1:B8cauxOH1W1v7rd8RdI/MWnoR4Ze0wIHWrb90qczxj4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6/go.mod h1:Lh/bc9XUf8CfOY6Jp5aIkQtN+j1mc+nExc+KXj9jx2s= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 h1:bWNgNdRko2x6gqa0blfATqAZKZokPIeM1vfmQt2pnvM= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.7/go.mod h1:JuTnSoeePXmMVe9G8NcjjwgOKEfZ4cOjMuT2IBT/2eI= +github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= +github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cronokirby/safenum v0.29.0 h1:kf1/8vvN/yQjrZU3tR/vDb5OdIQp2uJ6WvPVbU61J+E= github.com/cronokirby/safenum v0.29.0/go.mod h1:AWp82xwEqKcnrpJPXPa1m0gF/OY8dzgL17ubUBnVygA= @@ -11,6 +39,10 @@ github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+ github.com/fxamacker/cbor/v2 v2.3.0 h1:aM45YGMctNakddNNAezPxDUpv38j44Abh+hifNuqXik= github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -28,6 +60,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.0 h1:1SGx3IvKWFUU/xl+/7kjdcjjMcvVSm+3dMo/N42afC8= @@ -39,15 +72,23 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -57,17 +98,27 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 h1:rw6UNGRMfarCepjI8qOepea/SXwIBVfTKjztZ5gBbq4= golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/ecdsa/signature.go b/pkg/ecdsa/signature.go index c9712597..ddf8c6b0 100644 --- a/pkg/ecdsa/signature.go +++ b/pkg/ecdsa/signature.go @@ -44,3 +44,43 @@ func (sig Signature) RecoveryId() byte { return recid } + +// get a signature in ethereum format +func (sig Signature) SigEthereum() ([]byte, error) { + IsOverHalfOrder := sig.S.IsOverHalfOrder() // s-values greater than secp256k1n/2 are considered invalid + + if IsOverHalfOrder { + sig.S.Negate() + } + + r, err := sig.R.MarshalBinary() + if err != nil { + return nil, err + } + s, err := sig.S.MarshalBinary() + if err != nil { + return nil, err + } + + rs := make([]byte, 0, 65) + rs = append(rs, r...) + rs = append(rs, s...) + + if IsOverHalfOrder { + v := rs[0] - 2 // Convert to Ethereum signature format with 'recovery id' v at the end. + copy(rs, rs[1:]) + rs[64] = v ^ 1 + } else { + v := rs[0] - 2 + copy(rs, rs[1:]) + rs[64] = v + } + + r[0] = rs[64] + 2 + if err := sig.R.UnmarshalBinary(r); err != nil { + return nil, err + } + + return rs, nil +} + diff --git a/pkg/math/arith/int.go b/pkg/math/arith/int.go index d6aea392..38c4106c 100644 --- a/pkg/math/arith/int.go +++ b/pkg/math/arith/int.go @@ -60,3 +60,11 @@ func IsInIntervalLPrimeEps(n *safenum.Int) bool { } return n.TrueLen() <= params.LPrimePlusEpsilon } + +// IsInIntervalLEpsPlus1RootN returns true if n ∈ [-2¹⁺ˡ⁺ᵉ√N,…,2¹⁺ˡ⁺ᵉ√N], for a Paillier modulus N. +func IsInIntervalLEpsPlus1RootN(n *safenum.Int) bool { + if n == nil { + return false + } + return n.TrueLen() <= 1+params.LPlusEpsilon+(params.BitsIntModN/2) +} diff --git a/pkg/math/curve/curve.go b/pkg/math/curve/curve.go index 5ecbc546..aa6d3fba 100644 --- a/pkg/math/curve/curve.go +++ b/pkg/math/curve/curve.go @@ -91,6 +91,8 @@ type Scalar interface { // // This can be accomplished with Act, but can be made more efficient, in many cases. ActOnBase() Point + + IsOverHalfOrder() bool } // Point represents an element of our Elliptic Curve group. diff --git a/pkg/math/curve/secp256k1.go b/pkg/math/curve/secp256k1.go index 89401008..2bdfb932 100644 --- a/pkg/math/curve/secp256k1.go +++ b/pkg/math/curve/secp256k1.go @@ -139,6 +139,10 @@ func (s *Secp256k1Scalar) Negate() Scalar { return s } +func (s *Secp256k1Scalar) IsOverHalfOrder() bool { + return s.value.IsOverHalfOrder() +} + func (s *Secp256k1Scalar) Equal(that Scalar) bool { other := secp256k1CastScalar(that) diff --git a/pkg/math/sample/plus_minus.go b/pkg/math/sample/plus_minus.go index def0d720..b9ed0800 100644 --- a/pkg/math/sample/plus_minus.go +++ b/pkg/math/sample/plus_minus.go @@ -28,6 +28,11 @@ func IntervalLPrime(rand io.Reader) *safenum.Int { return sampleNeg(rand, params.LPrime) } +// IntervalEps returns an integer in the range ± 2ᵉ, but with constant-time properties. +func IntervalEps(rand io.Reader) *safenum.Int { + return sampleNeg(rand, params.Epsilon) +} + // IntervalLEps returns an integer in the range ± 2ˡ⁺ᵉ, but with constant-time properties. func IntervalLEps(rand io.Reader) *safenum.Int { return sampleNeg(rand, params.LPlusEpsilon) @@ -43,11 +48,26 @@ func IntervalLN(rand io.Reader) *safenum.Int { return sampleNeg(rand, params.L+params.BitsIntModN) } +// IntervalLN2 returns an integer in the range ± 2ˡ•N², where N is the size of a Paillier modulus. +func IntervalLN2(rand io.Reader) *safenum.Int { + return sampleNeg(rand, params.L+(2*params.BitsIntModN)) +} + // IntervalLEpsN returns an integer in the range ± 2ˡ⁺ᵉ•N, where N is the size of a Paillier modulus. func IntervalLEpsN(rand io.Reader) *safenum.Int { return sampleNeg(rand, params.LPlusEpsilon+params.BitsIntModN) } +// IntervalLEpsN2 returns an integer in the range ± 2ˡ⁺ᵉ•N², where N is the size of a Paillier modulus. +func IntervalLEpsN2(rand io.Reader) *safenum.Int { + return sampleNeg(rand, params.LPlusEpsilon+(2*params.BitsIntModN)) +} + +// IntervalLEpsRootN returns an integer in the range ± 2ˡ⁺ᵉ•√N, where N is the size of a Paillier modulus. +func IntervalLEpsRootN(rand io.Reader) *safenum.Int { + return sampleNeg(rand, params.LPlusEpsilon+(params.BitsIntModN/2)) +} + // IntervalScalar returns an integer in the range ±q, with q the size of a Scalar. func IntervalScalar(rand io.Reader, group curve.Curve) *safenum.Int { return sampleNeg(rand, group.ScalarBits()) diff --git a/pkg/pedersen/pedersen.go b/pkg/pedersen/pedersen.go index d8078097..1a1bc8c7 100644 --- a/pkg/pedersen/pedersen.go +++ b/pkg/pedersen/pedersen.go @@ -59,6 +59,9 @@ func ValidateParameters(n *safenum.Modulus, s, t *safenum.Nat) error { // N = p•q, p ≡ q ≡ 3 mod 4. func (p Parameters) N() *safenum.Modulus { return p.n.Modulus } +// N, but as an arith modulus, which is sometimes useful +func (p Parameters) NArith() *arith.Modulus { return p.n } + // S = r² mod N. func (p Parameters) S() *safenum.Nat { return p.s } diff --git a/pkg/zk/fac/fac.go b/pkg/zk/fac/fac.go new file mode 100644 index 00000000..7739cd93 --- /dev/null +++ b/pkg/zk/fac/fac.go @@ -0,0 +1,153 @@ +package zkfac + +import ( + "crypto/rand" + + "github.com/cronokirby/safenum" + "github.com/taurusgroup/multi-party-sig/pkg/hash" + "github.com/taurusgroup/multi-party-sig/pkg/math/arith" + "github.com/taurusgroup/multi-party-sig/pkg/math/sample" + "github.com/taurusgroup/multi-party-sig/pkg/pedersen" +) + +type Public struct { + Aux *pedersen.Parameters +} + +type Private struct { + P, Q *safenum.Nat +} + +type Commitment struct { + P *safenum.Nat + Q *safenum.Nat + A *safenum.Nat + B *safenum.Nat + T *safenum.Nat +} + +type Proof struct { + Comm Commitment + Sigma *safenum.Int + Z1 *safenum.Int + Z2 *safenum.Int + W1 *safenum.Int + W2 *safenum.Int + V *safenum.Int +} + +func NewProof(private Private, hash *hash.Hash, public Public) *Proof { + N := public.Aux.NArith() + + // Figure 28, point 1. + alpha := sample.IntervalLEpsRootN(rand.Reader) + beta := sample.IntervalLEpsRootN(rand.Reader) + mu := sample.IntervalLN(rand.Reader) + nu := sample.IntervalLN(rand.Reader) + sigma := sample.IntervalLN2(rand.Reader) + r := sample.IntervalLEpsN2(rand.Reader) + x := sample.IntervalLEpsN(rand.Reader) + y := sample.IntervalLEpsN(rand.Reader) + + pInt := new(safenum.Int).SetNat(private.P) + qInt := new(safenum.Int).SetNat(private.Q) + P := public.Aux.Commit(pInt, mu) + Q := public.Aux.Commit(qInt, nu) + A := public.Aux.Commit(alpha, x) + B := public.Aux.Commit(beta, y) + T := N.ExpI(Q, alpha) + T.ModMul(T, N.ExpI(public.Aux.T(), r), N.Modulus) + + comm := Commitment{P, Q, A, B, T} + + // Figure 28, point 2: + e, _ := challenge(hash, public, comm) + + // Figure 28, point 3: + // "..., and sends (z, u, v) to the verifier, where" + // DEVIATION: + // This seems like another typo, because there's no "u", + // so I assume they meant "sends (z1, z2, w1, w2, v)". + z1 := new(safenum.Int).Mul(e, pInt, -1) + z1.Add(z1, alpha, -1) + z2 := new(safenum.Int).Mul(e, qInt, -1) + z2.Add(z2, beta, -1) + w1 := new(safenum.Int).Mul(e, mu, -1) + w1.Add(w1, x, -1) + w2 := new(safenum.Int).Mul(e, nu, -1) + w2.Add(w2, y, -1) + sigmaHat := new(safenum.Int).Mul(nu, pInt, -1) + sigmaHat = sigmaHat.Neg(1) + sigmaHat.Add(sigmaHat, sigma, -1) + v := new(safenum.Int).Mul(e, sigmaHat, -1) + v.Add(v, r, -1) + + return &Proof{ + Comm: comm, + Sigma: sigma, + Z1: z1, + Z2: z2, + W1: w1, + W2: w2, + V: v, + } +} + +func (p *Proof) Verify(public Public, hash *hash.Hash) bool { + if p == nil { + return false + } + + e, err := challenge(hash, public, p.Comm) + if err != nil { + return false + } + + N := public.Aux.N() + NArith := public.Aux.NArith() + // Setting R this way avoid issues with the other exponent functions which + // might try and apply the CRT. + R := new(safenum.Nat).SetNat(public.Aux.S()) + R.ExpI(R, new(safenum.Int).SetNat(N.Nat()), N) + R.ModMul(R, NArith.ExpI(public.Aux.T(), p.Sigma), N) + + lhs := public.Aux.Commit(p.Z1, p.W1) + rhs := NArith.ExpI(p.Comm.P, e) + rhs.ModMul(rhs, p.Comm.A, N) + if lhs.Eq(rhs) != 1 { + return false + } + + lhs = public.Aux.Commit(p.Z2, p.W2) + rhs = NArith.ExpI(p.Comm.Q, e) + rhs.ModMul(rhs, p.Comm.B, N) + if lhs.Eq(rhs) != 1 { + return false + } + + lhs = NArith.ExpI(p.Comm.Q, p.Z1) + lhs.ModMul(lhs, NArith.ExpI(public.Aux.T(), p.V), N) + rhs = NArith.ExpI(R, e) + rhs.ModMul(rhs, p.Comm.T, N) + if lhs.Eq(rhs) != 1 { + return false + } + + // DEVIATION: for the bounds to work, we add an extra bit, to ensure that we don't have spurious failures. + return arith.IsInIntervalLEpsPlus1RootN(p.Z1) && arith.IsInIntervalLEpsPlus1RootN(p.Z2) +} + +func challenge(hash *hash.Hash, public Public, commitment Commitment) (*safenum.Int, error) { + err := hash.WriteAny(public.Aux, commitment.P, commitment.Q, commitment.A, commitment.B, commitment.T) + if err != nil { + return nil, err + } + // Figure 28, point 2: + // "Verifier replies with e <- +-q" + // DEVIATION: + // This doesn't make any sense, since we don't know the secret factor q, + // and involving the size of scalars doesn't make sense. + // I think that this is a typo in the paper, and instead it should + // be +-2^eps. + return sample.IntervalEps(hash.Digest()), nil +} diff --git a/pkg/zk/fac/fac_test.go b/pkg/zk/fac/fac_test.go new file mode 100644 index 00000000..7d7f869e --- /dev/null +++ b/pkg/zk/fac/fac_test.go @@ -0,0 +1,41 @@ +package zkfac + +import ( + "testing" + + "github.com/fxamacker/cbor/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/taurusgroup/multi-party-sig/pkg/hash" + "github.com/taurusgroup/multi-party-sig/pkg/paillier" + "github.com/taurusgroup/multi-party-sig/pkg/pool" +) + +func TestFac(t *testing.T) { + pl := pool.NewPool(0) + defer pl.TearDown() + + sk := paillier.NewSecretKey(pl) + ped, _ := sk.GeneratePedersen() + + public := Public{ + Aux: ped, + } + + proof := NewProof(Private{ + P: sk.P(), + Q: sk.Q(), + }, hash.New(), public) + assert.True(t, proof.Verify(public, hash.New())) + + out, err := cbor.Marshal(proof) + require.NoError(t, err, "failed to marshal proof") + proof2 := &Proof{} + require.NoError(t, cbor.Unmarshal(out, proof2), "failed to unmarshal proof") + out2, err := cbor.Marshal(proof2) + require.NoError(t, err, "failed to marshal 2nd proof") + proof3 := &Proof{} + require.NoError(t, cbor.Unmarshal(out2, proof3), "failed to unmarshal 2nd proof") + + assert.True(t, proof3.Verify(public, hash.New())) +} diff --git a/protocols/cmp/keygen/round3.go b/protocols/cmp/keygen/round3.go index 7b9f07cc..a115d487 100644 --- a/protocols/cmp/keygen/round3.go +++ b/protocols/cmp/keygen/round3.go @@ -8,11 +8,13 @@ import ( "github.com/taurusgroup/multi-party-sig/internal/round" "github.com/taurusgroup/multi-party-sig/internal/types" "github.com/taurusgroup/multi-party-sig/pkg/hash" + "github.com/taurusgroup/multi-party-sig/pkg/math/arith" "github.com/taurusgroup/multi-party-sig/pkg/math/curve" "github.com/taurusgroup/multi-party-sig/pkg/math/polynomial" "github.com/taurusgroup/multi-party-sig/pkg/paillier" "github.com/taurusgroup/multi-party-sig/pkg/party" "github.com/taurusgroup/multi-party-sig/pkg/pedersen" + zkfac "github.com/taurusgroup/multi-party-sig/pkg/zk/fac" zkmod "github.com/taurusgroup/multi-party-sig/pkg/zk/mod" zkprm "github.com/taurusgroup/multi-party-sig/pkg/zk/prm" zksch "github.com/taurusgroup/multi-party-sig/pkg/zk/sch" @@ -53,6 +55,7 @@ type broadcast3 struct { // - verify degree of VSS polynomial Fⱼ "in-the-exponent" // - if keygen, verify Fⱼ(0) != ∞ // - if refresh, verify Fⱼ(0) == ∞ +// // - validate Paillier // - validate Pedersen // - validate commitments. @@ -168,9 +171,15 @@ func (r *round3) Finalize(out chan<- *round.Message) (round.Session, error) { Q: r.PaillierSecret.Q(), }, h.Clone(), zkprm.Public{N: r.NModulus[r.SelfID()], S: r.S[r.SelfID()], T: r.T[r.SelfID()]}, r.Pool) + // Prove that the factors of N are relatively large + fac := zkfac.NewProof(zkfac.Private{P: r.PaillierSecret.P(), Q: r.PaillierSecret.Q()}, h.Clone(), zkfac.Public{ + Aux: pedersen.New(arith.ModulusFromFactors(r.PaillierSecret.P(), r.PaillierSecret.Q()), r.S[r.SelfID()], r.T[r.SelfID()]), + }) + if err := r.BroadcastMessage(out, &broadcast4{ Mod: mod, Prm: prm, + Fac: fac, }); err != nil { return r, err } diff --git a/protocols/cmp/keygen/round4.go b/protocols/cmp/keygen/round4.go index b2713385..e7b22d72 100644 --- a/protocols/cmp/keygen/round4.go +++ b/protocols/cmp/keygen/round4.go @@ -5,11 +5,13 @@ import ( "github.com/taurusgroup/multi-party-sig/internal/round" "github.com/taurusgroup/multi-party-sig/internal/types" + "github.com/taurusgroup/multi-party-sig/pkg/math/arith" "github.com/taurusgroup/multi-party-sig/pkg/math/curve" "github.com/taurusgroup/multi-party-sig/pkg/math/polynomial" "github.com/taurusgroup/multi-party-sig/pkg/paillier" "github.com/taurusgroup/multi-party-sig/pkg/party" "github.com/taurusgroup/multi-party-sig/pkg/pedersen" + zkfac "github.com/taurusgroup/multi-party-sig/pkg/zk/fac" zkmod "github.com/taurusgroup/multi-party-sig/pkg/zk/mod" zkprm "github.com/taurusgroup/multi-party-sig/pkg/zk/prm" "github.com/taurusgroup/multi-party-sig/protocols/cmp/config" @@ -36,6 +38,7 @@ type broadcast4 struct { round.NormalBroadcastContent Mod *zkmod.Proof Prm *zkprm.Proof + Fac *zkfac.Proof } // StoreBroadcastMessage implements round.BroadcastRound. @@ -57,6 +60,11 @@ func (r *round4) StoreBroadcastMessage(msg round.Message) error { if !body.Prm.Verify(zkprm.Public{N: r.NModulus[from], S: r.S[from], T: r.T[from]}, r.HashForID(from), r.Pool) { return errors.New("failed to validate prm proof") } + + // verify zkfac + if !body.Fac.Verify(zkfac.Public{Aux: pedersen.New(arith.ModulusFromN(r.NModulus[from]), r.S[from], r.T[from])}, r.HashForID(from)) { + return errors.New("failed to validate prm proof") + } return nil } diff --git a/protocols/cmp/presign/abort2.go b/protocols/cmp/presign/abort2.go index effe7c79..e064f5d3 100644 --- a/protocols/cmp/presign/abort2.go +++ b/protocols/cmp/presign/abort2.go @@ -46,7 +46,7 @@ func (r *abort2) StoreBroadcastMessage(msg round.Message) error { r.KShares[from] = r.Group().NewScalar().SetNat(body.KProof.Plaintext.Mod(r.Group().Order())) if !body.YHatProof.Verify(r.HashForID(from), zklog.Public{ - H: r.ElGamalK[from].L, + H: r.ElGamalChi[from].L, X: r.ElGamal[from], Y: body.YHat, }) { @@ -88,7 +88,7 @@ func (r *abort2) Finalize(chan<- *round.Message) (round.Session, error) { } - if !M.Equal(r.ElGamalK[j].M) { + if !M.Equal(r.ElGamalChi[j].M) { culprits = append(culprits, j) } } diff --git a/protocols/cmp/presign/presign7.go b/protocols/cmp/presign/presign7.go index b77da2fe..7772fd69 100644 --- a/protocols/cmp/presign/presign7.go +++ b/protocols/cmp/presign/presign7.go @@ -100,14 +100,14 @@ func (r *presign7) Finalize(out chan<- *round.Message) (round.Session, error) { // ∑ⱼ Sⱼ ?= X if !r.PublicKey.Equal(PublicKeyComputed) { - YHat := r.ElGamalKNonce.Act(r.ElGamal[r.SelfID()]) + YHat := r.ElGamalChiNonce.Act(r.ElGamal[r.SelfID()]) YHatProof := zklog.NewProof(r.Group(), r.HashForID(r.SelfID()), zklog.Public{ - H: r.ElGamalKNonce.ActOnBase(), + H: r.ElGamalChiNonce.ActOnBase(), X: r.ElGamal[r.SelfID()], Y: YHat, }, zklog.Private{ - A: r.ElGamalKNonce, - B: r.SecretElGamal, + A: r.SecretElGamal, + B: r.ElGamalChiNonce, }) ChiProofs := make(map[party.ID]*abortNth, r.N()-1) diff --git a/protocols/frost/keygen/config.go b/protocols/frost/keygen/config.go index 00f3f776..dc8946b9 100644 --- a/protocols/frost/keygen/config.go +++ b/protocols/frost/keygen/config.go @@ -141,7 +141,7 @@ func (r *TaprootConfig) Clone() *TaprootConfig { PrivateShare: curve.Secp256k1{}.NewScalar().Set(r.PrivateShare).(*curve.Secp256k1Scalar), PublicKey: publicKeyCopy, ChainKey: chainKeyCopy, - VerificationShares: r.VerificationShares, + VerificationShares: verificationSharesCopy, } } diff --git a/protocols/frost/keygen/keygen.go b/protocols/frost/keygen/keygen.go index f3c6b2bf..7d903595 100644 --- a/protocols/frost/keygen/keygen.go +++ b/protocols/frost/keygen/keygen.go @@ -34,9 +34,9 @@ func StartKeygenCommon(taproot bool, group curve.Curve, participants []party.ID, Group: group, } if taproot { - info.ProtocolID = protocolID - } else { info.ProtocolID = protocolIDTaproot + } else { + info.ProtocolID = protocolID } helper, err := round.NewSession(info, sessionID, nil)