Botan  1.10.9
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 
15 namespace Botan {
16 
17 namespace {
18 
19 enum 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 
30 void ubi_512(MemoryRegion<u64bit>& H,
31  MemoryRegion<u64bit>& T,
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 
100  THREEFISH_8_ROUNDS(1,2);
101  THREEFISH_8_ROUNDS(3,4);
102  THREEFISH_8_ROUNDS(5,6);
103  THREEFISH_8_ROUNDS(7,8);
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 
128 void 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 
138 void initial_block(MemoryRegion<u64bit>& H,
139  MemoryRegion<u64bit>& T,
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 
173 Skein_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 
185 std::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 
205 void 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 
234 void 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 }
T load_le(const byte in[], size_t off)
Definition: loadstor.h:116
void store_le(u16bit in, byte out[2])
Definition: loadstor.h:427
std::invalid_argument Invalid_Argument
Definition: exceptn.h:20
byte get_byte(size_t byte_num, T input)
Definition: get_byte.h:21
void copy(const T in[], size_t n)
Definition: secmem.h:120
std::string name() const
Definition: skein_512.cpp:185
unsigned char byte
Definition: types.h:22
unsigned long long u64bit
Definition: types.h:49
#define THREEFISH_8_ROUNDS(R1, R2)
HashFunction * clone() const
Definition: skein_512.cpp:192
size_t size() const
Definition: secmem.h:29
Skein_512(size_t output_bits=512, const std::string &personalization="")
Definition: skein_512.cpp:173
std::string to_string(u64bit n, size_t min_len)
Definition: parsing.cpp:42
void zeroise(MemoryRegion< T > &vec)
Definition: secmem.h:415
unsigned int u32bit
Definition: types.h:32