Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
hmac.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Completed, auditors: [Federico], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
8
11#include <algorithm>
12#include <array>
13#include <cstdint>
14#include <string>
15#include <vector>
16
17namespace bb::crypto {
18inline void secure_erase_bytes(void* ptr, size_t size)
19{
20 volatile uint8_t* p = static_cast<volatile uint8_t*>(ptr);
21 while (size-- > 0) {
22 *p++ = 0;
23 }
24}
25
26template <typename T, size_t N> inline void secure_erase(std::array<T, N>& buffer)
27{
28 secure_erase_bytes(buffer.data(), buffer.size() * sizeof(T));
29}
30
31template <typename T> inline void secure_erase(std::vector<T>& buffer)
32{
33 if (!buffer.empty()) {
34 secure_erase_bytes(buffer.data(), buffer.size() * sizeof(T));
35 }
36}
37
48template <typename Hash, typename MessageContainer, typename KeyContainer>
49std::array<uint8_t, Hash::OUTPUT_SIZE> hmac(const MessageContainer& message, const KeyContainer& key)
50{
51 constexpr size_t B = Hash::BLOCK_SIZE;
52 // ensures truncated_key fits into k_prime
53 static_assert(Hash::OUTPUT_SIZE <= B);
54 constexpr uint8_t IPAD_CONST = 0x36;
55 constexpr uint8_t OPAD_CONST = 0x5c;
58 ipad.fill(IPAD_CONST);
59 opad.fill(OPAD_CONST);
60
61 // initialize k_prime to 0x00,...,0x00
62 // copy key or truncated key to start.
63 std::array<uint8_t, B> k_prime{};
64 if (key.size() > B) {
65 std::vector<uint8_t> key_buffer(key.begin(), key.end());
66 auto truncated_key = Hash::hash(key_buffer);
67 std::copy(truncated_key.begin(), truncated_key.end(), k_prime.begin());
68 secure_erase(key_buffer);
69 secure_erase(truncated_key);
70 } else {
71 std::copy(key.begin(), key.end(), k_prime.begin());
72 }
73
75 for (size_t i = 0; i < B; ++i) {
76 h1[i] = k_prime[i] ^ opad[i];
77 }
78
80 for (size_t i = 0; i < B; ++i) {
81 h2[i] = k_prime[i] ^ ipad[i];
82 }
83 secure_erase(k_prime);
84
85 std::vector<uint8_t> message_buffer;
86 message_buffer.reserve(B + message.size());
87 std::copy(h2.begin(), h2.end(), std::back_inserter(message_buffer));
88 std::copy(message.begin(), message.end(), std::back_inserter(message_buffer));
89
90 auto h3 = Hash::hash(message_buffer);
91 secure_erase(h2);
92 secure_erase(message_buffer);
93
94 std::vector<uint8_t> hmac_buffer;
95 hmac_buffer.reserve(B + Hash::OUTPUT_SIZE);
96 std::copy(h1.begin(), h1.end(), std::back_inserter(hmac_buffer));
97 std::copy(h3.begin(), h3.end(), std::back_inserter(hmac_buffer));
98
99 auto hmac_key = Hash::hash(hmac_buffer);
100 secure_erase(h1);
101 secure_erase(h3);
102 secure_erase(hmac_buffer);
103
105 std::copy(hmac_key.begin(), hmac_key.end(), result.begin());
106 secure_erase(hmac_key);
107 return result;
108}
109
121template <typename Hash, typename Fr, typename MessageContainer, typename KeyContainer>
122Fr deterministic_nonce_rfc6979(const MessageContainer& message, const KeyContainer& key)
123 requires(Hash::OUTPUT_SIZE == 32)
124{
125 using serialize::read;
126 using serialize::write;
127
128 static_assert(Hash::OUTPUT_SIZE == 32,
129 "Hash output size must be 32 bytes for our implementation of RFC6979 nonce generation");
130 static constexpr size_t INITIAL_BUFFER_SIZE = 32; // Equal to 8 * (Hash::OUTPUT_SIZE + 7/ 8)
131 static constexpr size_t MODULUS_BIT_LENGTH = Fr::modulus.get_msb() + 1;
132
133 // Hash the message
134 std::vector<uint8_t> message_buffer(message.begin(), message.end());
135 auto hashed_message = Hash::hash(message_buffer);
136 secure_erase(message_buffer);
137 // Round trip reduces the hash modulo Fr::modulus
138 Fr hashed_message_fr = Fr::serialize_from_buffer(hashed_message.data());
139 hashed_message = {};
140 Fr::serialize_to_buffer(hashed_message_fr, &hashed_message[0]);
141
142 // Concatenate the private key and the hashed message
143 std::vector<uint8_t> seed_material;
144 seed_material.reserve(key.size() + hashed_message.size());
145 std::ranges::copy(key, std::back_inserter(seed_material));
146 std::ranges::copy(hashed_message, std::back_inserter(seed_material));
147 secure_erase(hashed_message);
148
149 // Initialize the buffers V and K
151 v_buffer.fill(0x01);
153 key_buffer.fill(0x00);
154
155 // Temporary buffer for first HMAC round: V || 0x00 || seed_material
156 std::vector<uint8_t> tmp_buffer;
157 tmp_buffer.reserve(INITIAL_BUFFER_SIZE + 1 + seed_material.size());
158 std::ranges::copy(v_buffer, std::back_inserter(tmp_buffer));
159 tmp_buffer.emplace_back(0x00);
160 std::ranges::copy(seed_material, std::back_inserter(tmp_buffer));
161
162 // First HMAC round: K = HMAC(K, V || 0x00 || seed_material), V = HMAC(K, V)
163 key_buffer = hmac<Hash, std::vector<uint8_t>, std::array<uint8_t, INITIAL_BUFFER_SIZE>>(tmp_buffer, key_buffer);
164 v_buffer = hmac<Hash, std::array<uint8_t, INITIAL_BUFFER_SIZE>, std::array<uint8_t, INITIAL_BUFFER_SIZE>>(
165 v_buffer, key_buffer);
166
167 // Temporary buffer for second HMAC round: V || 0x01 || seed_material
168 tmp_buffer.clear();
169 tmp_buffer.reserve(INITIAL_BUFFER_SIZE + 1 + seed_material.size());
170 std::ranges::copy(v_buffer, std::back_inserter(tmp_buffer));
171 tmp_buffer.emplace_back(0x01);
172 std::ranges::copy(seed_material, std::back_inserter(tmp_buffer));
173
174 // Second HMAC round: K = HMAC(K, V || 0x01 || seed_material), V = HMAC(K, V)
175 key_buffer = hmac<Hash, std::vector<uint8_t>, std::array<uint8_t, INITIAL_BUFFER_SIZE>>(tmp_buffer, key_buffer);
176 v_buffer = hmac<Hash, std::array<uint8_t, INITIAL_BUFFER_SIZE>, std::array<uint8_t, INITIAL_BUFFER_SIZE>>(
177 v_buffer, key_buffer);
178
179 // Loop until we get a valid k: 0 < k < Fr::modulus
180 uint256_t k = 0;
181 while (k == 0 || k >= static_cast<uint256_t>(Fr::modulus)) {
182 v_buffer = hmac<Hash, std::array<uint8_t, INITIAL_BUFFER_SIZE>, std::array<uint8_t, INITIAL_BUFFER_SIZE>>(
183 v_buffer, key_buffer);
184
185 // Trim the output if required
186 if (Hash::OUTPUT_SIZE * 8 > MODULUS_BIT_LENGTH) {
187 std::vector<uint8_t> trimmed_v_buffer(v_buffer.begin(), v_buffer.end());
188 // Read the hash output
189 const uint8_t* ptr = trimmed_v_buffer.data();
190 uint256_t trimmed_v;
191 read(ptr, trimmed_v);
192
193 // Bit shift the output
194 trimmed_v = trimmed_v >> (Hash::OUTPUT_SIZE * 8 - MODULUS_BIT_LENGTH);
195
196 // Set k
197 k = trimmed_v;
198 } else {
199 const uint8_t* ptr = v_buffer.data();
200 read(ptr, k);
201 }
202
203 if ((k > 0) && (k < static_cast<uint256_t>(Fr::modulus))) {
204 break;
205 }
206
207 std::vector<uint8_t> tmp_buffer;
208 tmp_buffer.reserve(INITIAL_BUFFER_SIZE + 1);
209 std::ranges::copy(v_buffer, std::back_inserter(tmp_buffer));
210 tmp_buffer.emplace_back(0x00);
211 key_buffer = hmac<Hash, std::vector<uint8_t>, std::array<uint8_t, INITIAL_BUFFER_SIZE>>(tmp_buffer, key_buffer);
212 secure_erase(tmp_buffer);
213 v_buffer = hmac<Hash, std::array<uint8_t, INITIAL_BUFFER_SIZE>, std::array<uint8_t, INITIAL_BUFFER_SIZE>>(
214 v_buffer, key_buffer);
215 }
216
217 secure_erase(seed_material);
218 secure_erase(tmp_buffer);
219 secure_erase(v_buffer);
220 secure_erase(key_buffer);
221
222 return Fr(k);
223}
224} // namespace bb::crypto
constexpr uint64_t get_msb() const
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:50
std::array< uint8_t, Hash::OUTPUT_SIZE > hmac(const MessageContainer &message, const KeyContainer &key)
Compute an HMAC given a secret key and a message, see https://datatracker.ietf.org/doc/html/rfc2104.
Definition hmac.hpp:49
void read(B &it, SchnorrProofOfPossession< G1, Hash > &proof_of_possession)
void secure_erase(std::array< T, N > &buffer)
Definition hmac.hpp:26
Fr deterministic_nonce_rfc6979(const MessageContainer &message, const KeyContainer &key)
Deterministic nonce derivation according to RFC6979 specification (https://nvlpubs....
Definition hmac.hpp:122
void secure_erase_bytes(void *ptr, size_t size)
Definition hmac.hpp:18
void read(auto &it, msgpack_concepts::HasMsgPack auto &obj)
Automatically derived read for any object that defines .msgpack() (implicitly defined by MSGPACK_FIEL...
void write(auto &buf, const msgpack_concepts::HasMsgPack auto &obj)
Automatically derived write for any object that defines .msgpack() (implicitly defined by MSGPACK_FIE...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::ScalarField Fr
static constexpr uint256_t modulus
static field serialize_from_buffer(const uint8_t *buffer)
static void serialize_to_buffer(const field &value, uint8_t *buffer)