nx_voprf.nx
buildroot/runtime/hub/nx_voprf.nx
about
nx_voprf.nx -- RFC 9497 OPRF(P-256, SHA-256) Mode Base.
"Verifiable" in the file name is aspirational: this V1 ships ONLY
the Base mode of the OPRF protocol per RFC 9497 §3. The Verifiable
mode (VOPRF) and Partially-Oblivious mode (POPRF) add DLEQ proof
generation + verification (~300 more lines). OPAQUE PAKE
(V-MODAUTH-2) requires ONLY Base mode per IETF
draft-irtf-cfrg-opaque-13 §6.1, so V1 scope ends here.
OPRF = Oblivious Pseudorandom Function. Two parties:
Server: holds a long-term scalar skS in [1, n-1].
Client: has an input string x.
At protocol end, client learns y = F(skS, x) where F is a
pseudorandom function, and:
- Server NEVER sees x (only a blinded curve point).
- Client NEVER sees skS.
- Both confirm y matches without sending it on the wire.
OPAQUE uses OPRF to derive a per-password key (from password +
server's OPRF key) that the client uses to encrypt their long-term
secret. The result: even if the server is compromised, an attacker
cannot mount offline crack on the password (they don't have the
OPRF key and the protocol requires interaction to evaluate).
COMPOSES (per "avoid duplicate primitives" cardinal):
hub/nx_h2c_p256 hash_to_curve_p256 + hash_to_field
nx_p256_scalar_mul scalar * P256Point
nx_p256_modn scalar arithmetic mod n (group order)
nx_p256_point P256Point + load_g + to_affine + sgn0
nx_u256 8-limb LE u256 byte conversion
nx_csprng random scalar source
sha256 Finalize() hash + part of DST
COMPOSED BY:
hub/nx_opaque_pake.nx V-MODAUTH-2 (uses Blind/Eval/Finalize)
bench/nx_voprf_smoke.sh RFC 9497 §A.1.1 test vectors (queued)
SPEC REFERENCES:
RFC 9497 §3 Base mode protocol (Blind / BlindEvaluate / Finalize)
RFC 9497 §3.2 contextString construction
dependencies 11 imports · 4 importers
diagram shows first 10 each side; +1 more imports, +0 more importers in the complete lists below.
imports: nx_syscalls.nxnx_u256.nxnx_p256_field.nxnx_p256_field_inv.nxnx_p256_modn.nxnx_p256_point.nxnx_p256_point_add.nxnx_p256_scalar_mul.nxnx_csprng.nxsha256.nxnx_h2c_p256.nx
imported by: nx_opaque_core.nxnx_opaque_pake.nxnx_voprf_finalize.nxnx_voprf_rfc_kat.nx
structs
| none |
consts
| 72 | const NX_VOPRF_OK: i64 = 0 |
| 73 | const NX_VOPRF_BAD_INPUT: i64 = 1320 |
| 74 | const NX_VOPRF_BUF_OVERFLOW: i64 = 1321 |
| 75 | const NX_VOPRF_CSPRNG_FAILED: i64 = 1322 |
| 76 | const NX_VOPRF_H2C_FAILED: i64 = 1323 |
| 77 | const NX_VOPRF_INVALID_ELEMENT: i64 = 1324 |
| 78 | const NX_VOPRF_INVALID_SCALAR: i64 = 1325 |
| 79 | const NX_VOPRF_DESERIALIZE_FAILED: i64 = 1326 |
| 82 | const NX_VOPRF_SCALAR_BYTES: i64 = 32 // P-256 scalar = 32 bytes (256 bits) |
| 83 | const NX_VOPRF_ELEMENT_BYTES: i64 = 33 // compressed P-256 point: 1-byte prefix + 32-byte X |
| 84 | const NX_VOPRF_FINALIZE_OUT_BYTES: i64 = 32 // SHA-256 output |
| 85 | const NX_VOPRF_MAX_INPUT_LEN: i64 = 4096 // sanity cap; OPAQUE passes passphrase (typ < 256B) |
| 86 | const NX_VOPRF_RANDOM_RETRY_MAX: i64 = 16 // rejection sampling cap |
| 89 | const NX_VOPRF_COMPRESS_EVEN: i64 = 0x02 // Y is even |
| 90 | const NX_VOPRF_COMPRESS_ODD: i64 = 0x03 // Y is odd |
| 109 | const NX_VOPRF_CONTEXT_STR_LEN: i64 = 20 |
| 142 | const NX_VOPRF_DST_H2C_LEN: i64 = 32 |
functions
| 111 | func _voprf_load_context_string(out: *u8) -> i64 |
| 144 | func _voprf_load_dst_h2c(out: *u8) -> i64 |
| 161 | func nx_voprf_serialize_element(point: *P256Point, out_33: *u8) -> i64 |
| 175 | func nx_voprf_deserialize_element(bytes_33: *u8, out_point: *P256Point) -> i64 |
| 234 | func nx_voprf_random_scalar(out_32: *u8) -> i64 |
| 269 | func nx_voprf_blind( |
| 321 | func nx_voprf_blind_evaluate( |