Botan 3.13.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 <botan/internal/streebog_const.h>
18#include <array>
19#include <bit>
20
21#if defined(BOTAN_HAS_CPUID)
22 #include <botan/internal/cpuid.h>
23#endif
24
25namespace Botan {
26
27namespace {
28
29// Build the combined T-tables at compile time
30consteval std::array<std::array<uint64_t, 256>, 8> streebog_Ax_table() noexcept {
31 std::array<std::array<uint64_t, 256>, 8> Ax = {};
32
33 for(size_t j = 0; j != 8; ++j) {
34 for(size_t x = 0; x != 256; ++x) {
35 Ax[j][x] = poly_mul<0x1D>(STREEBOG_L[j], STREEBOG_S[x]);
36 }
37 }
38
39 return Ax;
40}
41
42const constinit auto STREEBOG_Ax = streebog_Ax_table();
43
44inline uint64_t force_le(uint64_t x) {
45 if constexpr(std::endian::native == std::endian::little) {
46 return x;
47 } else if constexpr(std::endian::native == std::endian::big) {
48 return reverse_bytes(x);
49 } else {
50 store_le(x, reinterpret_cast<uint8_t*>(&x));
51 return x;
52 }
53}
54
55inline void lps(uint64_t block[8]) {
56 const uint64_t block2[8] = {block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7]};
57 const std::span<const uint8_t> r{reinterpret_cast<const uint8_t*>(block2), 64};
58
59 for(int i = 0; i < 8; ++i) {
60 block[i] = force_le(STREEBOG_Ax[0][r[i + 0 * 8]]) ^ force_le(STREEBOG_Ax[1][r[i + 1 * 8]]) ^
61 force_le(STREEBOG_Ax[2][r[i + 2 * 8]]) ^ force_le(STREEBOG_Ax[3][r[i + 3 * 8]]) ^
62 force_le(STREEBOG_Ax[4][r[i + 4 * 8]]) ^ force_le(STREEBOG_Ax[5][r[i + 5 * 8]]) ^
63 force_le(STREEBOG_Ax[6][r[i + 6 * 8]]) ^ force_le(STREEBOG_Ax[7][r[i + 7 * 8]]);
64 }
65}
66
67} //namespace
68
69std::unique_ptr<HashFunction> Streebog::copy_state() const {
70 return std::make_unique<Streebog>(*this);
71}
72
73Streebog::Streebog(size_t output_bits) : m_output_bits(output_bits), m_count(0), m_h(8), m_S(8) {
74 if(output_bits != 256 && output_bits != 512) {
75 throw Invalid_Argument(fmt("Streebog: Invalid output length {}", output_bits));
76 }
77
78 clear();
79}
80
81std::string Streebog::name() const {
82 return fmt("Streebog-{}", m_output_bits);
83}
84
85std::string Streebog::provider() const {
86#if defined(BOTAN_HAS_STREEBOG_AVX512_GFNI)
88 return *feat;
89 }
90#endif
91
92 return "base";
93}
94
95/*
96* Clear memory of sensitive data
97*/
99 m_count = 0;
100 m_buffer.clear();
101 zeroise(m_S);
102
103 const uint64_t fill = (m_output_bits == 512) ? 0 : 0x0101010101010101;
104 std::fill(m_h.begin(), m_h.end(), fill);
105}
106
107/*
108* Update the hash
109*/
110void Streebog::add_data(std::span<const uint8_t> input) {
111 BufferSlicer in(input);
112
113 while(!in.empty()) {
114 if(const auto one_block = m_buffer.handle_unaligned_data(in)) {
115 compress(one_block->data());
116 m_count += 512;
117 }
118
119 if(m_buffer.in_alignment()) {
120 while(const auto aligned_block = m_buffer.next_aligned_block_to_process(in)) {
121 compress(aligned_block->data());
122 m_count += 512;
123 }
124 }
125 }
126}
127
128/*
129* Finalize a hash
130*/
131void Streebog::final_result(std::span<uint8_t> output) {
132 const auto pos = m_buffer.elements_in_buffer();
133
134 const uint8_t padding = 0x01;
135 m_buffer.append({&padding, 1});
136 m_buffer.fill_up_with_zeros();
137
138 compress(m_buffer.consume().data());
139 m_count += pos * 8;
140
141 m_buffer.fill_up_with_zeros();
142 store_le(m_count, m_buffer.directly_modify_first(sizeof(m_count)).data());
143 compress(m_buffer.consume().data(), true);
144
145 compress_64(m_S.data(), true);
146
147 const size_t offset = 8 - output_length() / 8;
148 const size_t count = output_length() / sizeof(uint64_t);
149 typecast_copy(output, std::span<const uint64_t>(&m_h[offset], count));
150 clear();
151}
152
153void Streebog::compress(const uint8_t input[], bool last_block) {
154 uint64_t M[8];
155 typecast_copy(M, std::span<const uint8_t>(input, 64));
156 compress_64(M, last_block);
157}
158
159namespace {
160
161void increment_s(bool last_block, const uint64_t M[8], uint64_t S[8]) {
162 if(!last_block) {
163 uint64_t carry = 0;
164 for(int i = 0; i < 8; i++) {
165 const uint64_t m = force_le(M[i]);
166 const uint64_t hi = force_le(S[i]);
167 const uint64_t t = hi + m + carry;
168
169 S[i] = force_le(t);
170 if(t != m) {
171 carry = (t < m) ? 1 : 0;
172 }
173 }
174 }
175}
176
177} // namespace
178
179void Streebog::compress_64(const uint64_t M[], bool last_block) {
180 const uint64_t N = last_block ? 0 : force_le(m_count);
181
182#if defined(BOTAN_HAS_STREEBOG_AVX512_GFNI)
184 compress_64_avx512_gfni(m_h.data(), M, N);
185 increment_s(last_block, M, m_S.data());
186 return;
187 }
188#endif
189
190 uint64_t hN[8];
191 uint64_t A[8];
192
193 copy_mem(hN, m_h.data(), 8);
194 hN[0] ^= N;
195 lps(hN);
196
197 copy_mem(A, hN, 8);
198
199 for(size_t i = 0; i != 8; ++i) {
200 hN[i] ^= M[i];
201 }
202
203 for(size_t i = 0; i < 12; ++i) { // NOLINT(modernize-loop-convert)
204 for(size_t j = 0; j != 8; ++j) {
205 A[j] ^= force_le(STREEBOG_C[i][7 - j]);
206 }
207 lps(A);
208
209 lps(hN);
210 for(size_t j = 0; j != 8; ++j) {
211 hN[j] ^= A[j];
212 }
213 }
214
215 for(size_t i = 0; i != 8; ++i) {
216 m_h[i] ^= hN[i] ^ M[i];
217 }
218
219 increment_s(last_block, M, m_S.data());
220}
221
222} // namespace Botan
static std::optional< std::string > check(CPUID::Feature feat)
Definition cpuid.h:67
static bool has(CPUID::Feature feat)
Definition cpuid.h:94
void compress(const uint8_t input[], bool lastblock=false)
Definition streebog.cpp:153
void add_data(std::span< const uint8_t > input) override
Definition streebog.cpp:110
std::string provider() const override
Definition streebog.cpp:85
size_t output_length() const override
Definition streebog.h:23
void compress_64(const uint64_t input[], bool lastblock=false)
Definition streebog.cpp:179
std::unique_ptr< HashFunction > copy_state() const override
Definition streebog.cpp:69
void final_result(std::span< uint8_t > out) override
Definition streebog.cpp:131
Streebog(size_t output_bits)
Definition streebog.cpp:73
void clear() override
Definition streebog.cpp:98
std::string name() const override
Definition streebog.cpp:81
void zeroise(std::vector< T, Alloc > &vec)
Definition secmem.h:241
const constexpr uint8_t STREEBOG_S[256]
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr void typecast_copy(ToR &&out, const FromR &in)
Definition mem_ops.h:176
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
constexpr auto store_le(ParamTs &&... params)
Definition loadstor.h:736
constexpr T reverse_bytes(T x)
Definition bswap.h:27
constexpr T poly_mul(T x, uint8_t y)
Definition bit_ops.h:306
void carry(int64_t &h0, int64_t &h1)
constexpr uint64_t STREEBOG_C[12][8]
constexpr uint64_t STREEBOG_L[8]