code wiki / (root) / nx_argon2id_b0_bisect.nx

nx_argon2id_b0_bisect.nx source

↩ module page · 104 lines · 4079 B

1// nx_argon2id_b0_bisect.nx -- bisect step 2: verify B[0] derivation. 2// 3// expect_exit: 0 4// 5// Expected B[0] first 32 bytes (computed via Python implementing 6// RFC 9106 sec 3.4 H' on the known H_0): 7// dd59a5e70e1b91a4 1038690fb83c433d d014fc81c93b6b62 cad0e5f58d797780 8// 9// license_tier: ORIGINAL 10 11import "nx_syscalls_x86_64.nx" 12import "nx_blake2b.nx" 13import "nx_blake2b_long.nx" 14import "nx_argon2_block.nx" 15import "nx_argon2id.nx" 16const K_MAGIC_1024: i64 = 1024 17const K_MAGIC_2048: i64 = 2048 18 19func make_ctx() -> *NxArgon2idCtx { 20 let raw: *u8 = sys_mmap(NX_ARGON2ID_CTX_BYTES) 21 let ctx: *NxArgon2idCtx = raw as *NxArgon2idCtx 22 ctx.memory_blocks = sys_mmap(8 * K_MAGIC_1024) 23 ctx.h0_buf = sys_mmap(64) 24 ctx.prepend_buf = sys_mmap(K_MAGIC_2048) 25 ctx.prev_buf = sys_mmap(64) 26 ctx.curr_buf = sys_mmap(64) 27 ctx.zero_block = sys_mmap(K_MAGIC_1024) 28 ctx.z_buf = sys_mmap(K_MAGIC_1024) 29 ctx.tmp_block = sys_mmap(K_MAGIC_1024) 30 ctx.addr_block = sys_mmap(K_MAGIC_1024) 31 ctx.final_block = sys_mmap(K_MAGIC_1024) 32 ctx.h0_input = sys_mmap(K_MAGIC_2048) 33 ctx.b2b_ctx = sys_mmap(NX_BLAKE2B_CTX_BYTES) as *NxBlake2b 34 ctx.b2b_buf = sys_mmap(128) 35 ctx.b2b_sv = sys_mmap(128) as *i64 36 ctx.b2b_sm = sys_mmap(128) as *i64 37 ctx.g_r = sys_mmap(K_MAGIC_1024) as *i64 38 ctx.g_rs = sys_mmap(K_MAGIC_1024) as *i64 39 ctx.g_col = sys_mmap(128) as *i64 40 return ctx 41} 42 43func main() -> i64 { 44 let ctx: *NxArgon2idCtx = make_ctx() 45 let password: *u8 = "password" as *u8 46 let salt: *u8 = "somesalt" as *u8 47 48 // Step 1: H_0 49 if _ar2_h0(ctx, password, 8, salt, 8, 1, 32, 8, 2) != NX_BLAKE2B_OK { return 1 } 50 51 // Step 2: build H_0 || le32(0) || le32(0) into h0_input 52 // (mirroring what nx_argon2id_hash does) 53 var k: i64 = 0 54 while k < 64 { ctx.h0_input[k] = ctx.h0_buf[k]; k = k + 1 } 55 ctx.h0_input[64] = 0 as u8; ctx.h0_input[65] = 0 as u8 56 ctx.h0_input[66] = 0 as u8; ctx.h0_input[67] = 0 as u8 57 ctx.h0_input[68] = 0 as u8; ctx.h0_input[69] = 0 as u8 58 ctx.h0_input[70] = 0 as u8; ctx.h0_input[71] = 0 as u8 59 60 // Compute B[0] = H'(1024, h0_input[0..72]) 61 let b0_ptr: *u8 = ((ctx.memory_blocks as i64) + 0) as *u8 62 if nx_blake2b_long(ctx.h0_input, 72, 63 b0_ptr, K_MAGIC_1024, 64 ctx.prepend_buf, ctx.prev_buf, ctx.curr_buf, 65 ctx.b2b_ctx, ctx.b2b_buf, 66 ctx.b2b_sv, ctx.b2b_sm) != NX_B2BL_OK { return 2 } 67 68 // Check last 32 bytes (the H' fix specifically affects bytes 992..1023). 69 // Expected last 32 bytes from C reference: FF 2C B3 DC 3A 3A B3 E8 ... 70 // From the libargon2 dump captured 2026-05-17: 71 // B[0] last 32 = ff2cb3dc3a3ab3e8... (was wrong with off-by-one H') 72 // 73 // Rather than hardcode all 32 expected bytes here (and have to keep 74 // the test in sync with reference), this bisect now compares first 75 // AND last 8 bytes -- a sentinel pair that fails BOTH if H' loop 76 // bounds were wrong (last bytes) AND if H' input encoding was wrong 77 // (first bytes). 78 let expected_first8: *u8 = sys_mmap(8) 79 expected_first8[0] = 221 as u8; expected_first8[1] = 89 as u8 80 expected_first8[2] = 165 as u8; expected_first8[3] = 231 as u8 81 expected_first8[4] = 14 as u8; expected_first8[5] = 27 as u8 82 expected_first8[6] = 145 as u8; expected_first8[7] = 164 as u8 83 84 var i: i64 = 0 85 while i < 8 { 86 if b0_ptr[i] != expected_first8[i] { return 100 + i } 87 i = i + 1 88 } 89 90 // Last 8 bytes from Python pyref (verified byte-exact against 91 // libargon2) for B[0] at offset 1016..1023: 1e 37 ff 6e a1 39 19 3a. 92 let expected_last8: *u8 = sys_mmap(8) 93 expected_last8[0] = 30 as u8; expected_last8[1] = 55 as u8 94 expected_last8[2] = 255 as u8; expected_last8[3] = 110 as u8 95 expected_last8[4] = 161 as u8; expected_last8[5] = 57 as u8 96 expected_last8[6] = 25 as u8; expected_last8[7] = 58 as u8 97 98 i = 0 99 while i < 8 { 100 if b0_ptr[1016 + i] != expected_last8[i] { return 200 + i } 101 i = i + 1 102 } 103 return 0 104}