Botan 3.11.0
Crypto and TLS for C&
streebog.cpp
Go to the documentation of this file.
1/*
2* Streebog (GOST R 34.11-2012)
3* (C) 2017 Ribose Inc.
4* (C) 2018,2026 Jack Lloyd
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/internal/streebog.h>
10
11#include <botan/exceptn.h>
12#include <botan/internal/bit_ops.h>
13#include <botan/internal/bswap.h>
14#include <botan/internal/buffer_slicer.h>
15#include <botan/internal/fmt.h>
16#include <botan/internal/loadstor.h>
17#include <array>
18#include <bit>
19
20namespace Botan {
21
22namespace {
23
24// Build the combined T-tables at compile time
25consteval std::array<std::array<uint64_t, 256>, 8> streebog_Ax_table() noexcept {
26 // Streebog sbox (same as Kuznyechik's), RFC 6986 Section 6.2
27 alignas(256) const constexpr uint8_t S[256] = {
28 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4, 77, 233, 119, 240, 219, 147, 46,
29 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, 249, 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66,
30 139, 1, 142, 79, 5, 132, 2, 174, 227, 106, 143, 160, 6, 11, 237, 152, 127, 212, 211, 31, 235, 52,
31 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, 58, 206, 204, 181, 112, 14, 86, 8, 12, 118, 18,
32 191, 114, 19, 71, 156, 183, 93, 135, 21, 161, 150, 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158,
33 178, 177, 50, 117, 25, 61, 255, 53, 138, 126, 109, 84, 198, 128, 195, 189, 13, 87, 223, 245, 36, 169,
34 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, 3, 224, 15, 236, 222, 122, 148, 176, 188, 220, 232,
35 40, 80, 78, 51, 10, 74, 167, 151, 96, 115, 30, 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65,
36 173, 69, 70, 146, 39, 94, 85, 47, 140, 163, 165, 125, 105, 213, 149, 59, 7, 88, 179, 64, 134, 172,
37 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, 225, 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144,
38 202, 216, 133, 97, 32, 113, 103, 164, 45, 43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82, 89, 166,
39 116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57, 75, 99, 182,
40 };
41
42 // Columns of the 8x8 linear transformation matrix over GF(2^8)
43 const constexpr uint64_t L[8] = {
44 0x641c314b2b8ee083,
45 0xa48b474f9ef5dc18,
46 0xf97d86d98a327728,
47 0x5b068c651810a89e,
48 0x0321658cba93c138,
49 0xaccc9ca9328a8950,
50 0x46b60f011a83988e,
51 0x83478b07b2468764,
52 };
53
54 std::array<std::array<uint64_t, 256>, 8> Ax = {};
55
56 for(size_t j = 0; j != 8; ++j) {
57 for(size_t x = 0; x != 256; ++x) {
58 Ax[j][x] = poly_mul<0x1D>(L[j], S[x]);
59 }
60 }
61
62 return Ax;
63}
64
65const constinit auto STREEBOG_Ax = streebog_Ax_table();
66
67// Iteration constants C[1]..C[12] from GOST R 34.11-2012 (RFC 6986 Section 6.5)
68// Word order matches the RFC (big-endian presentation); indexed with 7-j below
69// clang-format off
70const constexpr uint64_t STREEBOG_C[12][8] = {
71 {0xb1085bda1ecadae9, 0xebcb2f81c0657c1f, 0x2f6a76432e45d016, 0x714eb88d7585c4fc,
72 0x4b7ce09192676901, 0xa2422a08a460d315, 0x05767436cc744d23, 0xdd806559f2a64507},
73 {0x6fa3b58aa99d2f1a, 0x4fe39d460f70b5d7, 0xf3feea720a232b98, 0x61d55e0f16b50131,
74 0x9ab5176b12d69958, 0x5cb561c2db0aa7ca, 0x55dda21bd7cbcd56, 0xe679047021b19bb7},
75 {0xf574dcac2bce2fc7, 0x0a39fc286a3d8435, 0x06f15e5f529c1f8b, 0xf2ea7514b1297b7b,
76 0xd3e20fe490359eb1, 0xc1c93a376062db09, 0xc2b6f443867adb31, 0x991e96f50aba0ab2},
77 {0xef1fdfb3e81566d2, 0xf948e1a05d71e4dd, 0x488e857e335c3c7d, 0x9d721cad685e353f,
78 0xa9d72c82ed03d675, 0xd8b71333935203be, 0x3453eaa193e837f1, 0x220cbebc84e3d12e},
79 {0x4bea6bacad474799, 0x9a3f410c6ca92363, 0x7f151c1f1686104a, 0x359e35d7800fffbd,
80 0xbfcd1747253af5a3, 0xdfff00b723271a16, 0x7a56a27ea9ea63f5, 0x601758fd7c6cfe57},
81 {0xae4faeae1d3ad3d9, 0x6fa4c33b7a3039c0, 0x2d66c4f95142a46c, 0x187f9ab49af08ec6,
82 0xcffaa6b71c9ab7b4, 0x0af21f66c2bec6b6, 0xbf71c57236904f35, 0xfa68407a46647d6e},
83 {0xf4c70e16eeaac5ec, 0x51ac86febf240954, 0x399ec6c7e6bf87c9, 0xd3473e33197a93c9,
84 0x0992abc52d822c37, 0x06476983284a0504, 0x3517454ca23c4af3, 0x8886564d3a14d493},
85 {0x9b1f5b424d93c9a7, 0x03e7aa020c6e4141, 0x4eb7f8719c36de1e, 0x89b4443b4ddbc49a,
86 0xf4892bcb929b0690, 0x69d18d2bd1a5c42f, 0x36acc2355951a8d9, 0xa47f0dd4bf02e71e},
87 {0x378f5a541631229b, 0x944c9ad8ec165fde, 0x3a7d3a1b25894224, 0x3cd955b7e00d0984,
88 0x800a440bdbb2ceb1, 0x7b2b8a9aa6079c54, 0x0e38dc92cb1f2a60, 0x7261445183235adb},
89 {0xabbedea680056f52, 0x382ae548b2e4f3f3, 0x8941e71cff8a78db, 0x1fffe18a1b336103,
90 0x9fe76702af69334b, 0x7a1e6c303b7652f4, 0x3698fad1153bb6c3, 0x74b4c7fb98459ced},
91 {0x7bcd9ed0efc889fb, 0x3002c6cd635afe94, 0xd8fa6bbbebab0761, 0x2001802114846679,
92 0x8a1d71efea48b9ca, 0xefbacd1d7d476e98, 0xdea2594ac06fd85d, 0x6bcaa4cd81f32d1b},
93 {0x378ee767f11631ba, 0xd21380b00449b17a, 0xcda43c32bcdf1d77, 0xf82012d430219f9b,
94 0x5d80ef9d1891cc86, 0xe71da4aa88e12852, 0xfaf417d5d9b21b99, 0x48bc924af11bd720},
95};
96
97// clang-format on
98
99inline uint64_t force_le(uint64_t x) {
100 if constexpr(std::endian::native == std::endian::little) {
101 return x;
102 } else if constexpr(std::endian::native == std::endian::big) {
103 return reverse_bytes(x);
104 } else {
105 store_le(x, reinterpret_cast<uint8_t*>(&x));
106 return x;
107 }
108}
109
110inline void lps(uint64_t block[8]) {
111 const uint64_t block2[8] = {block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7]};
112 const std::span<const uint8_t> r{reinterpret_cast<const uint8_t*>(block2), 64};
113
114 for(int i = 0; i < 8; ++i) {
115 block[i] = force_le(STREEBOG_Ax[0][r[i + 0 * 8]]) ^ force_le(STREEBOG_Ax[1][r[i + 1 * 8]]) ^
116 force_le(STREEBOG_Ax[2][r[i + 2 * 8]]) ^ force_le(STREEBOG_Ax[3][r[i + 3 * 8]]) ^
117 force_le(STREEBOG_Ax[4][r[i + 4 * 8]]) ^ force_le(STREEBOG_Ax[5][r[i + 5 * 8]]) ^
118 force_le(STREEBOG_Ax[6][r[i + 6 * 8]]) ^ force_le(STREEBOG_Ax[7][r[i + 7 * 8]]);
119 }
120}
121
122} //namespace
123
124std::unique_ptr<HashFunction> Streebog::copy_state() const {
125 return std::make_unique<Streebog>(*this);
126}
127
128Streebog::Streebog(size_t output_bits) : m_output_bits(output_bits), m_count(0), m_h(8), m_S(8) {
129 if(output_bits != 256 && output_bits != 512) {
130 throw Invalid_Argument(fmt("Streebog: Invalid output length {}", output_bits));
131 }
132
133 clear();
134}
135
136std::string Streebog::name() const {
137 return fmt("Streebog-{}", m_output_bits);
138}
139
140/*
141* Clear memory of sensitive data
142*/
144 m_count = 0;
145 m_buffer.clear();
146 zeroise(m_S);
147
148 const uint64_t fill = (m_output_bits == 512) ? 0 : 0x0101010101010101;
149 std::fill(m_h.begin(), m_h.end(), fill);
150}
151
152/*
153* Update the hash
154*/
155void Streebog::add_data(std::span<const uint8_t> input) {
156 BufferSlicer in(input);
157
158 while(!in.empty()) {
159 if(const auto one_block = m_buffer.handle_unaligned_data(in)) {
160 compress(one_block->data());
161 m_count += 512;
162 }
163
164 if(m_buffer.in_alignment()) {
165 while(const auto aligned_block = m_buffer.next_aligned_block_to_process(in)) {
166 compress(aligned_block->data());
167 m_count += 512;
168 }
169 }
170 }
171}
172
173/*
174* Finalize a hash
175*/
176void Streebog::final_result(std::span<uint8_t> output) {
177 const auto pos = m_buffer.elements_in_buffer();
178
179 const uint8_t padding = 0x01;
180 m_buffer.append({&padding, 1});
181 m_buffer.fill_up_with_zeros();
182
183 compress(m_buffer.consume().data());
184 m_count += pos * 8;
185
186 m_buffer.fill_up_with_zeros();
187 store_le(m_count, m_buffer.directly_modify_first(sizeof(m_count)).data());
188 compress(m_buffer.consume().data(), true);
189
190 compress_64(m_S.data(), true);
191 // FIXME
192 std::memcpy(output.data(), &m_h[8 - output_length() / 8], output_length());
193 clear();
194}
195
196void Streebog::compress(const uint8_t input[], bool last_block) {
197 uint64_t M[8];
198 std::memcpy(M, input, 64);
199
200 compress_64(M, last_block);
201}
202
203void Streebog::compress_64(const uint64_t M[], bool last_block) {
204 const uint64_t N = last_block ? 0 : force_le(m_count);
205
206 uint64_t hN[8];
207 uint64_t A[8];
208
209 copy_mem(hN, m_h.data(), 8);
210 hN[0] ^= N;
211 lps(hN);
212
213 copy_mem(A, hN, 8);
214
215 for(size_t i = 0; i != 8; ++i) {
216 hN[i] ^= M[i];
217 }
218
219 for(size_t i = 0; i < 12; ++i) { // NOLINT(modernize-loop-convert)
220 for(size_t j = 0; j != 8; ++j) {
221 A[j] ^= force_le(STREEBOG_C[i][7 - j]);
222 }
223 lps(A);
224
225 lps(hN);
226 for(size_t j = 0; j != 8; ++j) {
227 hN[j] ^= A[j];
228 }
229 }
230
231 for(size_t i = 0; i != 8; ++i) {
232 m_h[i] ^= hN[i] ^ M[i];
233 }
234
235 if(!last_block) {
236 uint64_t carry = 0;
237 for(int i = 0; i < 8; i++) {
238 const uint64_t m = force_le(M[i]);
239 const uint64_t hi = force_le(m_S[i]);
240 const uint64_t t = hi + m + carry;
241
242 m_S[i] = force_le(t);
243 if(t != m) {
244 carry = (t < m) ? 1 : 0;
245 }
246 }
247 }
248}
249
250} // namespace Botan
void compress(const uint8_t input[], bool lastblock=false)
Definition streebog.cpp:196
void add_data(std::span< const uint8_t > input) override
Definition streebog.cpp:155
size_t output_length() const override
Definition streebog.h:23
void compress_64(const uint64_t input[], bool lastblock=false)
Definition streebog.cpp:203
std::unique_ptr< HashFunction > copy_state() const override
Definition streebog.cpp:124
void final_result(std::span< uint8_t > out) override
Definition streebog.cpp:176
Streebog(size_t output_bits)
Definition streebog.cpp:128
void clear() override
Definition streebog.cpp:143
std::string name() const override
Definition streebog.cpp:136
void zeroise(std::vector< T, Alloc > &vec)
Definition secmem.h:124
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr T reverse_bytes(T x)
Definition bswap.h:27
constexpr auto store_le(ParamTs &&... params)
Definition loadstor.h:736
constexpr T poly_mul(T x, uint8_t y)
Definition bit_ops.h:306
void carry(int64_t &h0, int64_t &h1)