v / vlib / crypto / blake3 / blake3.v
364 lines · 297 sloc · 9.27 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1// Copyright (c) 2023 Kim Shrier. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4// Package blake3 implements the Blake3 cryptographic hash
5// as described in:
6// https://github.com/BLAKE3-team/BLAKE3-specs/blob/master/blake3.pdf
7// Version 20211102173700
8
9module blake3
10
11import encoding.binary
12
13// size256 is the size, in bytes, of a Blake3 256 checksum.
14pub const size256 = 32
15
16// key_length is the length, in bytes, of a Blake3 key
17pub const key_length = 32
18
19// block_size is the block size, in bytes, of the Blake3 hash functions.
20pub const block_size = 64
21
22// chunk_size is the chunk size, in bytes, of the Blake3 hash functions.
23// A chunk consists of 16 blocks.
24pub const chunk_size = 1024
25
26// G rotation constants
27const r1 = 16
28const r2 = 12
29const r3 = 8
30const r4 = 7
31
32// negative G rotation constants so we can rotate right.
33const nr1 = -1 * r1
34const nr2 = -1 * r2
35const nr3 = -1 * r3
36const nr4 = -1 * r4
37
38// initialization vector
39const iv = [
40 u32(0x6a09e667),
41 u32(0xbb67ae85),
42 u32(0x3c6ef372),
43 u32(0xa54ff53a),
44 u32(0x510e527f),
45 u32(0x9b05688c),
46 u32(0x1f83d9ab),
47 u32(0x5be0cd19),
48]
49
50// message word schedule permutations
51const sigma = [
52 [u8(0), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]!,
53 [u8(2), 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8]!,
54 [u8(3), 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1]!,
55 [u8(10), 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6]!,
56 [u8(12), 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4]!,
57 [u8(9), 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7]!,
58 [u8(11), 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13]!,
59]!
60
61// internal flags
62enum Flags as u32 {
63 chunk_start = 1 << 0
64 chunk_end = 1 << 1
65 parent = 1 << 2
66 root = 1 << 3
67 keyed_hash = 1 << 4
68 derive_key_context = 1 << 5
69 derive_key_material = 1 << 6
70}
71
72struct KeyWordsLengthError {
73 Error
74 length u32
75}
76
77fn (err KeyWordsLengthError) msg() string {
78 return 'key_words length, ${err.length} bytes, must be 8 32-bit words'
79}
80
81struct DigestFlagsError {
82 Error
83 flags u32
84}
85
86fn (err DigestFlagsError) msg() string {
87 lines := [
88 'Digest flags ${err.flags:08x} must either be 0 or have only one bit set.',
89 'legal bits are:',
90 ' keyed_hash ${u32(Flags.keyed_hash):08x}',
91 ' derive_key_context ${u32(Flags.derive_key_context):08x}',
92 ' derive_key_material ${u32(Flags.derive_key_material):08x}',
93 ]
94
95 return lines.join('\n')
96}
97
98// empty tree node
99struct Empty {}
100
101// parent node containing the propagated chaining value
102struct Node {
103 chaining_value []u32
104}
105
106fn (n Node) str() string {
107 return 'Node chaining_value: ${n.chaining_value[0]:08x} ${n.chaining_value[1]:08x} ${n.chaining_value[2]:08x} ${n.chaining_value[3]:08x} ${n.chaining_value[4]:08x} ${n.chaining_value[5]:08x} ${n.chaining_value[6]:08x} ${n.chaining_value[7]:08x}'
108}
109
110type TreeNode = Empty | Node
111
112// data needed to generate arbitrary amounts of hash output
113struct HashState {
114mut:
115 words []u32
116 chaining_value []u32
117 block_words []u32
118 block_len u32
119 flags u32
120}
121
122// Digest holds the state needed to compute a Blake3 hash
123struct Digest {
124 key_words []u32 // these form the initial chaining value of a chunk
125 flags u32 // only the keyed_hash, derive_key_context, or derive_key_material bits
126mut:
127 chunk_counter u64 // number of the next chunk to be created
128 input []u8 // unconsumed input
129 binary_edge []TreeNode // the right-hand edge of the binary tree
130}
131
132// Digest.new_hash initializes a Digest structure for a Blake3 hash
133pub fn Digest.new_hash() !Digest {
134 return Digest.new(iv, 0)
135}
136
137// Digest.new_keyed_hash initializes a Digest structure for a Blake3 keyed hash
138pub fn Digest.new_keyed_hash(key []u8) !Digest {
139 // treat the key bytes as little endian u32 values
140 mut key_words := []u32{len: 8}
141 for i in 0 .. 8 {
142 key_words[i] = binary.little_endian_u32_at(key, i * 4)
143 }
144
145 return Digest.new(key_words, u32(Flags.keyed_hash))
146}
147
148// Digest.new_derive_key_hash initializes a Digest structure for deriving a Blake3 key
149pub fn Digest.new_derive_key_hash(context []u8) !Digest {
150 mut context_digest := Digest.new(iv, u32(Flags.derive_key_context))!
151
152 context_digest.write(context)!
153 context_key := context_digest.checksum_internal(key_length)
154
155 // treat the context key bytes as little endian u32 values
156 mut key_words := []u32{len: 8}
157 for i in 0 .. 8 {
158 key_words[i] = binary.little_endian_u32_at(context_key, i * 4)
159 }
160
161 return Digest.new(key_words, u32(Flags.derive_key_material))
162}
163
164fn Digest.new(key_words []u32, flags u32) !Digest {
165 if key_words.len != 8 {
166 return KeyWordsLengthError{
167 length: u32(key_words.len)
168 }
169 }
170
171 // flags must be 0 for performing a blake3 hash. Other bits modify
172 // the type of hash performed. Only 1 of the keyed_hash,
173 // derive_key_context, or derive_key_material bits can be set.
174 // Having other bits or multiple bits set is invalid at the
175 // Digest level.
176 match flags {
177 u32(0) {}
178 u32(Flags.keyed_hash) {}
179 u32(Flags.derive_key_context) {}
180 u32(Flags.derive_key_material) {}
181 else {
182 return DigestFlagsError{
183 flags: flags
184 }
185 }
186 }
187
188 return Digest{
189 key_words: key_words
190 flags: flags
191 chunk_counter: 0
192 input: []u8{}
193 binary_edge: []TreeNode{}
194 }
195}
196
197// write adds bytes to the hash
198pub fn (mut d Digest) write(data []u8) ! {
199 // if no data is being added to the hash, just return.
200 if data.len == 0 {
201 return
202 }
203
204 d.input << data
205
206 // if we have more than 1024 bytes in the input,
207 // process it in chunks.
208
209 for d.input.len > chunk_size {
210 mut chunk := Chunk{}
211 words := chunk.process_input(d.input[..chunk_size], d.key_words, d.chunk_counter, d.flags,
212 false)
213
214 d.add_node(Node{ chaining_value: words[..8] }, 0)
215
216 d.chunk_counter += 1
217 d.input = d.input[chunk_size..]
218 }
219}
220
221// checksum finalizes the hash and returns the generated bytes.
222//
223// This is the point in the hashing operation that we need to know
224// how many bytes of hash to generate. Normally this is 32 but can
225// be any size up to 2**64.
226pub fn (mut d Digest) checksum(size u64) []u8 {
227 return d.checksum_internal(size)
228}
229
230fn (mut d Digest) checksum_internal(size u64) []u8 {
231 // process the last of the input
232 mut chunk := Chunk{}
233
234 root := d.chunk_counter == 0
235 mut words := chunk.process_input(d.input, d.key_words, d.chunk_counter, d.flags, root)
236
237 // starting with the current (last) chunk, work your way up to the
238 // top of the tree.
239
240 mut state := HashState{
241 words: words
242 chaining_value: chunk.chaining_value
243 block_words: chunk.block_words
244 block_len: chunk.block_len
245 flags: chunk.flags
246 }
247
248 mut right_node := Node{
249 chaining_value: words[..8].clone()
250 }
251
252 for i, left_node in d.binary_edge {
253 match left_node {
254 Empty {
255 // nothing to do here, just skip it
256 }
257 Node {
258 mut block_words := left_node.chaining_value.clone()
259 block_words << right_node.chaining_value
260
261 mut flags := d.flags | u32(Flags.parent)
262 flags |= if i == d.binary_edge.len - 1 { u32(Flags.root) } else { u32(0) }
263
264 words = f(d.key_words, block_words, u64(0), block_size, flags)
265
266 state.words = words
267 state.chaining_value = d.key_words
268 state.block_words = block_words
269 state.block_len = block_size
270 state.flags = flags
271
272 right_node = Node{
273 chaining_value: words[..8].clone()
274 }
275 }
276 }
277 }
278
279 return root_output_bytes(state, size)
280}
281
282fn root_output_bytes(state HashState, size u64) []u8 {
283 mut output := []u8{cap: int(size)}
284 mut bytes_needed := size
285
286 mut block_number := u64(0)
287 mut words := state.words.clone()
288
289 for bytes_needed > 0 {
290 for word in words {
291 mut hash_bytes := []u8{len: 4, cap: 4}
292 binary.little_endian_put_u32(mut hash_bytes, word)
293
294 for hash_byte in hash_bytes {
295 output << hash_byte
296 bytes_needed -= 1
297
298 if bytes_needed == 0 {
299 return output
300 }
301 }
302 }
303
304 block_number += 1
305 words = f(state.chaining_value, state.block_words, block_number, state.block_len,
306 state.flags)
307 }
308
309 return output
310}
311
312@[direct_array_access]
313fn (mut d Digest) add_node(node Node, level u8) {
314 // if we are above the highst level,
315 // just add the node at the top
316 if d.binary_edge.len == level {
317 d.binary_edge << node
318 return
319 }
320
321 edge_node := d.binary_edge[level]
322
323 match edge_node {
324 Empty {
325 d.binary_edge[level] = node
326 }
327 Node {
328 mut block_words := edge_node.chaining_value.clone()
329 block_words << node.chaining_value
330
331 words := f(d.key_words, block_words, u64(0), block_size, d.flags | u32(Flags.parent))
332 parent_node := Node{
333 chaining_value: words[..8]
334 }
335
336 d.binary_edge[level] = Empty{}
337
338 d.add_node(parent_node, level + 1)
339 }
340 }
341
342 return
343}
344
345// sum256 returns the Blake3 256 bit hash of the data.
346pub fn sum256(data []u8) []u8 {
347 mut d := Digest.new_hash() or { panic(err) }
348 d.write(data) or { panic(err) }
349 return d.checksum_internal(size256)
350}
351
352// sum_keyed256 returns the Blake3 256 bit keyed hash of the data.
353pub fn sum_keyed256(data []u8, key []u8) []u8 {
354 mut d := Digest.new_keyed_hash(key) or { panic(err) }
355 d.write(data) or { panic(err) }
356 return d.checksum_internal(size256)
357}
358
359// sum_derived_key256 returns the Blake3 256 bit derived key hash of the key material
360pub fn sum_derive_key256(context []u8, key_material []u8) []u8 {
361 mut d := Digest.new_derive_key_hash(context) or { panic(err) }
362 d.write(key_material) or { panic(err) }
363 return d.checksum_internal(size256)
364}
365