code wiki / _hdl_build / nx_voprf_rfc_kat.nx
nx_voprf_rfc_kat.nx source
↩ module page · 105 lines · 4603 B
1// nx_voprf_rfc_kat.nx -- ENGINEER verification of the OPAQUE building block (the auth crown jewel's
2// foundation). nx_voprf.nx (RFC 9497 OPRF P-256/SHA-256, Base mode) shipped WITHOUT a test = the whole
3// modern-auth charter rested on UNVERIFIED crypto. This gates it against the RFC's OWN authoritative
4// test vector (Appendix A.3.1.1).
5//
6// KEY INSIGHT: the OPRF output is BLIND-INDEPENDENT -- F(skS, input) is fixed regardless of the random
7// blind. So even though nx_voprf_blind draws its own CSPRNG blind (we cannot inject the RFC's fixed
8// blind), the FULL round trip Blind->BlindEvaluate->Finalize must reproduce the RFC's Output EXACTLY.
9// That single assert validates hash-to-curve (RFC 9380), P-256 scalar-mult, the blind/unblind modular
10// inverse, and the Finalize hash -- the entire stack -- against the real standard.
11//
12// RFC 9497 A.3.1.1 (OPRF(P-256, SHA-256), Base mode):
13// skSm = 159749d750713afe245d2d39ccfaae8381c53ce92d098a9375ee70739c7ac0bf
14// Input = 00
15// Output = a0b34de5fa4c5b6da07e72af73cc507cceeb48981b97b7285fc375345fe495dd
16// (we use our own random Blind; the Output is invariant, so this is a true KAT.)
17// license_tier: ORIGINAL
18import "hub/nx_voprf.nx"
19import "hub/nx_voprf_finalize.nx"
20import "nx_syscalls.nx"
21
22func vk_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
23
24// hex nibble -> value
25func vk_nib(c: i64) -> i64 {
26 if c >= 48 { if c <= 57 { return c - 48 } }
27 if c >= 97 { if c <= 102 { return c - 87 } }
28 if c >= 65 { if c <= 70 { return c - 55 } }
29 return 0
30}
31// decode NUL-terminated hex string into out; returns byte count
32func vk_hex(hexs: *u8, out: *u8) -> i64 {
33 var i: i64 = 0
34 var o: i64 = 0
35 while hexs[i*2] != (0 as u8) {
36 out[o] = ((vk_nib(hexs[i*2] as i64) << 4) | vk_nib(hexs[i*2+1] as i64)) as u8
37 o = o + 1
38 i = i + 1
39 }
40 return o
41}
42func vk_hexdump(label: *u8, b: *u8) -> i64 {
43 vk_w(label)
44 let hx: *u8 = "0123456789abcdef" as *u8
45 let line: *u8 = sys_mmap(80)
46 var i: i64 = 0
47 while i < 32 {
48 line[i*2] = hx[((b[i] as i64) >> 4) & 15]
49 line[i*2+1] = hx[(b[i] as i64) & 15]
50 i = i + 1
51 }
52 line[64] = 10 as u8
53 sys_write(1, line, 65)
54 return 0
55}
56func vk_eq32(a: *u8, b: *u8) -> i64 {
57 var i: i64 = 0
58 while i < 32 { if (a[i] as i64) != (b[i] as i64) { return 0 } i = i + 1 }
59 return 1
60}
61
62// full OPRF round trip for input[0..in_n) under server skS_32 -> out_32. returns 1 on success.
63func vk_oprf(skS: *u8, input: *u8, in_n: i64, out32: *u8) -> i64 {
64 let blind: *u8 = sys_mmap(32)
65 let blinded: *u8 = sys_mmap(33)
66 let evaluated: *u8 = sys_mmap(33)
67 if nx_voprf_blind(input, in_n, blind, blinded) != NX_VOPRF_OK { return 0 }
68 if nx_voprf_blind_evaluate(skS, blinded, evaluated) != NX_VOPRF_OK { return 0 }
69 if nx_voprf_finalize(input, in_n, blind, evaluated, out32) != NX_VOPRF_OK { return 0 }
70 return 1
71}
72
73func main() -> i64 {
74 let skS: *u8 = sys_mmap(32)
75 vk_hex("159749d750713afe245d2d39ccfaae8381c53ce92d098a9375ee70739c7ac0bf" as *u8, skS)
76 let want: *u8 = sys_mmap(32)
77 vk_hex("a0b34de5fa4c5b6da07e72af73cc507cceeb48981b97b7285fc375345fe495dd" as *u8, want)
78
79 let input: *u8 = sys_mmap(8)
80 input[0] = 0 as u8 // RFC Input = 0x00, length 1
81 let out1: *u8 = sys_mmap(32)
82 let out2: *u8 = sys_mmap(32)
83
84 var bad: i64 = 0
85
86 // KAT1: authoritative round trip == RFC Output (with our own random blind)
87 if vk_oprf(skS, input, 1, out1) != 1 { bad = bad + 1; vk_w("FAIL oprf-run-1\n" as *u8) }
88 if vk_eq32(out1, want) == 0 { bad = bad + 1; vk_w("FAIL KAT1 output != RFC A.3.1.1\n" as *u8) }
89
90 // KAT2: blind-independence -- a SECOND run (fresh CSPRNG blind) yields the same RFC Output
91 if vk_oprf(skS, input, 1, out2) != 1 { bad = bad + 1; vk_w("FAIL oprf-run-2\n" as *u8) }
92 if vk_eq32(out2, want) == 0 { bad = bad + 1; vk_w("FAIL KAT2 blind-dependence (output drifted)\n" as *u8) }
93
94 // KAT3: input-sensitivity -- a different input must NOT match the RFC Output
95 let input2: *u8 = sys_mmap(8)
96 input2[0] = 1 as u8
97 let out3: *u8 = sys_mmap(32)
98 if vk_oprf(skS, input2, 1, out3) != 1 { bad = bad + 1; vk_w("FAIL oprf-run-3\n" as *u8) }
99 if vk_eq32(out3, want) == 1 { bad = bad + 1; vk_w("FAIL KAT3 input-insensitive (collision)\n" as *u8) }
100
101 if bad == 0 { vk_hexdump("out=" as *u8, out1); vk_w("VOPRF RFC9497 A.3.1.1 KAT GREEN (round-trip=RFC, blind-independent, input-sensitive)\n" as *u8); sys_exit(0) }
102 vk_w("VOPRF KAT FAILED\n" as *u8)
103 sys_exit(1)
104 return 1
105}