/src/botan/src/lib/hash/skein/skein_512.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * The Skein-512 hash function |
3 | | * (C) 2009,2010,2014 Jack Lloyd |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #include <botan/skein_512.h> |
9 | | #include <botan/loadstor.h> |
10 | | #include <botan/exceptn.h> |
11 | | #include <algorithm> |
12 | | |
13 | | namespace Botan { |
14 | | |
15 | | Skein_512::Skein_512(size_t arg_output_bits, |
16 | | const std::string& arg_personalization) : |
17 | | m_personalization(arg_personalization), |
18 | | m_output_bits(arg_output_bits), |
19 | | m_threefish(new Threefish_512), |
20 | | m_T(2), m_buffer(64), m_buf_pos(0) |
21 | 0 | { |
22 | 0 | if(m_output_bits == 0 || m_output_bits % 8 != 0 || m_output_bits > 512) |
23 | 0 | throw Invalid_Argument("Bad output bits size for Skein-512"); |
24 | 0 | |
25 | 0 | initial_block(); |
26 | 0 | } |
27 | | |
28 | | std::string Skein_512::name() const |
29 | 0 | { |
30 | 0 | if(m_personalization != "") |
31 | 0 | return "Skein-512(" + std::to_string(m_output_bits) + "," + |
32 | 0 | m_personalization + ")"; |
33 | 0 | return "Skein-512(" + std::to_string(m_output_bits) + ")"; |
34 | 0 | } |
35 | | |
36 | | HashFunction* Skein_512::clone() const |
37 | 0 | { |
38 | 0 | return new Skein_512(m_output_bits, m_personalization); |
39 | 0 | } |
40 | | |
41 | | std::unique_ptr<HashFunction> Skein_512::copy_state() const |
42 | 0 | { |
43 | 0 | std::unique_ptr<Skein_512> copy(new Skein_512(m_output_bits, m_personalization)); |
44 | 0 |
|
45 | 0 | copy->m_threefish->m_K = this->m_threefish->m_K; |
46 | 0 | copy->m_T = this->m_T; |
47 | 0 | copy->m_buffer = this->m_buffer; |
48 | 0 | copy->m_buf_pos = this->m_buf_pos; |
49 | 0 |
|
50 | 0 | // work around GCC 4.8 bug |
51 | 0 | return std::unique_ptr<HashFunction>(copy.release()); |
52 | 0 | } |
53 | | |
54 | | void Skein_512::clear() |
55 | 0 | { |
56 | 0 | zeroise(m_buffer); |
57 | 0 | m_buf_pos = 0; |
58 | 0 |
|
59 | 0 | initial_block(); |
60 | 0 | } |
61 | | |
62 | | void Skein_512::reset_tweak(type_code type, bool is_final) |
63 | 0 | { |
64 | 0 | m_T[0] = 0; |
65 | 0 |
|
66 | 0 | m_T[1] = (static_cast<uint64_t>(type) << 56) | |
67 | 0 | (static_cast<uint64_t>(1) << 62) | |
68 | 0 | (static_cast<uint64_t>(is_final) << 63); |
69 | 0 | } |
70 | | |
71 | | void Skein_512::initial_block() |
72 | 0 | { |
73 | 0 | const uint8_t zeros[64] = { 0 }; |
74 | 0 |
|
75 | 0 | m_threefish->set_key(zeros, sizeof(zeros)); |
76 | 0 |
|
77 | 0 | // ASCII("SHA3") followed by version (0x0001) code |
78 | 0 | uint8_t config_str[32] = { 0x53, 0x48, 0x41, 0x33, 0x01, 0x00, 0 }; |
79 | 0 | store_le(uint32_t(m_output_bits), config_str + 8); |
80 | 0 |
|
81 | 0 | reset_tweak(SKEIN_CONFIG, true); |
82 | 0 | ubi_512(config_str, sizeof(config_str)); |
83 | 0 |
|
84 | 0 | if(m_personalization != "") |
85 | 0 | { |
86 | 0 | /* |
87 | 0 | This is a limitation of this implementation, and not of the |
88 | 0 | algorithm specification. Could be fixed relatively easily, but |
89 | 0 | doesn't seem worth the trouble. |
90 | 0 | */ |
91 | 0 | if(m_personalization.length() > 64) |
92 | 0 | throw Invalid_Argument("Skein personalization must be less than 64 bytes"); |
93 | 0 | |
94 | 0 | const uint8_t* bits = cast_char_ptr_to_uint8(m_personalization.data()); |
95 | 0 | reset_tweak(SKEIN_PERSONALIZATION, true); |
96 | 0 | ubi_512(bits, m_personalization.length()); |
97 | 0 | } |
98 | 0 |
|
99 | 0 | reset_tweak(SKEIN_MSG, false); |
100 | 0 | } |
101 | | |
102 | | void Skein_512::ubi_512(const uint8_t msg[], size_t msg_len) |
103 | 0 | { |
104 | 0 | secure_vector<uint64_t> M(8); |
105 | 0 |
|
106 | 0 | do |
107 | 0 | { |
108 | 0 | const size_t to_proc = std::min<size_t>(msg_len, 64); |
109 | 0 | m_T[0] += to_proc; |
110 | 0 |
|
111 | 0 | load_le(M.data(), msg, to_proc / 8); |
112 | 0 |
|
113 | 0 | if(to_proc % 8) |
114 | 0 | { |
115 | 0 | for(size_t j = 0; j != to_proc % 8; ++j) |
116 | 0 | M[to_proc/8] |= static_cast<uint64_t>(msg[8*(to_proc/8)+j]) << (8*j); |
117 | 0 | } |
118 | 0 |
|
119 | 0 | m_threefish->skein_feedfwd(M, m_T); |
120 | 0 |
|
121 | 0 | // clear first flag if set |
122 | 0 | m_T[1] &= ~(static_cast<uint64_t>(1) << 62); |
123 | 0 |
|
124 | 0 | msg_len -= to_proc; |
125 | 0 | msg += to_proc; |
126 | 0 | } while(msg_len); |
127 | 0 | } |
128 | | |
129 | | void Skein_512::add_data(const uint8_t input[], size_t length) |
130 | 0 | { |
131 | 0 | if(length == 0) |
132 | 0 | return; |
133 | 0 | |
134 | 0 | if(m_buf_pos) |
135 | 0 | { |
136 | 0 | buffer_insert(m_buffer, m_buf_pos, input, length); |
137 | 0 | if(m_buf_pos + length > 64) |
138 | 0 | { |
139 | 0 | ubi_512(m_buffer.data(), m_buffer.size()); |
140 | 0 |
|
141 | 0 | input += (64 - m_buf_pos); |
142 | 0 | length -= (64 - m_buf_pos); |
143 | 0 | m_buf_pos = 0; |
144 | 0 | } |
145 | 0 | } |
146 | 0 |
|
147 | 0 | const size_t full_blocks = (length - 1) / 64; |
148 | 0 |
|
149 | 0 | if(full_blocks) |
150 | 0 | ubi_512(input, 64*full_blocks); |
151 | 0 |
|
152 | 0 | length -= full_blocks * 64; |
153 | 0 |
|
154 | 0 | buffer_insert(m_buffer, m_buf_pos, input + full_blocks * 64, length); |
155 | 0 | m_buf_pos += length; |
156 | 0 | } |
157 | | |
158 | | void Skein_512::final_result(uint8_t out[]) |
159 | 0 | { |
160 | 0 | m_T[1] |= (static_cast<uint64_t>(1) << 63); // final block flag |
161 | 0 |
|
162 | 0 | for(size_t i = m_buf_pos; i != m_buffer.size(); ++i) |
163 | 0 | m_buffer[i] = 0; |
164 | 0 |
|
165 | 0 | ubi_512(m_buffer.data(), m_buf_pos); |
166 | 0 |
|
167 | 0 | const uint8_t counter[8] = { 0 }; |
168 | 0 |
|
169 | 0 | reset_tweak(SKEIN_OUTPUT, true); |
170 | 0 | ubi_512(counter, sizeof(counter)); |
171 | 0 |
|
172 | 0 | copy_out_vec_le(out, m_output_bits / 8, m_threefish->m_K); |
173 | 0 |
|
174 | 0 | m_buf_pos = 0; |
175 | 0 | initial_block(); |
176 | 0 | } |
177 | | |
178 | | } |