barretenberg
Loading...
Searching...
No Matches
sponge.hpp
1#pragma once
2
3#include <array>
4#include <cstddef>
5#include <cstdint>
6#include <span>
7
8#include "barretenberg/numeric/uint256/uint256.hpp"
9
10namespace crypto {
11
26template <typename FF, size_t rate, size_t capacity, size_t t, typename Permutation> class FieldSponge {
27 public:
35 enum Mode {
36 ABSORB,
37 SQUEEZE,
38 };
39
40 // sponge state. t = rate + capacity. capacity = 1 field element (~256 bits)
41 std::array<FF, t> state;
42
43 // cached elements that have been absorbed.
44 std::array<FF, rate> cache;
45 size_t cache_size = 0;
46 Mode mode = Mode::ABSORB;
47
48 FieldSponge(FF domain_iv = 0)
49 {
50 for (size_t i = 0; i < rate; ++i) {
51 state[i] = 0;
52 }
53 state[rate] = domain_iv;
54 }
55
56 std::array<FF, rate> perform_duplex()
57 {
58 // zero-pad the cache
59 for (size_t i = cache_size; i < rate; ++i) {
60 cache[i] = 0;
61 }
62 // add the cache into sponge state
63 for (size_t i = 0; i < rate; ++i) {
64 state[i] += cache[i];
65 }
66 state = Permutation::permutation(state);
67 // return `rate` number of field elements from the sponge state.
68 std::array<FF, rate> output;
69 for (size_t i = 0; i < rate; ++i) {
70 output[i] = state[i];
71 }
72 return output;
73 }
74
75 void absorb(const FF& input)
76 {
77 if (mode == Mode::ABSORB && cache_size == rate) {
78 // If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache
79 perform_duplex();
80 cache[0] = input;
81 cache_size = 1;
82 } else if (mode == Mode::ABSORB && cache_size < rate) {
83 // If we're absorbing, and the cache is not full, add the input into the cache
84 cache[cache_size] = input;
85 cache_size += 1;
86 } else if (mode == Mode::SQUEEZE) {
87 // If we're in squeeze mode, switch to absorb mode and add the input into the cache.
88 // N.B. I don't think this code path can be reached?!
89 cache[0] = input;
90 cache_size = 1;
91 mode = Mode::ABSORB;
92 }
93 }
94
95 FF squeeze()
96 {
97 if (mode == Mode::SQUEEZE && cache_size == 0) {
98 // If we're in squeze mode and the cache is empty, there is nothing left to squeeze out of the sponge!
99 // Switch to absorb mode.
100 mode = Mode::ABSORB;
101 cache_size = 0;
102 }
103 if (mode == Mode::ABSORB) {
104 // If we're in absorb mode, apply sponge permutation to compress the cache, populate cache with compressed
105 // state and switch to squeeze mode. Note: this code block will execute if the previous `if` condition was
106 // matched
107 auto new_output_elements = perform_duplex();
108 mode = Mode::SQUEEZE;
109 for (size_t i = 0; i < rate; ++i) {
110 cache[i] = new_output_elements[i];
111 }
112 cache_size = rate;
113 }
114 // By this point, we should have a non-empty cache. Pop one item off the top of the cache and return it.
115 FF result = cache[0];
116 for (size_t i = 1; i < cache_size; ++i) {
117 cache[i - 1] = cache[i];
118 }
119 cache_size -= 1;
120 cache[cache_size] = 0;
121 return result;
122 }
123
132 template <size_t out_len, bool is_variable_length> static std::array<FF, out_len> hash_internal(std::span<FF> input)
133 {
134 size_t in_len = input.size();
135 const uint256_t iv = (static_cast<uint256_t>(in_len) << 64) + out_len - 1;
136 FieldSponge sponge(iv);
137
138 for (size_t i = 0; i < in_len; ++i) {
139 sponge.absorb(input[i]);
140 }
141
142 // In the case where the hash preimage is variable-length, we append `1` to the end of the input, to distinguish
143 // from fixed-length hashes. (the combination of this additional field element + the hash IV ensures
144 // fixed-length and variable-length hashes do not collide)
145 if constexpr (is_variable_length) {
146 sponge.absorb(1);
147 }
148
149 std::array<FF, out_len> output;
150 for (size_t i = 0; i < out_len; ++i) {
151 output[i] = sponge.squeeze();
152 }
153 return output;
154 }
155
156 template <size_t out_len> static std::array<FF, out_len> hash_fixed_length(std::span<FF> input)
157 {
158 return hash_internal<out_len, false>(input);
159 }
160 static FF hash_fixed_length(std::span<FF> input) { return hash_fixed_length<1>(input)[0]; }
161
162 template <size_t out_len> static std::array<FF, out_len> hash_variable_length(std::span<FF> input)
163 {
164 return hash_internal<out_len, true>(input);
165 }
166 static FF hash_variable_length(std::span<FF> input) { return hash_variable_length<1>(input)[0]; }
167};
168} // namespace crypto
Implements a cryptographic sponge over prime fields. Implements the sponge specification from the Com...
Definition: sponge.hpp:26
static std::array< FF, out_len > hash_internal(std::span< FF > input)
Use the sponge to hash an input string.
Definition: sponge.hpp:132
Mode
Defines what phase of the sponge algorithm we are in.
Definition: sponge.hpp:35
Definition: uint256.hpp:25
Definition: aes128.cpp:9