/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 | | |
17 | | namespace Botan { |
18 | | |
19 | | extern const uint64_t STREEBOG_Ax[8][256]; |
20 | | extern const uint64_t STREEBOG_C[12][8]; |
21 | | |
22 | 0 | std::unique_ptr<HashFunction> Streebog::copy_state() const { |
23 | 0 | return std::make_unique<Streebog>(*this); |
24 | 0 | } |
25 | | |
26 | 1.34k | Streebog::Streebog(size_t output_bits) : m_output_bits(output_bits), m_count(0), m_h(8), m_S(8) { |
27 | 1.34k | if(output_bits != 256 && output_bits != 512) { |
28 | 0 | throw Invalid_Argument(fmt("Streebog: Invalid output length {}", output_bits)); |
29 | 0 | } |
30 | | |
31 | 1.34k | clear(); |
32 | 1.34k | } |
33 | | |
34 | 11 | std::string Streebog::name() const { |
35 | 11 | return fmt("Streebog-{}", m_output_bits); |
36 | 11 | } |
37 | | |
38 | | /* |
39 | | * Clear memory of sensitive data |
40 | | */ |
41 | 89.2k | void Streebog::clear() { |
42 | 89.2k | m_count = 0; |
43 | 89.2k | m_buffer.clear(); |
44 | 89.2k | zeroise(m_S); |
45 | | |
46 | 89.2k | const uint64_t fill = (m_output_bits == 512) ? 0 : 0x0101010101010101; |
47 | 89.2k | std::fill(m_h.begin(), m_h.end(), fill); |
48 | 89.2k | } |
49 | | |
50 | | /* |
51 | | * Update the hash |
52 | | */ |
53 | 329k | void Streebog::add_data(std::span<const uint8_t> input) { |
54 | 329k | BufferSlicer in(input); |
55 | | |
56 | 577k | while(!in.empty()) { |
57 | 247k | if(const auto one_block = m_buffer.handle_unaligned_data(in)) { |
58 | 1.56k | compress(one_block->data()); |
59 | 1.56k | m_count += 512; |
60 | 1.56k | } |
61 | | |
62 | 247k | if(m_buffer.in_alignment()) { |
63 | 297k | while(const auto aligned_block = m_buffer.next_aligned_block_to_process(in)) { |
64 | 170k | compress(aligned_block->data()); |
65 | 170k | m_count += 512; |
66 | 170k | } |
67 | 127k | } |
68 | 247k | } |
69 | 329k | } |
70 | | |
71 | | /* |
72 | | * Finalize a hash |
73 | | */ |
74 | 86.9k | void Streebog::final_result(std::span<uint8_t> output) { |
75 | 86.9k | const auto pos = m_buffer.elements_in_buffer(); |
76 | | |
77 | 86.9k | const uint8_t padding = 0x01; |
78 | 86.9k | m_buffer.append({&padding, 1}); |
79 | 86.9k | m_buffer.fill_up_with_zeros(); |
80 | | |
81 | 86.9k | compress(m_buffer.consume().data()); |
82 | 86.9k | m_count += pos * 8; |
83 | | |
84 | 86.9k | m_buffer.fill_up_with_zeros(); |
85 | 86.9k | store_le(m_count, m_buffer.directly_modify_first(sizeof(m_count)).data()); |
86 | 86.9k | compress(m_buffer.consume().data(), true); |
87 | | |
88 | 86.9k | compress_64(m_S.data(), true); |
89 | | // FIXME |
90 | 86.9k | std::memcpy(output.data(), &m_h[8 - output_length() / 8], output_length()); |
91 | 86.9k | clear(); |
92 | 86.9k | } |
93 | | |
94 | | namespace { |
95 | | |
96 | 740M | inline uint64_t force_le(uint64_t x) { |
97 | 740M | #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) |
98 | 740M | return x; |
99 | | #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) |
100 | | return reverse_bytes(x); |
101 | | #else |
102 | | store_le(x, reinterpret_cast<uint8_t*>(&x)); |
103 | | return x; |
104 | | #endif |
105 | 740M | } |
106 | | |
107 | 10.8M | inline void lps(uint64_t block[8]) { |
108 | 10.8M | uint8_t r[64]; |
109 | | // FIXME |
110 | 10.8M | std::memcpy(r, block, 64); |
111 | | |
112 | 97.3M | for(int i = 0; i < 8; ++i) { |
113 | 86.5M | block[i] = force_le(STREEBOG_Ax[0][r[i + 0 * 8]]) ^ force_le(STREEBOG_Ax[1][r[i + 1 * 8]]) ^ |
114 | 86.5M | force_le(STREEBOG_Ax[2][r[i + 2 * 8]]) ^ force_le(STREEBOG_Ax[3][r[i + 3 * 8]]) ^ |
115 | 86.5M | force_le(STREEBOG_Ax[4][r[i + 4 * 8]]) ^ force_le(STREEBOG_Ax[5][r[i + 5 * 8]]) ^ |
116 | 86.5M | force_le(STREEBOG_Ax[6][r[i + 6 * 8]]) ^ force_le(STREEBOG_Ax[7][r[i + 7 * 8]]); |
117 | 86.5M | } |
118 | 10.8M | } |
119 | | |
120 | | } //namespace |
121 | | |
122 | 345k | void Streebog::compress(const uint8_t input[], bool last_block) { |
123 | 345k | uint64_t M[8]; |
124 | 345k | std::memcpy(M, input, 64); |
125 | | |
126 | 345k | compress_64(M, last_block); |
127 | 345k | } |
128 | | |
129 | 432k | void Streebog::compress_64(const uint64_t M[], bool last_block) { |
130 | 432k | const uint64_t N = last_block ? 0 : force_le(m_count); |
131 | | |
132 | 432k | uint64_t hN[8]; |
133 | 432k | uint64_t A[8]; |
134 | | |
135 | 432k | copy_mem(hN, m_h.data(), 8); |
136 | 432k | hN[0] ^= N; |
137 | 432k | lps(hN); |
138 | | |
139 | 432k | copy_mem(A, hN, 8); |
140 | | |
141 | 3.89M | for(size_t i = 0; i != 8; ++i) { |
142 | 3.46M | hN[i] ^= M[i]; |
143 | 3.46M | } |
144 | | |
145 | 5.62M | for(size_t i = 0; i < 12; ++i) { |
146 | 46.7M | for(size_t j = 0; j != 8; ++j) { |
147 | 41.5M | A[j] ^= force_le(STREEBOG_C[i][j]); |
148 | 41.5M | } |
149 | 5.19M | lps(A); |
150 | | |
151 | 5.19M | lps(hN); |
152 | 46.7M | for(size_t j = 0; j != 8; ++j) { |
153 | 41.5M | hN[j] ^= A[j]; |
154 | 41.5M | } |
155 | 5.19M | } |
156 | | |
157 | 3.89M | for(size_t i = 0; i != 8; ++i) { |
158 | 3.46M | m_h[i] ^= hN[i] ^ M[i]; |
159 | 3.46M | } |
160 | | |
161 | 432k | if(!last_block) { |
162 | 258k | uint64_t carry = 0; |
163 | 2.32M | for(int i = 0; i < 8; i++) { |
164 | 2.06M | const uint64_t m = force_le(M[i]); |
165 | 2.06M | const uint64_t hi = force_le(m_S[i]); |
166 | 2.06M | const uint64_t t = hi + m + carry; |
167 | | |
168 | 2.06M | m_S[i] = force_le(t); |
169 | 2.06M | if(t != m) { |
170 | 1.36M | carry = (t < m); |
171 | 1.36M | } |
172 | 2.06M | } |
173 | 258k | } |
174 | 432k | } |
175 | | |
176 | | } // namespace Botan |