xref: /aosp_15_r20/external/boringssl/src/crypto/fipsmodule/ec/make_p256-nistz-tests.go (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
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