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}