nx_p384_point.nx source
↩ module page · 290 lines · 8561 B
1// nx_p384_point.nx -- NIST P-384 elliptic curve point operations.
2//
3// Curve: y^2 = x^3 - 3x + b mod p (same form as P-256, just larger
4// field). Stored in Jacobian (X, Y, Z) coordinates.
5//
6// Mirrors nx_p256_point.nx 1:1. Doubling formula is unchanged
7// (a = -3 special form valid for both NIST P-256 and P-384).
8//
9// API:
10// struct P384Point { x: *i64, y: *i64, z: *i64 }
11// p384_point_alloc() -> *P384Point
12// p384_point_zero(p) set p to canonical infinity (1,1,0)
13// p384_point_is_infinity(p) -> 0|1 (Z == 0)
14// p384_point_copy(dst, src)
15// p384_point_set_affine(p, x, y)
16// p384_point_load_b(out) curve constant b
17// p384_point_load_g(out_g) base point G (Z=1)
18// p384_point_on_curve(p) requires p in affine
19// p384_point_to_affine(p) normalize in place
20// p384_point_eq(a, b) affine-equality test
21// p384_point_double(out, in_pt) Jacobian doubling
22//
23// license_tier: INDEPENDENT_REDERIVE
24// genealogy_id: international-research-sources/nist/fips_186_5
25// lineage_id: nishi_p384_point_q10
26
27// nx_safety_envelope:
28// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
29// sil_target: SIL1
30// evidence: [bulk_applied_2026-05-20, p384-point-double-jacobian]
31// verdict: NOT_YET_EVALUATED
32
33import "nx_syscalls.nx"
34import "nx_u384.nx"
35import "nx_p384_field.nx"
36import "nx_p384_field_mul_fast.nx" // FAST Solinas reduction (drop-in, KAT-gated vs the slow bit-by-bit mul) -- was the ~640ms/verify bottleneck
37import "nx_p384_field_inv.nx"
38
39struct P384Point {
40 x: *i64,
41 y: *i64,
42 z: *i64,
43}
44
45const NX_P384_POINT_OK: i64 = 1
46const NX_P384_POINT_BAD: i64 = 2
47const NX_P384_POINT_VERDICT_N: i64 = 3
48
49func nx_p384_point_verdict_is_valid(v: i64) -> i64 {
50 if v < NX_P384_POINT_OK { return 0 }
51 if v >= NX_P384_POINT_VERDICT_N { return 0 }
52 return 1
53}
54
55func p384_point_alloc() -> *P384Point {
56 let raw: *u8 = nx_u384_scratch(24) // arena-backed (certloop leak fix) -- reclaimed by save/restore
57 let p: *P384Point = raw as *P384Point
58 p.x = u384_alloc()
59 p.y = u384_alloc()
60 p.z = u384_alloc()
61 p384_field_one(p.x)
62 p384_field_one(p.y)
63 p384_field_zero(p.z)
64 return p
65}
66
67func p384_point_zero(p: *P384Point) -> i64 {
68 p384_field_one(p.x)
69 p384_field_one(p.y)
70 p384_field_zero(p.z)
71 return 0
72}
73
74func p384_point_is_infinity(p: *P384Point) -> i64 {
75 return u384_is_zero(p.z)
76}
77
78func p384_point_copy(dst: *P384Point, src: *P384Point) -> i64 {
79 u384_copy(dst.x, src.x)
80 u384_copy(dst.y, src.y)
81 u384_copy(dst.z, src.z)
82 return 0
83}
84
85func p384_point_set_affine(p: *P384Point, x: *i64, y: *i64) -> i64 {
86 u384_copy(p.x, x)
87 u384_copy(p.y, y)
88 p384_field_one(p.z)
89 return 0
90}
91
92// Curve constant b.
93// b = 0xb3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112
94// 0314088f 5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef
95// (FIPS 186-5 §D.1.2.4)
96func p384_point_load_b(out: *i64) -> i64 {
97 out[0] = 0xD3EC2AEF
98 out[1] = 0x2A85C8ED
99 out[2] = 0x8A2ED19D
100 out[3] = 0xC656398D
101 out[4] = 0x5013875A
102 out[5] = 0x0314088F
103 out[6] = 0xFE814112
104 out[7] = 0x181D9C6E
105 out[8] = 0xE3F82D19
106 out[9] = 0x988E056B
107 out[10] = 0xE23EE7E4
108 out[11] = 0xB3312FA7
109 return 0
110}
111
112// Base point G with Z = 1.
113// Gx = 0xaa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98
114// 59f741e0 82542a38 5502f25d bf55296c 3a545e38 72760ab7
115// Gy = 0x3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c
116// e9da3113 b5f0b8c0 0a60b1ce 1d7e819d 7a431d7c 90ea0e5f
117func p384_point_load_g(out_g: *P384Point) -> i64 {
118 out_g.x[0] = 0x72760AB7
119 out_g.x[1] = 0x3A545E38
120 out_g.x[2] = 0xBF55296C
121 out_g.x[3] = 0x5502F25D
122 out_g.x[4] = 0x82542A38
123 out_g.x[5] = 0x59F741E0
124 out_g.x[6] = 0x8BA79B98
125 out_g.x[7] = 0x6E1D3B62
126 out_g.x[8] = 0xF320AD74
127 out_g.x[9] = 0x8EB1C71E
128 out_g.x[10] = 0xBE8B0537
129 out_g.x[11] = 0xAA87CA22
130
131 out_g.y[0] = 0x90EA0E5F
132 out_g.y[1] = 0x7A431D7C
133 out_g.y[2] = 0x1D7E819D
134 out_g.y[3] = 0x0A60B1CE
135 out_g.y[4] = 0xB5F0B8C0
136 out_g.y[5] = 0xE9DA3113
137 out_g.y[6] = 0x289A147C
138 out_g.y[7] = 0xF8F41DBD
139 out_g.y[8] = 0x9292DC29
140 out_g.y[9] = 0x5D9E98BF
141 out_g.y[10] = 0x96262C6F
142 out_g.y[11] = 0x3617DE4A
143
144 p384_field_one(out_g.z)
145 return 0
146}
147
148// Verify an affine point (Z = 1) lies on y^2 = x^3 - 3x + b.
149// Infinity treated as on-curve.
150func p384_point_on_curve(p: *P384Point) -> i64 {
151 if p384_point_is_infinity(p) == 1 { return 1 }
152
153 let x: *i64 = u384_alloc()
154 let y: *i64 = u384_alloc()
155 u384_copy(x, p.x)
156 u384_copy(y, p.y)
157
158 let lhs: *i64 = u384_alloc()
159 p384_field_sq_fast(lhs, y)
160
161 let x2: *i64 = u384_alloc()
162 let x3: *i64 = u384_alloc()
163 p384_field_sq_fast(x2, x)
164 p384_field_mul_fast(x3, x2, x)
165 let three_x: *i64 = u384_alloc()
166 p384_field_add(three_x, x, x)
167 p384_field_add(three_x, three_x, x)
168 let b: *i64 = u384_alloc()
169 p384_point_load_b(b)
170 let rhs: *i64 = u384_alloc()
171 p384_field_sub(rhs, x3, three_x)
172 p384_field_add(rhs, rhs, b)
173
174 return p384_field_eq(lhs, rhs)
175}
176
177// Convert Jacobian -> canonical affine in place.
178func p384_point_to_affine(p: *P384Point) -> i64 {
179 if p384_point_is_infinity(p) == 1 { return 0 }
180
181 let z_inv: *i64 = u384_alloc()
182 let z_inv2: *i64 = u384_alloc()
183 let z_inv3: *i64 = u384_alloc()
184 p384_field_inv(z_inv, p.z)
185 p384_field_sq_fast(z_inv2, z_inv)
186 p384_field_mul_fast(z_inv3, z_inv2, z_inv)
187
188 p384_field_mul_fast(p.x, p.x, z_inv2)
189 p384_field_mul_fast(p.y, p.y, z_inv3)
190 p384_field_one(p.z)
191 return 0
192}
193
194// Auto-normalize equality. Inputs unchanged.
195func p384_point_eq(a: *P384Point, b: *P384Point) -> i64 {
196 let a_inf: i64 = p384_point_is_infinity(a)
197 let b_inf: i64 = p384_point_is_infinity(b)
198 if a_inf == 1 {
199 if b_inf == 1 { return 1 }
200 return 0
201 }
202 if b_inf == 1 { return 0 }
203
204 let ac: *P384Point = p384_point_alloc()
205 let bc: *P384Point = p384_point_alloc()
206 p384_point_copy(ac, a)
207 p384_point_copy(bc, b)
208 p384_point_to_affine(ac)
209 p384_point_to_affine(bc)
210 if p384_field_eq(ac.x, bc.x) != 1 { return 0 }
211 if p384_field_eq(ac.y, bc.y) != 1 { return 0 }
212 return 1
213}
214
215// Jacobian doubling with a = -3 special form. Same formula as
216// P-256 (SEC 1 v2.0 §2.2.1.3) because both NIST P-curves have a=-3.
217// delta = Z1^2
218// gamma = Y1^2
219// beta = X1 * gamma
220// alpha = 3 * (X1 - delta) * (X1 + delta)
221// X3 = alpha^2 - 8 * beta
222// Z3 = (Y1 + Z1)^2 - gamma - delta
223// Y3 = alpha * (4 * beta - X3) - 8 * gamma^2
224func p384_point_double(out: *P384Point, in_pt: *P384Point) -> i64 {
225 if p384_point_is_infinity(in_pt) == 1 {
226 p384_point_zero(out)
227 return 0
228 }
229 if u384_is_zero(in_pt.y) == 1 {
230 p384_point_zero(out)
231 return 0
232 }
233
234 let X1: *i64 = u384_alloc()
235 let Y1: *i64 = u384_alloc()
236 let Z1: *i64 = u384_alloc()
237 u384_copy(X1, in_pt.x)
238 u384_copy(Y1, in_pt.y)
239 u384_copy(Z1, in_pt.z)
240
241 let delta: *i64 = u384_alloc()
242 let gamma: *i64 = u384_alloc()
243 let beta: *i64 = u384_alloc()
244 let alpha: *i64 = u384_alloc()
245 let tmp1: *i64 = u384_alloc()
246 let tmp2: *i64 = u384_alloc()
247
248 p384_field_sq_fast(delta, Z1)
249 p384_field_sq_fast(gamma, Y1)
250 p384_field_mul_fast(beta, X1, gamma)
251
252 p384_field_sub(tmp1, X1, delta)
253 p384_field_add(tmp2, X1, delta)
254 p384_field_mul_fast(alpha, tmp1, tmp2)
255 p384_field_add(tmp1, alpha, alpha)
256 p384_field_add(alpha, tmp1, alpha)
257
258 let X3: *i64 = u384_alloc()
259 p384_field_sq_fast(X3, alpha)
260 p384_field_add(tmp1, beta, beta)
261 p384_field_add(tmp1, tmp1, tmp1)
262 p384_field_add(tmp2, tmp1, tmp1)
263 p384_field_sub(X3, X3, tmp2)
264
265 let Z3: *i64 = u384_alloc()
266 p384_field_add(tmp1, Y1, Z1)
267 p384_field_sq_fast(tmp2, tmp1)
268 p384_field_sub(tmp2, tmp2, gamma)
269 p384_field_sub(Z3, tmp2, delta)
270
271 let Y3: *i64 = u384_alloc()
272 p384_field_add(tmp1, beta, beta)
273 p384_field_add(tmp1, tmp1, tmp1)
274 p384_field_sub(tmp1, tmp1, X3)
275 p384_field_mul_fast(Y3, alpha, tmp1)
276 p384_field_sq_fast(tmp1, gamma)
277 p384_field_add(tmp2, tmp1, tmp1)
278 p384_field_add(tmp2, tmp2, tmp2)
279 p384_field_add(tmp2, tmp2, tmp2)
280 p384_field_sub(Y3, Y3, tmp2)
281
282 u384_copy(out.x, X3)
283 u384_copy(out.y, Y3)
284 u384_copy(out.z, Z3)
285 return 0
286}
287
288func main() -> i64 {
289 return 0
290}