nx_p384_scalar_mul_gate.nx source
↩ module page · 74 lines · 5126 B
1// nx_p384_scalar_mul_gate.nx -- DIFFERENTIAL referee: the new 4-bit windowed p384_scalar_mul MUST equal the
2// trusted double-and-add p384_scalar_mul_dbladd for k*G across edge, limb/nibble-boundary, and spread scalars.
3// p384_point_eq normalizes to affine so the two algorithms' different projective representations don't false-fail.
4// A wrong scalar-mul diverges from the trusted oracle -> RED. GREEN + exit 0 iff every case matches.
5// license_tier: ORIGINAL expect_exit: 0
6import "nx_p384_scalar_mul.nx"
7import "nx_p384_point.nx"
8import "nx_syscalls.nx"
9import "nx_gate_verdict.nx"
10
11func pg_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
12func pg_putn(v: i64) -> i64 {
13 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
14 var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
15 let d: *u8 = sys_mmap(24); var k: i64 = 0
16 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
17 var j: i64 = k - 1
18 while j >= 0 { sys_write(1, ((d as i64)+j) as *u8, 1); j = j - 1 }
19 return 0
20}
21func pg_zero12(k: *i64) -> i64 { var i: i64 = 0; while i < 12 { k[i] = 0; i = i + 1 } return 0 }
22// 1 iff windowed p384_scalar_mul(k,G) == trusted p384_scalar_mul_dbladd(k,G)
23func pg_match(g: *P384Point, k: *i64) -> i64 {
24 let ow: *P384Point = p384_point_alloc()
25 let od: *P384Point = p384_point_alloc()
26 p384_scalar_mul(ow, k, g)
27 p384_scalar_mul_dbladd(od, k, g)
28 return p384_point_eq(ow, od)
29}
30
31func main() -> i64 {
32 pg_puts("p384 scalar-mul DIFFERENTIAL gate (windowed default == trusted double-and-add, k*G, affine-normalized eq)\n" as *u8)
33 let g: *P384Point = p384_point_alloc()
34 p384_point_load_g(g)
35 let k: *i64 = sys_mmap(12 * 8)
36 var pass: i64 = 0
37 var total: i64 = 0
38
39 pg_zero12(k); total = total + 1; pass = pass + pg_match(g, k) // k=0 (O)
40 pg_zero12(k); k[0] = 1; total = total + 1; pass = pass + pg_match(g, k) // k=1 (G)
41 pg_zero12(k); k[0] = 2; total = total + 1; pass = pass + pg_match(g, k) // k=2
42 pg_zero12(k); k[0] = 3; total = total + 1; pass = pass + pg_match(g, k) // k=3
43 pg_zero12(k); k[0] = 15; total = total + 1; pass = pass + pg_match(g, k) // k=15 (max nibble)
44 pg_zero12(k); k[0] = 16; total = total + 1; pass = pass + pg_match(g, k) // k=16 (nibble carry)
45 pg_zero12(k); k[0] = 255; total = total + 1; pass = pass + pg_match(g, k) // k=255
46 pg_zero12(k); k[0] = 0xFFFFFFFF; total = total + 1; pass = pass + pg_match(g, k) // limb0 full
47 pg_zero12(k); k[1] = 1; total = total + 1; pass = pass + pg_match(g, k) // k=2^32 (limb boundary)
48 pg_zero12(k); k[11] = 0xF0000000; total = total + 1; pass = pass + pg_match(g, k) // top nibble (MSB window)
49 let a: *i64 = k; var ai: i64 = 0; pg_zero12(k); while ai < 12 { a[ai] = 0x12345678; ai = ai + 1 }
50 total = total + 1; pass = pass + pg_match(g, k) // spread 0x12345678
51 var bi: i64 = 0; pg_zero12(k); while bi < 12 { k[bi] = 0xFFFFFFFF; bi = bi + 1 }
52 total = total + 1; pass = pass + pg_match(g, k) // all-Fs (>= group order)
53 var ci: i64 = 0; pg_zero12(k); while ci < 12 { k[ci] = 0xA5A5A5A5; ci = ci + 1 }
54 total = total + 1; pass = pass + pg_match(g, k) // alternating 0xA5
55
56 // ALIASING: out == in (a caller reusing one buffer). Structurally safe -- base snapshots p up front, out is
57 // written only at the very end -- but PROVEN here: 5*G computed in place must still equal dbladd(5*G).
58 let gp: *P384Point = p384_point_alloc(); p384_point_copy(gp, g)
59 pg_zero12(k); k[0] = 5
60 p384_scalar_mul(gp, k, gp)
61 let od5: *P384Point = p384_point_alloc(); p384_scalar_mul_dbladd(od5, k, g)
62 total = total + 1; pass = pass + p384_point_eq(gp, od5) // in-place 5*G == dbladd(5*G)
63
64 pg_puts("p384_scalar_mul_gate pass=" as *u8); pg_putn(pass); pg_puts("/" as *u8); pg_putn(total); pg_puts("\n" as *u8)
65 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
66 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
67 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
68 let ctr__dry: *i64 = gv_ctr()
69 ctr__dry[0] = pass
70 ctr__dry[1] = total
71 let rc__dry: i64 = gv_verdict("P384-SCALAR-MUL-GATE" as *u8, ctr__dry, "windowed scalar-mul byte-identical to trusted double-and-add across edge+boundary+spread scalars" as *u8)
72 sys_exit(rc__dry)
73 return rc__dry
74}