Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
blake3s.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#include "blake3s.hpp"
10
11namespace bb::stdlib {
12
13using namespace blake_util;
14
15/*
16 * Core Blake3s functions. These are similar to that of Blake2s except for a few
17 * constant parameters and fewer rounds.
18 *
19 */
20template <typename Builder>
21void Blake3s<Builder>::compress_pre(field_ct state[BLAKE3_STATE_SIZE],
22 const field_ct cv[BLAKE3_CV_WORDS],
23 const byte_array_ct& block,
24 uint8_t block_len,
25 uint8_t flags)
26{
27 field_ct block_words[BLAKE3_STATE_SIZE];
28 for (size_t i = 0; i < BLAKE3_STATE_SIZE; ++i) {
29 block_words[i] = field_ct(block.slice(i * 4, 4).reverse());
30 }
31
32 state[0] = cv[0];
33 state[1] = cv[1];
34 state[2] = cv[2];
35 state[3] = cv[3];
36 state[4] = cv[4];
37 state[5] = cv[5];
38 state[6] = cv[6];
39 state[7] = cv[7];
40 state[8] = field_ct(block.get_context(), uint256_t(IV[0]));
41 state[9] = field_ct(block.get_context(), uint256_t(IV[1]));
42 state[10] = field_ct(block.get_context(), uint256_t(IV[2]));
43 state[11] = field_ct(block.get_context(), uint256_t(IV[3]));
44 state[12] = field_ct(block.get_context(), 0);
45 state[13] = field_ct(block.get_context(), 0);
46 state[14] = field_ct(block.get_context(), uint256_t(block_len));
47 state[15] = field_ct(block.get_context(), uint256_t(flags));
48
49 for (size_t idx = 0; idx < 7; idx++) {
50 round_fn(state, block_words, idx, true);
51 }
52}
53
54template <typename Builder>
56 const byte_array_ct& block,
57 uint8_t block_len,
58 uint8_t flags)
59{
60 field_ct state[BLAKE3_STATE_SIZE];
61 compress_pre(state, cv, block, block_len, flags);
62
70 for (size_t i = 0; i < (BLAKE3_STATE_SIZE >> 1); i++) {
71 const auto lookup = plookup_read<Builder>::get_lookup_accumulators(BLAKE_XOR, state[i], state[i + 8], true);
72 cv[i] = lookup[ColumnIdx::C3][0];
73 }
74}
75
76template <typename Builder>
77void Blake3s<Builder>::compress_xof(const field_ct cv[BLAKE3_CV_WORDS],
78 const byte_array_ct& block,
79 uint8_t block_len,
80 uint8_t flags,
81 byte_array_ct& out)
82{
83 field_ct state[BLAKE3_STATE_SIZE];
84
85 compress_pre(state, cv, block, block_len, flags);
86
91 for (size_t i = 0; i < (BLAKE3_STATE_SIZE >> 1); i++) {
92 const auto lookup_1 = plookup_read<Builder>::get_lookup_accumulators(BLAKE_XOR, state[i], state[i + 8], true);
93 // byte_array(field, num_bytes) constructor adds range constraints for each byte
94 byte_array_ct out_bytes_1(lookup_1[ColumnIdx::C3][0], 4);
95 // Safe write: both out and out_bytes_1 are constrained
96 out.write_at(out_bytes_1.reverse(), i * 4);
97
98 const auto lookup_2 = plookup_read<Builder>::get_lookup_accumulators(BLAKE_XOR, state[i + 8], cv[i], true);
99 // byte_array(field, num_bytes) constructor adds range constraints for each byte
100 byte_array_ct out_bytes_2(lookup_2[ColumnIdx::C3][0], 4);
101 // Safe write: both out and out_bytes_2 are constrained
102 out.write_at(out_bytes_2.reverse(), (i + 8) * 4);
103 }
104}
105
106template <typename Builder>
108 const byte_array_ct& block,
109 uint8_t block_len,
110 uint8_t flags)
111{
112 byte_array_ct block_copy = block;
113 // Initialize output_t with all fields
114 output_t ret{ .input_cv = {}, .block = block_copy, .block_len = block_len, .flags = flags };
115
116 for (size_t i = 0; i < (BLAKE3_OUT_LEN >> 2); ++i) {
117 ret.input_cv[i] = input_cv[i];
118 }
119
120 return ret;
121}
122
123/*
124 * Blake3s wrapper functions.
125 *
126 */
127template <typename Builder> void Blake3s<Builder>::hasher_init(blake3_hasher* self)
128{
129 for (size_t i = 0; i < (BLAKE3_KEY_LEN >> 2); ++i) {
130 self->key[i] = field_ct(uint256_t(IV[i]));
131 self->cv[i] = field_ct(uint256_t(IV[i]));
132 }
133 // Create zero-filled constant buffer (no constraints needed)
134 self->buf = byte_array_ct::constant_padding(self->context, BLAKE3_BLOCK_LEN);
135 self->buf_len = 0;
136 self->blocks_compressed = 0;
137 self->flags = 0;
138}
139
140template <typename Builder>
141void Blake3s<Builder>::hasher_update(blake3_hasher* self, const byte_array_ct& input, size_t input_len)
142{
143 if (input_len == 0) {
144 return;
145 }
146
147 size_t start_counter = 0;
148 while (input_len > BLAKE3_BLOCK_LEN) {
149 compress_in_place(self->cv,
150 input.slice(start_counter, BLAKE3_BLOCK_LEN),
151 BLAKE3_BLOCK_LEN,
152 self->flags | maybe_start_flag(self));
153 self->blocks_compressed = static_cast<uint8_t>(self->blocks_compressed + 1);
154 start_counter += BLAKE3_BLOCK_LEN;
155 input_len -= BLAKE3_BLOCK_LEN;
156 }
157
158 size_t take = BLAKE3_BLOCK_LEN - ((size_t)self->buf_len);
159 if (take > input_len) {
160 take = input_len;
161 }
162 // Copy bytes from input to buf (input is constrained)
163 byte_array_ct input_slice = input.slice(start_counter, take);
164 self->buf.write_at(input_slice, self->buf_len);
165
166 self->buf_len = static_cast<uint8_t>(self->buf_len + (uint8_t)take);
167 input_len -= take;
168}
169
170template <typename Builder> void Blake3s<Builder>::hasher_finalize(const blake3_hasher* self, byte_array_ct& out)
171{
172 uint8_t block_flags = self->flags | maybe_start_flag(self) | CHUNK_END;
173 output_t output = make_output(self->cv, self->buf, self->buf_len, block_flags);
174
175 // Create zero-filled constant buffer for compress_xof output (no constraints needed)
176 byte_array_ct wide_buf = byte_array_ct::constant_padding(out.get_context(), BLAKE3_BLOCK_LEN);
177 compress_xof(output.input_cv, output.block, output.block_len, output.flags | ROOT, wide_buf);
178 // Extract the output bytes by slicing (propagates constraint status)
179 out = wide_buf.slice(0, BLAKE3_OUT_LEN);
180}
181
182template <typename Builder> byte_array<Builder> Blake3s<Builder>::hash(const byte_array_ct& input)
183{
184 BB_ASSERT(input.size() <= BLAKE3_CHUNK_LEN,
185 "Barretenberg does not support blake3s with input lengths greater than 1024 bytes.");
186
187 // Create zero-filled constant buffer for hasher (will be properly initialized by hasher_init)
188 Builder* ctx = input.get_context();
189 byte_array_ct buf = byte_array_ct::constant_padding(ctx, BLAKE3_BLOCK_LEN);
190
191 blake3_hasher hasher{
192 .key = {}, .cv = {}, .buf = buf, .buf_len = 0, .blocks_compressed = 0, .flags = 0, .context = ctx
193 };
194
195 hasher_init(&hasher);
196 hasher_update(&hasher, input, input.size());
197
198 // Create output buffer (constants)
199 byte_array_ct result = byte_array_ct::constant_padding(ctx, BLAKE3_OUT_LEN);
200 hasher_finalize(&hasher, result);
201 return result;
202}
203
204template class Blake3s<UltraCircuitBuilder>;
205template class Blake3s<MegaCircuitBuilder>;
206} // namespace bb::stdlib
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
static void hasher_init(blake3_hasher *self)
Definition blake3s.cpp:127
static output_t make_output(const field_ct input_cv[BLAKE3_CV_WORDS], const byte_array_ct &block, uint8_t block_len, uint8_t flags)
Definition blake3s.cpp:107
static byte_array_ct hash(const byte_array_ct &input)
Definition blake3s.cpp:182
static void compress_in_place(field_ct cv[BLAKE3_CV_WORDS], const byte_array_ct &block, uint8_t block_len, uint8_t flags)
Definition blake3s.cpp:55
static void hasher_finalize(const blake3_hasher *self, byte_array_ct &out)
Definition blake3s.cpp:170
static void compress_xof(const field_ct cv[BLAKE3_CV_WORDS], const byte_array_ct &block, uint8_t block_len, uint8_t flags, byte_array_ct &out)
Definition blake3s.cpp:77
static void compress_pre(field_ct state[BLAKE3_STATE_SIZE], const field_ct cv[BLAKE3_CV_WORDS], const byte_array_ct &block, uint8_t block_len, uint8_t flags)
Definition blake3s.cpp:21
static void hasher_update(blake3_hasher *self, const byte_array_ct &input, size_t input_len)
Definition blake3s.cpp:141
Represents a dynamic array of bytes in-circuit.
byte_array slice(size_t offset) const
Slice bytes from the byte array starting at offset. Does not add any constraints.
byte_array reverse() const
Reverse the bytes in the byte array.
byte_array & write_at(byte_array const &other, size_t index)
Overwrites this byte_array starting at index with the contents of other.
size_t size() const
Builder * get_context() const
static byte_array constant_padding(Builder *parent_context, size_t num_bytes, uint8_t value=0)
Builder * context
Definition field.hpp:56
uint8_t const * buf
Definition data_store.hpp:9
stdlib::field_t< Builder > field_ct
void round_fn(field_t< Builder > state[BLAKE_STATE_SIZE], field_t< Builder > msg[BLAKE_STATE_SIZE], size_t round, const bool which_blake=false)
field_ct cv[BLAKE3_CV_WORDS]
Definition blake3s.hpp:43
field_ct key[BLAKE3_CV_WORDS]
Definition blake3s.hpp:42
field_ct input_cv[BLAKE3_CV_WORDS]
Definition blake3s.hpp:52