code wiki / hub / nx_opaque_core.nx

nx_opaque_core.nx source

↩ module page · 408 lines · 18814 B

1// nx_opaque_core.nx -- V-MODAUTH-2a: OPAQUE (RFC 9807) shared primitives. 2// 3// OPAQUE = the asymmetric PAKE the modern-auth charter names as the auth 4// crown jewel: the server NEVER sees the password and stores NOTHING that 5// permits an offline dictionary attack without ALSO stealing the server's 6// long-term oprf_seed (and even then must pay the memory-hard KSF per guess). 7// 8// This module is the primitive floor the rest of V-MODAUTH-2 composes: 9// nx_opq_hash_to_scalar RFC 9497 §4.4 HashToScalar (P-256 group order n) 10// nx_opq_derive_keypair RFC 9497 §3.2 DeriveKeyPair (seed -> sk, pk) 11// nx_opq_dh RFC 9807 §6.4.1.2 DiffieHellman (P-256, compressed out) 12// nx_opq_expand_label RFC 9807 §6.4.2.1 Expand-Label (TLS 1.3 style, "OPAQUE-" prefix) 13// nx_opq_derive_secret RFC 9807 §6.4.2.1 Derive-Secret 14// nx_opq_stretch RFC 9807 KSF hook: mode 0 = Identity (RFC KAT), mode 1 = argon2id 15// nx_opq_randomized_password RFC 9807 §5.2.3 Extract("", oprf_output || stretched) 16// nx_opq_ct_eq constant-time tag compare 17// 18// COMPOSES (avoid duplicate primitives): 19// hub/nx_voprf + hub/nx_voprf_finalize RFC 9497 OPRF (Blind/BlindEvaluate/Finalize) -- VERIFIED CAPREG299 20// hub/nx_h2c_p256 expand_message_xmd (RFC 9380) 21// nx_p256_modn / nx_p256_point / nx_p256_scalar_mul group arithmetic 22// nx_hkdf / nx_hmac Extract / Expand / MAC 23// nx_argon2id memory-hard KSF (RFC 9106) -- vault KDF v2 lineage 24// 25// COMPOSED BY: 26// hub/nx_opaque_envelope.nx §4 Store/Recover 27// hub/nx_opaque_3dh.nx §6.4 AKE key schedule 28// hub/nx_opaque_pake.nx §5 + §6 top-level registration/login 29// 30// SPEC REFERENCES: 31// RFC 9807 (OPAQUE) §4, §5, §6; test vectors Appendix C.1.5/C.1.6 (P256-SHA256) 32// RFC 9497 (OPRF) §3.2 DeriveKeyPair, §4.4 HashToScalar for P256-SHA256 33// RFC 8446 §7.1 HKDF-Expand-Label encoding (repurposed with "OPAQUE-" prefix) 34// vectors staged: knowledge/specs/2026-06-10-rfc9807-opaque-p256-vectors.ref 35// license_tier: ORIGINAL 36 37import "nx_syscalls.nx" 38import "nx_u256.nx" 39import "nx_p256_field.nx" 40import "nx_p256_field_inv.nx" 41import "nx_p256_modn.nx" 42import "nx_p256_point.nx" 43import "nx_p256_point_add.nx" 44import "nx_p256_scalar_mul.nx" 45import "nx_csprng.nx" 46import "sha256.nx" 47import "nx_hmac.nx" 48import "nx_hkdf.nx" 49import "nx_argon2id.nx" 50import "hub/nx_h2c_p256.nx" 51import "hub/nx_voprf.nx" 52import "hub/nx_voprf_finalize.nx" 53 54// ===== Sealed verdict surface (codes 1440-1459) ================================================= 55const NX_OPQ_OK: i64 = 0 56const NX_OPQ_BAD_INPUT: i64 = 1440 57const NX_OPQ_BUF_OVERFLOW: i64 = 1441 58const NX_OPQ_DERIVE_FAILED: i64 = 1442 // DeriveKeyPair exhausted 255 counters (negligible) 59const NX_OPQ_DH_FAILED: i64 = 1443 // invalid peer point / infinity result 60const NX_OPQ_ENVELOPE_AUTH_FAIL: i64 = 1444 // Recover: auth_tag mismatch (wrong password) 61const NX_OPQ_SERVER_AUTH_FAIL: i64 = 1445 // client: KE2 server_mac mismatch 62const NX_OPQ_CLIENT_AUTH_FAIL: i64 = 1446 // server: KE3 client_mac mismatch 63const NX_OPQ_OPRF_FAILED: i64 = 1447 64const NX_OPQ_KSF_FAILED: i64 = 1448 65const NX_OPQ_CSPRNG_FAILED: i64 = 1449 66 67// ===== Suite parameters: OPAQUE-3DH over OPRF(P-256, SHA-256) per RFC 9807 §7 ==================== 68const NX_OPQ_NH: i64 = 32 // hash output 69const NX_OPQ_NPK: i64 = 33 // compressed P-256 public key 70const NX_OPQ_NSK: i64 = 32 // scalar private key 71const NX_OPQ_NM: i64 = 32 // MAC output 72const NX_OPQ_NX: i64 = 32 // AKE secret length 73const NX_OPQ_NOK: i64 = 32 // OPRF private key 74const NX_OPQ_NOE: i64 = 33 // serialized OPRF group element 75const NX_OPQ_NN: i64 = 32 // nonce length 76const NX_OPQ_NSEED: i64 = 32 // keypair derivation seed length 77 78const NX_OPQ_MAX_ID_LEN: i64 = 128 // client/server identity cap (charter realm cap) 79const NX_OPQ_MAX_PW_LEN: i64 = 256 // matches NX_MAUTH_MAX_PASSPHRASE_LEN 80const NX_OPQ_MAX_CONTEXT_LEN: i64 = 64 81 82// KSF modes for nx_opq_stretch: 83const NX_OPQ_KSF_IDENTITY: i64 = 0 // RFC test-vector configuration (KAT only; NEVER production) 84const NX_OPQ_KSF_ARGON2ID: i64 = 1 // production: memory-hard per charter + vault KDF v2 lineage 85 86// argon2id KSF salt: fixed all-zero 16 bytes. Deterministic by REQUIREMENT (the same 87// password must re-derive the same envelope keys at every login); per-user uniqueness 88// already comes from the per-credential OPRF key, so a fixed KSF salt adds no risk 89// (RFC 9807 §7 lists scrypt(S=zeroes(16)) the same way). 90const NX_OPQ_KSF_SALT_BYTES: i64 = 16 91// argon2id per-call scratch sizes (nx_opq_stretch): named ONCE so map and release cannot disagree. 92const NX_OPQ_KIB: i64 = 1024 93const NX_OPQ_A2_SMALL: i64 = 64 // h0 / prev / curr blake2b digests 94const NX_OPQ_A2_PREPEND: i64 = 1100 // H0 prepend scratch 95const NX_OPQ_A2_BLOCK: i64 = 1024 // one argon2 block (and the g_r/g_rs/h0_input/addr/tmp/final/zero blocks) 96const NX_OPQ_A2_B2B: i64 = 128 // blake2b buf/sv/sm + g_col 97// zeroing helper for secret-bearing scratch (a plain byte loop; nothing here is timing-sensitive) 98func _opq_wipe(p: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { p[i] = 0 as u8; i = i + 1 } return 0 } 99 100// ===== constant-time compare ================================================= 101 102func nx_opq_ct_eq(a: *u8, b: *u8, n: i64) -> i64 { 103 var diff: i64 = 0 104 var i: i64 = 0 105 while i < n { 106 diff = diff | ((a[i] as i64) ^ (b[i] as i64)) 107 i = i + 1 108 } 109 if diff == 0 { return 1 } 110 return 0 111} 112 113// ===== HashToScalar per RFC 9497 §4.4 (P256-SHA256 suite) ================================================= 114// 115// uniform = expand_message_xmd_sha256(msg, DST, 48); out = OS2IP(uniform) mod n. 116// Reduction mirrors _h2c_reduce_48be_mod_p but over the GROUP ORDER n: 117// value = high16 * 2^256 + low32; out = (high16 * Rn + low) mod n, Rn = 2^256 mod n. 118 119func _opq_load_Rn(out: *i64) -> i64 { 120 // Rn = 2^256 - n (n > 2^255 so 2^256 mod n = 2^256 - n) 121 // = 0x00000000 FFFFFFFF 00000000 00000000 43190552 58E8617B 0C46353D 039CDAAF (BE words) 122 out[0] = 0x039CDAAF as i64 123 out[1] = 0x0C46353D as i64 124 out[2] = 0x58E8617B as i64 125 out[3] = 0x43190552 as i64 126 out[4] = 0x00000000 as i64 127 out[5] = 0x00000000 as i64 128 out[6] = 0xFFFFFFFF as i64 129 out[7] = 0x00000000 as i64 130 return NX_OPQ_OK 131} 132 133func _opq_reduce_48be_mod_n(bytes_48: *u8, out_scalar: *i64) -> i64 { 134 let high_u256: *i64 = u256_alloc() 135 let low_u256: *i64 = u256_alloc() 136 let Rn: *i64 = u256_alloc() 137 let n: *i64 = u256_alloc() 138 139 let high_be_32: *u8 = sys_mmap(32) 140 var i: i64 = 0 141 while i < 16 { high_be_32[i] = 0 as u8; i = i + 1 } 142 var j: i64 = 0 143 while j < 16 { high_be_32[16 + j] = bytes_48[j]; j = j + 1 } 144 u256_load_be(high_u256, high_be_32) 145 u256_load_be(low_u256, (bytes_48 as i64 + 16) as *u8) 146 147 p256_modn_load_n(n) 148 if u256_cmp(low_u256, n) >= 0 { 149 let tmp: *i64 = u256_alloc() 150 u256_sub_with_borrow(tmp, low_u256, n) 151 u256_copy(low_u256, tmp) 152 } 153 154 _opq_load_Rn(Rn) 155 let prod: *i64 = u256_alloc() 156 p256_modn_mul(prod, high_u256, Rn) 157 p256_modn_add(out_scalar, prod, low_u256) 158 return NX_OPQ_OK 159} 160 161// msg -> scalar in [0, n-1] as 32-byte BE. dst is caller-built ("DeriveKeyPair" || contextString etc). 162func nx_opq_hash_to_scalar(msg: *u8, msg_n: i64, dst: *u8, dst_n: i64, out_32: *u8) -> i64 { 163 if (msg as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 164 if (dst as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 165 if (out_32 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 166 let uniform: *u8 = sys_mmap(48) 167 let rc: i64 = nx_h2c_expand_message_xmd_sha256(msg, msg_n, dst, dst_n, 48, uniform) 168 if rc != NX_H2C_OK { return 0 - NX_OPQ_DERIVE_FAILED } 169 let scalar: *i64 = u256_alloc() 170 _opq_reduce_48be_mod_n(uniform, scalar) 171 u256_store_be(out_32, scalar) 172 return NX_OPQ_OK 173} 174 175// ===== DeriveKeyPair per RFC 9497 §3.2 ================================================= 176// 177// deriveInput = seed || I2OSP(len(info), 2) || info 178// loop counter 0..255: sk = HashToScalar(deriveInput || I2OSP(counter,1), 179// DST = "DeriveKeyPair" || contextString) 180// until sk != 0; pk = ScalarMultGen(sk) compressed. 181// contextString = the OPRF one (20 bytes, mode 0x00, P256-SHA256) -- reuses nx_voprf's loader. 182 183func nx_opq_derive_keypair( 184 seed_32: *u8, 185 info: *u8, info_n: i64, 186 out_sk_32: *u8, 187 out_pk_33: *u8 188) -> i64 { 189 if (seed_32 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 190 if (info as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 191 if info_n < 0 { return 0 - NX_OPQ_BAD_INPUT } 192 if info_n > 255 { return 0 - NX_OPQ_BAD_INPUT } 193 if (out_sk_32 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 194 195 // DST = "DeriveKeyPair" (13) || contextString (20) = 33 bytes 196 let dst: *u8 = sys_mmap(40) 197 let dkp: *u8 = "DeriveKeyPair" as *u8 198 var d: i64 = 0 199 while d < 13 { dst[d] = dkp[d]; d = d + 1 } 200 let ctx20: *u8 = sys_mmap(NX_VOPRF_CONTEXT_STR_LEN) 201 _voprf_load_context_string(ctx20) 202 var c: i64 = 0 203 while c < NX_VOPRF_CONTEXT_STR_LEN { dst[13 + c] = ctx20[c]; c = c + 1 } 204 let dst_n: i64 = 13 + NX_VOPRF_CONTEXT_STR_LEN 205 206 // deriveInput || counter byte 207 let din_n: i64 = NX_OPQ_NSEED + 2 + info_n 208 let buf: *u8 = sys_mmap(din_n + 1) 209 var i: i64 = 0 210 while i < NX_OPQ_NSEED { buf[i] = seed_32[i]; i = i + 1 } 211 buf[NX_OPQ_NSEED] = ((info_n >> 8) & 0xFF) as u8 212 buf[NX_OPQ_NSEED + 1] = (info_n & 0xFF) as u8 213 var k: i64 = 0 214 while k < info_n { buf[NX_OPQ_NSEED + 2 + k] = info[k]; k = k + 1 } 215 216 let sk_limbs: *i64 = u256_alloc() 217 var counter: i64 = 0 218 var found: i64 = 0 219 while counter < 256 { 220 if found == 0 { 221 buf[din_n] = (counter & 0xFF) as u8 222 let rc: i64 = nx_opq_hash_to_scalar(buf, din_n + 1, dst, dst_n, out_sk_32) 223 if rc != NX_OPQ_OK { return rc } 224 u256_load_be(sk_limbs, out_sk_32) 225 if u256_is_zero(sk_limbs) == 0 { found = 1 } 226 } 227 counter = counter + 1 228 } 229 if found == 0 { return 0 - NX_OPQ_DERIVE_FAILED } 230 231 if (out_pk_33 as i64) != 0 { 232 let g: *P256Point = p256_point_alloc() 233 p256_point_load_g(g) 234 let pk_pt: *P256Point = p256_point_alloc() 235 p256_scalar_mul(pk_pt, sk_limbs, g) 236 return nx_voprf_serialize_element(pk_pt, out_pk_33) 237 } 238 return NX_OPQ_OK 239} 240 241// ===== DiffieHellman per RFC 9807 §6.4.1.2 (P-256) ================================================= 242// 243// out = SerializeElement(sk * Deserialize(pk_33)) -- 33-byte compressed shared point. 244 245func nx_opq_dh(sk_32: *u8, pk_33: *u8, out_33: *u8) -> i64 { 246 if (sk_32 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 247 if (pk_33 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 248 if (out_33 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 249 let peer: *P256Point = p256_point_alloc() 250 if nx_voprf_deserialize_element(pk_33, peer) != NX_VOPRF_OK { 251 return 0 - NX_OPQ_DH_FAILED 252 } 253 let sk_limbs: *i64 = u256_alloc() 254 u256_load_be(sk_limbs, sk_32) 255 if u256_is_zero(sk_limbs) == 1 { return 0 - NX_OPQ_DH_FAILED } 256 let shared: *P256Point = p256_point_alloc() 257 p256_scalar_mul(shared, sk_limbs, peer) 258 if p256_point_is_infinity(shared) == 1 { return 0 - NX_OPQ_DH_FAILED } 259 return nx_voprf_serialize_element(shared, out_33) 260} 261 262// ===== Expand-Label / Derive-Secret per RFC 9807 §6.4.2.1 ================================================= 263// 264// CustomLabel = I2OSP(Length, 2) || I2OSP(7 + len(label), 1) || "OPAQUE-" || label 265// || I2OSP(len(context), 1) || context 266// Expand-Label(secret, label, context, L) = Expand(secret, CustomLabel, L) 267 268func nx_opq_expand_label( 269 secret_32: *u8, 270 label: *u8, label_n: i64, 271 context: *u8, context_n: i64, 272 out_len: i64, out: *u8 273) -> i64 { 274 if (secret_32 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 275 if (label as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 276 if label_n < 1 { return 0 - NX_OPQ_BAD_INPUT } 277 if label_n > 248 { return 0 - NX_OPQ_BAD_INPUT } 278 if context_n < 0 { return 0 - NX_OPQ_BAD_INPUT } 279 if context_n > 255 { return 0 - NX_OPQ_BAD_INPUT } 280 if (out as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 281 282 let cl_n: i64 = 2 + 1 + 7 + label_n + 1 + context_n 283 let cl: *u8 = sys_mmap(cl_n + 8) 284 var pos: i64 = 0 285 cl[0] = ((out_len >> 8) & 0xFF) as u8 286 cl[1] = (out_len & 0xFF) as u8 287 cl[2] = ((7 + label_n) & 0xFF) as u8 288 pos = 3 289 let prefix: *u8 = "OPAQUE-" as *u8 290 var i: i64 = 0 291 while i < 7 { cl[pos + i] = prefix[i]; i = i + 1 } 292 pos = pos + 7 293 var j: i64 = 0 294 while j < label_n { cl[pos + j] = label[j]; j = j + 1 } 295 pos = pos + label_n 296 cl[pos] = (context_n & 0xFF) as u8 297 pos = pos + 1 298 var k: i64 = 0 299 while k < context_n { cl[pos + k] = context[k]; k = k + 1 } 300 pos = pos + context_n 301 302 if hkdf_expand(secret_32, cl, pos, out_len, out) != 0 { return 0 - NX_OPQ_DERIVE_FAILED } 303 return NX_OPQ_OK 304} 305 306// Derive-Secret(secret, label, transcript_hash_32) -> Nx bytes. 307func nx_opq_derive_secret( 308 secret_32: *u8, 309 label: *u8, label_n: i64, 310 transcript_hash: *u8, transcript_hash_n: i64, 311 out_32: *u8 312) -> i64 { 313 return nx_opq_expand_label(secret_32, label, label_n, 314 transcript_hash, transcript_hash_n, 315 NX_OPQ_NX, out_32) 316} 317 318// ===== KSF Stretch + randomized_password per RFC 9807 §5.2.3 ================================================= 319// 320// stretched = Stretch(oprf_output); randomized_password = Extract("", oprf_output || stretched). 321// ksf_mode IDENTITY exists ONLY so the RFC KAT (whose config is KSF=Identity) can gate the 322// surrounding protocol; production callers (nx_modern_auth_flow) pass ARGON2ID. 323 324func nx_opq_stretch( 325 oprf_output_32: *u8, 326 ksf_mode: i64, ksf_m_kib: i64, ksf_t: i64, 327 out_stretched_32: *u8 328) -> i64 { 329 if (oprf_output_32 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 330 if (out_stretched_32 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 331 if ksf_mode == NX_OPQ_KSF_IDENTITY { 332 var i: i64 = 0 333 while i < 32 { out_stretched_32[i] = oprf_output_32[i]; i = i + 1 } 334 return NX_OPQ_OK 335 } 336 if ksf_mode != NX_OPQ_KSF_ARGON2ID { return 0 - NX_OPQ_BAD_INPUT } 337 338 // Sizes are NAMED once and used by BOTH the map and the release below, so they cannot drift apart. 339 let mem_bytes: i64 = ksf_m_kib * NX_OPQ_KIB 340 let ctx: *NxArgon2idCtx = sys_mmap(NX_ARGON2ID_CTX_BYTES) as *NxArgon2idCtx 341 ctx.memory_blocks = sys_mmap(mem_bytes) 342 ctx.h0_buf = sys_mmap(NX_OPQ_A2_SMALL) 343 ctx.prepend_buf = sys_mmap(NX_OPQ_A2_PREPEND) 344 ctx.prev_buf = sys_mmap(NX_OPQ_A2_SMALL) 345 ctx.curr_buf = sys_mmap(NX_OPQ_A2_SMALL) 346 ctx.zero_block = sys_mmap(NX_OPQ_A2_BLOCK) 347 ctx.z_buf = sys_mmap(NX_OPQ_A2_BLOCK) 348 ctx.tmp_block = sys_mmap(NX_OPQ_A2_BLOCK) 349 ctx.addr_block = sys_mmap(NX_OPQ_A2_BLOCK) 350 ctx.final_block = sys_mmap(NX_OPQ_A2_BLOCK) 351 ctx.h0_input = sys_mmap(NX_OPQ_A2_BLOCK) 352 ctx.b2b_ctx = sys_mmap(NX_BLAKE2B_CTX_BYTES) as *NxBlake2b 353 ctx.b2b_buf = sys_mmap(NX_OPQ_A2_B2B) 354 ctx.b2b_sv = sys_mmap(NX_OPQ_A2_B2B) as *i64 355 ctx.b2b_sm = sys_mmap(NX_OPQ_A2_B2B) as *i64 356 ctx.g_r = sys_mmap(NX_OPQ_A2_BLOCK) as *i64 357 ctx.g_rs = sys_mmap(NX_OPQ_A2_BLOCK) as *i64 358 ctx.g_col = sys_mmap(NX_OPQ_A2_B2B) as *i64 359 360 let salt: *u8 = sys_mmap(NX_OPQ_KSF_SALT_BYTES) 361 var s: i64 = 0 362 while s < NX_OPQ_KSF_SALT_BYTES { salt[s] = 0 as u8; s = s + 1 } 363 364 let rc: i64 = nx_argon2id_hash(ctx, oprf_output_32, 32, 365 salt, NX_OPQ_KSF_SALT_BYTES, 366 1, 32, ksf_m_kib, ksf_t, 367 out_stretched_32) 368 // RELEASE THE KSF ARENA (2026-08-18, lane F box health). This function mapped ksf_m_kib KiB (19-64 MiB in 369 // production) plus ~10 KB of scratch PER CALL -- one call per login/register/recover -- and never freed a 370 // byte, so every login left tens of MiB of PASSWORD-DERIVED argon2 state resident (then swapped) for the 371 // daemon's whole life. WIPE FIRST, THEN UNMAP: munmap frees pages, it does not scrub what swap already holds, 372 // but a wiped page is never written back and a wiped arena-class buffer never resurfaces in a later 373 // allocation. Everything that saw secret-derived bytes is wiped; the >NXA_SMALL_MAX mappings are then 374 // released (sub-256 B buffers live in the syscall arena, where munmap is a documented no-op). 375 // out_stretched_32 is the ONLY output; nx_argon2id_hash retains no pointer into ctx. 376 _opq_wipe(ctx.memory_blocks, mem_bytes) 377 _opq_wipe(ctx.h0_buf, NX_OPQ_A2_SMALL); _opq_wipe(ctx.prepend_buf, NX_OPQ_A2_PREPEND) 378 _opq_wipe(ctx.prev_buf, NX_OPQ_A2_SMALL); _opq_wipe(ctx.curr_buf, NX_OPQ_A2_SMALL) 379 _opq_wipe(ctx.z_buf, NX_OPQ_A2_BLOCK); _opq_wipe(ctx.tmp_block, NX_OPQ_A2_BLOCK); _opq_wipe(ctx.addr_block, NX_OPQ_A2_BLOCK) 380 _opq_wipe(ctx.final_block, NX_OPQ_A2_BLOCK); _opq_wipe(ctx.h0_input, NX_OPQ_A2_BLOCK) 381 _opq_wipe(ctx.b2b_ctx as *u8, NX_BLAKE2B_CTX_BYTES); _opq_wipe(ctx.b2b_buf, NX_OPQ_A2_B2B) 382 _opq_wipe(ctx.b2b_sv as *u8, NX_OPQ_A2_B2B); _opq_wipe(ctx.b2b_sm as *u8, NX_OPQ_A2_B2B) 383 _opq_wipe(ctx.g_r as *u8, NX_OPQ_A2_BLOCK); _opq_wipe(ctx.g_rs as *u8, NX_OPQ_A2_BLOCK); _opq_wipe(ctx.g_col as *u8, NX_OPQ_A2_B2B) 384 sys_munmap(ctx.memory_blocks, mem_bytes) 385 sys_munmap(ctx.prepend_buf, NX_OPQ_A2_PREPEND) 386 sys_munmap(ctx.zero_block, NX_OPQ_A2_BLOCK); sys_munmap(ctx.z_buf, NX_OPQ_A2_BLOCK); sys_munmap(ctx.tmp_block, NX_OPQ_A2_BLOCK) 387 sys_munmap(ctx.addr_block, NX_OPQ_A2_BLOCK); sys_munmap(ctx.final_block, NX_OPQ_A2_BLOCK); sys_munmap(ctx.h0_input, NX_OPQ_A2_BLOCK) 388 sys_munmap(ctx.g_r as *u8, NX_OPQ_A2_BLOCK); sys_munmap(ctx.g_rs as *u8, NX_OPQ_A2_BLOCK) 389 if rc != NX_AR2_OK { return 0 - NX_OPQ_KSF_FAILED } 390 return NX_OPQ_OK 391} 392 393func nx_opq_randomized_password( 394 oprf_output_32: *u8, 395 stretched_32: *u8, 396 out_rwd_32: *u8 397) -> i64 { 398 if (oprf_output_32 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 399 if (stretched_32 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 400 if (out_rwd_32 as i64) == 0 { return 0 - NX_OPQ_BAD_INPUT } 401 let ikm: *u8 = sys_mmap(64) 402 var i: i64 = 0 403 while i < 32 { ikm[i] = oprf_output_32[i]; i = i + 1 } 404 var j: i64 = 0 405 while j < 32 { ikm[32 + j] = stretched_32[j]; j = j + 1 } 406 hkdf_extract(0 as *u8, 0, ikm, 64, out_rwd_32) 407 return NX_OPQ_OK 408}