Skip to content

Commit a29442b

Browse files
committed
core/crypto: Start work on the NIST curves
1 parent 81375e5 commit a29442b

File tree

9 files changed

+2508
-0
lines changed

9 files changed

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

0 commit comments

Comments
 (0)