aboutsummaryrefslogtreecommitdiff
path: root/src/crypto/elliptic/p224_test.go
blob: eeb24d97ea82aaf37fb6af29a17d536a03994192 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package elliptic

import (
	"math/big"
	"math/bits"
	"math/rand"
	"reflect"
	"testing"
	"testing/quick"
)

var toFromBigTests = []string{
	"0",
	"1",
	"23",
	"b70e0cb46bb4bf7f321390b94a03c1d356c01122343280d6105c1d21",
	"706a46d476dcb76798e6046d89474788d164c18032d268fd10704fa6",
}

func p224AlternativeToBig(in *p224FieldElement) *big.Int {
	ret := new(big.Int)
	tmp := new(big.Int)

	for i := len(in) - 1; i >= 0; i-- {
		ret.Lsh(ret, 28)
		tmp.SetInt64(int64(in[i]))
		ret.Add(ret, tmp)
	}
	ret.Mod(ret, P224().Params().P)
	return ret
}

func TestP224ToFromBig(t *testing.T) {
	for i, test := range toFromBigTests {
		n, _ := new(big.Int).SetString(test, 16)
		var x p224FieldElement
		p224FromBig(&x, n)
		m := p224ToBig(&x)
		if n.Cmp(m) != 0 {
			t.Errorf("#%d: %x != %x", i, n, m)
		}
		q := p224AlternativeToBig(&x)
		if n.Cmp(q) != 0 {
			t.Errorf("#%d: %x != %x (alternative)", i, n, q)
		}
	}
}

// quickCheckConfig32 will make each quickcheck test run (32 * -quickchecks)
// times. The default value of -quickchecks is 100.
var quickCheckConfig32 = &quick.Config{MaxCountScale: 32}

// weirdLimbs can be combined to generate a range of edge-case field elements.
var weirdLimbs = [...]uint32{
	0, 1, (1 << 29) - 1,
	(1 << 12), (1 << 12) - 1,
	(1 << 28), (1 << 28) - 1,
}

func generateLimb(rand *rand.Rand) uint32 {
	const bottom29Bits = 0x1fffffff
	n := rand.Intn(len(weirdLimbs) + 3)
	switch n {
	case len(weirdLimbs):
		// Random value.
		return uint32(rand.Int31n(1 << 29))
	case len(weirdLimbs) + 1:
		// Sum of two values.
		k := generateLimb(rand) + generateLimb(rand)
		return k & bottom29Bits
	case len(weirdLimbs) + 2:
		// Difference of two values.
		k := generateLimb(rand) - generateLimb(rand)
		return k & bottom29Bits
	default:
		return weirdLimbs[n]
	}
}

func (p224FieldElement) Generate(rand *rand.Rand, size int) reflect.Value {
	return reflect.ValueOf(p224FieldElement{
		weirdLimbs[rand.Intn(len(weirdLimbs))],
		weirdLimbs[rand.Intn(len(weirdLimbs))],
		weirdLimbs[rand.Intn(len(weirdLimbs))],
		weirdLimbs[rand.Intn(len(weirdLimbs))],
		weirdLimbs[rand.Intn(len(weirdLimbs))],
		weirdLimbs[rand.Intn(len(weirdLimbs))],
		weirdLimbs[rand.Intn(len(weirdLimbs))],
		weirdLimbs[rand.Intn(len(weirdLimbs))],
	})
}

func isInBounds(x *p224FieldElement) bool {
	return bits.Len32(x[0]) <= 29 &&
		bits.Len32(x[1]) <= 29 &&
		bits.Len32(x[2]) <= 29 &&
		bits.Len32(x[3]) <= 29 &&
		bits.Len32(x[4]) <= 29 &&
		bits.Len32(x[5]) <= 29 &&
		bits.Len32(x[6]) <= 29 &&
		bits.Len32(x[7]) <= 29
}

func TestP224Mul(t *testing.T) {
	mulMatchesBigInt := func(a, b, out p224FieldElement) bool {
		var tmp p224LargeFieldElement
		p224Mul(&out, &a, &b, &tmp)

		exp := new(big.Int).Mul(p224AlternativeToBig(&a), p224AlternativeToBig(&b))
		exp.Mod(exp, P224().Params().P)
		got := p224AlternativeToBig(&out)
		if exp.Cmp(got) != 0 || !isInBounds(&out) {
			t.Logf("a = %x", a)
			t.Logf("b = %x", b)
			t.Logf("p224Mul(a, b) = %x = %v", out, got)
			t.Logf("a * b = %v", exp)
			return false
		}

		return true
	}

	a := p224FieldElement{0xfffffff, 0xfffffff, 0xf00ffff, 0x20f, 0x0, 0x0, 0x0, 0x0}
	b := p224FieldElement{1, 0, 0, 0, 0, 0, 0, 0}
	if !mulMatchesBigInt(a, b, p224FieldElement{}) {
		t.Fail()
	}

	if err := quick.Check(mulMatchesBigInt, quickCheckConfig32); err != nil {
		t.Error(err)
	}
}

func TestP224Square(t *testing.T) {
	squareMatchesBigInt := func(a, out p224FieldElement) bool {
		var tmp p224LargeFieldElement
		p224Square(&out, &a, &tmp)

		exp := p224AlternativeToBig(&a)
		exp.Mul(exp, exp)
		exp.Mod(exp, P224().Params().P)
		got := p224AlternativeToBig(&out)
		if exp.Cmp(got) != 0 || !isInBounds(&out) {
			t.Logf("a = %x", a)
			t.Logf("p224Square(a, b) = %x = %v", out, got)
			t.Logf("a * a = %v", exp)
			return false
		}

		return true
	}

	if err := quick.Check(squareMatchesBigInt, quickCheckConfig32); err != nil {
		t.Error(err)
	}
}

func TestP224Add(t *testing.T) {
	addMatchesBigInt := func(a, b, out p224FieldElement) bool {
		p224Add(&out, &a, &b)

		exp := new(big.Int).Add(p224AlternativeToBig(&a), p224AlternativeToBig(&b))
		exp.Mod(exp, P224().Params().P)
		got := p224AlternativeToBig(&out)
		if exp.Cmp(got) != 0 {
			t.Logf("a = %x", a)
			t.Logf("b = %x", b)
			t.Logf("p224Add(a, b) = %x = %v", out, got)
			t.Logf("a + b = %v", exp)
			return false
		}

		return true
	}

	if err := quick.Check(addMatchesBigInt, quickCheckConfig32); err != nil {
		t.Error(err)
	}
}

func TestP224Reduce(t *testing.T) {
	reduceMatchesBigInt := func(a p224FieldElement) bool {
		out := a
		// TODO: generate higher values for functions like p224Reduce that are
		// expected to work with higher input bounds.
		p224Reduce(&out)

		exp := p224AlternativeToBig(&a)
		got := p224AlternativeToBig(&out)
		if exp.Cmp(got) != 0 || !isInBounds(&out) {
			t.Logf("a = %x = %v", a, exp)
			t.Logf("p224Reduce(a) = %x = %v", out, got)
			return false
		}

		return true
	}

	if err := quick.Check(reduceMatchesBigInt, quickCheckConfig32); err != nil {
		t.Error(err)
	}
}

func TestP224Contract(t *testing.T) {
	contractMatchesBigInt := func(a, out p224FieldElement) bool {
		p224Contract(&out, &a)

		exp := p224AlternativeToBig(&a)
		got := p224AlternativeToBig(&out)
		if exp.Cmp(got) != 0 {
			t.Logf("a = %x = %v", a, exp)
			t.Logf("p224Contract(a) = %x = %v", out, got)
			return false
		}

		// Check that out < P.
		for i := range p224P {
			k := 8 - i - 1
			if out[k] > p224P[k] {
				t.Logf("p224Contract(a) = %x", out)
				return false
			}
			if out[k] < p224P[k] {
				return true
			}
		}
		t.Logf("p224Contract(a) = %x", out)
		return false
	}

	if !contractMatchesBigInt(p224P, p224FieldElement{}) {
		t.Error("p224Contract(p) is broken")
	}
	pMinus1 := p224FieldElement{0, 0, 0, 0xffff000, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff}
	if !contractMatchesBigInt(pMinus1, p224FieldElement{}) {
		t.Error("p224Contract(p - 1) is broken")
	}
	// Check that we can handle input above p, but lowest limb zero.
	a := p224FieldElement{0, 1, 0, 0xffff000, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff}
	if !contractMatchesBigInt(a, p224FieldElement{}) {
		t.Error("p224Contract(p + 2²⁸) is broken")
	}
	// Check that we can handle input above p, but lowest three limbs zero.
	b := p224FieldElement{0, 0, 0, 0xffff001, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff}
	if !contractMatchesBigInt(b, p224FieldElement{}) {
		t.Error("p224Contract(p + 2⁸⁴) is broken")
	}

	if err := quick.Check(contractMatchesBigInt, quickCheckConfig32); err != nil {
		t.Error(err)
	}
}

func TestP224IsZero(t *testing.T) {
	if got := p224IsZero(&p224FieldElement{}); got != 1 {
		t.Errorf("p224IsZero(0) = %d, expected 1", got)
	}
	if got := p224IsZero((*p224FieldElement)(&p224P)); got != 1 {
		t.Errorf("p224IsZero(p) = %d, expected 1", got)
	}
	if got := p224IsZero(&p224FieldElement{1}); got != 0 {
		t.Errorf("p224IsZero(1) = %d, expected 0", got)
	}

	isZeroMatchesBigInt := func(a p224FieldElement) bool {
		isZero := p224IsZero(&a)

		big := p224AlternativeToBig(&a)
		if big.Sign() == 0 && isZero != 1 {
			return false
		}
		if big.Sign() != 0 && isZero != 0 {
			return false
		}
		return true
	}

	if err := quick.Check(isZeroMatchesBigInt, quickCheckConfig32); err != nil {
		t.Error(err)
	}
}

func TestP224Invert(t *testing.T) {
	var out p224FieldElement

	p224Invert(&out, &p224FieldElement{})
	if got := p224IsZero(&out); got != 1 {
		t.Errorf("p224Invert(0) = %x, expected 0", out)
	}

	p224Invert(&out, (*p224FieldElement)(&p224P))
	if got := p224IsZero(&out); got != 1 {
		t.Errorf("p224Invert(p) = %x, expected 0", out)
	}

	p224Invert(&out, &p224FieldElement{1})
	p224Contract(&out, &out)
	if out != (p224FieldElement{1}) {
		t.Errorf("p224Invert(1) = %x, expected 1", out)
	}

	var tmp p224LargeFieldElement
	a := p224FieldElement{1, 2, 3, 4, 5, 6, 7, 8}
	p224Invert(&out, &a)
	p224Mul(&out, &out, &a, &tmp)
	p224Contract(&out, &out)
	if out != (p224FieldElement{1}) {
		t.Errorf("p224Invert(a) * a = %x, expected 1", out)
	}
}