From f52bcbfdcf4c1aec024c820272fbeafdd0d4be8a Mon Sep 17 00:00:00 2001 From: armfazh Date: Tue, 24 Nov 2020 21:20:36 -0800 Subject: [PATCH] Allows to compile p384 with arch others than arm64 and amd64. --- ecc/p384/api_test.go | 56 ----------- ecc/p384/arith.go | 2 +- ecc/p384/arith_amd64.go | 2 +- ecc/p384/arith_amd64.s | 2 +- ecc/p384/arith_arm64.s | 2 +- ecc/p384/arith_test.go | 2 +- ecc/p384/opt_test.go | 78 ++++++++++++++ ecc/p384/p384.go | 182 --------------------------------- ecc/p384/p384_generic.go | 20 ++++ ecc/p384/p384_test.go | 212 ++++++++++++++++++--------------------- ecc/p384/p384opt.go | 187 ++++++++++++++++++++++++++++++++++ ecc/p384/point.go | 2 +- ecc/p384/point_test.go | 2 +- ecc/p384/tableBase.go | 2 +- 14 files changed, 389 insertions(+), 362 deletions(-) delete mode 100644 ecc/p384/api_test.go create mode 100644 ecc/p384/opt_test.go create mode 100644 ecc/p384/p384_generic.go create mode 100644 ecc/p384/p384opt.go diff --git a/ecc/p384/api_test.go b/ecc/p384/api_test.go deleted file mode 100644 index 726eb1883..000000000 --- a/ecc/p384/api_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// +build arm64 amd64 - -package p384_test - -import ( - "crypto/elliptic" - "crypto/rand" - "fmt" - "testing" - - "github.com/cloudflare/circl/ecc/p384" -) - -func BenchmarkScalarMult(b *testing.B) { - curve := p384.P384() - params := curve.Params() - - K, _ := rand.Int(rand.Reader, params.N) - M, _ := rand.Int(rand.Reader, params.N) - N, _ := rand.Int(rand.Reader, params.N) - k := K.Bytes() - m := M.Bytes() - n := N.Bytes() - - b.Run("kG", func(b *testing.B) { - for i := 0; i < b.N; i++ { - curve.ScalarBaseMult(k) - } - }) - b.Run("kP", func(b *testing.B) { - for i := 0; i < b.N; i++ { - curve.ScalarMult(params.Gx, params.Gy, k) - } - }) - b.Run("kG+lP", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _ = curve.CombinedMult(params.Gx, params.Gy, m, n) - } - }) -} - -func Example_p384() { - // import "github.com/cloudflare/circl/ecc/p384" - // import "crypto/elliptic" - circl := p384.P384() - stdlib := elliptic.P384() - - params := circl.Params() - K, _ := rand.Int(rand.Reader, params.N) - k := K.Bytes() - - x1, y1 := circl.ScalarBaseMult(k) - x2, y2 := stdlib.ScalarBaseMult(k) - fmt.Printf("%v, %v", x1.Cmp(x2) == 0, y1.Cmp(y2) == 0) - // Output: true, true -} diff --git a/ecc/p384/arith.go b/ecc/p384/arith.go index 3bf56fc93..b4f1401e0 100644 --- a/ecc/p384/arith.go +++ b/ecc/p384/arith.go @@ -1,4 +1,4 @@ -// +build arm64 amd64 +// +build !noasm,arm64 !noasm,amd64 package p384 diff --git a/ecc/p384/arith_amd64.go b/ecc/p384/arith_amd64.go index da2c198f8..de26df93b 100644 --- a/ecc/p384/arith_amd64.go +++ b/ecc/p384/arith_amd64.go @@ -1,4 +1,4 @@ -// +build amd64 +// +build amd64,!noasm package p384 diff --git a/ecc/p384/arith_amd64.s b/ecc/p384/arith_amd64.s index 608cfdb62..7866bca4a 100644 --- a/ecc/p384/arith_amd64.s +++ b/ecc/p384/arith_amd64.s @@ -1,4 +1,4 @@ -// +build amd64 +// +build amd64,!noasm #include "textflag.h" diff --git a/ecc/p384/arith_arm64.s b/ecc/p384/arith_arm64.s index 2aa81e448..ec93991d4 100644 --- a/ecc/p384/arith_arm64.s +++ b/ecc/p384/arith_arm64.s @@ -1,4 +1,4 @@ -// +build arm64 +// +build arm64,!noasm #include "textflag.h" diff --git a/ecc/p384/arith_test.go b/ecc/p384/arith_test.go index 651caa5f9..ce8db02d7 100644 --- a/ecc/p384/arith_test.go +++ b/ecc/p384/arith_test.go @@ -1,4 +1,4 @@ -// +build arm64 amd64 +// +build !noasm,arm64 !noasm,amd64 package p384 diff --git a/ecc/p384/opt_test.go b/ecc/p384/opt_test.go new file mode 100644 index 000000000..e43f0d142 --- /dev/null +++ b/ecc/p384/opt_test.go @@ -0,0 +1,78 @@ +// +build !noasm,arm64 !noasm,amd64 + +package p384 + +import ( + "crypto/elliptic" + "math/big" + "testing" + + "github.com/cloudflare/circl/internal/test" +) + +func TestInternals(t *testing.T) { + t.Run("absolute", func(t *testing.T) { + cases := []int32{-2, -1, 0, 1, 2} + expected := []int32{2, 1, 0, 1, 2} + for i := range cases { + got := absolute(cases[i]) + want := expected[i] + if got != want { + test.ReportError(t, got, want, cases[i]) + } + } + }) + + t.Run("toOdd", func(t *testing.T) { + var c curve + k := []byte{0xF0} + oddK, _ := c.toOdd(k) + got := len(oddK) + want := 48 + if got != want { + test.ReportError(t, got, want) + } + + oddK[sizeFp-1] = 0x0 + smallOddK, _ := c.toOdd(oddK) + got = len(smallOddK) + want = 48 + if got != want { + test.ReportError(t, got, want) + } + }) + + t.Run("special k", func(t *testing.T) { + cases := []struct { // known cases that require complete addition + w uint + k int + }{ + {w: 2, k: 2}, + {w: 5, k: 6}, + {w: 6, k: 38}, + {w: 7, k: 102}, + {w: 9, k: 230}, + {w: 12, k: 742}, + {w: 14, k: 4838}, + {w: 17, k: 21222}, + {w: 19, k: 152294}, + } + + var c curve + + StdCurve := elliptic.P384() + params := StdCurve.Params() + for _, caseI := range cases { + k := big.NewInt(int64(caseI.k)).Bytes() + gotX, gotY := c.scalarMultOmega(params.Gx, params.Gy, k, caseI.w) + wantX, wantY := StdCurve.ScalarMult(params.Gx, params.Gy, k) + + if gotX.Cmp(wantX) != 0 { + test.ReportError(t, gotX, wantX, caseI) + } + if gotY.Cmp(wantY) != 0 { + test.ReportError(t, gotY, wantY, caseI) + } + } + }) +} diff --git a/ecc/p384/p384.go b/ecc/p384/p384.go index a787ccf5c..75005d08d 100644 --- a/ecc/p384/p384.go +++ b/ecc/p384/p384.go @@ -1,13 +1,8 @@ -// +build arm64 amd64 - package p384 import ( "crypto/elliptic" - "crypto/subtle" "math/big" - - "github.com/cloudflare/circl/math" ) // Curve is used to provide the extended functionality and performance of @@ -22,11 +17,6 @@ type Curve interface { CombinedMult(Qx, Qy *big.Int, m, n []byte) (Px, Py *big.Int) } -type curve struct{} - -// P384 returns a Curve which implements P-384 (see FIPS 186-3, section D.2.4). -func P384() Curve { return curve{} } - // Params returns the parameters for the curve. Note: The value returned by // this function fallbacks to the stdlib implementation of elliptic curve // operations. Use this method to only recover elliptic curve parameters. @@ -36,175 +26,3 @@ func (c curve) Params() *elliptic.CurveParams { return elliptic.P384().Params() func (c curve) IsAtInfinity(x, y *big.Int) bool { return x.Sign() == 0 && y.Sign() == 0 } - -// IsOnCurve reports whether the given (x,y) lies on the curve. -func (c curve) IsOnCurve(x, y *big.Int) bool { - x1, y1 := &fp384{}, &fp384{} - x1.SetBigInt(x) - y1.SetBigInt(y) - montEncode(x1, x1) - montEncode(y1, y1) - - y2, x3 := &fp384{}, &fp384{} - fp384Sqr(y2, y1) - fp384Sqr(x3, x1) - fp384Mul(x3, x3, x1) - - threeX := &fp384{} - fp384Add(threeX, x1, x1) - fp384Add(threeX, threeX, x1) - - fp384Sub(x3, x3, threeX) - fp384Add(x3, x3, &bb) - - return *y2 == *x3 -} - -// Add returns the sum of (x1,y1) and (x2,y2). -func (c curve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { - P := newAffinePoint(x1, y1).toJacobian() - P.mixadd(P, newAffinePoint(x2, y2)) - return P.toAffine().toInt() -} - -// Double returns 2*(x,y). -func (c curve) Double(x1, y1 *big.Int) (x, y *big.Int) { - P := newAffinePoint(x1, y1).toJacobian() - P.double() - return P.toAffine().toInt() -} - -// reduceScalar shorten a scalar modulo the order of the curve. -func (c curve) reduceScalar(k []byte) []byte { - const max = sizeFp - if len(k) > max { - bigK := new(big.Int).SetBytes(k) - bigK.Mod(bigK, c.Params().N) - k = bigK.Bytes() - } - if len(k) < max { - k = append(make([]byte, max-len(k)), k...) - } - return k -} - -// toOdd performs k = (-k mod N) if k is even. -func (c curve) toOdd(k []byte) ([]byte, int) { - var X, Y big.Int - X.SetBytes(k) - Y.Neg(&X).Mod(&Y, c.Params().N) - isEven := 1 - int(X.Bit(0)) - x := X.Bytes() - y := Y.Bytes() - - if len(x) < len(y) { - x = append(make([]byte, len(y)-len(x)), x...) - } else if len(x) > len(y) { - y = append(make([]byte, len(x)-len(y)), y...) - } - subtle.ConstantTimeCopy(isEven, x, y) - return x, isEven -} - -// ScalarMult returns (Qx,Qy)=k*(Px,Py) where k is a number in big-endian form. -func (c curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) { - return c.scalarMultOmega(x1, y1, k, 5) -} - -func (c curve) scalarMultOmega(x1, y1 *big.Int, k []byte, omega uint) (x, y *big.Int) { - k = c.reduceScalar(k) - oddK, isEvenK := c.toOdd(k) - - var scalar big.Int - scalar.SetBytes(oddK) - if scalar.Sign() == 0 { - return new(big.Int), new(big.Int) - } - const bitsN = uint(384) - L := math.SignedDigit(&scalar, omega, bitsN) - - var R jacobianPoint - Q := zeroPoint().toJacobian() - TabP := newAffinePoint(x1, y1).oddMultiples(omega) - for i := len(L) - 1; i > 0; i-- { - for j := uint(0); j < omega-1; j++ { - Q.double() - } - idx := absolute(L[i]) >> 1 - for j := range TabP { - R.cmov(&TabP[j], subtle.ConstantTimeEq(int32(j), idx)) - } - R.cneg(int(L[i]>>31) & 1) - Q.add(Q, &R) - } - // Calculate the last iteration using complete addition formula. - for j := uint(0); j < omega-1; j++ { - Q.double() - } - idx := absolute(L[0]) >> 1 - for j := range TabP { - R.cmov(&TabP[j], subtle.ConstantTimeEq(int32(j), idx)) - } - R.cneg(int(L[0]>>31) & 1) - QQ := Q.toProjective() - QQ.completeAdd(QQ, R.toProjective()) - QQ.cneg(isEvenK) - return QQ.toAffine().toInt() -} - -// ScalarBaseMult returns k*G, where G is the base point of the group -// and k is an integer in big-endian form. -func (c curve) ScalarBaseMult(k []byte) (x, y *big.Int) { - params := c.Params() - return c.ScalarMult(params.Gx, params.Gy, k) -} - -// CombinedMult calculates P=mG+nQ, where G is the generator and Q=(x,y,z). -// The scalars m and n are integers in big-endian form. Non-constant time. -func (c curve) CombinedMult(xQ, yQ *big.Int, m, n []byte) (xP, yP *big.Int) { - const nOmega = uint(5) - var k big.Int - k.SetBytes(m) - nafM := math.OmegaNAF(&k, baseOmega) - k.SetBytes(n) - nafN := math.OmegaNAF(&k, nOmega) - - if len(nafM) > len(nafN) { - nafN = append(nafN, make([]int32, len(nafM)-len(nafN))...) - } else if len(nafM) < len(nafN) { - nafM = append(nafM, make([]int32, len(nafN)-len(nafM))...) - } - - TabQ := newAffinePoint(xQ, yQ).oddMultiples(nOmega) - var jR jacobianPoint - var aR affinePoint - P := zeroPoint().toJacobian() - for i := len(nafN) - 1; i >= 0; i-- { - P.double() - // Generator point - if nafM[i] != 0 { - idxM := absolute(nafM[i]) >> 1 - aR = baseOddMultiples[idxM] - if nafM[i] < 0 { - aR.neg() - } - P.mixadd(P, &aR) - } - // Input point - if nafN[i] != 0 { - idxN := absolute(nafN[i]) >> 1 - jR = TabQ[idxN] - if nafN[i] < 0 { - jR.neg() - } - P.add(P, &jR) - } - } - return P.toAffine().toInt() -} - -// absolute returns always a positive value. -func absolute(x int32) int32 { - mask := x >> 31 - return (x + mask) ^ mask -} diff --git a/ecc/p384/p384_generic.go b/ecc/p384/p384_generic.go new file mode 100644 index 000000000..3acc38550 --- /dev/null +++ b/ecc/p384/p384_generic.go @@ -0,0 +1,20 @@ +// +build noasm !amd64,!arm64 + +package p384 + +import ( + "crypto/elliptic" + "math/big" +) + +type curve struct{ elliptic.Curve } + +func P384() Curve { return curve{elliptic.P384()} } + +// CombinedMult calculates P=mG+nQ, where G is the generator and Q=(x,y,z). +// The scalars m and n are integers in big-endian form. Non-constant time. +func (c curve) CombinedMult(xQ, yQ *big.Int, m, n []byte) (xP, yP *big.Int) { + x1, y1 := c.ScalarBaseMult(m) + x2, y2 := c.ScalarMult(xQ, yQ, n) + return c.Add(x1, y1, x2, y2) +} diff --git a/ecc/p384/p384_test.go b/ecc/p384/p384_test.go index f7b815fed..86d3d1c34 100644 --- a/ecc/p384/p384_test.go +++ b/ecc/p384/p384_test.go @@ -1,18 +1,17 @@ -// +build arm64 amd64 - -package p384 +package p384_test import ( "crypto/elliptic" "crypto/rand" - "math/big" + "fmt" "testing" + "github.com/cloudflare/circl/ecc/p384" "github.com/cloudflare/circl/internal/test" ) func TestIsOnCurveTrue(t *testing.T) { - CirclCurve := P384() + CirclCurve := p384.P384() k := make([]byte, 384/8) for i := 0; i < 128; i++ { _, _ = rand.Read(k) @@ -35,7 +34,7 @@ func TestIsOnCurveTrue(t *testing.T) { func TestAffine(t *testing.T) { const testTimes = 1 << 7 - CirclCurve := P384() + CirclCurve := p384.P384() StdCurve := elliptic.P384() params := StdCurve.Params() @@ -75,107 +74,9 @@ func TestAffine(t *testing.T) { }) } -func TestScalarMult(t *testing.T) { - const testTimes = 1 << 7 - CirclCurve := P384() - StdCurve := elliptic.P384() - params := StdCurve.Params() - - t.Run("toOdd", func(t *testing.T) { - var c curve - k := []byte{0xF0} - oddK, _ := c.toOdd(k) - got := len(oddK) - want := 48 - if got != want { - test.ReportError(t, got, want) - } - - oddK[sizeFp-1] = 0x0 - smallOddK, _ := c.toOdd(oddK) - got = len(smallOddK) - want = 48 - if got != want { - test.ReportError(t, got, want) - } - }) - - t.Run("k=0", func(t *testing.T) { - k := []byte{0x0} - gotX, gotY := CirclCurve.ScalarMult(params.Gx, params.Gy, k) - got := CirclCurve.IsAtInfinity(gotX, gotY) - want := true - if got != want { - test.ReportError(t, got, want) - } - }) - - t.Run("special k", func(t *testing.T) { - cases := []struct { // known cases that require complete addition - w uint - k int - }{ - {w: 2, k: 2}, - {w: 5, k: 6}, - {w: 6, k: 38}, - {w: 7, k: 102}, - {w: 9, k: 230}, - {w: 12, k: 742}, - {w: 14, k: 4838}, - {w: 17, k: 21222}, - {w: 19, k: 152294}, - } - - var c curve - - for _, caseI := range cases { - k := big.NewInt(int64(caseI.k)).Bytes() - gotX, gotY := c.scalarMultOmega(params.Gx, params.Gy, k, caseI.w) - wantX, wantY := StdCurve.ScalarMult(params.Gx, params.Gy, k) - - if gotX.Cmp(wantX) != 0 { - test.ReportError(t, gotX, wantX, caseI) - } - if gotY.Cmp(wantY) != 0 { - test.ReportError(t, gotY, wantY, caseI) - } - } - }) - - t.Run("random k", func(t *testing.T) { - for i := 0; i < testTimes; i++ { - k, _ := rand.Int(rand.Reader, params.N) - gotX, gotY := CirclCurve.ScalarMult(params.Gx, params.Gy, k.Bytes()) - wantX, wantY := StdCurve.ScalarMult(params.Gx, params.Gy, k.Bytes()) - - if gotX.Cmp(wantX) != 0 { - test.ReportError(t, gotX, wantX, k) - } - if gotY.Cmp(wantY) != 0 { - test.ReportError(t, gotY, wantY) - } - } - }) - - t.Run("wrong P", func(t *testing.T) { - for i := 0; i < testTimes; i++ { - k, _ := rand.Int(rand.Reader, params.N) - x, _ := rand.Int(rand.Reader, params.P) - y, _ := rand.Int(rand.Reader, params.P) - - got := CirclCurve.IsOnCurve(CirclCurve.ScalarMult(x, y, k.Bytes())) - want := StdCurve.IsOnCurve(StdCurve.ScalarMult(x, y, k.Bytes())) - - if got != want { - test.ReportError(t, got, want, k, x, y) - } - } - }) -} - func TestScalarBaseMult(t *testing.T) { - const testTimes = 1 << 7 - CirclCurve := P384() + const testTimes = 1 << 6 + CirclCurve := p384.P384() StdCurve := elliptic.P384() t.Run("0P", func(t *testing.T) { @@ -238,9 +139,56 @@ func TestScalarBaseMult(t *testing.T) { }) } +func TestScalarMult(t *testing.T) { + const testTimes = 1 << 6 + CirclCurve := p384.P384() + StdCurve := elliptic.P384() + params := StdCurve.Params() + + t.Run("k=0", func(t *testing.T) { + k := []byte{0x0} + gotX, gotY := CirclCurve.ScalarMult(params.Gx, params.Gy, k) + got := CirclCurve.IsAtInfinity(gotX, gotY) + want := true + if got != want { + test.ReportError(t, got, want) + } + }) + + t.Run("random k", func(t *testing.T) { + for i := 0; i < testTimes; i++ { + k, _ := rand.Int(rand.Reader, params.N) + gotX, gotY := CirclCurve.ScalarMult(params.Gx, params.Gy, k.Bytes()) + wantX, wantY := StdCurve.ScalarMult(params.Gx, params.Gy, k.Bytes()) + + if gotX.Cmp(wantX) != 0 { + test.ReportError(t, gotX, wantX, k) + } + if gotY.Cmp(wantY) != 0 { + test.ReportError(t, gotY, wantY) + } + } + }) + + t.Run("wrong P", func(t *testing.T) { + for i := 0; i < testTimes; i++ { + k, _ := rand.Int(rand.Reader, params.N) + x, _ := rand.Int(rand.Reader, params.P) + y, _ := rand.Int(rand.Reader, params.P) + + got := CirclCurve.IsOnCurve(CirclCurve.ScalarMult(x, y, k.Bytes())) + want := StdCurve.IsOnCurve(StdCurve.ScalarMult(x, y, k.Bytes())) + + if got != want { + test.ReportError(t, got, want, k, x, y) + } + } + }) +} + func TestCombinedMult(t *testing.T) { const testTimes = 1 << 7 - CirclCurve := P384() + CirclCurve := p384.P384() StdCurve := elliptic.P384() params := StdCurve.Params() @@ -264,14 +212,46 @@ func TestCombinedMult(t *testing.T) { } } -func TestAbsolute(t *testing.T) { - cases := []int32{-2, -1, 0, 1, 2} - expected := []int32{2, 1, 0, 1, 2} - for i := range cases { - got := absolute(cases[i]) - want := expected[i] - if got != want { - test.ReportError(t, got, want, cases[i]) +func BenchmarkScalarMult(b *testing.B) { + curve := p384.P384() + params := curve.Params() + + K, _ := rand.Int(rand.Reader, params.N) + M, _ := rand.Int(rand.Reader, params.N) + N, _ := rand.Int(rand.Reader, params.N) + k := K.Bytes() + m := M.Bytes() + n := N.Bytes() + + b.Run("kG", func(b *testing.B) { + for i := 0; i < b.N; i++ { + curve.ScalarBaseMult(k) } - } + }) + b.Run("kP", func(b *testing.B) { + for i := 0; i < b.N; i++ { + curve.ScalarMult(params.Gx, params.Gy, k) + } + }) + b.Run("kG+lP", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = curve.CombinedMult(params.Gx, params.Gy, m, n) + } + }) +} + +func Example_p384() { + // import "github.com/cloudflare/circl/ecc/p384" + // import "crypto/elliptic" + circl := p384.P384() + stdlib := elliptic.P384() + + params := circl.Params() + K, _ := rand.Int(rand.Reader, params.N) + k := K.Bytes() + + x1, y1 := circl.ScalarBaseMult(k) + x2, y2 := stdlib.ScalarBaseMult(k) + fmt.Printf("%v, %v", x1.Cmp(x2) == 0, y1.Cmp(y2) == 0) + // Output: true, true } diff --git a/ecc/p384/p384opt.go b/ecc/p384/p384opt.go new file mode 100644 index 000000000..4c284a4cc --- /dev/null +++ b/ecc/p384/p384opt.go @@ -0,0 +1,187 @@ +// +build !noasm,arm64 !noasm,amd64 + +package p384 + +import ( + "crypto/subtle" + "math/big" + + "github.com/cloudflare/circl/math" +) + +type curve struct{} + +// P384 returns a Curve which implements P-384 (see FIPS 186-3, section D.2.4). +func P384() Curve { return curve{} } + +// IsOnCurve reports whether the given (x,y) lies on the curve. +func (c curve) IsOnCurve(x, y *big.Int) bool { + x1, y1 := &fp384{}, &fp384{} + x1.SetBigInt(x) + y1.SetBigInt(y) + montEncode(x1, x1) + montEncode(y1, y1) + + y2, x3 := &fp384{}, &fp384{} + fp384Sqr(y2, y1) + fp384Sqr(x3, x1) + fp384Mul(x3, x3, x1) + + threeX := &fp384{} + fp384Add(threeX, x1, x1) + fp384Add(threeX, threeX, x1) + + fp384Sub(x3, x3, threeX) + fp384Add(x3, x3, &bb) + + return *y2 == *x3 +} + +// Add returns the sum of (x1,y1) and (x2,y2). +func (c curve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { + P := newAffinePoint(x1, y1).toJacobian() + P.mixadd(P, newAffinePoint(x2, y2)) + return P.toAffine().toInt() +} + +// Double returns 2*(x,y). +func (c curve) Double(x1, y1 *big.Int) (x, y *big.Int) { + P := newAffinePoint(x1, y1).toJacobian() + P.double() + return P.toAffine().toInt() +} + +// reduceScalar shorten a scalar modulo the order of the curve. +func (c curve) reduceScalar(k []byte) []byte { + const max = sizeFp + if len(k) > max { + bigK := new(big.Int).SetBytes(k) + bigK.Mod(bigK, c.Params().N) + k = bigK.Bytes() + } + if len(k) < max { + k = append(make([]byte, max-len(k)), k...) + } + return k +} + +// toOdd performs k = (-k mod N) if k is even. +func (c curve) toOdd(k []byte) ([]byte, int) { + var X, Y big.Int + X.SetBytes(k) + Y.Neg(&X).Mod(&Y, c.Params().N) + isEven := 1 - int(X.Bit(0)) + x := X.Bytes() + y := Y.Bytes() + + if len(x) < len(y) { + x = append(make([]byte, len(y)-len(x)), x...) + } else if len(x) > len(y) { + y = append(make([]byte, len(x)-len(y)), y...) + } + subtle.ConstantTimeCopy(isEven, x, y) + return x, isEven +} + +// ScalarMult returns (Qx,Qy)=k*(Px,Py) where k is a number in big-endian form. +func (c curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) { + return c.scalarMultOmega(x1, y1, k, 5) +} + +func (c curve) scalarMultOmega(x1, y1 *big.Int, k []byte, omega uint) (x, y *big.Int) { + k = c.reduceScalar(k) + oddK, isEvenK := c.toOdd(k) + + var scalar big.Int + scalar.SetBytes(oddK) + if scalar.Sign() == 0 { + return new(big.Int), new(big.Int) + } + const bitsN = uint(384) + L := math.SignedDigit(&scalar, omega, bitsN) + + var R jacobianPoint + Q := zeroPoint().toJacobian() + TabP := newAffinePoint(x1, y1).oddMultiples(omega) + for i := len(L) - 1; i > 0; i-- { + for j := uint(0); j < omega-1; j++ { + Q.double() + } + idx := absolute(L[i]) >> 1 + for j := range TabP { + R.cmov(&TabP[j], subtle.ConstantTimeEq(int32(j), idx)) + } + R.cneg(int(L[i]>>31) & 1) + Q.add(Q, &R) + } + // Calculate the last iteration using complete addition formula. + for j := uint(0); j < omega-1; j++ { + Q.double() + } + idx := absolute(L[0]) >> 1 + for j := range TabP { + R.cmov(&TabP[j], subtle.ConstantTimeEq(int32(j), idx)) + } + R.cneg(int(L[0]>>31) & 1) + QQ := Q.toProjective() + QQ.completeAdd(QQ, R.toProjective()) + QQ.cneg(isEvenK) + return QQ.toAffine().toInt() +} + +// ScalarBaseMult returns k*G, where G is the base point of the group +// and k is an integer in big-endian form. +func (c curve) ScalarBaseMult(k []byte) (x, y *big.Int) { + params := c.Params() + return c.ScalarMult(params.Gx, params.Gy, k) +} + +// CombinedMult calculates P=mG+nQ, where G is the generator and Q=(x,y,z). +// The scalars m and n are integers in big-endian form. Non-constant time. +func (c curve) CombinedMult(xQ, yQ *big.Int, m, n []byte) (xP, yP *big.Int) { + const nOmega = uint(5) + var k big.Int + k.SetBytes(m) + nafM := math.OmegaNAF(&k, baseOmega) + k.SetBytes(n) + nafN := math.OmegaNAF(&k, nOmega) + + if len(nafM) > len(nafN) { + nafN = append(nafN, make([]int32, len(nafM)-len(nafN))...) + } else if len(nafM) < len(nafN) { + nafM = append(nafM, make([]int32, len(nafN)-len(nafM))...) + } + + TabQ := newAffinePoint(xQ, yQ).oddMultiples(nOmega) + var jR jacobianPoint + var aR affinePoint + P := zeroPoint().toJacobian() + for i := len(nafN) - 1; i >= 0; i-- { + P.double() + // Generator point + if nafM[i] != 0 { + idxM := absolute(nafM[i]) >> 1 + aR = baseOddMultiples[idxM] + if nafM[i] < 0 { + aR.neg() + } + P.mixadd(P, &aR) + } + // Input point + if nafN[i] != 0 { + idxN := absolute(nafN[i]) >> 1 + jR = TabQ[idxN] + if nafN[i] < 0 { + jR.neg() + } + P.add(P, &jR) + } + } + return P.toAffine().toInt() +} + +// absolute returns always a positive value. +func absolute(x int32) int32 { + mask := x >> 31 + return (x + mask) ^ mask +} diff --git a/ecc/p384/point.go b/ecc/p384/point.go index 26a34d173..dca9c618b 100644 --- a/ecc/p384/point.go +++ b/ecc/p384/point.go @@ -1,4 +1,4 @@ -// +build arm64 amd64 +// +build !noasm,arm64 !noasm,amd64 package p384 diff --git a/ecc/p384/point_test.go b/ecc/p384/point_test.go index e70c0dc2a..9cbe2b249 100644 --- a/ecc/p384/point_test.go +++ b/ecc/p384/point_test.go @@ -1,4 +1,4 @@ -// +build arm64 amd64 +// +build !noasm,arm64 !noasm,amd64 package p384 diff --git a/ecc/p384/tableBase.go b/ecc/p384/tableBase.go index 948d72da0..f59b14065 100644 --- a/ecc/p384/tableBase.go +++ b/ecc/p384/tableBase.go @@ -1,4 +1,4 @@ -// +build arm64 amd64 +// +build !noasm,arm64 !noasm,amd64 package p384