libxcks  0.1.0.1
blake3.hpp
Go to the documentation of this file.
1 /*
2  * libxcks
3  * Copyright (C) 2022 Julien Couot
4  *
5  * This program is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13  * License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
24 #ifndef INC_BLAKE3_HPP_3FD9634A_21F0_43D6_A445_D8860A200C6E
25 #define INC_BLAKE3_HPP_3FD9634A_21F0_43D6_A445_D8860A200C6E
26 
27 //---------------------------------------------------------------------------
28 #include "checksumex.hpp"
29 //---------------------------------------------------------------------------
30 
31 
32 namespace libxcks
33 {
56 class BLAKE3 final : public ChecksumEx
57 {
59  private:
60  enum class Flags : uint8_t
61  {
62  CHUNK_START = 1 << 0,
63  CHUNK_END = 1 << 1,
64  PARENT = 1 << 2,
65  ROOT = 1 << 3,
66  KEYED_HASH = 1 << 4,
67  DERIVE_KEY_CONTEXT = 1 << 5,
68  DERIVE_KEY_MATERIAL = 1 << 6,
69  };
70 
71  static constexpr size_t BLAKE3_KEY_LEN = 32;
72  static constexpr size_t BLAKE3_OUT_LEN = 32;
73  static constexpr size_t BLAKE3_BLOCK_LEN = 64;
74  static constexpr size_t BLAKE3_MAX_DEPTH = 54;
75  static constexpr size_t BLAKE3_CHUNK_LEN = 1024;
76  static constexpr uint32_t IV[8] = { UINT32_C(0x6A09E667), UINT32_C(0xBB67AE85),
77  UINT32_C(0x3C6EF372), UINT32_C(0xA54FF53A),
78  UINT32_C(0x510E527F), UINT32_C(0x9B05688C),
79  UINT32_C(0x1F83D9AB), UINT32_C(0x5BE0CD19) };
80  static constexpr uint8_t MSG_SCHEDULE[7][16] =
81  {
82  { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
83  { 2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8 },
84  { 3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1 },
85  { 10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6 },
86  { 12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4 },
87  { 9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7 },
88  { 11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13 },
89  };
90 
91  class Output final
92  {
93  private:
94  uint32_t input_cv[8];
95  uint64_t counter;
96  uint8_t block[BLAKE3_BLOCK_LEN];
97  uint8_t block_len;
98  uint8_t flags;
99 
100  public:
101  Output(const uint32_t input_cv[8], const uint8_t block[BLAKE3_BLOCK_LEN],
102  uint8_t block_len, uint64_t counter, uint8_t flags);
103  Output() = default;
104  //Output(const Output&) = default;
105  Output& operator=(const Output&) = default;
106  void chainingValue(uint8_t cv[32]) const;
107  void rootBytes(uint64_t seek, uint8_t *out, size_t out_len);
108  static Output parentOutput(const uint8_t block[BLAKE3_BLOCK_LEN],
109  const uint32_t key[8], uint8_t flags);
110  };
111 
112  class ChunkState final
113  {
114  private:
115  uint32_t cv[8];
116  uint64_t chunk_counter;
117  uint8_t buf[BLAKE3_BLOCK_LEN];
118  uint8_t buf_len;
119  uint8_t blocks_compressed;
120  uint8_t flags;
121 
122  private:
123  size_t fillBuf(const uint8_t* input, size_t input_len);
124  uint8_t maybeStartFlag() const;
125 
126  public:
127  uint8_t getFlags() const { return flags; }
128  uint64_t getChunkCounter() const { return chunk_counter; }
129  void setChunkCounter(const uint64_t chunk_counter) { this->chunk_counter = chunk_counter; }
130  void init(const uint32_t key[8], const uint8_t flags);
131  void reset(const uint32_t key[8], const uint64_t chunk_counter);
132  size_t len() const;
133  void update(const uint8_t* input, size_t input_len);
134  Output stateOutput() const;
135  };
136 
137  protected:
138  // State of computation between the single steps.
139  uint32_t key[8];
140  ChunkState chunk;
141  uint8_t cv_stack_len;
142  // The stack size is MAX_DEPTH + 1 because we do lazy merging. For example,
143  // with 7 chunks, we have 3 entries in the stack. Adding an 8th chunk
144  // requires a 4th entry, rather than merging everything down to 1, because we
145  // don't know whether more input is coming. This is different from how the
146  // reference implementation does things.
147  uint8_t cv_stack[(BLAKE3_MAX_DEPTH + 1) * BLAKE3_OUT_LEN];
148 
149  private:
150  static constexpr size_t simdDegree() { return 1; }
151  static unsigned int popcnt(uint64_t x);
152  static unsigned int highestOne(uint64_t x);
153  static uint64_t roundDownToPowerOf2(uint64_t x);
154  static uint32_t counterLow(uint64_t counter);
155  static uint32_t counterHigh(uint64_t counter);
156  static uint32_t load32(const void *src);
157  static uint32_t rotr32(const uint32_t w, const uint32_t c);
158  static size_t left_len(size_t content_len);
159  static void store32(void *dst, uint32_t w);
160  static void storeCvWords(uint8_t bytes_out[32], uint32_t cv_words[8]);
161  static void g(uint32_t *state, const size_t a, const size_t b,
162  const size_t c, const size_t d, const uint32_t x,
163  const uint32_t y);
164  static void roundFn(uint32_t state[16], const uint32_t *msg, size_t round);
165  static void compressPre(uint32_t state[16], const uint32_t cv[8],
166  const uint8_t block[BLAKE3_BLOCK_LEN],
167  uint8_t block_len, uint64_t counter, uint8_t flags);
168  static void compressInPlace(uint32_t cv[8], const uint8_t block[BLAKE3_BLOCK_LEN],
169  uint8_t block_len, uint64_t counter, uint8_t flags);
170  static void compressXof(const uint32_t cv[8],
171  const uint8_t block[BLAKE3_BLOCK_LEN],
172  uint8_t block_len, uint64_t counter,
173  uint8_t flags, uint8_t out[64]);
174  static void hashOne(const uint8_t *input, size_t blocks, const uint32_t key[8],
175  uint64_t counter, uint8_t flags, uint8_t flags_start,
176  uint8_t flags_end, uint8_t out[BLAKE3_OUT_LEN]);
177  static void hashMany(const uint8_t *const *inputs, size_t num_inputs,
178  size_t blocks, const uint32_t key[8], uint64_t counter,
179  bool increment_counter, uint8_t flags, uint8_t flags_start,
180  uint8_t flags_end, uint8_t *out);
181  static size_t compressChunksParallel(const uint8_t *input, size_t input_len,
182  const uint32_t key[8],
183  uint64_t chunk_counter, uint8_t flags,
184  uint8_t *out);
185  static size_t compressParentsParallel(const uint8_t *child_chaining_values,
186  size_t num_chaining_values,
187  const uint32_t key[8], uint8_t flags,
188  uint8_t *out);
189  static size_t compressSubtreeWide(const uint8_t *input, size_t input_len,
190  const uint32_t key[8],
191  uint64_t chunk_counter,
192  uint8_t flags, uint8_t *out);
193  static void compressSubtreeToParentNode(const uint8_t *input,
194  size_t input_len,
195  const uint32_t key[8],
196  uint64_t chunk_counter,
197  uint8_t flags,
198  uint8_t out[2 * BLAKE3_OUT_LEN]);
199 
200  protected:
201  void mergeCvStack(uint64_t total_len);
202  void pushCv(uint8_t new_cv[BLAKE3_OUT_LEN], uint64_t chunk_counter);
203  void finalizeSeek(uint64_t seek, uint8_t *out, size_t out_len);
205 
206  public:
211 
215  void reset() override;
216 
225  uint8_t* getValue(uint8_t* buffer) const override;
226 
234  size_t getSize() const override { return BLAKE3_OUT_LEN; }
235 
242  void update(const uint8_t* buf, size_t len) override;
243 
244  public:
250  std::string getName() const override
251  {
252  return getHashName();
253  }
254 
264  ChecksumAlgoId getID() const override
265  {
266  return getIdentifier();
267  }
268 
274  static std::string getHashName()
275  {
276  return "BLAKE3";
277  }
278 
288  static constexpr ChecksumAlgoId getIdentifier()
289  {
290  return ChecksumAlgoId::BLAKE3;
291  }
292 
302  {
303  return new BLAKE3();
304  }
305 };
306 //---------------------------------------------------------------------------
307 } // namespace libxcks
308 //---------------------------------------------------------------------------
309 
310 #endif // INC_BLAKE3_HPP_3FD9634A_21F0_43D6_A445_D8860A200C6E
Add some utilities to Checksum class.
Computes the BLAKE3 hash from a byte stream.
Definition: blake3.hpp:57
uint8_t * getValue(uint8_t *buffer) const override
Returns the BLAKE3 hash value in the first 32 bytes of the given address.
static constexpr ChecksumAlgoId getIdentifier()
Returns an unique identifier for the hash algorithm.
Definition: blake3.hpp:288
std::string getName() const override
Returns the name of the checksum or the hash algorithm.
Definition: blake3.hpp:250
void reset() override
Resets the BLAKE3 hash to initial state of computation.
BLAKE3()
Default constructor.
static Checksum * getNewInstance()
Gets a new instance of this class.
Definition: blake3.hpp:301
void update(const uint8_t *buf, size_t len) override
Updates the BLAKE3 hash with specified array of bytes.
size_t getSize() const override
Returns the minimal size to allocate in memory to store the hash with the getValue(buffer) method.
Definition: blake3.hpp:234
static std::string getHashName()
Returns the name of the hash algorithm.
Definition: blake3.hpp:274
ChecksumAlgoId getID() const override
Returns an unique identifier for the checksum or the hash algorithm.
Definition: blake3.hpp:264
Computes a hash from a byte stream.
Definition: checksumex.hpp:72
Computes a checksum from a byte stream.
Definition: checksum.hpp:54
ChecksumAlgoId
Ids of algorithms of checksums.
Definition: types.hpp:65