1 /**
2 * The Skein-512 hash function
3 * 
4 * Copyright:
5 * (C) 2009,2014 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.hash.skein_512;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_SKEIN_512):
15 
16 import botan.hash.hash;
17 import botan.block.threefish;
18 import botan.utils.loadstor;
19 import botan.utils.parsing;
20 import botan.utils.exceptn;
21 import botan.utils.xor_buf;
22 import botan.utils.types;
23 import botan.utils.get_byte;
24 import std.conv : to;
25 import std.algorithm;
26 
27 /**
28 * Skein-512, a SHA-3 candidate
29 */
30 final class Skein512 : HashFunction
31 {
32 public:
33     /**
34     * Params:
35     *  arg_output_bits = the output size of Skein in bits
36     *  arg_personalization = is a string that will paramaterize the
37     * hash output
38     */
39     this(size_t arg_output_bits = 512, string arg_personalization = "") 
40     {
41         m_personalization = arg_personalization;
42         m_output_bits = arg_output_bits;
43         m_threefish = new Threefish512;
44         m_T = SecureVector!ulong(2);
45         m_buffer = SecureVector!ubyte(64); 
46         m_buf_pos = 0;
47 
48         if (m_output_bits == 0 || m_output_bits % 8 != 0 || m_output_bits > 512)
49             throw new InvalidArgument("Bad output bits size for Skein-512");
50         
51         initialBlock();
52     }
53 
54     override @property size_t hashBlockSize() const { return 64; }
55     override @property size_t outputLength() const { return m_output_bits / 8; }
56 
57     override HashFunction clone() const
58     {
59         return new Skein512(m_output_bits, m_personalization);
60     }
61 
62     override @property string name() const
63     {
64         if (m_personalization != "")
65             return "Skein-512(" ~ to!string(m_output_bits) ~ "," ~ m_personalization ~ ")";
66         return "Skein-512(" ~ to!string(m_output_bits) ~ ")";
67     }
68 
69     override void clear()
70     {
71         zeroise(m_buffer);
72         m_buf_pos = 0;
73         
74         initialBlock();
75     }
76 
77 protected:
78     enum type_code {
79         SKEIN_KEY = 0,
80         SKEIN_CONFIG = 4,
81         SKEIN_PERSONALIZATION = 8,
82         SKEIN_PUBLIC_KEY = 12,
83         SKEIN_KEY_IDENTIFIER = 16,
84         SKEIN_NONCE = 20,
85         SKEIN_MSG = 48,
86         SKEIN_OUTPUT = 63
87     }
88 
89     override void addData(const(ubyte)* input, size_t length)
90     {
91         if (length == 0)
92             return;
93         
94         if (m_buf_pos)
95         {
96             bufferInsert(m_buffer, m_buf_pos, input, length);
97             if (m_buf_pos + length > 64)
98             {
99                 ubi_512(m_buffer.ptr, m_buffer.length);
100                 
101                 input += (64 - m_buf_pos);
102                 length -= (64 - m_buf_pos);
103                 m_buf_pos = 0;
104             }
105         }
106         
107         const size_t full_blocks = (length - 1) / 64;
108         
109         if (full_blocks)
110             ubi_512(input, 64*full_blocks);
111         
112         length -= full_blocks * 64;
113         
114         bufferInsert(m_buffer, m_buf_pos, input + full_blocks * 64, length);
115         m_buf_pos += length;
116     }
117 
118     override void finalResult(ubyte* output)
119     {
120         m_T[1] |= ((cast(ulong)1) << 63); // final block flag
121         
122         foreach (size_t i; m_buf_pos .. m_buffer.length)
123             m_buffer[i] = 0;
124         
125         ubi_512(m_buffer.ptr, m_buf_pos);
126         
127         const ubyte[8] counter;
128         
129         resetTweak(type_code.SKEIN_OUTPUT, true);
130         ubi_512(counter.ptr, counter.length);
131         
132         const size_t out_bytes = m_output_bits / 8;
133         
134         foreach (size_t i; 0 .. out_bytes)
135             output[i] = get_byte(7-i%8, m_threefish.m_K[i/8]);
136         
137         m_buf_pos = 0;
138         initialBlock();
139     }
140 
141     void ubi_512(const(ubyte)* msg, size_t msg_len)
142     {
143         SecureVector!ulong M = SecureVector!ulong(8);
144         
145         do
146         {
147             const size_t to_proc = std.algorithm.min(msg_len, 64);
148             m_T[0] += to_proc;
149             
150             loadLittleEndian(M.ptr, msg, to_proc / 8);
151             
152             if (to_proc % 8)
153             {
154                 foreach (size_t j; 0 .. (to_proc % 8))
155                     M[to_proc/8] |= cast(ulong)(msg[8*(to_proc/8)+j]) << (8*j);
156             }
157             
158             m_threefish.skeinFeedfwd(M, m_T);
159             
160             // clear first flag if set
161             m_T[1] &= ~(cast(ulong)(1) << 62);
162             
163             msg_len -= to_proc;
164             msg += to_proc;
165         } while (msg_len);
166     }
167 
168 
169     void initialBlock()
170     {
171         const ubyte[64] zeros;
172         
173         m_threefish.setKey(zeros.ptr, zeros.length);
174         
175         // ASCII("SHA3") followed by version (0x0001) code
176         ubyte[32] config_str;
177         config_str[0 .. 7] = [0x53, 0x48, 0x41, 0x33, 0x01, 0x00, 0 ];
178         storeLittleEndian(cast(uint) m_output_bits, config_str.ptr + 8);
179         
180         resetTweak(type_code.SKEIN_CONFIG, true);
181         ubi_512(config_str.ptr, config_str.length);
182         
183         if (m_personalization != "")
184         {
185             /*
186               This is a limitation of this implementation, and not of the
187               algorithm specification. Could be fixed relatively easily, but
188               doesn't seem worth the trouble.
189             */
190             if (m_personalization.length > 64)
191                 throw new InvalidArgument("Skein m_personalization must be less than 64 bytes");
192             
193             const(ubyte)* bits = cast(const(ubyte)*)(m_personalization.ptr);
194             resetTweak(type_code.SKEIN_PERSONALIZATION, true);
195             ubi_512(bits, m_personalization.length);
196         }
197         
198         resetTweak(type_code.SKEIN_MSG, false);
199     }
200 
201     void resetTweak(type_code type, bool fin)
202     {
203         m_T[0] = 0;
204         
205         m_T[1] = (cast(ulong)(type) << 56) |
206             (cast(ulong)(1) << 62) |
207                 (cast(ulong)(fin) << 63);
208     }
209 
210     string m_personalization;
211     size_t m_output_bits;
212 
213     Unique!Threefish512 m_threefish;
214     SecureVector!ulong m_T;
215     SecureVector!ubyte m_buffer;
216     size_t m_buf_pos;
217 }