code wiki / (root) / nx_p384_point.nx

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}