nx_p384_field_inv.nx source
↩ module page · 90 lines · 2771 B
1// nx_p384_field_inv.nx -- P-384 modular inverse via Fermat's
2// little theorem. Mirror of nx_p256_field_inv.nx, adapted to
3// 12-limb / 384-bit width.
4//
5// a^(p-2) mod p computed left-to-right square-and-multiply over
6// the 384 bits of (p-2).
7//
8// Performance note: portable bit-by-bit form (no addition-chain
9// optimization). ~384 squarings + ~variable mults; each mul
10// runs the 385-iteration bit-by-bit reduction in
11// nx_p384_field_mul. Slow but correct. Optimized addition
12// chain queued.
13//
14// API:
15// p384_field_load_p_minus_2(out)
16// p384_field_inv(out, a) out = a^(-1) mod p (Fermat)
17// p384_field_bit_at(limbs, bit_pos) -> 0|1
18//
19// Caveat: p384_field_inv(out, 0) returns 0. 0 has no inverse;
20// callers MUST guard zero by construction (point ops do).
21//
22// license_tier: INDEPENDENT_REDERIVE
23// genealogy_id: international-research-sources/nist/fips_186_5
24// lineage_id: nishi_p384_field_inv_q10
25
26// nx_safety_envelope:
27// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
28// sil_target: SIL1
29// evidence: [bulk_applied_2026-05-20, p384-field-inv]
30// verdict: NOT_YET_EVALUATED
31
32import "nx_syscalls.nx"
33import "nx_u384.nx"
34import "nx_p384_field.nx"
35import "nx_p384_field_mul.nx"
36import "nx_p384_field_mul_fast.nx" // fast Solinas mul+sq for the inverse's hot 384-iteration Fermat loop
37
38// p-2 differs from p only in limb[0]: 0xFFFFFFFF - 2 = 0xFFFFFFFD.
39func p384_field_load_p_minus_2(out: *i64) -> i64 {
40 out[0] = 0xFFFFFFFD
41 out[1] = 0
42 out[2] = 0
43 out[3] = 0xFFFFFFFF
44 out[4] = 0xFFFFFFFE
45 out[5] = 0xFFFFFFFF
46 out[6] = 0xFFFFFFFF
47 out[7] = 0xFFFFFFFF
48 out[8] = 0xFFFFFFFF
49 out[9] = 0xFFFFFFFF
50 out[10] = 0xFFFFFFFF
51 out[11] = 0xFFFFFFFF
52 return 0
53}
54
55// Extract bit at bit_pos (0 = LSB) from a 12-limb (32-bit/limb LE) integer.
56func p384_field_bit_at(limbs: *i64, bit_pos: i64) -> i64 {
57 let limb_idx: i64 = bit_pos / 32
58 let bit_in_limb: i64 = bit_pos - limb_idx * 32
59 let limb: i64 = limbs[limb_idx] & NX_U384_LIMB_MASK
60 return (limb >> bit_in_limb) & 1
61}
62
63// out = a^(-1) mod p via Fermat: a^(p-2).
64func p384_field_inv(out: *i64, a: *i64) -> i64 {
65 let exp: *i64 = u384_alloc()
66 p384_field_load_p_minus_2(exp)
67
68 // Keep input safe in case out aliases a.
69 let base: *i64 = u384_alloc()
70 u384_copy(base, a)
71
72 let result: *i64 = u384_alloc()
73 p384_field_one(result)
74
75 var bit_pos: i64 = 383
76 while bit_pos >= 0 {
77 p384_field_sq_fast(result, result)
78 if p384_field_bit_at(exp, bit_pos) == 1 {
79 p384_field_mul_fast(result, result, base)
80 }
81 bit_pos = bit_pos - 1
82 }
83
84 u384_copy(out, result)
85 return 0
86}
87
88func main() -> i64 {
89 return 0
90}