nx_ecdsa_p384.nx source
↩ module page · 121 lines · 4297 B
1// nx_ecdsa_p384.nx -- ECDSA-P384 signature verification orchestrator.
2//
3// Mirror of nx_ecdsa_p256.nx adapted to the 12-limb / 384-bit path.
4// Same verify algorithm (SEC 1 v2.0 §4.1.4):
5// Inputs: Q = (pub_x, pub_y), e (hash), (r, s)
6// 1. Range-check r, s ∈ [1, n-1]
7// 2. Verify Q on curve + not infinity
8// 3. e_n = e mod n
9// 4. s_inv = s^(-1) mod n
10// 5. u1 = (e_n * s_inv) mod n
11// 6. u2 = (r * s_inv) mod n
12// 7. R = u1*G + u2*Q
13// 8. Reject if R == infinity
14// 9. v = x_R mod n
15// 10. Accept iff v == r
16//
17// Per Cardinal 22 (composition of shipped primitives).
18//
19// license_tier: INDEPENDENT_REDERIVE
20// genealogy_id: international-research-sources/nist/sec1_v2_4.1.4 + fips_186_5
21// lineage_id: nishi_ecdsa_p384_q10
22
23// nx_safety_envelope:
24// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
25// sil_target: SIL1
26// evidence: [bulk_applied_2026-05-20, ecdsa-p384-verify-orchestrator]
27// verdict: NOT_YET_EVALUATED
28
29import "nx_syscalls.nx"
30import "nx_u384.nx"
31import "nx_p384_modn.nx"
32import "nx_p384_point.nx"
33import "nx_p384_point_add.nx"
34import "nx_p384_scalar_mul.nx"
35
36const NX_ECDSA_P384_OK: i64 = 1
37const NX_ECDSA_P384_BAD_R_RANGE: i64 = 2
38const NX_ECDSA_P384_BAD_S_RANGE: i64 = 3
39const NX_ECDSA_P384_PUBKEY_NOT_ON_CURVE: i64 = 4
40const NX_ECDSA_P384_PUBKEY_INFINITY: i64 = 5
41const NX_ECDSA_P384_INFINITY_RESULT: i64 = 6
42const NX_ECDSA_P384_BAD_SIG: i64 = 7
43const NX_ECDSA_P384_VERDICT_N: i64 = 8
44
45func nx_ecdsa_p384_verdict_is_valid(v: i64) -> i64 {
46 if v < NX_ECDSA_P384_OK { return 0 }
47 if v >= NX_ECDSA_P384_VERDICT_N { return 0 }
48 return 1
49}
50
51// Verify ECDSA-P384 signature.
52// Returns NX_ECDSA_P384_OK on valid, non-OK verdict on any failure.
53// certloop leak fix: a thin LIFO-frame wrapper -> every u384/point the verify + its scalar-muls allocate is
54// reclaimed in O(1) at return (the inner keeps its 9 early-returns untouched; the wrapper guarantees one restore).
55func nx_ecdsa_p384_verify(pub_x: *i64, pub_y: *i64,
56 hash_e: *i64,
57 sig_r: *i64, sig_s: *i64) -> i64 {
58 let _sav: i64 = nx_u384_scratch_save()
59 let v: i64 = _nx_ecdsa_p384_verify_inner(pub_x, pub_y, hash_e, sig_r, sig_s)
60 nx_u384_scratch_restore(_sav)
61 return v
62}
63func _nx_ecdsa_p384_verify_inner(pub_x: *i64, pub_y: *i64,
64 hash_e: *i64,
65 sig_r: *i64, sig_s: *i64) -> i64 {
66 let n: *i64 = u384_alloc()
67 p384_modn_load_n(n)
68
69 // ---- Range-check r, s ----
70 if u384_is_zero(sig_r) == 1 { return NX_ECDSA_P384_BAD_R_RANGE }
71 if u384_cmp(sig_r, n) >= 0 { return NX_ECDSA_P384_BAD_R_RANGE }
72 if u384_is_zero(sig_s) == 1 { return NX_ECDSA_P384_BAD_S_RANGE }
73 if u384_cmp(sig_s, n) >= 0 { return NX_ECDSA_P384_BAD_S_RANGE }
74
75 // ---- Pubkey on curve + not infinity ----
76 let Q: *P384Point = p384_point_alloc()
77 p384_point_set_affine(Q, pub_x, pub_y)
78 if p384_point_is_infinity(Q) == 1 { return NX_ECDSA_P384_PUBKEY_INFINITY }
79 if p384_point_on_curve(Q) != 1 { return NX_ECDSA_P384_PUBKEY_NOT_ON_CURVE }
80
81 // ---- e_n = e mod n ----
82 let e_n: *i64 = u384_alloc()
83 p384_modn_reduce(e_n, hash_e)
84
85 // ---- s_inv = s^(-1) mod n ----
86 let s_inv: *i64 = u384_alloc()
87 p384_modn_inv(s_inv, sig_s)
88
89 // ---- u1 = e_n * s_inv mod n, u2 = r * s_inv mod n ----
90 let u1: *i64 = u384_alloc()
91 let u2: *i64 = u384_alloc()
92 p384_modn_mul(u1, e_n, s_inv)
93 p384_modn_mul(u2, sig_r, s_inv)
94
95 // ---- R = u1*G + u2*Q ----
96 let G: *P384Point = p384_point_alloc()
97 p384_point_load_g(G)
98
99 let u1G: *P384Point = p384_point_alloc()
100 let u2Q: *P384Point = p384_point_alloc()
101 p384_scalar_mul(u1G, u1, G)
102 p384_scalar_mul(u2Q, u2, Q)
103
104 let R: *P384Point = p384_point_alloc()
105 p384_point_add(R, u1G, u2Q)
106
107 if p384_point_is_infinity(R) == 1 { return NX_ECDSA_P384_INFINITY_RESULT }
108
109 // ---- v = x_R mod n ----
110 p384_point_to_affine(R)
111 let v: *i64 = u384_alloc()
112 p384_modn_reduce(v, R.x)
113
114 // ---- Accept iff v == r ----
115 if p384_modn_eq(v, sig_r) == 1 { return NX_ECDSA_P384_OK }
116 return NX_ECDSA_P384_BAD_SIG
117}
118
119func main() -> i64 {
120 return 0
121}