diff --git a/caigo_test.go b/caigo_test.go index 646e0ff2..419ab3bf 100644 --- a/caigo_test.go +++ b/caigo_test.go @@ -147,6 +147,8 @@ func TestSignature(t *testing.T) { hash *big.Int rIn *big.Int sIn *big.Int + rOut *big.Int + sOut *big.Int raw string }{ { @@ -169,6 +171,27 @@ func TestSignature(t *testing.T) { rIn: types.StrToBig("2849277527182985104629156126825776904262411756563556603659114084811678482647"), sIn: types.StrToBig("3156340738553451171391693475354397094160428600037567299774561739201502791079"), }, + // Example ref: https://github.com/starkware-libs/crypto-cpp/blob/master/src/starkware/crypto/ecdsa_test.cc + // NOTICE: s component of the {r, s} signature is not available at source, but was manually computed/confirmed as + // `s := sc.InvModCurveSize(w)` for w: 0x1f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286e + { + private: types.HexToBN("0x3c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc"), + hash: types.HexToBN("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f"), + rIn: types.HexToBN("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882"), + sIn: types.HexToBN("4b6d75385aed025aa222f28a0adc6d58db78ff17e51c3f59e259b131cd5a1cc"), + }, + { + publicX: types.HexToBN("0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43"), + hash: types.HexToBN("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f"), + rIn: types.HexToBN("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882"), + sIn: types.HexToBN("4b6d75385aed025aa222f28a0adc6d58db78ff17e51c3f59e259b131cd5a1cc"), + }, + { + private: types.HexToBN("0x3c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc"), + hash: types.HexToBN("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f"), + rOut: types.HexToBN("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882"), + sOut: types.HexToBN("4b6d75385aed025aa222f28a0adc6d58db78ff17e51c3f59e259b131cd5a1cc"), + }, } var err error @@ -190,6 +213,12 @@ func TestSignature(t *testing.T) { if err != nil { t.Errorf("Could not sign good hash: %v\n", err) } + if tt.rOut != nil && tt.rOut.Cmp(tt.rIn) != 0 { + t.Errorf("Signature {r!, s} mismatch: %x != %x\n", tt.rIn, tt.rOut) + } + if tt.sOut != nil && tt.sOut.Cmp(tt.sIn) != 0 { + t.Errorf("Signature {r, s!} mismatch: %x != %x\n", tt.sIn, tt.sOut) + } } if !Curve.Verify(tt.hash, tt.rIn, tt.sIn, tt.publicX, tt.publicY) { diff --git a/curve.go b/curve.go index a49fdcaa..ad8e17e4 100644 --- a/curve.go +++ b/curve.go @@ -97,6 +97,17 @@ func init() { // // (ref: https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/crypto/starkware/crypto/signature/math_utils.py) func (sc StarkCurve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { + // As elliptic curves form a group, there is an additive identity that is the equivalent of 0 + // If ๐‘ƒ=0 or ๐‘„=0, then ๐‘ƒ+๐‘„=๐‘„ or ๐‘ƒ+๐‘„=๐‘ƒ, respectively + // NOTICE: the EC multiplication algorithm is using using `StarkCurve.rewriteScalar` trick + // to avoid this condition and provide constant-time execution. + if len(x1.Bits()) == 0 && len(y1.Bits()) == 0 { + return x2, y2 + } + if len(x2.Bits()) == 0 && len(y2.Bits()) == 0 { + return x1, y1 + } + yDelta := new(big.Int).Sub(y1, y2) xDelta := new(big.Int).Sub(x1, x2) @@ -166,11 +177,7 @@ func (sc StarkCurve) IsOnCurve(x, y *big.Int) bool { right = right.Add(right, sc.B) right = right.Mod(right, sc.P) - if left.Cmp(right) == 0 { - return true - } else { - return false - } + return left.Cmp(right) == 0 } // (ref: https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/crypto/starkware/crypto/signature/math_utils.py) @@ -227,48 +234,62 @@ func (sc StarkCurve) MimicEcMultAir(mout, x1, y1, x2, y2 *big.Int) (x *big.Int, // Multiplies by m a point on the elliptic curve with equation y^2 = x^3 + alpha*x + beta mod p. // Assumes affine form (x, y) is spread (x1 *big.Int, y1 *big.Int) and that 0 < m < order(point). // -// (ref: https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/crypto/starkware/crypto/signature/math_utils.py) -func (sc StarkCurve) EcMult(m, x1, y1 *big.Int) (x, y *big.Int) { - var _ecMult func(m, x1, y1 *big.Int) (x, y *big.Int) - - _add := func(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { - yDelta := new(big.Int).Sub(y1, y2) - xDelta := new(big.Int).Sub(x1, x2) - - m := DivMod(yDelta, xDelta, sc.P) - - xm := new(big.Int).Mul(m, m) - - x = new(big.Int).Sub(xm, x1) - x = x.Sub(x, x2) - x = x.Mod(x, sc.P) +// (ref: https://www.semanticscholar.org/paper/Elliptic-Curves-and-Side-Channel-Analysis-Joye/7fc91d3684f1ab63b97d125161daf57af60f2ad9/figure/1) +// (ref: https://cosade.telecom-paristech.fr/presentations/s2_p2.pdf) +func (sc StarkCurve) ecMult_DoubleAndAlwaysAdd(m, x1, y1 *big.Int) (x, y *big.Int) { + var _ecMult = func(m, x1, y1 *big.Int) (x, y *big.Int) { + // Two-index table initialization, Q[0] <- P + q := [2]struct { + x *big.Int + y *big.Int + }{ + { + x: x1, + y: y1, + }, + { + x: nil, + y: nil, + }, + } - y = new(big.Int).Sub(x1, x) - y = y.Mul(m, y) - y = y.Sub(y, y1) - y = y.Mod(y, sc.P) + // Run the algorithm, expects the most-significant bit is 1 + for i := sc.N.BitLen() - 2; i >= 0; i-- { + q[0].x, q[0].y = sc.Double(q[0].x, q[0].y) // Q[0] <- 2Q[0] + q[1].x, q[1].y = sc.Add(q[0].x, q[0].y, x1, y1) // Q[1] <- Q[0] + P + b := m.Bit(i) // b <- bit at position i + q[0].x, q[0].y = q[b].x, q[b].y // Q[0] <- Q[b] + } - return x, y + return q[0].x, q[0].y } - // alpha is our Y - _ecMult = func(m, x1, y1 *big.Int) (x, y *big.Int) { - if m.BitLen() == 1 { - return x1, y1 - } - mk := new(big.Int).Mod(m, big.NewInt(2)) - if mk.Cmp(big.NewInt(0)) == 0 { - h := new(big.Int).Div(m, big.NewInt(2)) - c, d := sc.Double(x1, y1) - return _ecMult(h, c, d) - } - n := new(big.Int).Sub(m, big.NewInt(1)) - e, f := _ecMult(n, x1, y1) - return _add(e, f, x1, y1) - } + return _ecMult(sc.rewriteScalar(m), x1, y1) +} - x, y = _ecMult(m, x1, y1) - return x, y +// Rewrites k into an equivalent scalar, such that the first bit (the most-significant +// bit for the Double-And-Always-Add or Montgomery algo) is 1. +// +// The k scalar rewriting obtains an equivalent scalar K = 2^n + (k - 2^n mod q), +// such that kยทG == KยทG and K has the n-th bit set to 1. The scalars are equal modulo +// the group order, k mod q == K mod q. +// +// Notice: The EC multiplication algorithms are typically presented as starting with the state (O, P0), +// where O is the identity element (or neutral point) of the curve. However, the neutral point is at infinity, +// which causes problems for some formulas (non constant-time execution for the naive implementation). +// The ladder then starts after the first step, when the state no longer contains the neutral point. +// (ref: https://www.shiftleft.org/papers/ladder/ladder-tches.pdf) +func (sc StarkCurve) rewriteScalar(k *big.Int) *big.Int { + size := new(big.Int).Lsh(big.NewInt(1), uint(sc.BitSize)) // 2ห†n + mod := new(big.Int).Mod(size, sc.N) // 2ห†n mod q + diff := new(big.Int).Sub(k, mod) // (k - 2ห†n mod q) + return new(big.Int).Add(size, diff) // 2ห†n + (k - 2ห†n mod q) +} + +// Multiplies by m a point on the elliptic curve with equation y^2 = x^3 + alpha*x + beta mod p. +// Assumes affine form (x, y) is spread (x1 *big.Int, y1 *big.Int) and that 0 < m < order(point). +func (sc StarkCurve) EcMult(m, x1, y1 *big.Int) (x, y *big.Int) { + return sc.ecMult_DoubleAndAlwaysAdd(m, x1, y1) } // Finds a nonnegative integer 0 <= x < p such that (m * x) % p == n diff --git a/curve_test.go b/curve_test.go index b2af6c41..7226fc82 100644 --- a/curve_test.go +++ b/curve_test.go @@ -1,11 +1,16 @@ package caigo import ( + "crypto/subtle" "fmt" + "math" "math/big" + "strings" "testing" + "time" "github.com/dontpanicdao/caigo/types" + "gonum.org/v1/gonum/stat" ) func BenchmarkPedersenHash(b *testing.B) { @@ -152,7 +157,7 @@ func TestAdd(t *testing.T) { } func TestMultAir(t *testing.T) { - testMult := []struct { + tests := []struct { r *big.Int x *big.Int y *big.Int @@ -168,7 +173,7 @@ func TestMultAir(t *testing.T) { }, } - for _, tt := range testMult { + for _, tt := range tests { x, y, err := Curve.MimicEcMultAir(tt.r, tt.x, tt.y, Curve.Gx, Curve.Gy) if err != nil { t.Errorf("MultAirERR %v\n", err) @@ -183,3 +188,234 @@ func TestMultAir(t *testing.T) { } } } + +type ecMultOption struct { + algo string + fn EcMultiFn + stddev float64 +} + +// Get multiple ec multiplication algo options to test and benchmark +func (sc StarkCurve) ecMultOptions() []ecMultOption { + return []ecMultOption{ + { + algo: "Double-And-Add", + fn: sc.ecMult_DoubleAndAdd, // original algo + }, + { + algo: "Double-And-Always-Add", + fn: sc.EcMult, // best algo (currently used) + }, + { + algo: "Montgomery-Ladder", + fn: sc.ecMult_Montgomery, + }, + { + algo: "Montgomery-Ladder-Lsh", + fn: sc.ecMult_MontgomeryLsh, + }, + } +} + +func FuzzEcMult(f *testing.F) { + // Generate the scalar value k, where 0 < k < order(point) + var _genScalar = func(a int, b int) (k *big.Int) { + k = new(big.Int).Mul(big.NewInt(int64(a)), big.NewInt(int64(b))) + k = k.Mul(k, k).Mul(k, k) // generate moar big number + k = k.Abs(k) + k = k.Add(k, big.NewInt(1)) // edge case: avoid zero + k = k.Mod(k, Curve.N) + return + } + + // Seed the fuzzer (examples) + f.Add(-12121501143923232, 142312310232324552) // negative numbers used as seeds but the resulting + f.Add(41289371293219038, -179566705053432322) // scalar is normalized to 0 < k < order(point) + f.Add(927302501143912223, 220390912389202149) + f.Add(874739451078007766, 868575557812948233) + f.Add(302150520188025637, 670505342647705232) + f.Add(778320444456588442, 932884823101831273) + f.Add(658844239552133924, 933442778319932884) + f.Add(494910213617956623, 976290247577832044) + + f.Fuzz(func(t *testing.T, a int, b int) { + k := _genScalar(a, b) + + var x0, y0 *big.Int + for _, tt := range Curve.ecMultOptions() { + x, y, err := Curve.privateToPoint(k, tt.fn) + if err != nil { + t.Errorf("EcMult err: %v, algo=%v\n", err, tt.algo) + } + + // Store the initial result from the first algo and test against it + if x0 == nil { + x0 = x + y0 = y + } else if x0.Cmp(x) != 0 { + t.Errorf("EcMult x mismatch: %v != %v, algo=%v\n", x, x0, tt.algo) + } else if y0.Cmp(y) != 0 { + t.Errorf("EcMult y mismatch: %v != %v, algo=%v\n", y, y0, tt.algo) + } + } + }) +} + +func BenchmarkEcMultAll(b *testing.B) { + // Generate the scalar value k, where n number of bits are set, no trailing zeros + var _genScalarBits = func(n int) (k *big.Int) { + k = big.NewInt(1) + for i := 1; i < n; i++ { + k = k.Lsh(k, 1).Add(k, big.NewInt(1)) + } + return + } + + ecMultiBest := ecMultOption{ + algo: "", + stddev: math.MaxFloat64, + } + + var out strings.Builder + for _, tt := range Curve.ecMultOptions() { + // test (+ time) injected ec multi fn performance via Curve.privateToPoint + var _test = func(k *big.Int) int64 { + start := time.Now() + Curve.privateToPoint(k, tt.fn) + return time.Since(start).Nanoseconds() + } + + xs := []float64{} + // generate numbers with 1 to 251 bits set + for i := 1; i < Curve.N.BitLen(); i++ { + k := _genScalarBits(i) + b.Run(fmt.Sprintf("%s/input_bits_len/%d", tt.algo, k.BitLen()), func(b *testing.B) { + ns := _test(k) + xs = append(xs, float64(ns)) + }) + } + + // generate numbers with 1 to 250 trailing zero bits set + k := _genScalarBits(Curve.N.BitLen() - 1) + for i := 1; i < Curve.N.BitLen()-1; i++ { + k.Rsh(k, uint(i)).Lsh(k, uint(i)) + b.Run(fmt.Sprintf("%s/input_bits_len/%d#%d", tt.algo, k.BitLen(), k.TrailingZeroBits()), func(b *testing.B) { + ns := _test(k) + xs = append(xs, float64(ns)) + }) + } + + // computes the weighted mean of the dataset. + // we don't have any weights (ie: all weights are 1) so we pass a nil slice. + mean := stat.Mean(xs, nil) + variance := stat.Variance(xs, nil) + stddev := math.Sqrt(variance) + // Keep track of the best one (min stddev) + if stddev < ecMultiBest.stddev { + ecMultiBest.stddev = stddev + ecMultiBest.algo = tt.algo + } + + out.WriteString("-----------------------------\n") + out.WriteString(fmt.Sprintf("algo= %v\n", tt.algo)) + out.WriteString(fmt.Sprintf("stats(ns)\n")) + out.WriteString(fmt.Sprintf(" mean= %v\n", mean)) + out.WriteString(fmt.Sprintf(" variance= %v\n", variance)) + out.WriteString(fmt.Sprintf(" std-dev= %v\n", stddev)) + out.WriteString("\n") + } + + // final stats output + fmt.Println(out.String()) + // assert benchmark result is as expected + expectedBest := "Double-And-Always-Add" + if ecMultiBest.algo != expectedBest { + b.Errorf("ecMultiBest.algo %v does not == expected %v\n", ecMultiBest.algo, expectedBest) + } +} + +// Multiplies by m a point on the elliptic curve with equation y^2 = x^3 + alpha*x + beta mod p. +// Assumes affine form (x, y) is spread (x1 *big.Int, y1 *big.Int) and that 0 < m < order(point). +// +// (ref: https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/crypto/starkware/crypto/signature/math_utils.py) +func (sc StarkCurve) ecMult_DoubleAndAdd(m, x1, y1 *big.Int) (x, y *big.Int) { + var _ecMult func(m, x1, y1 *big.Int) (x, y *big.Int) + _ecMult = func(m, x1, y1 *big.Int) (x, y *big.Int) { + if m.BitLen() == 1 { + return x1, y1 + } + mk := new(big.Int).Mod(m, big.NewInt(2)) + if mk.Cmp(big.NewInt(0)) == 0 { + h := new(big.Int).Div(m, big.NewInt(2)) + c, d := sc.Double(x1, y1) + return _ecMult(h, c, d) + } + n := new(big.Int).Sub(m, big.NewInt(1)) + e, f := _ecMult(n, x1, y1) + + return sc.Add(e, f, x1, y1) + } + + // Notice: no need for scalar rewrite trick via `StarkCurve.rewriteScalar` + // This algorithm is not affected, as it doesn't do a fixed number of operations, + // nor directly depends on the binary representation of the scalar. + return _ecMult(m, x1, y1) +} + +// Multiplies by m a point on the elliptic curve with equation y^2 = x^3 + alpha*x + beta mod p. +// Assumes affine form (x, y) is spread (x1 *big.Int, y1 *big.Int) and that 0 < m < order(point). +// +// (ref: https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder) +func (sc StarkCurve) ecMult_Montgomery(m, x1, y1 *big.Int) (x, y *big.Int) { + var _ecMultMontgomery = func(m, x0, y0, x1, y1 *big.Int) (x, y *big.Int) { + // Do constant number of operations + for i := sc.N.BitLen() - 1; i >= 0; i-- { + // Check if next bit set + if m.Bit(i) == 0 { + x1, y1 = sc.Add(x0, y0, x1, y1) + x0, y0 = sc.Double(x0, y0) + } else { + x0, y0 = sc.Add(x0, y0, x1, y1) + x1, y1 = sc.Double(x1, y1) + } + } + return x0, y0 + } + + return _ecMultMontgomery(sc.rewriteScalar(m), big.NewInt(0), big.NewInt(0), x1, y1) +} + +// Multiplies by m a point on the elliptic curve with equation y^2 = x^3 + alpha*x + beta mod p. +// Assumes affine form (x, y) is spread (x1 *big.Int, y1 *big.Int) and that 0 < m < order(point). +// +// (ref: https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder) +func (sc StarkCurve) ecMult_MontgomeryLsh(m, x1, y1 *big.Int) (x, y *big.Int) { + var _ecMultMontgomery = func(m, x0, y0, x1, y1 *big.Int) (x, y *big.Int) { + // Fill a fixed 32 byte buffer (2 ** 251) + // NOTICE: this will take an absolute value first + buf := m.FillBytes(make([]byte, 32)) + + for i, byte := range buf { + for bitNum := 0; bitNum < 8; bitNum++ { + // Skip first 4 bits, do constant 252 operations + if i == 0 && bitNum < 4 { + byte <<= 1 + continue + } + + // Check if next bit set + if subtle.ConstantTimeByteEq(byte&0x80, 0x80) == 0 { + x1, y1 = sc.Add(x0, y0, x1, y1) + x0, y0 = sc.Double(x0, y0) + } else { + x0, y0 = sc.Add(x0, y0, x1, y1) + x1, y1 = sc.Double(x1, y1) + } + byte <<= 1 + } + } + return x0, y0 + } + + return _ecMultMontgomery(sc.rewriteScalar(m), big.NewInt(0), big.NewInt(0), x1, y1) +} diff --git a/go.mod b/go.mod index 3d9cd646..716b6397 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 github.com/urfave/cli/v2 v2.3.0 golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 + gonum.org/v1/gonum v0.12.0 ) require ( @@ -20,7 +21,7 @@ require ( github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect - github.com/stretchr/testify v1.7.1 // indirect + github.com/stretchr/testify v1.8.0 // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect diff --git a/go.sum b/go.sum index e23c3d88..4ec0c594 100644 --- a/go.sum +++ b/go.sum @@ -307,14 +307,16 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= @@ -357,6 +359,7 @@ golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxT golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299 h1:zQpM52jfKHG6II1ISZY1ZcpygvuSFZpLwfluuF89XOg= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -502,6 +505,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o= +gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= @@ -561,8 +566,9 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/types/felt_test.go b/types/felt_test.go index 0a0ae5df..1694f0a0 100644 --- a/types/felt_test.go +++ b/types/felt_test.go @@ -1,9 +1,9 @@ package types import ( - "fmt" "bytes" "encoding/json" + "fmt" "math/big" "strconv" "testing" @@ -26,9 +26,9 @@ var ( var feltTest FeltTest type FeltTest struct { - MaxFelt *big.Int `json:"max_felt"` - LongString string `json:"long_string"` - Felts []FeltValue `json:"felts"` + MaxFelt *big.Int `json:"max_felt"` + LongString string `json:"long_string"` + Felts []FeltValue `json:"felts"` } type FeltValue struct { diff --git a/utils.go b/utils.go index 21668679..1bf26067 100644 --- a/utils.go +++ b/utils.go @@ -30,9 +30,19 @@ func (sc StarkCurve) GetRandomPrivateKey() (priv *big.Int, err error) { // obtain public key coordinates from stark curve given the private key func (sc StarkCurve) PrivateToPoint(privKey *big.Int) (x, y *big.Int, err error) { + return sc.privateToPoint(privKey, sc.EcMult) +} + +// ec multiplication fn +type EcMultiFn func(m, x1, y1 *big.Int) (x, y *big.Int) + +// obtain public key coordinates from stark curve given the private key +// NOTICE: configurable ec multiplication fn, used for testing +func (sc StarkCurve) privateToPoint(privKey *big.Int, ecMulti EcMultiFn) (x, y *big.Int, err error) { if privKey.Cmp(big.NewInt(0)) != 1 || privKey.Cmp(sc.N) != -1 { return x, y, fmt.Errorf("private key not in curve range") } - x, y = sc.EcMult(privKey, sc.EcGenX, sc.EcGenY) + + x, y = ecMulti(privKey, sc.EcGenX, sc.EcGenY) return x, y, nil }