nx_p384_point_add.nx source
↩ module page · 129 lines · 3803 B
1// nx_p384_point_add.nx -- Jacobian point addition for P-384.
2//
3// Mirror of nx_p256_point_add.nx. Same formula structure; only
4// the underlying field arithmetic differs (12-limb vs 8-limb).
5//
6// add-2007-bl Jacobian point addition (P1 != P2 general case):
7// U1 = X1 * Z2^2
8// U2 = X2 * Z1^2
9// S1 = Y1 * Z2^3
10// S2 = Y2 * Z1^3
11// if U1 == U2:
12// if S1 == S2: return 2*P1 (same point -> doubling)
13// else: return O (P1 = -P2)
14// H = U2 - U1
15// R = S2 - S1
16// X3 = R^2 - H^3 - 2*U1*H^2
17// Y3 = R * (U1*H^2 - X3) - S1*H^3
18// Z3 = H * Z1 * Z2
19//
20// license_tier: INDEPENDENT_REDERIVE
21// genealogy_id: international-research-sources/nist/sec1_v2_2.2.1.2
22// lineage_id: nishi_p384_point_add_q10
23
24// nx_safety_envelope:
25// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
26// sil_target: SIL1
27// evidence: [bulk_applied_2026-05-20, p384-point-add-jacobian]
28// verdict: NOT_YET_EVALUATED
29
30import "nx_syscalls.nx"
31import "nx_u384.nx"
32import "nx_p384_field.nx"
33import "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
34import "nx_p384_point.nx"
35
36// out = p1 + p2 on the curve. Aliasing-safe across all combos.
37func p384_point_add(out: *P384Point, p1: *P384Point, p2: *P384Point) -> i64 {
38 if p384_point_is_infinity(p1) == 1 {
39 p384_point_copy(out, p2)
40 return 0
41 }
42 if p384_point_is_infinity(p2) == 1 {
43 p384_point_copy(out, p1)
44 return 0
45 }
46
47 let X1: *i64 = u384_alloc()
48 let Y1: *i64 = u384_alloc()
49 let Z1: *i64 = u384_alloc()
50 let X2: *i64 = u384_alloc()
51 let Y2: *i64 = u384_alloc()
52 let Z2: *i64 = u384_alloc()
53 u384_copy(X1, p1.x)
54 u384_copy(Y1, p1.y)
55 u384_copy(Z1, p1.z)
56 u384_copy(X2, p2.x)
57 u384_copy(Y2, p2.y)
58 u384_copy(Z2, p2.z)
59
60 let Z1_sq: *i64 = u384_alloc()
61 let Z2_sq: *i64 = u384_alloc()
62 p384_field_sq_fast(Z1_sq, Z1)
63 p384_field_sq_fast(Z2_sq, Z2)
64 let U1: *i64 = u384_alloc()
65 let U2: *i64 = u384_alloc()
66 p384_field_mul_fast(U1, X1, Z2_sq)
67 p384_field_mul_fast(U2, X2, Z1_sq)
68
69 let Z1_cu: *i64 = u384_alloc()
70 let Z2_cu: *i64 = u384_alloc()
71 p384_field_mul_fast(Z1_cu, Z1_sq, Z1)
72 p384_field_mul_fast(Z2_cu, Z2_sq, Z2)
73 let S1: *i64 = u384_alloc()
74 let S2: *i64 = u384_alloc()
75 p384_field_mul_fast(S1, Y1, Z2_cu)
76 p384_field_mul_fast(S2, Y2, Z1_cu)
77
78 if p384_field_eq(U1, U2) == 1 {
79 if p384_field_eq(S1, S2) == 1 {
80 let snap: *P384Point = p384_point_alloc()
81 u384_copy(snap.x, X1)
82 u384_copy(snap.y, Y1)
83 u384_copy(snap.z, Z1)
84 p384_point_double(out, snap)
85 return 0
86 }
87 p384_point_zero(out)
88 return 0
89 }
90
91 let H: *i64 = u384_alloc()
92 let R: *i64 = u384_alloc()
93 p384_field_sub(H, U2, U1)
94 p384_field_sub(R, S2, S1)
95
96 let H2: *i64 = u384_alloc()
97 let H3: *i64 = u384_alloc()
98 let U1H2: *i64 = u384_alloc()
99 p384_field_sq_fast(H2, H)
100 p384_field_mul_fast(H3, H, H2)
101 p384_field_mul_fast(U1H2, U1, H2)
102
103 let X3: *i64 = u384_alloc()
104 let tmp: *i64 = u384_alloc()
105 p384_field_sq_fast(X3, R)
106 p384_field_sub(X3, X3, H3)
107 p384_field_add(tmp, U1H2, U1H2)
108 p384_field_sub(X3, X3, tmp)
109
110 let Y3: *i64 = u384_alloc()
111 let inner: *i64 = u384_alloc()
112 p384_field_sub(inner, U1H2, X3)
113 p384_field_mul_fast(Y3, R, inner)
114 p384_field_mul_fast(tmp, S1, H3)
115 p384_field_sub(Y3, Y3, tmp)
116
117 let Z3: *i64 = u384_alloc()
118 p384_field_mul_fast(Z3, H, Z1)
119 p384_field_mul_fast(Z3, Z3, Z2)
120
121 u384_copy(out.x, X3)
122 u384_copy(out.y, Y3)
123 u384_copy(out.z, Z3)
124 return 0
125}
126
127func main() -> i64 {
128 return 0
129}