Merge pull request #790 from gzliudan/upgrade_crypto

upgrade crypto package
This commit is contained in:
Daniel Liu 2025-01-10 13:21:01 +08:00 committed by GitHub
commit 5cff4564aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 435 additions and 159 deletions

View file

@ -105,8 +105,8 @@ func (e *gfP12) Mul(a, b *gfP12) *gfP12 {
}
func (e *gfP12) MulScalar(a *gfP12, b *gfP6) *gfP12 {
e.x.Mul(&e.x, b)
e.y.Mul(&e.y, b)
e.x.Mul(&a.x, b)
e.y.Mul(&a.y, b)
return e
}

51
crypto/bn256/gnark/g1.go Normal file
View file

@ -0,0 +1,51 @@
package bn256
import (
"math/big"
"github.com/consensys/gnark-crypto/ecc/bn254"
)
// G1 is the affine representation of a G1 group element.
//
// Since this code is used for precompiles, using Jacobian
// points are not beneficial because there are no intermediate
// points to allow us to save on inversions.
//
// Note: We also use this struct so that we can conform to the existing API
// that the precompiles want.
type G1 struct {
inner bn254.G1Affine
}
// Add adds `a` and `b` together, storing the result in `g`
func (g *G1) Add(a, b *G1) {
g.inner.Add(&a.inner, &b.inner)
}
// ScalarMult computes the scalar multiplication between `a` and
// `scalar`, storing the result in `g`
func (g *G1) ScalarMult(a *G1, scalar *big.Int) {
g.inner.ScalarMultiplication(&a.inner, scalar)
}
// Unmarshal deserializes `buf` into `g`
//
// Note: whether the deserialization is of a compressed
// or an uncompressed point, is encoded in the bytes.
//
// For our purpose, the point will always be serialized
// as uncompressed, ie 64 bytes.
//
// This method also checks whether the point is on the
// curve and in the prime order subgroup.
func (g *G1) Unmarshal(buf []byte) (int, error) {
return g.inner.SetBytes(buf)
}
// Marshal serializes the point into a byte slice.
//
// Note: The point is serialized as uncompressed.
func (p *G1) Marshal() []byte {
return p.inner.Marshal()
}

38
crypto/bn256/gnark/g2.go Normal file
View file

@ -0,0 +1,38 @@
package bn256
import (
"github.com/consensys/gnark-crypto/ecc/bn254"
)
// G2 is the affine representation of a G2 group element.
//
// Since this code is used for precompiles, using Jacobian
// points are not beneficial because there are no intermediate
// points and G2 in particular is only used for the pairing input.
//
// Note: We also use this struct so that we can conform to the existing API
// that the precompiles want.
type G2 struct {
inner bn254.G2Affine
}
// Unmarshal deserializes `buf` into `g`
//
// Note: whether the deserialization is of a compressed
// or an uncompressed point, is encoded in the bytes.
//
// For our purpose, the point will always be serialized
// as uncompressed, ie 128 bytes.
//
// This method also checks whether the point is on the
// curve and in the prime order subgroup.
func (g *G2) Unmarshal(buf []byte) (int, error) {
return g.inner.SetBytes(buf)
}
// Marshal serializes the point into a byte slice.
//
// Note: The point is serialized as uncompressed.
func (g *G2) Marshal() []byte {
return g.inner.Marshal()
}

65
crypto/bn256/gnark/gt.go Normal file
View file

@ -0,0 +1,65 @@
package bn256
import (
"fmt"
"math/big"
"github.com/consensys/gnark-crypto/ecc/bn254"
)
// GT is the affine representation of a GT field element.
//
// Note: GT is not explicitly used in mainline code.
// It is needed for fuzzing.
type GT struct {
inner bn254.GT
}
// Pair compute the optimal Ate pairing between a G1 and
// G2 element.
//
// Note: This method is not explicitly used in mainline code.
// It is needed for fuzzing. It should also be noted,
// that the output of this function may not match other
func Pair(a_ *G1, b_ *G2) *GT {
a := a_.inner
b := b_.inner
pairingOutput, err := bn254.Pair([]bn254.G1Affine{a}, []bn254.G2Affine{b})
if err != nil {
// Since this method is only called during fuzzing, it is okay to panic here.
// We do not return an error to match the interface of the other bn256 libraries.
panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err))
}
return &GT{
inner: pairingOutput,
}
}
// Unmarshal deserializes `buf` into `g`
//
// Note: This method is not explicitly used in mainline code.
// It is needed for fuzzing.
func (g *GT) Unmarshal(buf []byte) error {
return g.inner.SetBytes(buf)
}
// Marshal serializes the point into a byte slice.
//
// Note: This method is not explicitly used in mainline code.
// It is needed for fuzzing.
func (g *GT) Marshal() []byte {
bytes := g.inner.Bytes()
return bytes[:]
}
// Exp raises `base` to the power of `exponent`
//
// Note: This method is not explicitly used in mainline code.
// It is needed for fuzzing.
func (g *GT) Exp(base GT, exponent *big.Int) *GT {
g.inner.Exp(base.inner, exponent)
return g
}

View file

@ -0,0 +1,73 @@
package bn256
import (
"github.com/consensys/gnark-crypto/ecc/bn254"
)
// Computes the following relation: ∏ᵢ e(Pᵢ, Qᵢ) =? 1
//
// To explain why gnark returns a (bool, error):
//
// - If the function `e` does not return a result then internally
// an error is returned.
// - If `e` returns a result, then error will be nil,
// but if this value is not `1` then the boolean value will be false
//
// We therefore check for an error, and return false if its non-nil and
// then return the value of the boolean if not.
func PairingCheck(a_ []*G1, b_ []*G2) bool {
a := getInnerG1s(a_)
b := getInnerG2s(b_)
// Assume that len(a) == len(b)
//
// The pairing function will return
// false, if this is not the case.
size := len(a)
// Check if input is empty -- gnark will
// return false on an empty input, however
// the ossified behavior is to return true
// on an empty input, so we add this if statement.
if size == 0 {
return true
}
ok, err := bn254.PairingCheck(a, b)
if err != nil {
return false
}
return ok
}
// getInnerG1s gets the inner gnark G1 elements.
//
// These methods are used for two reasons:
//
// - We use a new type `G1`, so we need to convert from
// []*G1 to []*bn254.G1Affine
// - The gnark API accepts slices of values and not slices of
// pointers to values, so we need to return []bn254.G1Affine
// instead of []*bn254.G1Affine.
func getInnerG1s(pointerSlice []*G1) []bn254.G1Affine {
gnarkValues := make([]bn254.G1Affine, 0, len(pointerSlice))
for _, ptr := range pointerSlice {
if ptr != nil {
gnarkValues = append(gnarkValues, ptr.inner)
}
}
return gnarkValues
}
// getInnerG2s gets the inner gnark G2 elements.
//
// The rationale for this method is the same as `getInnerG1s`.
func getInnerG2s(pointerSlice []*G2) []bn254.G2Affine {
gnarkValues := make([]bn254.G2Affine, 0, len(pointerSlice))
for _, ptr := range pointerSlice {
if ptr != nil {
gnarkValues = append(gnarkValues, ptr.inner)
}
}
return gnarkValues
}

View file

@ -29,7 +29,7 @@ import (
)
// BUG(agl): this implementation is not constant time.
// TODO(agl): keep GF(p²) elements in Mongomery form.
// TODO(agl): keep GF(p²) elements in Montgomery form.
// G1 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.

View file

@ -125,8 +125,8 @@ func (e *gfP12) Mul(a, b *gfP12, pool *bnPool) *gfP12 {
}
func (e *gfP12) MulScalar(a *gfP12, b *gfP6, pool *bnPool) *gfP12 {
e.x.Mul(e.x, b, pool)
e.y.Mul(e.y, b, pool)
e.x.Mul(a.x, b, pool)
e.y.Mul(a.y, b, pool)
return e
}

View file

@ -45,12 +45,21 @@ const RecoveryIDOffset = 64
const DigestLength = 32
var (
secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
secp256k1N = S256().Params().N
secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
)
var errInvalidPubkey = errors.New("invalid secp256k1 public key")
// EllipticCurve contains curve operations.
type EllipticCurve interface {
elliptic.Curve
// Point marshaling/unmarshaing.
Marshal(x, y *big.Int) []byte
Unmarshal(data []byte) (x, y *big.Int)
}
// KeccakState wraps sha3.state. In addition to the usual hash methods, it also supports
// Read to get a variable amount of data from the hash state. Read is faster than Sum
// because it doesn't copy the internal state, but also modifies the internal state.
@ -148,7 +157,7 @@ func toECDSA(d []byte, strict bool) (*ecdsa.PrivateKey, error) {
return nil, errors.New("invalid private key, zero or negative")
}
priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d)
priv.PublicKey.X, priv.PublicKey.Y = S256().ScalarBaseMult(d)
if priv.PublicKey.X == nil {
return nil, errors.New("invalid private key")
}
@ -165,7 +174,7 @@ func FromECDSA(priv *ecdsa.PrivateKey) []byte {
// UnmarshalPubkey converts bytes to a secp256k1 public key.
func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
x, y := elliptic.Unmarshal(S256(), pub)
x, y := S256().Unmarshal(pub)
if x == nil {
return nil, errInvalidPubkey
}
@ -176,7 +185,7 @@ func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
if pub == nil || pub.X == nil || pub.Y == nil {
return nil
}
return elliptic.Marshal(S256(), pub.X, pub.Y)
return S256().Marshal(pub.X, pub.Y)
}
// HexToECDSA parses a secp256k1 private key.
@ -278,7 +287,5 @@ func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
}
func zeroBytes(bytes []byte) {
for i := range bytes {
bytes[i] = 0
}
clear(bytes)
}

View file

@ -40,6 +40,8 @@ import (
"hash"
"io"
"math/big"
"github.com/XinFinOrg/XDPoSChain/crypto"
)
var (
@ -95,15 +97,15 @@ func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey {
// Generate an elliptic curve public / private keypair. If params is nil,
// the recommended default parameters for the key will be chosen.
func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) {
pb, x, y, err := elliptic.GenerateKey(curve, rand)
sk, err := ecdsa.GenerateKey(curve, rand)
if err != nil {
return
}
prv = new(PrivateKey)
prv.PublicKey.X = x
prv.PublicKey.Y = y
prv.PublicKey.X = sk.X
prv.PublicKey.Y = sk.Y
prv.PublicKey.Curve = curve
prv.D = new(big.Int).SetBytes(pb)
prv.D = new(big.Int).Set(sk.D)
if params == nil {
params = ParamsFromCurve(curve)
}
@ -255,12 +257,15 @@ func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err e
d := messageTag(params.Hash, Km, em, s2)
Rb := elliptic.Marshal(pub.Curve, R.PublicKey.X, R.PublicKey.Y)
ct = make([]byte, len(Rb)+len(em)+len(d))
copy(ct, Rb)
copy(ct[len(Rb):], em)
copy(ct[len(Rb)+len(em):], d)
return ct, nil
if curve, ok := pub.Curve.(crypto.EllipticCurve); ok {
Rb := curve.Marshal(R.PublicKey.X, R.PublicKey.Y)
ct = make([]byte, len(Rb)+len(em)+len(d))
copy(ct, Rb)
copy(ct[len(Rb):], em)
copy(ct[len(Rb)+len(em):], d)
return ct, nil
}
return nil, ErrInvalidCurve
}
// Decrypt decrypts an ECIES ciphertext.
@ -297,21 +302,24 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
R := new(PublicKey)
R.Curve = prv.PublicKey.Curve
R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen])
if R.X == nil {
return nil, ErrInvalidPublicKey
}
z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
if err != nil {
return nil, err
}
Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
if curve, ok := R.Curve.(crypto.EllipticCurve); ok {
R.X, R.Y = curve.Unmarshal(c[:rLen])
if R.X == nil {
return nil, ErrInvalidPublicKey
}
d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
return nil, ErrInvalidMessage
}
z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
if err != nil {
return nil, err
}
Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
return symDecrypt(params, Ke, c[mStart:mEnd])
}
d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
return nil, ErrInvalidMessage
}
return symDecrypt(params, Ke, c[mStart:mEnd])
}
return nil, ErrInvalidCurve
}

View file

@ -79,52 +79,52 @@ type BitCurve struct {
BitSize int // the size of the underlying field
}
func (BitCurve *BitCurve) Params() *elliptic.CurveParams {
func (bitCurve *BitCurve) Params() *elliptic.CurveParams {
return &elliptic.CurveParams{
P: BitCurve.P,
N: BitCurve.N,
B: BitCurve.B,
Gx: BitCurve.Gx,
Gy: BitCurve.Gy,
BitSize: BitCurve.BitSize,
P: bitCurve.P,
N: bitCurve.N,
B: bitCurve.B,
Gx: bitCurve.Gx,
Gy: bitCurve.Gy,
BitSize: bitCurve.BitSize,
}
}
// IsOnCurve returns true if the given (x,y) lies on the BitCurve.
func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
func (bitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
// y² = x³ + b
y2 := new(big.Int).Mul(y, y) //y²
y2.Mod(y2, BitCurve.P) //y²%P
y2.Mod(y2, bitCurve.P) //y²%P
x3 := new(big.Int).Mul(x, x) //x²
x3.Mul(x3, x) //x³
x3.Add(x3, BitCurve.B) //x³+B
x3.Mod(x3, BitCurve.P) //(x³+B)%P
x3.Add(x3, bitCurve.B) //x³+B
x3.Mod(x3, bitCurve.P) //(x³+B)%P
return x3.Cmp(y2) == 0
}
// affineFromJacobian reverses the Jacobian transform. See the comment at the
// top of the file.
func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
func (bitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
if z.Sign() == 0 {
return new(big.Int), new(big.Int)
}
zinv := new(big.Int).ModInverse(z, BitCurve.P)
zinv := new(big.Int).ModInverse(z, bitCurve.P)
zinvsq := new(big.Int).Mul(zinv, zinv)
xOut = new(big.Int).Mul(x, zinvsq)
xOut.Mod(xOut, BitCurve.P)
xOut.Mod(xOut, bitCurve.P)
zinvsq.Mul(zinvsq, zinv)
yOut = new(big.Int).Mul(y, zinvsq)
yOut.Mod(yOut, BitCurve.P)
yOut.Mod(yOut, bitCurve.P)
return
}
// Add returns the sum of (x1,y1) and (x2,y2)
func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
func (bitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
// If one point is at infinity, return the other point.
// Adding the point at infinity to any point will preserve the other point.
if x1.Sign() == 0 && y1.Sign() == 0 {
@ -135,27 +135,27 @@ func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
}
z := new(big.Int).SetInt64(1)
if x1.Cmp(x2) == 0 && y1.Cmp(y2) == 0 {
return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z))
return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z))
}
return BitCurve.affineFromJacobian(BitCurve.addJacobian(x1, y1, z, x2, y2, z))
return bitCurve.affineFromJacobian(bitCurve.addJacobian(x1, y1, z, x2, y2, z))
}
// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
// (x2, y2, z2) and returns their sum, also in Jacobian form.
func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
func (bitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
z1z1 := new(big.Int).Mul(z1, z1)
z1z1.Mod(z1z1, BitCurve.P)
z1z1.Mod(z1z1, bitCurve.P)
z2z2 := new(big.Int).Mul(z2, z2)
z2z2.Mod(z2z2, BitCurve.P)
z2z2.Mod(z2z2, bitCurve.P)
u1 := new(big.Int).Mul(x1, z2z2)
u1.Mod(u1, BitCurve.P)
u1.Mod(u1, bitCurve.P)
u2 := new(big.Int).Mul(x2, z1z1)
u2.Mod(u2, BitCurve.P)
u2.Mod(u2, bitCurve.P)
h := new(big.Int).Sub(u2, u1)
if h.Sign() == -1 {
h.Add(h, BitCurve.P)
h.Add(h, bitCurve.P)
}
i := new(big.Int).Lsh(h, 1)
i.Mul(i, i)
@ -163,13 +163,13 @@ func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
s1 := new(big.Int).Mul(y1, z2)
s1.Mul(s1, z2z2)
s1.Mod(s1, BitCurve.P)
s1.Mod(s1, bitCurve.P)
s2 := new(big.Int).Mul(y2, z1)
s2.Mul(s2, z1z1)
s2.Mod(s2, BitCurve.P)
s2.Mod(s2, bitCurve.P)
r := new(big.Int).Sub(s2, s1)
if r.Sign() == -1 {
r.Add(r, BitCurve.P)
r.Add(r, bitCurve.P)
}
r.Lsh(r, 1)
v := new(big.Int).Mul(u1, i)
@ -179,7 +179,7 @@ func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
x3.Sub(x3, j)
x3.Sub(x3, v)
x3.Sub(x3, v)
x3.Mod(x3, BitCurve.P)
x3.Mod(x3, bitCurve.P)
y3 := new(big.Int).Set(r)
v.Sub(v, x3)
@ -187,33 +187,33 @@ func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
s1.Mul(s1, j)
s1.Lsh(s1, 1)
y3.Sub(y3, s1)
y3.Mod(y3, BitCurve.P)
y3.Mod(y3, bitCurve.P)
z3 := new(big.Int).Add(z1, z2)
z3.Mul(z3, z3)
z3.Sub(z3, z1z1)
if z3.Sign() == -1 {
z3.Add(z3, BitCurve.P)
z3.Add(z3, bitCurve.P)
}
z3.Sub(z3, z2z2)
if z3.Sign() == -1 {
z3.Add(z3, BitCurve.P)
z3.Add(z3, bitCurve.P)
}
z3.Mul(z3, h)
z3.Mod(z3, BitCurve.P)
z3.Mod(z3, bitCurve.P)
return x3, y3, z3
}
// Double returns 2*(x,y)
func (BitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
func (bitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
z1 := new(big.Int).SetInt64(1)
return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z1))
return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z1))
}
// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
// returns its double, also in Jacobian form.
func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
func (bitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
a := new(big.Int).Mul(x, x) //X1²
@ -231,30 +231,30 @@ func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int,
x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D
x3.Sub(f, x3) //F-2*D
x3.Mod(x3, BitCurve.P)
x3.Mod(x3, bitCurve.P)
y3 := new(big.Int).Sub(d, x3) //D-X3
y3.Mul(e, y3) //E*(D-X3)
y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C
y3.Mod(y3, BitCurve.P)
y3.Mod(y3, bitCurve.P)
z3 := new(big.Int).Mul(y, z) //Y1*Z1
z3.Mul(big.NewInt(2), z3) //3*Y1*Z1
z3.Mod(z3, BitCurve.P)
z3.Mod(z3, bitCurve.P)
return x3, y3, z3
}
// ScalarBaseMult returns k*G, where G is the base point of the group and k is
// an integer in big-endian form.
func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k)
func (bitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
return bitCurve.ScalarMult(bitCurve.Gx, bitCurve.Gy, k)
}
// Marshal converts a point into the form specified in section 4.3.6 of ANSI
// X9.62.
func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
byteLen := (BitCurve.BitSize + 7) >> 3
func (bitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
byteLen := (bitCurve.BitSize + 7) >> 3
ret := make([]byte, 1+2*byteLen)
ret[0] = 4 // uncompressed point flag
readBits(x, ret[1:1+byteLen])
@ -264,8 +264,8 @@ func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On
// error, x = nil.
func (BitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) {
byteLen := (BitCurve.BitSize + 7) >> 3
func (bitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) {
byteLen := (bitCurve.BitSize + 7) >> 3
if len(data) != 1+2*byteLen {
return
}

View file

@ -21,7 +21,7 @@ extern int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, const unsigned
*/
import "C"
func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
// Ensure scalar is exactly 32 bytes. We pad always, even if
// scalar is 32 bytes long, to avoid a timing side channel.
if len(scalar) > 32 {
@ -44,12 +44,8 @@ func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int,
// Unpack the result and clear temporaries.
x := new(big.Int).SetBytes(point[:32])
y := new(big.Int).SetBytes(point[32:])
for i := range point {
point[i] = 0
}
for i := range padded {
scalar[i] = 0
}
clear(point)
clear(scalar)
if res != 1 {
return nil, nil
}

View file

@ -9,6 +9,6 @@ package secp256k1
import "math/big"
func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
panic("ScalarMult is not available when secp256k1 is built without cgo")
}

View file

@ -10,7 +10,6 @@ package secp256k1
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"io"
@ -24,7 +23,7 @@ func generateKeyPair() (pubkey, privkey []byte) {
if err != nil {
panic(err)
}
pubkey = elliptic.Marshal(S256(), key.X, key.Y)
pubkey = S256().Marshal(key.X, key.Y)
privkey = make([]byte, 32)
blob := key.D.Bytes()
@ -49,7 +48,7 @@ func randSig() []byte {
}
// tests for malleability
// highest bit of signature ECDSA s value must be 0, in the 33th byte
// the highest bit of signature ECDSA s value must be 0, in the 33th byte
func compactSigCheck(t *testing.T, sig []byte) {
var b = int(sig[32])
if b < 0 {

View file

@ -21,7 +21,6 @@ package crypto
import (
"crypto/ecdsa"
"crypto/elliptic"
"errors"
"fmt"
@ -40,9 +39,7 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
if err != nil {
return nil, err
}
x, y := elliptic.Unmarshal(S256(), s)
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
return UnmarshalPubkey(s)
}
// Sign calculates an ECDSA signature.
@ -84,6 +81,6 @@ func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
}
// S256 returns an instance of the secp256k1 curve.
func S256() elliptic.Curve {
func S256() EllipticCurve {
return secp256k1.S256()
}

View file

@ -21,12 +21,12 @@ package crypto
import (
"crypto/ecdsa"
"crypto/elliptic"
"errors"
"fmt"
"math/big"
"github.com/btcsuite/btcd/btcec/v2"
btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
decred_ecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
)
// Ecrecover returns the uncompressed public key that created the given signature.
@ -39,16 +39,16 @@ func Ecrecover(hash, sig []byte) ([]byte, error) {
return bytes, err
}
func sigToPub(hash, sig []byte) (*btcec.PublicKey, error) {
func sigToPub(hash, sig []byte) (*secp256k1.PublicKey, error) {
if len(sig) != SignatureLength {
return nil, errors.New("invalid signature")
}
// Convert to btcec input format with 'recovery id' v at the beginning.
// Convert to secp256k1 input format with 'recovery id' v at the beginning.
btcsig := make([]byte, SignatureLength)
btcsig[0] = sig[RecoveryIDOffset] + 27
copy(btcsig[1:], sig)
pub, _, err := btc_ecdsa.RecoverCompact(btcsig, hash)
pub, _, err := decred_ecdsa.RecoverCompact(btcsig, hash)
return pub, err
}
@ -58,7 +58,13 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
if err != nil {
return nil, err
}
return pub.ToECDSA(), nil
// We need to explicitly set the curve here, because we're wrapping
// the original curve to add (un-)marshalling
return &ecdsa.PublicKey{
Curve: S256(),
X: pub.X(),
Y: pub.Y(),
}, nil
}
// Sign calculates an ECDSA signature.
@ -73,19 +79,16 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
if len(hash) != 32 {
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
}
if prv.Curve != btcec.S256() {
if prv.Curve != S256() {
return nil, errors.New("private key curve is not secp256k1")
}
// ecdsa.PrivateKey -> btcec.PrivateKey
var priv btcec.PrivateKey
// ecdsa.PrivateKey -> secp256k1.PrivateKey
var priv secp256k1.PrivateKey
if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() {
return nil, errors.New("invalid private key")
}
defer priv.Zero()
sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey
if err != nil {
return nil, err
}
sig := decred_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey
// Convert to Ethereum signature format with 'recovery id' v at the end.
v := sig[0] - 27
copy(sig, sig[1:])
@ -100,19 +103,19 @@ func VerifySignature(pubkey, hash, signature []byte) bool {
if len(signature) != 64 {
return false
}
var r, s btcec.ModNScalar
var r, s secp256k1.ModNScalar
if r.SetByteSlice(signature[:32]) {
return false // overflow
}
if s.SetByteSlice(signature[32:]) {
return false
}
sig := btc_ecdsa.NewSignature(&r, &s)
key, err := btcec.ParsePubKey(pubkey)
sig := decred_ecdsa.NewSignature(&r, &s)
key, err := secp256k1.ParsePubKey(pubkey)
if err != nil {
return false
}
// Reject malleable signatures. libsecp256k1 does this check but btcec doesn't.
// Reject malleable signatures. libsecp256k1 does this check but decred doesn't.
if s.IsOverHalfOrder() {
return false
}
@ -124,11 +127,17 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
if len(pubkey) != 33 {
return nil, errors.New("invalid compressed public key length")
}
key, err := btcec.ParsePubKey(pubkey)
key, err := secp256k1.ParsePubKey(pubkey)
if err != nil {
return nil, err
}
return key.ToECDSA(), nil
// We need to explicitly set the curve here, because we're wrapping
// the original curve to add (un-)marshalling
return &ecdsa.PublicKey{
Curve: S256(),
X: key.X(),
Y: key.Y(),
}, nil
}
// CompressPubkey encodes a public key to the 33-byte compressed format. The
@ -139,14 +148,46 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
// when constructing a PrivateKey.
func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
// NOTE: the coordinates may be validated with
// btcec.ParsePubKey(FromECDSAPub(pubkey))
var x, y btcec.FieldVal
// secp256k1.ParsePubKey(FromECDSAPub(pubkey))
var x, y secp256k1.FieldVal
x.SetByteSlice(pubkey.X.Bytes())
y.SetByteSlice(pubkey.Y.Bytes())
return btcec.NewPublicKey(&x, &y).SerializeCompressed()
return secp256k1.NewPublicKey(&x, &y).SerializeCompressed()
}
// S256 returns an instance of the secp256k1 curve.
func S256() elliptic.Curve {
return btcec.S256()
func S256() EllipticCurve {
return btCurve{secp256k1.S256()}
}
type btCurve struct {
*secp256k1.KoblitzCurve
}
// Marshal converts a point given as (x, y) into a byte slice.
func (curve btCurve) Marshal(x, y *big.Int) []byte {
byteLen := (curve.Params().BitSize + 7) / 8
ret := make([]byte, 1+2*byteLen)
ret[0] = 4 // uncompressed point
x.FillBytes(ret[1 : 1+byteLen])
y.FillBytes(ret[1+byteLen : 1+2*byteLen])
return ret
}
// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On
// error, x = nil.
func (curve btCurve) Unmarshal(data []byte) (x, y *big.Int) {
byteLen := (curve.Params().BitSize + 7) / 8
if len(data) != 1+2*byteLen {
return nil, nil
}
if data[0] != 4 { // uncompressed form
return nil, nil
}
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
y = new(big.Int).SetBytes(data[1+byteLen:])
return
}

View file

@ -71,7 +71,7 @@ func TestVerifySignature(t *testing.T) {
wrongkey := common.CopyBytes(testpubkey)
wrongkey[10]++
if VerifySignature(wrongkey, testmsg, sig) {
t.Errorf("signature valid with with wrong public key")
t.Errorf("signature valid with wrong public key")
}
}

3
go.mod
View file

@ -41,10 +41,10 @@ require (
)
require (
github.com/btcsuite/btcd/btcec/v2 v2.3.4
github.com/consensys/gnark-crypto v0.10.0
github.com/crate-crypto/go-kzg-4844 v0.7.0
github.com/deckarep/golang-set v1.8.0
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498
github.com/ethereum/c-kzg-4844 v0.4.0
github.com/go-yaml/yaml v2.1.0+incompatible
@ -64,7 +64,6 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/deepmap/oapi-codegen v1.6.0 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/go-ole/go-ole v1.2.5 // indirect

2
go.sum
View file

@ -8,8 +8,6 @@ github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=

View file

@ -23,11 +23,11 @@ import (
"math/big"
cloudflare "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare"
gnark "github.com/XinFinOrg/XDPoSChain/crypto/bn256/gnark"
google "github.com/XinFinOrg/XDPoSChain/crypto/bn256/google"
"github.com/consensys/gnark-crypto/ecc/bn254"
)
func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *bn254.G1Affine) {
func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *gnark.G1) {
_, xc, err := cloudflare.RandomG1(input)
if err != nil {
// insufficient input
@ -37,14 +37,14 @@ func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *bn254.G1Affine)
if _, err := xg.Unmarshal(xc.Marshal()); err != nil {
panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err))
}
xs := new(bn254.G1Affine)
if err := xs.Unmarshal(xc.Marshal()); err != nil {
xs := new(gnark.G1)
if _, err := xs.Unmarshal(xc.Marshal()); err != nil {
panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err))
}
return xc, xg, xs
}
func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine) {
func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *gnark.G2) {
_, xc, err := cloudflare.RandomG2(input)
if err != nil {
// insufficient input
@ -54,14 +54,14 @@ func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine)
if _, err := xg.Unmarshal(xc.Marshal()); err != nil {
panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err))
}
xs := new(bn254.G2Affine)
if err := xs.Unmarshal(xc.Marshal()); err != nil {
xs := new(gnark.G2)
if _, err := xs.Unmarshal(xc.Marshal()); err != nil {
panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err))
}
return xc, xg, xs
}
// fuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries.
// fuzzAdd fuzzes bn256 addition between the Google, Cloudflare and Gnark libraries.
func fuzzAdd(data []byte) int {
input := bytes.NewReader(data)
xc, xg, xs := getG1Points(input)
@ -72,7 +72,7 @@ func fuzzAdd(data []byte) int {
if yc == nil {
return 0
}
// Ensure both libs can parse the second curve point
// Ensure libs can parse the second curve point
// Add the two points and ensure they result in the same output
rc := new(cloudflare.G1)
rc.Add(xc, yc)
@ -80,9 +80,8 @@ func fuzzAdd(data []byte) int {
rg := new(google.G1)
rg.Add(xg, yg)
tmpX := new(bn254.G1Jac).FromAffine(xs)
tmpY := new(bn254.G1Jac).FromAffine(ys)
rs := new(bn254.G1Affine).FromJacobian(tmpX.AddAssign(tmpY))
rs := new(gnark.G1)
rs.Add(xs, ys)
if !bytes.Equal(rc.Marshal(), rg.Marshal()) {
panic("add mismatch: cloudflare/google")
@ -94,8 +93,8 @@ func fuzzAdd(data []byte) int {
return 1
}
// fuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare
// libraries.
// fuzzMul fuzzes bn256 scalar multiplication between the Google, Cloudflare
// and Gnark libraries.
func fuzzMul(data []byte) int {
input := bytes.NewReader(data)
pc, pg, ps := getG1Points(input)
@ -122,15 +121,13 @@ func fuzzMul(data []byte) int {
rg := new(google.G1)
rg.ScalarMult(pg, new(big.Int).SetBytes(buf))
rs := new(bn254.G1Jac)
psJac := new(bn254.G1Jac).FromAffine(ps)
rs.ScalarMultiplication(psJac, new(big.Int).SetBytes(buf))
rsAffine := new(bn254.G1Affine).FromJacobian(rs)
rs := new(gnark.G1)
rs.ScalarMult(ps, new(big.Int).SetBytes(buf))
if !bytes.Equal(rc.Marshal(), rg.Marshal()) {
panic("scalar mul mismatch: cloudflare/google")
}
if !bytes.Equal(rc.Marshal(), rsAffine.Marshal()) {
if !bytes.Equal(rc.Marshal(), rs.Marshal()) {
panic("scalar mul mismatch: cloudflare/gnark")
}
return 1
@ -150,17 +147,26 @@ func fuzzPair(data []byte) int {
// Pair the two points and ensure they result in the same output
clPair := cloudflare.Pair(pc, tc).Marshal()
gPair := google.Pair(pg, tg).Marshal()
sPair := gnark.Pair(ps, ts).Marshal()
if !bytes.Equal(clPair, gPair) {
panic("pairing mismatch: cloudflare/google")
}
cPair, err := bn254.Pair([]bn254.G1Affine{*ps}, []bn254.G2Affine{*ts})
if err != nil {
panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err))
normalizedClPair := normalizeGTToGnark(clPair).Marshal()
if !bytes.Equal(normalizedClPair, sPair) {
panic("pairing mismatch: cloudflare/gnark")
}
// gnark uses a different pairing algorithm which might produce
// different but also correct outputs, we need to scale the output by s
return 1
}
// normalizeGTToGnark scales a Cloudflare/Google GT element by `s`
// so that it can be compared with a gnark GT point.
//
// For the definition of `s` see 3.5 in https://eprint.iacr.org/2015/192.pdf
func normalizeGTToGnark(cloudflareOrGoogleGT []byte) *gnark.GT {
// Compute s = 2*u(6*u^2 + 3*u + 1)
u, _ := new(big.Int).SetString("0x44e992b44a6909f1", 0)
u_exp2 := new(big.Int).Exp(u, big.NewInt(2), nil) // u^2
u_6_exp2 := new(big.Int).Mul(big.NewInt(6), u_exp2) // 6*u^2
@ -170,14 +176,12 @@ func fuzzPair(data []byte) int {
u_2 := new(big.Int).Mul(big.NewInt(2), u) // 2*u
s := u_2.Mul(u_2, inner) // 2*u(6*u^2 + 3*u + 1)
gRes := new(bn254.GT)
if err := gRes.SetBytes(clPair); err != nil {
// Scale the Cloudflare/Google GT element by `s`
gRes := new(gnark.GT)
if err := gRes.Unmarshal(cloudflareOrGoogleGT); err != nil {
panic(err)
}
gRes = gRes.Exp(*gRes, s)
if !bytes.Equal(cPair.Marshal(), gRes.Marshal()) {
panic("pairing mismatch: cloudflare/gnark")
}
return 1
return gRes
}

View file

@ -21,7 +21,7 @@ import (
"testing"
"github.com/XinFinOrg/XDPoSChain/crypto/secp256k1"
"github.com/btcsuite/btcd/btcec/v2"
dcred_secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
)
func TestFuzzer(t *testing.T) {
@ -38,7 +38,7 @@ func Fuzz(f *testing.F) {
func fuzz(dataP1, dataP2 []byte) {
var (
curveA = secp256k1.S256()
curveB = btcec.S256()
curveB = dcred_secp256k1.S256()
)
// first point
x1, y1 := curveB.ScalarBaseMult(dataP1)