1// Copyright (c) 2018, Google Inc. 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15//go:build ignore 16 17package main 18 19import ( 20 "crypto/aes" 21 "crypto/cipher" 22 "crypto/elliptic" 23 "crypto/rand" 24 "fmt" 25 "io" 26 "math/big" 27) 28 29var ( 30 p256 elliptic.Curve 31 zero, one, p, R, Rinv *big.Int 32 deterministicRand io.Reader 33) 34 35type coordinates int 36 37const ( 38 affine coordinates = iota 39 jacobian 40) 41 42func init() { 43 p256 = elliptic.P256() 44 45 zero = new(big.Int) 46 one = new(big.Int).SetInt64(1) 47 48 p = p256.Params().P 49 50 R = new(big.Int) 51 R.SetBit(R, 256, 1) 52 R.Mod(R, p) 53 54 Rinv = new(big.Int).ModInverse(R, p) 55 56 deterministicRand = newDeterministicRand() 57} 58 59func modMul(z, x, y *big.Int) *big.Int { 60 z.Mul(x, y) 61 return z.Mod(z, p) 62} 63 64func toMontgomery(z, x *big.Int) *big.Int { 65 return modMul(z, x, R) 66} 67 68func fromMontgomery(z, x *big.Int) *big.Int { 69 return modMul(z, x, Rinv) 70} 71 72func isAffineInfinity(x, y *big.Int) bool { 73 // Infinity, in affine coordinates, is represented as (0, 0) by 74 // both Go, p256-x86_64-asm.pl and p256-armv8-asm.pl. 75 return x.Sign() == 0 && y.Sign() == 0 76} 77 78func randNonZeroInt(max *big.Int) *big.Int { 79 for { 80 r, err := rand.Int(deterministicRand, max) 81 if err != nil { 82 panic(err) 83 } 84 if r.Sign() != 0 { 85 return r 86 } 87 } 88} 89 90func randPoint() (x, y *big.Int) { 91 k := randNonZeroInt(p256.Params().N) 92 return p256.ScalarBaseMult(k.Bytes()) 93} 94 95func toJacobian(xIn, yIn *big.Int) (x, y, z *big.Int) { 96 if isAffineInfinity(xIn, yIn) { 97 // The Jacobian representation of infinity has Z = 0. Depending 98 // on the implementation, X and Y may be further constrained. 99 // Generalizing the curve equation to Jacobian coordinates for 100 // non-zero Z gives: 101 // 102 // y² = x³ - 3x + b, where x = X/Z² and y = Y/Z³ 103 // Y² = X³ + aXZ⁴ + bZ⁶ 104 // 105 // Taking that formula at Z = 0 gives Y² = X³. This constraint 106 // allows removing a special case in the point-on-curve check. 107 // 108 // BoringSSL, however, historically generated infinities with 109 // arbitrary X and Y and include the special case. We also have 110 // not verified that add and double preserve this 111 // property. Thus, generate test vectors with unrelated X and Y, 112 // to test that p256-x86_64-asm.pl and p256-armv8-asm.pl correctly 113 // handle unconstrained representations of infinity. 114 x = randNonZeroInt(p) 115 y = randNonZeroInt(p) 116 z = zero 117 return 118 } 119 120 z = randNonZeroInt(p) 121 122 // X = xZ² 123 y = modMul(new(big.Int), z, z) 124 x = modMul(new(big.Int), xIn, y) 125 126 // Y = yZ³ 127 modMul(y, y, z) 128 modMul(y, y, yIn) 129 return 130} 131 132func printMontgomery(name string, a *big.Int) { 133 a = toMontgomery(new(big.Int), a) 134 fmt.Printf("%s = %064x\n", name, a) 135} 136 137func printTestCase(ax, ay *big.Int, aCoord coordinates, bx, by *big.Int, bCoord coordinates) { 138 rx, ry := p256.Add(ax, ay, bx, by) 139 140 var az *big.Int 141 if aCoord == jacobian { 142 ax, ay, az = toJacobian(ax, ay) 143 } else if isAffineInfinity(ax, ay) { 144 az = zero 145 } else { 146 az = one 147 } 148 149 var bz *big.Int 150 if bCoord == jacobian { 151 bx, by, bz = toJacobian(bx, by) 152 } else if isAffineInfinity(bx, by) { 153 bz = zero 154 } else { 155 bz = one 156 } 157 158 fmt.Printf("Test = PointAdd\n") 159 printMontgomery("A.X", ax) 160 printMontgomery("A.Y", ay) 161 printMontgomery("A.Z", az) 162 printMontgomery("B.X", bx) 163 printMontgomery("B.Y", by) 164 printMontgomery("B.Z", bz) 165 printMontgomery("Result.X", rx) 166 printMontgomery("Result.Y", ry) 167 fmt.Printf("\n") 168} 169 170func main() { 171 fmt.Printf("# ∞ + ∞ = ∞.\n") 172 printTestCase(zero, zero, affine, zero, zero, affine) 173 174 fmt.Printf("# ∞ + ∞ = ∞, with an alternate representation of ∞.\n") 175 printTestCase(zero, zero, jacobian, zero, zero, jacobian) 176 177 gx, gy := p256.Params().Gx, p256.Params().Gy 178 fmt.Printf("# g + ∞ = g.\n") 179 printTestCase(gx, gy, affine, zero, zero, affine) 180 181 fmt.Printf("# g + ∞ = g, with an alternate representation of ∞.\n") 182 printTestCase(gx, gy, affine, zero, zero, jacobian) 183 184 fmt.Printf("# g + -g = ∞.\n") 185 minusGy := new(big.Int).Sub(p, gy) 186 printTestCase(gx, gy, affine, gx, minusGy, affine) 187 188 fmt.Printf("# Test some random Jacobian sums.\n") 189 for i := 0; i < 4; i++ { 190 ax, ay := randPoint() 191 bx, by := randPoint() 192 printTestCase(ax, ay, jacobian, bx, by, jacobian) 193 } 194 195 fmt.Printf("# Test some random Jacobian doublings.\n") 196 for i := 0; i < 4; i++ { 197 ax, ay := randPoint() 198 printTestCase(ax, ay, jacobian, ax, ay, jacobian) 199 } 200 201 fmt.Printf("# Test some random affine sums.\n") 202 for i := 0; i < 4; i++ { 203 ax, ay := randPoint() 204 bx, by := randPoint() 205 printTestCase(ax, ay, affine, bx, by, affine) 206 } 207 208 fmt.Printf("# Test some random affine doublings.\n") 209 for i := 0; i < 4; i++ { 210 ax, ay := randPoint() 211 printTestCase(ax, ay, affine, ax, ay, affine) 212 } 213} 214 215type deterministicRandom struct { 216 stream cipher.Stream 217} 218 219func newDeterministicRand() io.Reader { 220 block, err := aes.NewCipher(make([]byte, 128/8)) 221 if err != nil { 222 panic(err) 223 } 224 stream := cipher.NewCTR(block, make([]byte, block.BlockSize())) 225 return &deterministicRandom{stream} 226} 227 228func (r *deterministicRandom) Read(b []byte) (n int, err error) { 229 for i := range b { 230 b[i] = 0 231 } 232 r.stream.XORKeyStream(b, b) 233 return len(b), nil 234} 235