nx_opaque_core.nx
buildroot/runtime/hub/nx_opaque_core.nx
about
nx_opaque_core.nx -- V-MODAUTH-2a: OPAQUE (RFC 9807) shared primitives.
OPAQUE = the asymmetric PAKE the modern-auth charter names as the auth
crown jewel: the server NEVER sees the password and stores NOTHING that
permits an offline dictionary attack without ALSO stealing the server's
long-term oprf_seed (and even then must pay the memory-hard KSF per guess).
This module is the primitive floor the rest of V-MODAUTH-2 composes:
nx_opq_hash_to_scalar RFC 9497 §4.4 HashToScalar (P-256 group order n)
nx_opq_derive_keypair RFC 9497 §3.2 DeriveKeyPair (seed -> sk, pk)
nx_opq_dh RFC 9807 §6.4.1.2 DiffieHellman (P-256, compressed out)
nx_opq_expand_label RFC 9807 §6.4.2.1 Expand-Label (TLS 1.3 style, "OPAQUE-" prefix)
nx_opq_derive_secret RFC 9807 §6.4.2.1 Derive-Secret
nx_opq_stretch RFC 9807 KSF hook: mode 0 = Identity (RFC KAT), mode 1 = argon2id
nx_opq_randomized_password RFC 9807 §5.2.3 Extract("", oprf_output || stretched)
nx_opq_ct_eq constant-time tag compare
COMPOSES (avoid duplicate primitives):
hub/nx_voprf + hub/nx_voprf_finalize RFC 9497 OPRF (Blind/BlindEvaluate/Finalize) -- VERIFIED CAPREG299
hub/nx_h2c_p256 expand_message_xmd (RFC 9380)
nx_p256_modn / nx_p256_point / nx_p256_scalar_mul group arithmetic
nx_hkdf / nx_hmac Extract / Expand / MAC
nx_argon2id memory-hard KSF (RFC 9106) -- vault KDF v2 lineage
COMPOSED BY:
hub/nx_opaque_envelope.nx §4 Store/Recover
hub/nx_opaque_3dh.nx §6.4 AKE key schedule
hub/nx_opaque_pake.nx §5 + §6 top-level registration/login
SPEC REFERENCES:
RFC 9807 (OPAQUE) §4, §5, §6; test vectors Appendix C.1.5/C.1.6 (P256-SHA256)
RFC 9497 (OPRF) §3.2 DeriveKeyPair, §4.4 HashToScalar for P256-SHA256
RFC 8446 §7.1 HKDF-Expand-Label encoding (repurposed with "OPAQUE-" prefix)
vectors staged: knowledge/specs/2026-06-10-rfc9807-opaque-p256-vectors.ref
license_tier: ORIGINAL
dependencies 16 imports · 5 importers
diagram shows first 10 each side; +6 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_hmac.nxnx_hkdf.nxnx_argon2id.nxnx_h2c_p256.nxnx_voprf.nxnx_voprf_finalize.nx
imported by: nx_opaque_3dh.nxnx_opaque_envelope.nxnx_opaque_pake.nxnx_opq_probe1.nxnx_user_account_store.nx
structs
| none |
consts
| 55 | const NX_OPQ_OK: i64 = 0 |
| 56 | const NX_OPQ_BAD_INPUT: i64 = 1440 |
| 57 | const NX_OPQ_BUF_OVERFLOW: i64 = 1441 |
| 58 | const NX_OPQ_DERIVE_FAILED: i64 = 1442 // DeriveKeyPair exhausted 255 counters (negligible) |
| 59 | const NX_OPQ_DH_FAILED: i64 = 1443 // invalid peer point / infinity result |
| 60 | const NX_OPQ_ENVELOPE_AUTH_FAIL: i64 = 1444 // Recover: auth_tag mismatch (wrong password) |
| 61 | const NX_OPQ_SERVER_AUTH_FAIL: i64 = 1445 // client: KE2 server_mac mismatch |
| 62 | const NX_OPQ_CLIENT_AUTH_FAIL: i64 = 1446 // server: KE3 client_mac mismatch |
| 63 | const NX_OPQ_OPRF_FAILED: i64 = 1447 |
| 64 | const NX_OPQ_KSF_FAILED: i64 = 1448 |
| 65 | const NX_OPQ_CSPRNG_FAILED: i64 = 1449 |
| 68 | const NX_OPQ_NH: i64 = 32 // hash output |
| 69 | const NX_OPQ_NPK: i64 = 33 // compressed P-256 public key |
| 70 | const NX_OPQ_NSK: i64 = 32 // scalar private key |
| 71 | const NX_OPQ_NM: i64 = 32 // MAC output |
| 72 | const NX_OPQ_NX: i64 = 32 // AKE secret length |
| 73 | const NX_OPQ_NOK: i64 = 32 // OPRF private key |
| 74 | const NX_OPQ_NOE: i64 = 33 // serialized OPRF group element |
| 75 | const NX_OPQ_NN: i64 = 32 // nonce length |
| 76 | const NX_OPQ_NSEED: i64 = 32 // keypair derivation seed length |
| 78 | const NX_OPQ_MAX_ID_LEN: i64 = 128 // client/server identity cap (charter realm cap) |
| 79 | const NX_OPQ_MAX_PW_LEN: i64 = 256 // matches NX_MAUTH_MAX_PASSPHRASE_LEN |
| 80 | const NX_OPQ_MAX_CONTEXT_LEN: i64 = 64 |
| 83 | const NX_OPQ_KSF_IDENTITY: i64 = 0 // RFC test-vector configuration (KAT only; NEVER production) |
| 84 | const NX_OPQ_KSF_ARGON2ID: i64 = 1 // production: memory-hard per charter + vault KDF v2 lineage |
| 90 | const NX_OPQ_KSF_SALT_BYTES: i64 = 16 |
functions
| 94 | func nx_opq_ct_eq(a: *u8, b: *u8, n: i64) -> i64 |
| 111 | func _opq_load_Rn(out: *i64) -> i64 called by 1: _opq_reduce_48be_mod_n |
| 125 | func _opq_reduce_48be_mod_n(bytes_48: *u8, out_scalar: *i64) -> i64 called by 1: nx_opq_hash_to_scalar calls 10: u256_allocsys_mmapu256_load_bep256_modn_load_nu256_cmpu256_sub_with_borrow+4 |
| 154 | func nx_opq_hash_to_scalar(msg: *u8, msg_n: i64, dst: *u8, dst_n: i64, out_32: *u8) -> i64 |
| 175 | func nx_opq_derive_keypair( |
| 237 | func nx_opq_dh(sk_32: *u8, pk_33: *u8, out_33: *u8) -> i64 |
| 260 | func nx_opq_expand_label( |
| 299 | func nx_opq_derive_secret( |
| 316 | func nx_opq_stretch( |
| 362 | func nx_opq_randomized_password( |