Coverage Report

Created: 2025-04-11 06:34

/src/botan/src/lib/hash/streebog/streebog.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* Streebog
3
* (C) 2017 Ribose Inc.
4
* (C) 2018 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/bswap.h>
13
#include <botan/internal/fmt.h>
14
#include <botan/internal/loadstor.h>
15
#include <botan/internal/stl_util.h>
16
#include <bit>
17
18
namespace Botan {
19
20
extern const uint64_t STREEBOG_Ax[8][256];
21
extern const uint64_t STREEBOG_C[12][8];
22
23
0
std::unique_ptr<HashFunction> Streebog::copy_state() const {
24
0
   return std::make_unique<Streebog>(*this);
25
0
}
26
27
138
Streebog::Streebog(size_t output_bits) : m_output_bits(output_bits), m_count(0), m_h(8), m_S(8) {
28
138
   if(output_bits != 256 && output_bits != 512) {
29
0
      throw Invalid_Argument(fmt("Streebog: Invalid output length {}", output_bits));
30
0
   }
31
32
138
   clear();
33
138
}
34
35
9
std::string Streebog::name() const {
36
9
   return fmt("Streebog-{}", m_output_bits);
37
9
}
38
39
/*
40
* Clear memory of sensitive data
41
*/
42
276
void Streebog::clear() {
43
276
   m_count = 0;
44
276
   m_buffer.clear();
45
276
   zeroise(m_S);
46
47
276
   const uint64_t fill = (m_output_bits == 512) ? 0 : 0x0101010101010101;
48
276
   std::fill(m_h.begin(), m_h.end(), fill);
49
276
}
50
51
/*
52
* Update the hash
53
*/
54
138
void Streebog::add_data(std::span<const uint8_t> input) {
55
138
   BufferSlicer in(input);
56
57
414
   while(!in.empty()) {
58
276
      if(const auto one_block = m_buffer.handle_unaligned_data(in)) {
59
0
         compress(one_block->data());
60
0
         m_count += 512;
61
0
      }
62
63
276
      if(m_buffer.in_alignment()) {
64
1.23k
         while(const auto aligned_block = m_buffer.next_aligned_block_to_process(in)) {
65
1.10k
            compress(aligned_block->data());
66
1.10k
            m_count += 512;
67
1.10k
         }
68
138
      }
69
276
   }
70
138
}
71
72
/*
73
* Finalize a hash
74
*/
75
138
void Streebog::final_result(std::span<uint8_t> output) {
76
138
   const auto pos = m_buffer.elements_in_buffer();
77
78
138
   const uint8_t padding = 0x01;
79
138
   m_buffer.append({&padding, 1});
80
138
   m_buffer.fill_up_with_zeros();
81
82
138
   compress(m_buffer.consume().data());
83
138
   m_count += pos * 8;
84
85
138
   m_buffer.fill_up_with_zeros();
86
138
   store_le(m_count, m_buffer.directly_modify_first(sizeof(m_count)).data());
87
138
   compress(m_buffer.consume().data(), true);
88
89
138
   compress_64(m_S.data(), true);
90
   // FIXME
91
138
   std::memcpy(output.data(), &m_h[8 - output_length() / 8], output_length());
92
138
   clear();
93
138
}
94
95
namespace {
96
97
2.60M
inline uint64_t force_le(uint64_t x) {
98
2.60M
   if constexpr(std::endian::native == std::endian::little) {
99
2.60M
      return x;
100
   } else if constexpr(std::endian::native == std::endian::big) {
101
      return reverse_bytes(x);
102
   } else {
103
      store_le(x, reinterpret_cast<uint8_t*>(&x));
104
      return x;
105
   }
106
2.60M
}
107
108
37.8k
inline void lps(uint64_t block[8]) {
109
37.8k
   uint8_t r[64];
110
   // FIXME
111
37.8k
   std::memcpy(r, block, 64);
112
113
340k
   for(int i = 0; i < 8; ++i) {
114
303k
      block[i] = force_le(STREEBOG_Ax[0][r[i + 0 * 8]]) ^ force_le(STREEBOG_Ax[1][r[i + 1 * 8]]) ^
115
303k
                 force_le(STREEBOG_Ax[2][r[i + 2 * 8]]) ^ force_le(STREEBOG_Ax[3][r[i + 3 * 8]]) ^
116
303k
                 force_le(STREEBOG_Ax[4][r[i + 4 * 8]]) ^ force_le(STREEBOG_Ax[5][r[i + 5 * 8]]) ^
117
303k
                 force_le(STREEBOG_Ax[6][r[i + 6 * 8]]) ^ force_le(STREEBOG_Ax[7][r[i + 7 * 8]]);
118
303k
   }
119
37.8k
}
120
121
}  //namespace
122
123
1.37k
void Streebog::compress(const uint8_t input[], bool last_block) {
124
1.37k
   uint64_t M[8];
125
1.37k
   std::memcpy(M, input, 64);
126
127
1.37k
   compress_64(M, last_block);
128
1.37k
}
129
130
1.51k
void Streebog::compress_64(const uint64_t M[], bool last_block) {
131
1.51k
   const uint64_t N = last_block ? 0 : force_le(m_count);
132
133
1.51k
   uint64_t hN[8];
134
1.51k
   uint64_t A[8];
135
136
1.51k
   copy_mem(hN, m_h.data(), 8);
137
1.51k
   hN[0] ^= N;
138
1.51k
   lps(hN);
139
140
1.51k
   copy_mem(A, hN, 8);
141
142
13.6k
   for(size_t i = 0; i != 8; ++i) {
143
12.1k
      hN[i] ^= M[i];
144
12.1k
   }
145
146
19.6k
   for(size_t i = 0; i < 12; ++i) {
147
163k
      for(size_t j = 0; j != 8; ++j) {
148
145k
         A[j] ^= force_le(STREEBOG_C[i][j]);
149
145k
      }
150
18.1k
      lps(A);
151
152
18.1k
      lps(hN);
153
163k
      for(size_t j = 0; j != 8; ++j) {
154
145k
         hN[j] ^= A[j];
155
145k
      }
156
18.1k
   }
157
158
13.6k
   for(size_t i = 0; i != 8; ++i) {
159
12.1k
      m_h[i] ^= hN[i] ^ M[i];
160
12.1k
   }
161
162
1.51k
   if(!last_block) {
163
1.23k
      uint64_t carry = 0;
164
11.1k
      for(int i = 0; i < 8; i++) {
165
9.91k
         const uint64_t m = force_le(M[i]);
166
9.91k
         const uint64_t hi = force_le(m_S[i]);
167
9.91k
         const uint64_t t = hi + m + carry;
168
169
9.91k
         m_S[i] = force_le(t);
170
9.91k
         if(t != m) {
171
8.80k
            carry = (t < m);
172
8.80k
         }
173
9.91k
      }
174
1.23k
   }
175
1.51k
}
176
177
}  // namespace Botan