nx_ghash_clmul_gate.nx source
↩ module page · 92 lines · 3771 B
1// nx_ghash_clmul_gate.nx -- KAT-equality gate: the PCLMULQDQ GHASH backend
2// (nx_ghash_mul_clmul) must be BYTE-IDENTICAL to the canonical bit-by-bit reference
3// (nx_ghash_mul) on the NIST vector, edge cases, and many pseudo-random inputs.
4// The bit-by-bit reference is the trusted oracle. Any mismatch fails LOUD with a code.
5//
6// expect_exit: 0
7// license_tier: ORIGINAL
8
9import "nx_syscalls.nx"
10import "nx_ghash.nx"
11import "nx_ghash_clmul.nx"
12
13func g_ps(s: *u8, n: i64) -> i64 { sys_write(1, s, n); return 0 }
14func g_pn(v: i64) -> i64 {
15 let b: *u8 = sys_mmap(24); var x: i64 = v
16 if x < 0 { x = 0 - x }
17 var i: i64 = 22
18 if x == 0 { b[i] = 0x30 as u8; i = i - 1 }
19 else { while x > 0 { b[i] = (0x30 + (x - (x/10)*10)) as u8; x = x/10; i = i - 1 } }
20 sys_write(1, ((b as i64) + i + 1) as *u8, 22 - i); return 0
21}
22
23// compare two 16-byte buffers: 0 = equal, else (1 + first differing index)
24func eq16(a: *u8, b: *u8) -> i64 {
25 var i: i64 = 0
26 while i < 16 { if (a[i] & 0xff) != (b[i] & 0xff) { return 1 + i } i = i + 1 }
27 return 0
28}
29
30// one mul both ways; return 0 if identical, else nonzero.
31// Oracle = the bit-by-bit reference (nx_ghash_mul now delegates to clmul, so we must
32// call the _bitwise reference directly or the comparison would be clmul-vs-clmul).
33func check(x: *u8, h: *u8, zr: *u8, zc: *u8) -> i64 {
34 nx_ghash_mul_bitwise(x, h, zr)
35 nx_ghash_mul_clmul(x, h, zc)
36 return eq16(zr, zc)
37}
38
39func main() -> i64 {
40 let x: *u8 = sys_mmap(16)
41 let h: *u8 = sys_mmap(16)
42 let zr: *u8 = sys_mmap(16)
43 let zc: *u8 = sys_mmap(16)
44 var i: i64 = 0
45
46 // --- edge: x = 0 -> product 0 ---
47 i = 0; while i < 16 { x[i] = 0; h[i] = (i * 7 + 3) as u8; i = i + 1 }
48 if check(x, h, zr, zc) != 0 { g_ps("FAIL x=0\n" as *u8, 8); sys_exit(1); return 1 }
49
50 // --- edge: h = 0 -> product 0 ---
51 i = 0; while i < 16 { x[i] = (i * 5 + 1) as u8; h[i] = 0; i = i + 1 }
52 if check(x, h, zr, zc) != 0 { g_ps("FAIL h=0\n" as *u8, 8); sys_exit(2); return 2 }
53
54 // --- edge: all-ones (exercises the reduction overflow path) ---
55 i = 0; while i < 16 { x[i] = 0xff as u8; h[i] = 0xff as u8; i = i + 1 }
56 if check(x, h, zr, zc) != 0 { g_ps("FAIL all-ones\n" as *u8, 13); sys_exit(3); return 3 }
57
58 // --- NIST SP 800-38D Test Case 2: H and C ---
59 h[0]=0x66; h[1]=0xe9; h[2]=0x4b; h[3]=0xd4; h[4]=0xef; h[5]=0x8a; h[6]=0x2c; h[7]=0x3b
60 h[8]=0x88; h[9]=0x4c; h[10]=0xfa; h[11]=0x59; h[12]=0xca; h[13]=0x34; h[14]=0x2b; h[15]=0x2e
61 x[0]=0x03; x[1]=0x88; x[2]=0xda; x[3]=0xce; x[4]=0x60; x[5]=0xb6; x[6]=0xa3; x[7]=0x92
62 x[8]=0xf3; x[9]=0x28; x[10]=0xc2; x[11]=0xb9; x[12]=0x71; x[13]=0xb2; x[14]=0xfe; x[15]=0x78
63 if check(x, h, zr, zc) != 0 { g_ps("FAIL NIST-vec\n" as *u8, 13); sys_exit(4); return 4 }
64
65 // --- many pseudo-random pairs (LCG, deterministic) ---
66 var st: i64 = 0x123456789abcdef
67 var n: i64 = 0
68 let N: i64 = 2000
69 while n < N {
70 i = 0
71 while i < 16 {
72 st = st * 6364136223846793005 + 1442695040888963407
73 x[i] = (gh_byte_from(st, 0)) as u8
74 st = st * 6364136223846793005 + 1442695040888963407
75 h[i] = (gh_byte_from(st, 1)) as u8
76 i = i + 1
77 }
78 let r: i64 = check(x, h, zr, zc)
79 if r != 0 { g_ps("FAIL rand n=" as *u8, 11); g_pn(n); g_ps(" idx=" as *u8, 5); g_pn(r - 1); g_ps("\n" as *u8, 1); sys_exit(5); return 5 }
80 n = n + 1
81 }
82
83 g_ps("ghash-clmul == reference: " as *u8, 26); g_pn(N + 4); g_ps(" vectors OK\n" as *u8, 12)
84 sys_exit(0)
85 return 0
86}
87
88// extract a byte from LCG state (different slices for x vs h to decorrelate)
89func gh_byte_from(st: i64, which: i64) -> i64 {
90 if which == 0 { return (st >> 40) & 0xff }
91 return (st >> 24) & 0xff
92}