/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 | | #include <botan/internal/loadstor.h> |
11 | | #include <botan/exceptn.h> |
12 | | |
13 | | namespace Botan { |
14 | | |
15 | | extern const uint64_t STREEBOG_Ax[8][256]; |
16 | | extern const uint64_t STREEBOG_C[12][8]; |
17 | | |
18 | | std::unique_ptr<HashFunction> Streebog::copy_state() const |
19 | 0 | { |
20 | 0 | return std::unique_ptr<HashFunction>(new Streebog(*this)); |
21 | 0 | } |
22 | | |
23 | | Streebog::Streebog(size_t output_bits) : |
24 | | m_output_bits(output_bits), |
25 | | m_count(0), |
26 | | m_position(0), |
27 | | m_buffer(64), |
28 | | m_h(8), |
29 | | m_S(8) |
30 | 0 | { |
31 | 0 | if(output_bits != 256 && output_bits != 512) |
32 | 0 | throw Invalid_Argument("Streebog: Invalid output length " + |
33 | 0 | std::to_string(output_bits)); |
34 | | |
35 | 0 | clear(); |
36 | 0 | } |
37 | | |
38 | | std::string Streebog::name() const |
39 | 0 | { |
40 | 0 | return "Streebog-" + std::to_string(m_output_bits); |
41 | 0 | } |
42 | | |
43 | | /* |
44 | | * Clear memory of sensitive data |
45 | | */ |
46 | | void Streebog::clear() |
47 | 0 | { |
48 | 0 | m_count = 0; |
49 | 0 | m_position = 0; |
50 | 0 | zeroise(m_buffer); |
51 | 0 | zeroise(m_S); |
52 | |
|
53 | 0 | const uint64_t fill = (m_output_bits == 512) ? 0 : 0x0101010101010101; |
54 | 0 | std::fill(m_h.begin(), m_h.end(), fill); |
55 | 0 | } |
56 | | |
57 | | /* |
58 | | * Update the hash |
59 | | */ |
60 | | void Streebog::add_data(const uint8_t input[], size_t length) |
61 | 0 | { |
62 | 0 | const size_t block_size = m_buffer.size(); |
63 | |
|
64 | 0 | if(m_position) |
65 | 0 | { |
66 | 0 | buffer_insert(m_buffer, m_position, input, length); |
67 | |
|
68 | 0 | if(m_position + length >= block_size) |
69 | 0 | { |
70 | 0 | compress(m_buffer.data()); |
71 | 0 | m_count += 512; |
72 | 0 | input += (block_size - m_position); |
73 | 0 | length -= (block_size - m_position); |
74 | 0 | m_position = 0; |
75 | 0 | } |
76 | 0 | } |
77 | |
|
78 | 0 | const size_t full_blocks = length / block_size; |
79 | 0 | const size_t remaining = length % block_size; |
80 | |
|
81 | 0 | for(size_t i = 0; i != full_blocks; ++i) |
82 | 0 | { |
83 | 0 | compress(input + block_size * i); |
84 | 0 | m_count += 512; |
85 | 0 | } |
86 | |
|
87 | 0 | buffer_insert(m_buffer, m_position, input + full_blocks * block_size, remaining); |
88 | 0 | m_position += remaining; |
89 | 0 | } |
90 | | |
91 | | /* |
92 | | * Finalize a hash |
93 | | */ |
94 | | void Streebog::final_result(uint8_t output[]) |
95 | 0 | { |
96 | 0 | m_buffer[m_position++] = 0x01; |
97 | |
|
98 | 0 | if(m_position != m_buffer.size()) |
99 | 0 | clear_mem(&m_buffer[m_position], m_buffer.size() - m_position); |
100 | |
|
101 | 0 | compress(m_buffer.data()); |
102 | 0 | m_count += (m_position - 1) * 8; |
103 | |
|
104 | 0 | zeroise(m_buffer); |
105 | 0 | store_le(m_count, m_buffer.data()); |
106 | 0 | compress(m_buffer.data(), true); |
107 | |
|
108 | 0 | compress_64(m_S.data(), true); |
109 | | // FIXME |
110 | 0 | std::memcpy(output, &m_h[8 - output_length() / 8], output_length()); |
111 | 0 | clear(); |
112 | 0 | } |
113 | | |
114 | | namespace { |
115 | | |
116 | | inline uint64_t force_le(uint64_t x) |
117 | 0 | { |
118 | 0 | #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) |
119 | 0 | return x; |
120 | | #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) |
121 | | return reverse_bytes(x); |
122 | | #else |
123 | | store_le(x, reinterpret_cast<uint8_t*>(&x)); |
124 | | return x; |
125 | | #endif |
126 | 0 | } |
127 | | |
128 | | inline void lps(uint64_t block[8]) |
129 | 0 | { |
130 | 0 | uint8_t r[64]; |
131 | | // FIXME |
132 | 0 | std::memcpy(r, block, 64); |
133 | |
|
134 | 0 | for(int i = 0; i < 8; ++i) |
135 | 0 | { |
136 | 0 | block[i] = force_le(STREEBOG_Ax[0][r[i + 0*8]]) ^ |
137 | 0 | force_le(STREEBOG_Ax[1][r[i + 1*8]]) ^ |
138 | 0 | force_le(STREEBOG_Ax[2][r[i + 2*8]]) ^ |
139 | 0 | force_le(STREEBOG_Ax[3][r[i + 3*8]]) ^ |
140 | 0 | force_le(STREEBOG_Ax[4][r[i + 4*8]]) ^ |
141 | 0 | force_le(STREEBOG_Ax[5][r[i + 5*8]]) ^ |
142 | 0 | force_le(STREEBOG_Ax[6][r[i + 6*8]]) ^ |
143 | 0 | force_le(STREEBOG_Ax[7][r[i + 7*8]]); |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | | } //namespace |
148 | | |
149 | | void Streebog::compress(const uint8_t input[], bool last_block) |
150 | 0 | { |
151 | 0 | uint64_t M[8]; |
152 | 0 | std::memcpy(M, input, 64); |
153 | |
|
154 | 0 | compress_64(M, last_block); |
155 | 0 | } |
156 | | |
157 | | void Streebog::compress_64(const uint64_t M[], bool last_block) |
158 | 0 | { |
159 | 0 | uint64_t N = force_le(last_block ? 0ULL : m_count); |
160 | |
|
161 | 0 | uint64_t hN[8]; |
162 | 0 | uint64_t A[8]; |
163 | |
|
164 | 0 | copy_mem(hN, m_h.data(), 8); |
165 | 0 | hN[0] ^= N; |
166 | 0 | lps(hN); |
167 | |
|
168 | 0 | copy_mem(A, hN, 8); |
169 | |
|
170 | 0 | for(size_t i = 0; i != 8; ++i) |
171 | 0 | { |
172 | 0 | hN[i] ^= M[i]; |
173 | 0 | } |
174 | |
|
175 | 0 | for(size_t i = 0; i < 12; ++i) |
176 | 0 | { |
177 | 0 | for(size_t j = 0; j != 8; ++j) |
178 | 0 | A[j] ^= force_le(STREEBOG_C[i][j]); |
179 | 0 | lps(A); |
180 | |
|
181 | 0 | lps(hN); |
182 | 0 | for(size_t j = 0; j != 8; ++j) |
183 | 0 | hN[j] ^= A[j]; |
184 | 0 | } |
185 | |
|
186 | 0 | for(size_t i = 0; i != 8; ++i) |
187 | 0 | { |
188 | 0 | m_h[i] ^= hN[i] ^ M[i]; |
189 | 0 | } |
190 | |
|
191 | 0 | if(!last_block) |
192 | 0 | { |
193 | 0 | uint64_t carry = 0; |
194 | 0 | for(int i = 0; i < 8; i++) |
195 | 0 | { |
196 | 0 | const uint64_t m = force_le(M[i]); |
197 | 0 | const uint64_t hi = force_le(m_S[i]); |
198 | 0 | const uint64_t t = hi + m + carry; |
199 | |
|
200 | 0 | m_S[i] = force_le(t); |
201 | 0 | if(t != m) |
202 | 0 | carry = (t < m); |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | | } |