Botan 1.10.17
skein_512.cpp
Go to the documentation of this file.
1/*
2* The Skein-512 hash function
3* (C) 2009-2010 Jack Lloyd
4*
5* Distributed under the terms of the Botan license
6*/
7
8#include <botan/skein_512.h>
9#include <botan/loadstor.h>
10#include <botan/parsing.h>
11#include <botan/exceptn.h>
12#include <botan/rotate.h>
13#include <algorithm>
14
15namespace Botan {
16
17namespace {
18
19enum type_code {
20 SKEIN_KEY = 0,
21 SKEIN_CONFIG = 4,
22 SKEIN_PERSONALIZATION = 8,
23 SKEIN_PUBLIC_KEY = 12,
24 SKEIN_KEY_IDENTIFIER = 16,
25 SKEIN_NONCE = 20,
26 SKEIN_MSG = 48,
27 SKEIN_OUTPUT = 63
28};
29
30void ubi_512(MemoryRegion<u64bit>& H,
32 const byte msg[], size_t msg_len)
33 {
34 do
35 {
36 const size_t to_proc = std::min<size_t>(msg_len, 64);
37 T[0] += to_proc;
38
39 u64bit M[8] = { 0 };
40
41 load_le(M, msg, to_proc / 8);
42
43 if(to_proc % 8)
44 {
45 for(size_t j = 0; j != to_proc % 8; ++j)
46 M[to_proc/8] |= static_cast<u64bit>(msg[8*(to_proc/8)+j]) << (8*j);
47 }
48
49 H[8] = H[0] ^ H[1] ^ H[2] ^ H[3] ^
50 H[4] ^ H[5] ^ H[6] ^ H[7] ^ 0x1BD11BDAA9FC1A22;
51
52 T[2] = T[0] ^ T[1];
53
54 u64bit X0 = M[0] + H[0];
55 u64bit X1 = M[1] + H[1];
56 u64bit X2 = M[2] + H[2];
57 u64bit X3 = M[3] + H[3];
58 u64bit X4 = M[4] + H[4];
59 u64bit X5 = M[5] + H[5] + T[0];
60 u64bit X6 = M[6] + H[6] + T[1];
61 u64bit X7 = M[7] + H[7];
62
63#define THREEFISH_ROUND(I1,I2,I3,I4,I5,I6,I7,I8,ROT1,ROT2,ROT3,ROT4) \
64 do { \
65 X##I1 += X##I2; X##I2 = rotate_left(X##I2, ROT1) ^ X##I1; \
66 X##I3 += X##I4; X##I4 = rotate_left(X##I4, ROT2) ^ X##I3; \
67 X##I5 += X##I6; X##I6 = rotate_left(X##I6, ROT3) ^ X##I5; \
68 X##I7 += X##I8; X##I8 = rotate_left(X##I8, ROT4) ^ X##I7; \
69 } while(0);
70
71#define THREEFISH_INJECT_KEY(r) \
72 do { \
73 X0 += H[(r ) % 9]; \
74 X1 += H[(r+1) % 9]; \
75 X2 += H[(r+2) % 9]; \
76 X3 += H[(r+3) % 9]; \
77 X4 += H[(r+4) % 9]; \
78 X5 += H[(r+5) % 9] + T[(r ) % 3]; \
79 X6 += H[(r+6) % 9] + T[(r+1) % 3]; \
80 X7 += H[(r+7) % 9] + (r); \
81 } while(0);
82
83#define THREEFISH_8_ROUNDS(R1,R2) \
84 do { \
85 THREEFISH_ROUND(0,1,2,3,4,5,6,7, 46,36,19,37); \
86 THREEFISH_ROUND(2,1,4,7,6,5,0,3, 33,27,14,42); \
87 THREEFISH_ROUND(4,1,6,3,0,5,2,7, 17,49,36,39); \
88 THREEFISH_ROUND(6,1,0,7,2,5,4,3, 44, 9,54,56); \
89 \
90 THREEFISH_INJECT_KEY(R1); \
91 \
92 THREEFISH_ROUND(0,1,2,3,4,5,6,7, 39,30,34,24); \
93 THREEFISH_ROUND(2,1,4,7,6,5,0,3, 13,50,10,17); \
94 THREEFISH_ROUND(4,1,6,3,0,5,2,7, 25,29,39,43); \
95 THREEFISH_ROUND(6,1,0,7,2,5,4,3, 8,35,56,22); \
96 \
97 THREEFISH_INJECT_KEY(R2); \
98 } while(0);
99
104 THREEFISH_8_ROUNDS(9,10);
105 THREEFISH_8_ROUNDS(11,12);
106 THREEFISH_8_ROUNDS(13,14);
107 THREEFISH_8_ROUNDS(15,16);
108 THREEFISH_8_ROUNDS(17,18);
109
110 // message feed forward
111 H[0] = X0 ^ M[0];
112 H[1] = X1 ^ M[1];
113 H[2] = X2 ^ M[2];
114 H[3] = X3 ^ M[3];
115 H[4] = X4 ^ M[4];
116 H[5] = X5 ^ M[5];
117 H[6] = X6 ^ M[6];
118 H[7] = X7 ^ M[7];
119
120 // clear first flag if set
121 T[1] &= ~(static_cast<u64bit>(1) << 62);
122
123 msg_len -= to_proc;
124 msg += to_proc;
125 } while(msg_len);
126 }
127
128void reset_tweak(MemoryRegion<u64bit>& T,
129 type_code type, bool final)
130 {
131 T[0] = 0;
132
133 T[1] = (static_cast<u64bit>(type) << 56) |
134 (static_cast<u64bit>(1) << 62) |
135 (static_cast<u64bit>(final) << 63);
136 }
137
138void initial_block(MemoryRegion<u64bit>& H,
140 size_t output_bits,
141 const std::string& personalization)
142 {
143 zeroise(H);
144
145 // ASCII("SHA3") followed by version (0x0001) code
146 byte config_str[32] = { 0x53, 0x48, 0x41, 0x33, 0x01, 0x00, 0 };
147 store_le(u32bit(output_bits), config_str + 8);
148
149 reset_tweak(T, SKEIN_CONFIG, true);
150 ubi_512(H, T, config_str, sizeof(config_str));
151
152 if(personalization != "")
153 {
154 /*
155 This is a limitation of this implementation, and not of the
156 algorithm specification. Could be fixed relatively easily, but
157 doesn't seem worth the trouble.
158 */
159 if(personalization.length() > 64)
160 throw Invalid_Argument("Skein personalization must be <= 64 bytes");
161
162 const byte* bits = reinterpret_cast<const byte*>(personalization.data());
163
164 reset_tweak(T, SKEIN_PERSONALIZATION, true);
165 ubi_512(H, T, bits, personalization.length());
166 }
167
168 reset_tweak(T, SKEIN_MSG, false);
169 }
170
171}
172
173Skein_512::Skein_512(size_t arg_output_bits,
174 const std::string& arg_personalization) :
175 personalization(arg_personalization),
176 output_bits(arg_output_bits),
177 H(9), T(3), buffer(64), buf_pos(0)
178 {
179 if(output_bits == 0 || output_bits % 8 != 0 || output_bits > 64*1024)
180 throw Invalid_Argument("Bad output bits size for Skein-512");
181
182 initial_block(H, T, output_bits, personalization);
183 }
184
185std::string Skein_512::name() const
186 {
187 if(personalization != "")
188 return "Skein-512(" + to_string(output_bits) + "," + personalization + ")";
189 return "Skein-512(" + to_string(output_bits) + ")";
190 }
191
193 {
194 return new Skein_512(output_bits, personalization);
195 }
196
198 {
199 zeroise(H);
200 zeroise(T);
201 zeroise(buffer);
202 buf_pos = 0;
203 }
204
205void Skein_512::add_data(const byte input[], size_t length)
206 {
207 if(length == 0)
208 return;
209
210 if(buf_pos)
211 {
212 buffer.copy(buf_pos, input, length);
213 if(buf_pos + length > 64)
214 {
215 ubi_512(H, T, &buffer[0], buffer.size());
216
217 input += (64 - buf_pos);
218 length -= (64 - buf_pos);
219 buf_pos = 0;
220 }
221 }
222
223 const size_t full_blocks = (length - 1) / 64;
224
225 if(full_blocks)
226 ubi_512(H, T, input, 64*full_blocks);
227
228 length -= full_blocks * 64;
229
230 buffer.copy(buf_pos, input + full_blocks * 64, length);
231 buf_pos += length;
232 }
233
234void Skein_512::final_result(byte out[])
235 {
236 T[1] |= (static_cast<u64bit>(1) << 63); // final block flag
237
238 for(size_t i = buf_pos; i != buffer.size(); ++i)
239 buffer[i] = 0;
240
241 ubi_512(H, T, &buffer[0], buf_pos);
242
243 byte counter[8] = { 0 };
244
245 size_t out_bytes = output_bits / 8;
246
247 SecureVector<u64bit> H_out(9);
248
249 while(out_bytes)
250 {
251 const size_t to_proc = std::min<size_t>(out_bytes, 64);
252
253 H_out.copy(&H[0], 8);
254
255 reset_tweak(T, SKEIN_OUTPUT, true);
256 ubi_512(H_out, T, counter, sizeof(counter));
257
258 for(size_t i = 0; i != to_proc; ++i)
259 out[i] = get_byte(7-i%8, H_out[i/8]);
260
261 out_bytes -= to_proc;
262 out += to_proc;
263
264 for(size_t i = 0; i != sizeof(counter); ++i)
265 if(++counter[i])
266 break;
267 }
268
269 buf_pos = 0;
270 initial_block(H, T, output_bits, personalization);
271 }
272
273}
size_t size() const
Definition secmem.h:29
void copy(const T in[], size_t n)
Definition secmem.h:126
HashFunction * clone() const
std::string name() const
Skein_512(size_t output_bits=512, const std::string &personalization="")
void zeroise(MemoryRegion< T > &vec)
Definition secmem.h:428
byte get_byte(size_t byte_num, T input)
Definition get_byte.h:21
unsigned long long u64bit
Definition types.h:49
std::string to_string(u64bit n, size_t min_len)
Definition parsing.cpp:42
T load_le(const byte in[], size_t off)
Definition loadstor.h:116
std::invalid_argument Invalid_Argument
Definition exceptn.h:20
unsigned int u32bit
Definition types.h:32
void store_le(u16bit in, byte out[2])
Definition loadstor.h:427
#define THREEFISH_8_ROUNDS(R1, R2)