Skip to content

Commit eb35e9c

Browse files
committed
core/crypto: Start work on the NIST curves
1 parent 635eb80 commit eb35e9c

File tree

13 files changed

+7257
-0
lines changed

13 files changed

+7257
-0
lines changed
Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
package field_p256r1
2+
3+
import subtle "core:crypto/_subtle"
4+
import "core:encoding/endian"
5+
import "core:math/bits"
6+
import "core:mem"
7+
8+
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
9+
mem.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
10+
}
11+
12+
fe_clear_vec :: proc "contextless" (
13+
arg1: []^Montgomery_Domain_Field_Element,
14+
) {
15+
for fe in arg1 {
16+
fe_clear(fe)
17+
}
18+
}
19+
20+
fe_from_bytes :: proc "contextless" (
21+
out1: ^Montgomery_Domain_Field_Element,
22+
arg1: []byte,
23+
unsafe_assume_canonical := false,
24+
) -> bool {
25+
ensure_contextless(len(arg1) == 32, "p256r1: invalid fe input buffer")
26+
27+
// Note: We assume the input is in big-endian.
28+
tmp := Non_Montgomery_Domain_Field_Element {
29+
endian.unchecked_get_u64be(arg1[24:]),
30+
endian.unchecked_get_u64be(arg1[16:]),
31+
endian.unchecked_get_u64be(arg1[8:]),
32+
endian.unchecked_get_u64be(arg1[0:]),
33+
}
34+
defer mem.zero_explicit(&tmp, size_of(tmp))
35+
36+
// Check that tmp is in the the range [0, ELL).
37+
if !unsafe_assume_canonical {
38+
_, borrow := bits.sub_u64(ELL[0] - 1, tmp[0], 0)
39+
_, borrow = bits.sub_u64(ELL[1], tmp[1], borrow)
40+
_, borrow = bits.sub_u64(ELL[2], tmp[2], borrow)
41+
_, borrow = bits.sub_u64(ELL[3], tmp[3], borrow)
42+
if borrow != 0 {
43+
return false
44+
}
45+
}
46+
47+
fe_to_montgomery(out1, &tmp)
48+
49+
return true
50+
}
51+
52+
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
53+
ensure_contextless(len(out1) == 32, "p256r1: invalid fe output buffer")
54+
55+
tmp: Non_Montgomery_Domain_Field_Element
56+
fe_from_montgomery(&tmp, arg1)
57+
58+
// Note: Likewise, output in big-endian.
59+
endian.unchecked_put_u64be(out1[24:], tmp[0])
60+
endian.unchecked_put_u64be(out1[16:], tmp[1])
61+
endian.unchecked_put_u64be(out1[8:], tmp[2])
62+
endian.unchecked_put_u64be(out1[0:], tmp[3])
63+
64+
mem.zero_explicit(&tmp, size_of(tmp))
65+
}
66+
67+
@(require_results)
68+
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
69+
tmp: Montgomery_Domain_Field_Element
70+
fe_sub(&tmp, arg1, arg2)
71+
72+
// This will only underflow iff arg1 == arg2, and we return the borrow,
73+
// which will be 1.
74+
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
75+
76+
fe_clear(&tmp)
77+
78+
return int(is_eq)
79+
}
80+
81+
@(require_results)
82+
fe_is_odd :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> int {
83+
tmp: Non_Montgomery_Domain_Field_Element
84+
defer mem.zero_explicit(&tmp, size_of(tmp))
85+
86+
fe_from_montgomery(&tmp, arg1)
87+
return int(tmp[0] & 1)
88+
}
89+
90+
fe_pow2k :: proc "contextless" (
91+
out1: ^Montgomery_Domain_Field_Element,
92+
arg1: ^Montgomery_Domain_Field_Element,
93+
arg2: uint,
94+
) {
95+
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
96+
if arg2 == 0 {
97+
fe_one(out1)
98+
return
99+
}
100+
101+
fe_square(out1, arg1)
102+
for _ in 1 ..< arg2 {
103+
fe_square(out1, out1)
104+
}
105+
}
106+
107+
fe_inv :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
108+
// Inversion computation is derived from the addition chain:
109+
//
110+
// _10 = 2*1
111+
// _11 = 1 + _10
112+
// _110 = 2*_11
113+
// _111 = 1 + _110
114+
// _111000 = _111 << 3
115+
// _111111 = _111 + _111000
116+
// x12 = _111111 << 6 + _111111
117+
// x15 = x12 << 3 + _111
118+
// x16 = 2*x15 + 1
119+
// x32 = x16 << 16 + x16
120+
// i53 = x32 << 15
121+
// x47 = x15 + i53
122+
// i263 = ((i53 << 17 + 1) << 143 + x47) << 47
123+
// return (x47 + i263) << 2
124+
//
125+
// Operations: 255 squares 11 multiplies
126+
//
127+
// Generated by github.com/mmcloughlin/addchain v0.4.0.
128+
129+
// Note: Need to stash `arg1` (`xx`) in the case that `out1`/`arg1` alias,
130+
// as `arg1` is used after `out1` has been altered.
131+
t0, t1, xx: Montgomery_Domain_Field_Element = ---, ---, arg1^
132+
133+
// Step 1: z = x^0x2
134+
fe_square(out1, arg1)
135+
136+
// Step 2: z = x^0x3
137+
fe_mul(out1, &xx, out1)
138+
139+
// Step 3: z = x^0x6
140+
fe_square(out1, out1)
141+
142+
// Step 4: z = x^0x7
143+
fe_mul(out1, &xx, out1)
144+
145+
// Step 7: t0 = x^0x38
146+
fe_pow2k(&t0, out1, 3)
147+
148+
// Step 8: t0 = x^0x3f
149+
fe_mul(&t0, out1, &t0)
150+
151+
// Step 14: t1 = x^0xfc0
152+
fe_pow2k(&t1, &t0, 6)
153+
154+
// Step 15: t0 = x^0xfff
155+
fe_mul(&t0, &t0, &t1)
156+
157+
// Step 18: t0 = x^0x7ff8
158+
fe_pow2k(&t0, &t0, 3)
159+
160+
// Step 19: z = x^0x7fff
161+
fe_mul(out1, out1, &t0)
162+
163+
// Step 20: t0 = x^0xfffe
164+
fe_square(&t0, out1)
165+
166+
// Step 21: t0 = x^0xffff
167+
fe_mul(&t0, &xx, &t0)
168+
169+
// Step 37: t1 = x^0xffff0000
170+
fe_pow2k(&t1, &t0, 16)
171+
172+
// Step 38: t0 = x^0xffffffff
173+
fe_mul(&t0, &t0, &t1)
174+
175+
// Step 53: t0 = x^0x7fffffff8000
176+
fe_pow2k(&t0, &t0, 15)
177+
178+
// Step 54: z = x^0x7fffffffffff
179+
fe_mul(out1, out1, &t0)
180+
181+
// Step 71: t0 = x^0xffffffff00000000
182+
fe_pow2k(&t0, &t0, 17)
183+
184+
// Step 72: t0 = x^0xffffffff00000001
185+
fe_mul(&t0, &xx, &t0)
186+
187+
// Step 215: t0 = x^0x7fffffff80000000800000000000000000000000000000000000
188+
fe_pow2k(&t0, &t0, 143)
189+
190+
// Step 216: t0 = x^0x7fffffff800000008000000000000000000000007fffffffffff
191+
fe_mul(&t0, out1, &t0)
192+
193+
// Step 263: t0 = x^0x3fffffffc00000004000000000000000000000003fffffffffff800000000000
194+
fe_pow2k(&t0, &t0, 47)
195+
196+
// Step 264: z = x^0x3fffffffc00000004000000000000000000000003fffffffffffffffffffffff
197+
fe_mul(out1, out1, &t0)
198+
199+
// Step 266: z = x^0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc
200+
fe_pow2k(out1, out1, 2)
201+
202+
fe_mul(out1, out1, &xx)
203+
204+
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &t1, &xx})
205+
}
206+
207+
@(require_results)
208+
fe_sqrt :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) -> int {
209+
// Square root candidate can be derived via exponentiation by `(p + 1) / 4`
210+
// From sage: 28948022302589062190674361737351893382521535853822578548883407827216774463488
211+
//
212+
// // Inversion computation is derived from the addition chain:
213+
//
214+
// _10 = 2*1
215+
// _11 = 1 + _10
216+
// _1100 = _11 << 2
217+
// _1111 = _11 + _1100
218+
// _11110000 = _1111 << 4
219+
// _11111111 = _1111 + _11110000
220+
// x16 = _11111111 << 8 + _11111111
221+
// x32 = x16 << 16 + x16
222+
// return ((x32 << 32 + 1) << 96 + 1) << 94
223+
//
224+
// Operations: 253 squares 7 multiplies
225+
//
226+
// Generated by github.com/mmcloughlin/addchain v0.4.0.
227+
228+
// Likewise this tramples over arg1, so stash another copy.
229+
t0, xx: Montgomery_Domain_Field_Element = ---, arg1^
230+
231+
// Step 1: z = x^0x2
232+
fe_square(out1, arg1)
233+
234+
// Step 2: z = x^0x3
235+
fe_mul(out1, &xx, out1)
236+
237+
// Step 4: t0 = x^0xc
238+
fe_pow2k(&t0, &xx, 2)
239+
240+
// Step 5: z = x^0xf
241+
fe_mul(out1, out1, &t0)
242+
243+
// Step 9: t0 = x^0xf0
244+
fe_pow2k(&t0, out1, 4)
245+
246+
// Step 10: z = x^0xff
247+
fe_mul(out1, out1, &t0)
248+
249+
// Step 18: t0 = x^0xff00
250+
fe_pow2k(&t0, out1, 8)
251+
252+
// Step 19: z = x^0xffff
253+
fe_mul(out1, out1, &t0)
254+
255+
// Step 35: t0 = x^0xffff0000
256+
fe_pow2k(&t0, out1, 16)
257+
258+
// Step 36: z = x^0xffffffff
259+
fe_mul(out1, out1, &t0)
260+
261+
// Step 68: z = x^0xffffffff00000000
262+
fe_pow2k(out1, out1, 32)
263+
264+
// Step 69: z = x^0xffffffff00000001
265+
fe_mul(out1, &xx, out1)
266+
267+
// Step 165: z = x^0xffffffff00000001000000000000000000000000
268+
fe_pow2k(out1, out1, 96)
269+
270+
// Step 166: z = x^0xffffffff00000001000000000000000000000001
271+
fe_mul(out1, &xx, out1)
272+
273+
// Step 260: z = x^0x3fffffffc0000000400000000000000000000000400000000000000000000000
274+
fe_pow2k(out1, out1, 94)
275+
276+
// Ensure that our candidate is actually the square root.
277+
check, zero: Montgomery_Domain_Field_Element
278+
fe_square(&check, out1)
279+
280+
is_valid := fe_equal(&check, &xx)
281+
fe_cond_select(out1, &zero, out1, is_valid)
282+
283+
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &xx, &check})
284+
285+
return is_valid
286+
287+
}
288+
289+
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
290+
out1[0] = 0
291+
out1[1] = 0
292+
out1[2] = 0
293+
out1[3] = 0
294+
}
295+
296+
fe_set :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
297+
x1 := arg1[0]
298+
x2 := arg1[1]
299+
x3 := arg1[2]
300+
x4 := arg1[3]
301+
out1[0] = x1
302+
out1[1] = x2
303+
out1[2] = x3
304+
out1[3] = x4
305+
}
306+
307+
@(optimization_mode = "none")
308+
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Montgomery_Domain_Field_Element, arg1: int) {
309+
mask := (u64(arg1) * 0xffffffffffffffff)
310+
x := (out1[0] ~ out2[0]) & mask
311+
x1, y1 := out1[0] ~ x, out2[0] ~ x
312+
x = (out1[1] ~ out2[1]) & mask
313+
x2, y2 := out1[1] ~ x, out2[1] ~ x
314+
x = (out1[2] ~ out2[2]) & mask
315+
x3, y3 := out1[2] ~ x, out2[2] ~ x
316+
x = (out1[3] ~ out2[3]) & mask
317+
x4, y4 := out1[3] ~ x, out2[3] ~ x
318+
out1[0], out2[0] = x1, y1
319+
out1[1], out2[1] = x2, y2
320+
out1[2], out2[2] = x3, y3
321+
out1[3], out2[3] = x4, y4
322+
}
323+
324+
@(optimization_mode = "none")
325+
fe_cond_select :: #force_no_inline proc "contextless" (
326+
out1, arg1, arg2: ^Montgomery_Domain_Field_Element,
327+
arg3: int,
328+
) {
329+
mask := (u64(arg3) * 0xffffffffffffffff)
330+
x1 := ((mask & arg2[0]) | ((~mask) & arg1[0]))
331+
x2 := ((mask & arg2[1]) | ((~mask) & arg1[1]))
332+
x3 := ((mask & arg2[2]) | ((~mask) & arg1[2]))
333+
x4 := ((mask & arg2[3]) | ((~mask) & arg1[3]))
334+
out1[0] = x1
335+
out1[1] = x2
336+
out1[2] = x3
337+
out1[3] = x4
338+
}
339+
340+
fe_cond_negate :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element, ctrl: int) {
341+
tmp1: Montgomery_Domain_Field_Element = ---
342+
fe_opp(&tmp1, arg1)
343+
fe_cond_select(out1, arg1, &tmp1, ctrl)
344+
345+
fe_clear(&tmp1)
346+
}

0 commit comments

Comments
 (0)