code wiki / (root) / nx_blake2b_long.nx

nx_blake2b_long.nx source

↩ module page · 192 lines · 7254 B

1// nx_blake2b_long.nx -- RFC 9106 sec 3.4 variable-length BLAKE2b H'. 2// 3// Argon2 needs hash outputs LONGER than BLAKE2b's 64-byte max (each 4// memory block is 1024 bytes). H' chains BLAKE2b calls to extend 5// the output to any T in [1, 2^32 - 1] bytes. 6// 7// Per cardinal feedback-bits-up-exceed-never-match: substrate's own 8// H' chained on substrate's own BLAKE2b; nothing imported from 9// argon2_ref / argon2_optimized. 10// 11// ===== Algorithm (RFC 9106 sec 3.4) ============================== 12// 13// H'(T, A) where T is output length in bytes, A is the input 14// message of arbitrary length. 15// 16// Phase 1: prepend the 4-byte little-endian length T to A: 17// input_prime = le32(T) || A 18// 19// Phase 2: 20// if T <= 64: 21// return BLAKE2b(input_prime, out_len = T) 22// else: 23// V_1 = BLAKE2b(input_prime, out_len = 64) (64 bytes) 24// r = ceil(T / 32) - 2 (number 25// of 32-byte 26// full chunks 27// beyond V_1) 28// output[0..31] = V_1[0..31] 29// for i = 2 .. r + 1: 30// V_i = BLAKE2b(V_{i-1}, out_len = 64) (64 bytes) 31// output[(i-1)*32 .. i*32 - 1] = V_i[0..31] 32// V_last = BLAKE2b(V_{r+1}, out_len = T - 32*r) 33// output[(r+1)*32 .. T - 1] = V_last 34// 35// Note: r counts the number of full V_2..V_{r+1} contributions of 36// 32 bytes; V_1 contributes the FIRST 32 bytes; V_last (variable 37// length T - 32*r) contributes the tail (which is ALSO 32 bytes 38// when T is a multiple of 32, or whatever remainder otherwise). 39// 40// The H' construction is what gives Argon2 its 1024-byte memory 41// blocks: H'(1024, derivation_input) generates the first block 42// of each lane. 43// 44// ===== Caller-owns-memory pattern ================================ 45// 46// No sys_mmap inside the library. Caller passes: 47// - prepend_buf: u8 buffer of size >= input_len + 4 (for le32(T) || A) 48// - prev: u8 buffer of size 64 (holds V_{i-1}) 49// - curr: u8 buffer of size 64 (holds V_i) 50// - ctx + buf128 + scratch_v + scratch_m: BLAKE2b's caller-owned set 51// 52// license_tier: ORIGINAL 53 54// nx_safety_envelope: 55// intended_use: variable-length BLAKE2b hash for Argon2 56// sil_target: SIL2 (algorithm mature; verified against 57// internal-consistency of BLAKE2b core) 58// evidence: [rfc_9106_sec_3_4_internal_consistency, 59// boundary_at_T_eq_64, 60// boundary_at_T_eq_65, 61// T_eq_128_two_chunk_check] 62// verdict: NOT_YET_EVALUATED 63 64import "nx_blake2b.nx" 65 66// ===== Verdict ==================================================== 67 68const NX_B2BL_OK: i64 = 0 69const NX_B2BL_BAD_OUT: i64 = 1 70const NX_B2BL_BAD_ARG: i64 = 2 71const NX_B2BL_VERDICT_N: i64 = 3 72 73func nx_b2bl_verdict_name(v: i64) -> *u8 { 74 if v == NX_B2BL_OK { return "OK" } 75 if v == NX_B2BL_BAD_OUT { return "BAD_OUT" } 76 if v == NX_B2BL_BAD_ARG { return "BAD_ARG" } 77 return "UNKNOWN" 78} 79 80// ===== Helpers ==================================================== 81 82// Write le32(T) into buf[off..off+3]. 83func _b2bl_write_le32(buf: *u8, off: i64, v: i64) -> i64 { 84 buf[off + 0] = (v & 255) as u8 85 buf[off + 1] = ((v >> 8) & 255) as u8 86 buf[off + 2] = ((v >> 16) & 255) as u8 87 buf[off + 3] = ((v >> 24) & 255) as u8 88 return 0 89} 90 91func _b2bl_copy(dst: *u8, doff: i64, src: *u8, soff: i64, n: i64) -> i64 { 92 var i: i64 = 0 93 while i < n { 94 dst[doff + i] = src[soff + i] 95 i = i + 1 96 } 97 return 0 98} 99 100// ===== Entry point ================================================ 101// 102// nx_blake2b_long(input, input_len, out, out_len, 103// prepend_buf, prev, curr, 104// ctx, buf128, scratch_v, scratch_m) -> verdict 105// 106// Required buffer sizes: 107// prepend_buf >= input_len + 4 108// prev >= 64 109// curr >= 64 110// ctx == NX_BLAKE2B_CTX_BYTES 111// buf128 == 128 112// scratch_v >= 16 i64 113// scratch_m >= 16 i64 114 115func nx_blake2b_long(input: *u8, input_len: i64, 116 out: *u8, out_len: i64, 117 prepend_buf: *u8, 118 prev: *u8, curr: *u8, 119 ctx: *NxBlake2b, buf128: *u8, 120 scratch_v: *i64, scratch_m: *i64) -> i64 { 121 if out_len < 1 { return NX_B2BL_BAD_OUT } 122 if input_len < 0 { return NX_B2BL_BAD_ARG } 123 if input_len > 0 { 124 if input == (0 as *u8) { return NX_B2BL_BAD_ARG } 125 } 126 if out == (0 as *u8) { return NX_B2BL_BAD_ARG } 127 if prepend_buf == (0 as *u8) { return NX_B2BL_BAD_ARG } 128 if prev == (0 as *u8) { return NX_B2BL_BAD_ARG } 129 if curr == (0 as *u8) { return NX_B2BL_BAD_ARG } 130 131 // Build le32(out_len) || input in prepend_buf. 132 _b2bl_write_le32(prepend_buf, 0, out_len) 133 var i: i64 = 0 134 while i < input_len { 135 prepend_buf[4 + i] = input[i] 136 i = i + 1 137 } 138 let pre_len: i64 = 4 + input_len 139 140 if out_len <= 64 { 141 // Single BLAKE2b call. 142 return nx_blake2b_hash(prepend_buf, pre_len, 143 0 as *u8, 0, 144 out, out_len, 145 ctx, buf128, 146 scratch_v, scratch_m) 147 } 148 149 // Multi-block: V_1 = BLAKE2b(prepend_buf, 64) 150 let v1: i64 = nx_blake2b_hash(prepend_buf, pre_len, 151 0 as *u8, 0, 152 prev, 64, 153 ctx, buf128, 154 scratch_v, scratch_m) 155 if v1 != NX_BLAKE2B_OK { return NX_B2BL_BAD_ARG } 156 157 // Write first 32 bytes of V_1 into out. 158 _b2bl_copy(out, 0, prev, 0, 32) 159 160 // r = ceil(out_len / 32) - 2 161 let r: i64 = ((out_len + 31) / 32) - 2 162 163 // RFC 9106 sec 3.4: Loop V_i = BLAKE2b(V_{i-1}, 64) for i = 2..r 164 // (INCLUSIVE). Each V_i contributes 32 bytes at out[(i-1)*32 .. i*32-1]. 165 // Earlier version looped i = 2..r+1 (off-by-one) and used 32-byte 166 // tail instead of full T-32*r tail; broke Argon2id cross-val at 167 // byte 992 of B[0] for T=1024. 168 var ii: i64 = 2 169 while ii <= r { 170 let vv: i64 = nx_blake2b_hash(prev, 64, 171 0 as *u8, 0, 172 curr, 64, 173 ctx, buf128, 174 scratch_v, scratch_m) 175 if vv != NX_BLAKE2B_OK { return NX_B2BL_BAD_ARG } 176 _b2bl_copy(out, (ii - 1) * 32, curr, 0, 32) 177 _b2bl_copy(prev, 0, curr, 0, 64) 178 ii = ii + 1 179 } 180 181 // Final: V_{r+1} = BLAKE2b(V_r, out_len - 32*r); FULL tail length. 182 let tail_len: i64 = out_len - 32 * r 183 let vlast: i64 = nx_blake2b_hash(prev, 64, 184 0 as *u8, 0, 185 curr, tail_len, 186 ctx, buf128, 187 scratch_v, scratch_m) 188 if vlast != NX_BLAKE2B_OK { return NX_B2BL_BAD_ARG } 189 _b2bl_copy(out, r * 32, curr, 0, tail_len) 190 191 return NX_B2BL_OK 192}