| // Copyright (c) 2018, Google Inc. |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| //go:build ignore |
| |
| package main |
| |
| import ( |
| "crypto/aes" |
| "crypto/cipher" |
| "crypto/elliptic" |
| "crypto/rand" |
| "fmt" |
| "io" |
| "math/big" |
| ) |
| |
| var ( |
| p256 elliptic.Curve |
| zero, one, p, R, Rinv *big.Int |
| deterministicRand io.Reader |
| ) |
| |
| type coordinates int |
| |
| const ( |
| affine coordinates = iota |
| jacobian |
| ) |
| |
| func init() { |
| p256 = elliptic.P256() |
| |
| zero = new(big.Int) |
| one = new(big.Int).SetInt64(1) |
| |
| p = p256.Params().P |
| |
| R = new(big.Int) |
| R.SetBit(R, 256, 1) |
| R.Mod(R, p) |
| |
| Rinv = new(big.Int).ModInverse(R, p) |
| |
| deterministicRand = newDeterministicRand() |
| } |
| |
| func modMul(z, x, y *big.Int) *big.Int { |
| z.Mul(x, y) |
| return z.Mod(z, p) |
| } |
| |
| func toMontgomery(z, x *big.Int) *big.Int { |
| return modMul(z, x, R) |
| } |
| |
| func fromMontgomery(z, x *big.Int) *big.Int { |
| return modMul(z, x, Rinv) |
| } |
| |
| func isAffineInfinity(x, y *big.Int) bool { |
| // Infinity, in affine coordinates, is represented as (0, 0) by |
| // both Go, p256-x86_64-asm.pl and p256-armv8-asm.pl. |
| return x.Sign() == 0 && y.Sign() == 0 |
| } |
| |
| func randNonZeroInt(max *big.Int) *big.Int { |
| for { |
| r, err := rand.Int(deterministicRand, max) |
| if err != nil { |
| panic(err) |
| } |
| if r.Sign() != 0 { |
| return r |
| } |
| } |
| } |
| |
| func randPoint() (x, y *big.Int) { |
| k := randNonZeroInt(p256.Params().N) |
| return p256.ScalarBaseMult(k.Bytes()) |
| } |
| |
| func toJacobian(xIn, yIn *big.Int) (x, y, z *big.Int) { |
| if isAffineInfinity(xIn, yIn) { |
| // The Jacobian representation of infinity has Z = 0. Depending |
| // on the implementation, X and Y may be further constrained. |
| // Generalizing the curve equation to Jacobian coordinates for |
| // non-zero Z gives: |
| // |
| // y² = x³ - 3x + b, where x = X/Z² and y = Y/Z³ |
| // Y² = X³ + aXZ⁴ + bZ⁶ |
| // |
| // Taking that formula at Z = 0 gives Y² = X³. This constraint |
| // allows removing a special case in the point-on-curve check. |
| // |
| // BoringSSL, however, historically generated infinities with |
| // arbitrary X and Y and include the special case. We also have |
| // not verified that add and double preserve this |
| // property. Thus, generate test vectors with unrelated X and Y, |
| // to test that p256-x86_64-asm.pl and p256-armv8-asm.pl correctly |
| // handle unconstrained representations of infinity. |
| x = randNonZeroInt(p) |
| y = randNonZeroInt(p) |
| z = zero |
| return |
| } |
| |
| z = randNonZeroInt(p) |
| |
| // X = xZ² |
| y = modMul(new(big.Int), z, z) |
| x = modMul(new(big.Int), xIn, y) |
| |
| // Y = yZ³ |
| modMul(y, y, z) |
| modMul(y, y, yIn) |
| return |
| } |
| |
| func printMontgomery(name string, a *big.Int) { |
| a = toMontgomery(new(big.Int), a) |
| fmt.Printf("%s = %064x\n", name, a) |
| } |
| |
| func printTestCase(ax, ay *big.Int, aCoord coordinates, bx, by *big.Int, bCoord coordinates) { |
| rx, ry := p256.Add(ax, ay, bx, by) |
| |
| var az *big.Int |
| if aCoord == jacobian { |
| ax, ay, az = toJacobian(ax, ay) |
| } else if isAffineInfinity(ax, ay) { |
| az = zero |
| } else { |
| az = one |
| } |
| |
| var bz *big.Int |
| if bCoord == jacobian { |
| bx, by, bz = toJacobian(bx, by) |
| } else if isAffineInfinity(bx, by) { |
| bz = zero |
| } else { |
| bz = one |
| } |
| |
| fmt.Printf("Test = PointAdd\n") |
| printMontgomery("A.X", ax) |
| printMontgomery("A.Y", ay) |
| printMontgomery("A.Z", az) |
| printMontgomery("B.X", bx) |
| printMontgomery("B.Y", by) |
| printMontgomery("B.Z", bz) |
| printMontgomery("Result.X", rx) |
| printMontgomery("Result.Y", ry) |
| fmt.Printf("\n") |
| } |
| |
| func main() { |
| fmt.Printf("# ∞ + ∞ = ∞.\n") |
| printTestCase(zero, zero, affine, zero, zero, affine) |
| |
| fmt.Printf("# ∞ + ∞ = ∞, with an alternate representation of ∞.\n") |
| printTestCase(zero, zero, jacobian, zero, zero, jacobian) |
| |
| gx, gy := p256.Params().Gx, p256.Params().Gy |
| fmt.Printf("# g + ∞ = g.\n") |
| printTestCase(gx, gy, affine, zero, zero, affine) |
| |
| fmt.Printf("# g + ∞ = g, with an alternate representation of ∞.\n") |
| printTestCase(gx, gy, affine, zero, zero, jacobian) |
| |
| fmt.Printf("# g + -g = ∞.\n") |
| minusGy := new(big.Int).Sub(p, gy) |
| printTestCase(gx, gy, affine, gx, minusGy, affine) |
| |
| fmt.Printf("# Test some random Jacobian sums.\n") |
| for i := 0; i < 4; i++ { |
| ax, ay := randPoint() |
| bx, by := randPoint() |
| printTestCase(ax, ay, jacobian, bx, by, jacobian) |
| } |
| |
| fmt.Printf("# Test some random Jacobian doublings.\n") |
| for i := 0; i < 4; i++ { |
| ax, ay := randPoint() |
| printTestCase(ax, ay, jacobian, ax, ay, jacobian) |
| } |
| |
| fmt.Printf("# Test some random affine sums.\n") |
| for i := 0; i < 4; i++ { |
| ax, ay := randPoint() |
| bx, by := randPoint() |
| printTestCase(ax, ay, affine, bx, by, affine) |
| } |
| |
| fmt.Printf("# Test some random affine doublings.\n") |
| for i := 0; i < 4; i++ { |
| ax, ay := randPoint() |
| printTestCase(ax, ay, affine, ax, ay, affine) |
| } |
| } |
| |
| type deterministicRandom struct { |
| stream cipher.Stream |
| } |
| |
| func newDeterministicRand() io.Reader { |
| block, err := aes.NewCipher(make([]byte, 128/8)) |
| if err != nil { |
| panic(err) |
| } |
| stream := cipher.NewCTR(block, make([]byte, block.BlockSize())) |
| return &deterministicRandom{stream} |
| } |
| |
| func (r *deterministicRandom) Read(b []byte) (n int, err error) { |
| for i := range b { |
| b[i] = 0 |
| } |
| r.stream.XORKeyStream(b, b) |
| return len(b), nil |
| } |