diff --git a/caigo_test.go b/caigo_test.go index 897f19cc..46c57a63 100644 --- a/caigo_test.go +++ b/caigo_test.go @@ -147,6 +147,8 @@ func TestGeneral_Signature(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 TestGeneral_Signature(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 TestGeneral_Signature(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 409019bf..bc7d96e3 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 TestGeneral_Add(t *testing.T) { } func TestGeneral_MultAir(t *testing.T) { - testMult := []struct { + tests := []struct { r *big.Int x *big.Int y *big.Int @@ -168,7 +173,7 @@ func TestGeneral_MultAir(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 TestGeneral_MultAir(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 568539b3..46813fea 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,8 @@ require ( github.com/joho/godotenv v1.4.0 github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 github.com/urfave/cli/v2 v2.10.2 - golang.org/x/crypto v0.2.0 + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 + gonum.org/v1/gonum v0.12.0 ) require ( diff --git a/go.sum b/go.sum index 3937b974..a28fa14b 100644 --- a/go.sum +++ b/go.sum @@ -43,13 +43,16 @@ github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y= github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= -golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +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/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= +gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o= +gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= 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 }