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}