Embedded SDK
Embedded SDK
cx_blake3.c
Go to the documentation of this file.
1 #ifdef HAVE_BLAKE3
2 
3 #include "cx_blake3_ref.h"
4 #include "cx_utils.h"
5 #include "cx_ram.h"
6 
7 #include <string.h>
8 
13 cx_err_t cx_blake3_init_default(cx_blake3_t *hash)
14 {
15  blake3_init_ctx(hash, IV, 0);
16  return CX_OK;
17 }
18 
22 cx_err_t cx_blake3_init_keyed(cx_blake3_t *hash, const uint8_t *key)
23 {
24  uint32_t key_words[BLAKE3_NB_OF_WORDS];
25  load_key_words(key, key_words);
26  blake3_init_ctx(hash, key_words, KEYED_HASH);
27  return CX_OK;
28 }
29 
35 cx_err_t cx_blake3_init_derive_key(cx_blake3_t *hash, const void *context, size_t context_len)
36 {
37  uint8_t context_key[BLAKE3_KEY_LEN];
38  uint32_t context_key_words[BLAKE3_NB_OF_WORDS];
39  cx_err_t error;
40 
41  // The context string context is hashed with the key words set to IV_0,...,IV_7
42  // The DERIVE_KEY_CONTEXT flag is set for every compression
43  // Then the key material context_key_words is hashed with the key words set to the
44  // the first 8 output words of the first stage (context_key)
45 
46  blake3_init_ctx(hash, IV, DERIVE_KEY_CONTEXT);
47  CX_CHECK(cx_blake3_update(hash, context, context_len));
48  CX_CHECK(cx_blake3_final(hash, context_key, BLAKE3_KEY_LEN));
49  load_key_words(context_key, context_key_words);
50  blake3_init_ctx(hash, context_key_words, DERIVE_KEY_MATERIAL);
51 end:
52  return error;
53 }
54 
55 cx_err_t cx_blake3_init(cx_blake3_t *hash,
56  uint8_t mode,
57  const uint8_t *key,
58  const void *context,
59  size_t context_len)
60 {
61  switch (mode) {
62  case 0:
63  cx_blake3_init_default(hash);
64  break;
65  case KEYED_HASH:
66  if (NULL == key) {
67  return CX_INVALID_PARAMETER;
68  }
69  cx_blake3_init_keyed(hash, key);
70  break;
71  case DERIVE_KEY_CONTEXT:
72  if ((NULL == context) || (!context_len)) {
73  return CX_INVALID_PARAMETER;
74  }
75  cx_blake3_init_derive_key(hash, context, context_len);
76  break;
77  default:
78  return CX_INVALID_PARAMETER;
79  }
80  return CX_OK;
81 }
82 
83 cx_err_t cx_blake3_update(cx_blake3_t *hash, const void *input, size_t input_len)
84 {
85  const uint8_t *input_bytes = (const uint8_t *) input;
86  size_t state_len;
87  size_t nb_bytes;
88 
89  if (!input_len) {
90  return CX_OK;
91  }
92 
93  state_len = BLAKE3_BLOCK_LEN * (hash->chunk).blocks_compressed + (hash->chunk).buffer_len;
94 
95  if (state_len > 0) {
96  nb_bytes = BLAKE3_CHUNK_LEN - state_len;
97  if (nb_bytes > input_len) {
98  nb_bytes = input_len;
99  }
100  blake3_state_update(&hash->chunk, input_bytes, nb_bytes);
101  input_bytes += nb_bytes;
102  input_len -= nb_bytes;
103 
104  if (input_len > 0) {
105  cx_blake3_state_out_t output = blake3_state_output(&hash->chunk);
106  uint8_t chunk_cv[32];
107  blake3_output_chain(&output, chunk_cv);
108  blake3_hasher_push_cv(hash, chunk_cv, (hash->chunk).t);
109  blake3_state_reset(&hash->chunk, hash->key, (hash->chunk).t + 1);
110  }
111  else {
112  return CX_OK;
113  }
114  }
115 
116  while (input_len > BLAKE3_CHUNK_LEN) {
117  size_t subtree_len;
118  uint64_t count;
119  uint64_t subtree_chunks;
120  subtree_len = 1ULL << highest_one(input_len | 1);
121  count = (hash->chunk).t * BLAKE3_CHUNK_LEN;
122 
123  while ((((uint64_t) (subtree_len - 1)) & count) != 0) {
124  subtree_len /= 2;
125  }
126 
127  subtree_chunks = subtree_len / BLAKE3_CHUNK_LEN;
128  if (subtree_len <= BLAKE3_CHUNK_LEN) {
129  cx_blake3_state_t chunk_state;
130  uint8_t cv[BLAKE3_OUT_LEN];
131  blake3_state_init(&chunk_state, hash->key, (hash->chunk).d);
132  chunk_state.t = (hash->chunk).t;
133  blake3_state_update(&chunk_state, input_bytes, subtree_len);
134  cx_blake3_state_out_t output = blake3_state_output(&chunk_state);
135  blake3_output_chain(&output, cv);
136  blake3_hasher_push_cv(hash, cv, chunk_state.t);
137  }
138  else {
139  uint8_t cv_pair[2 * BLAKE3_OUT_LEN];
140  blake3_compress_subtree_to_parent(
141  input_bytes, subtree_len, hash->key, (hash->chunk).t, (hash->chunk).d, cv_pair);
142  blake3_hasher_push_cv(hash, cv_pair, (hash->chunk).t);
143  blake3_hasher_push_cv(
144  hash, cv_pair + BLAKE3_OUT_LEN, (hash->chunk).t + (subtree_chunks / 2));
145  }
146  (hash->chunk).t += subtree_chunks;
147  input_bytes += subtree_len;
148  input_len -= subtree_len;
149  }
150 
151  if (input_len > 0) {
152  blake3_state_update(&hash->chunk, input_bytes, input_len);
153  blake3_hasher_merge_cv(hash, (hash->chunk).t);
154  }
155  return CX_OK;
156 }
157 
158 cx_err_t cx_blake3_final(cx_blake3_t *hash, uint8_t *output, size_t out_len)
159 {
160  if (!out_len) {
161  return CX_INVALID_PARAMETER;
162  }
163  cx_blake3_state_out_t chunk_out;
164 
165  // If the subtree stack is empty, then the current chunk is the root.
166  if (!hash->cv_stack_len) {
167  chunk_out = blake3_state_output(&hash->chunk);
168  blake3_output_root_bytes(&chunk_out, output, out_len);
169  return CX_OK;
170  }
171 
172  size_t cvs_remaining;
173  size_t state_len
174  = BLAKE3_BLOCK_LEN * (hash->chunk).blocks_compressed + (hash->chunk).buffer_len;
175  uint8_t parent_block[BLAKE3_BLOCK_LEN];
176 
177  if (state_len > 0) {
178  cvs_remaining = hash->cv_stack_len;
179  chunk_out = blake3_state_output(&hash->chunk);
180  }
181  else {
182  // There are always at least 2 CVs in the stack in this case.
183  cvs_remaining = hash->cv_stack_len - 2;
184  memcpy(chunk_out.input_cv, hash->key, BLAKE3_OUT_LEN);
185  memcpy(chunk_out.block, hash->cv_stack + cvs_remaining * BLAKE3_OUT_LEN, BLAKE3_BLOCK_LEN);
186  chunk_out.block_len = BLAKE3_BLOCK_LEN;
187  chunk_out.counter = 0;
188  chunk_out.d = (hash->chunk).d | PARENT;
189  }
190  while (cvs_remaining > 0) {
191  cvs_remaining -= 1;
192  memcpy(parent_block, hash->cv_stack + cvs_remaining * BLAKE3_OUT_LEN, BLAKE3_OUT_LEN);
193  blake3_output_chain(&chunk_out, parent_block + BLAKE3_OUT_LEN);
194  memcpy(chunk_out.input_cv, hash->key, BLAKE3_OUT_LEN);
195  memcpy(chunk_out.block, parent_block, BLAKE3_BLOCK_LEN);
196  chunk_out.block_len = BLAKE3_BLOCK_LEN;
197  chunk_out.counter = 0;
198  chunk_out.d = (hash->chunk).d | PARENT;
199  }
200  blake3_output_root_bytes(&chunk_out, output, out_len);
201 
202  return CX_OK;
203 }
204 
205 static bool cx_blake3_validate_context(const cx_blake3_t *hash)
206 {
207  if ((NULL == hash) || !hash->is_init) {
208  return false;
209  }
210  return true;
211 }
212 
213 cx_err_t cx_blake3(cx_blake3_t *hash,
214  uint8_t mode,
215  const void *input,
216  size_t input_len,
217  uint8_t *out,
218  size_t out_len)
219 {
220  cx_err_t error;
221  // Check the context
222  if (!cx_blake3_validate_context(hash)) {
223  return CX_INTERNAL_ERROR;
224  }
225  CX_CHECK(cx_blake3_update(hash, input, input_len));
226 
227  if (mode & LAST) {
228  CX_CHECK(cx_blake3_final(hash, out, out_len));
229  }
230 end:
231  return error;
232 }
233 
234 #endif // HAVE_BLAKE3
unsigned char uint8_t
Definition: usbd_conf.h:53