1 /**
2 * Salsa20 / XSalsa20
3 * 
4 * Copyright:
5 * (C) 1999-2010 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.stream.salsa20;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_SALSA20):
15 
16 import botan.stream.stream_cipher;
17 import botan.utils.loadstor;
18 import botan.utils.rotate;
19 import botan.utils.xor_buf;
20 import botan.utils.types;
21 import botan.utils.mem_ops;
22 import std.format : format;
23 
24 /**
25 * DJB's Salsa20 (and XSalsa20)
26 */
27 final class Salsa20 : StreamCipher, SymmetricAlgorithm
28 {
29 public:
30     /*
31     * Combine cipher stream with message
32     */
33     override void cipher(const(ubyte)* input, ubyte* output, size_t length)
34     {
35         while (length >= m_buffer.length - m_position)
36         {
37             xorBuf(output, input, &m_buffer[m_position], m_buffer.length - m_position);
38             length -= (m_buffer.length - m_position);
39             input += (m_buffer.length - m_position);
40             output += (m_buffer.length - m_position);
41             salsa20(*cast(ubyte[64]*) m_buffer.ptr, *cast(uint[16]*) m_state.ptr);
42             
43             ++m_state[8];
44             m_state[9] += (m_state[8] == 0);
45             
46             m_position = 0;
47         }
48         
49         xorBuf(output, input, &m_buffer[m_position], length);
50         
51         m_position += length;
52     }
53 
54 
55     /*
56     * Return the name of this type
57     */
58     override void setIv(const(ubyte)* iv, size_t length)
59     {
60         if (!validIvLength(length))
61             throw new InvalidIVLength(name(), length);
62         
63         if (length == 8)
64         {
65             // Salsa20
66             m_state[6] = loadLittleEndian!uint(iv, 0);
67             m_state[7] = loadLittleEndian!uint(iv, 1);
68         }
69         else
70         {
71             // XSalsa20
72             m_state[6] = loadLittleEndian!uint(iv, 0);
73             m_state[7] = loadLittleEndian!uint(iv, 1);
74             m_state[8] = loadLittleEndian!uint(iv, 2);
75             m_state[9] = loadLittleEndian!uint(iv, 3);
76             
77             SecureVector!uint hsalsa = SecureVector!uint(8);
78             hsalsa20(*cast(uint[8]*) hsalsa.ptr, *cast(uint[16]*) m_state.ptr);
79             
80             m_state[ 1] = hsalsa[0];
81             m_state[ 2] = hsalsa[1];
82             m_state[ 3] = hsalsa[2];
83             m_state[ 4] = hsalsa[3];
84             m_state[ 6] = loadLittleEndian!uint(iv, 4);
85             m_state[ 7] = loadLittleEndian!uint(iv, 5);
86             m_state[11] = hsalsa[4];
87             m_state[12] = hsalsa[5];
88             m_state[13] = hsalsa[6];
89             m_state[14] = hsalsa[7];
90         }
91         
92         m_state[8] = 0;
93         m_state[9] = 0;
94         
95         salsa20(*cast(ubyte[64]*) m_buffer.ptr, *cast(uint[16]*) m_state.ptr);
96         ++m_state[8];
97         m_state[9] += (m_state[8] == 0);
98         
99         m_position = 0;
100     }
101 
102     override bool validIvLength(size_t iv_len) const
103     { return (iv_len == 8 || iv_len == 24); }
104 
105     KeyLengthSpecification keySpec() const
106     {
107         return KeyLengthSpecification(16, 32, 16);
108     }
109 
110     /*
111     * Clear memory of sensitive data
112     */
113     void clear()
114     {
115         zap(m_state);
116         zap(m_buffer);
117         m_position = 0;
118     }
119 
120     /*
121     * Return the name of this type
122     */
123     @property string name() const
124     {
125         return "Salsa20";
126     }
127 
128     override Salsa20 clone() const { return new Salsa20; }
129 protected:
130     /*
131     * Salsa20 Key Schedule
132     */
133     override void keySchedule(const(ubyte)* key, size_t length)
134     {
135         __gshared immutable uint[] TAU = [ 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 ];
136 
137         __gshared immutable uint[] SIGMA = [ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 ];
138         
139         const uint[] CONSTANTS = (length == 16) ? TAU : SIGMA;
140         
141         m_state.resize(16);
142         m_buffer.resize(64);
143         
144         m_state[0] = CONSTANTS[0];
145         m_state[5] = CONSTANTS[1];
146         m_state[10] = CONSTANTS[2];
147         m_state[15] = CONSTANTS[3];
148         
149         m_state[1] = loadLittleEndian!uint(key, 0);
150         m_state[2] = loadLittleEndian!uint(key, 1);
151         m_state[3] = loadLittleEndian!uint(key, 2);
152         m_state[4] = loadLittleEndian!uint(key, 3);
153         
154         if (length == 32)
155             key += 16;
156         
157         m_state[11] = loadLittleEndian!uint(key, 0);
158         m_state[12] = loadLittleEndian!uint(key, 1);
159         m_state[13] = loadLittleEndian!uint(key, 2);
160         m_state[14] = loadLittleEndian!uint(key, 3);
161         
162         m_position = 0;
163         
164         const ubyte[8] ZERO;
165         setIv(ZERO.ptr, ZERO.length);
166     }
167 
168     SecureVector!uint m_state;
169     SecureVector!ubyte m_buffer;
170     size_t m_position;
171 }
172 
173 
174 private:
175 
176 /*
177 * Generate HSalsa20 cipher stream (for XSalsa20 IV setup)
178 */
179 void hsalsa20(ref uint[8] output, in uint[16] input)
180 {
181     uint x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3],
182         x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7],
183         x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11],
184         x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15];
185     
186     foreach (size_t i; 0 .. 10)
187     {
188         mixin(    SALSA20_QUARTER_ROUND!(x00, x04, x08, x12) ~
189                 SALSA20_QUARTER_ROUND!(x05, x09, x13, x01) ~
190                 SALSA20_QUARTER_ROUND!(x10, x14, x02, x06) ~
191                 SALSA20_QUARTER_ROUND!(x15, x03, x07, x11) ~
192                 
193                 SALSA20_QUARTER_ROUND!(x00, x01, x02, x03) ~
194                 SALSA20_QUARTER_ROUND!(x05, x06, x07, x04) ~
195                 SALSA20_QUARTER_ROUND!(x10, x11, x08, x09) ~
196                 SALSA20_QUARTER_ROUND!(x15, x12, x13, x14)
197               );
198     }
199     
200     output[0] = x00;
201     output[1] = x05;
202     output[2] = x10;
203     output[3] = x15;
204     output[4] = x06;
205     output[5] = x07;
206     output[6] = x08;
207     output[7] = x09;
208 }
209 
210 /*
211 * Generate Salsa20 cipher stream
212 */
213 void salsa20(ref ubyte[64] output, in uint[16] input)
214 {
215     uint x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3],
216         x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7],
217         x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11],
218         x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15];
219     
220     foreach (size_t i; 0 .. 10)
221     {
222         mixin(    SALSA20_QUARTER_ROUND!(x00, x04, x08, x12) ~
223                 SALSA20_QUARTER_ROUND!(x05, x09, x13, x01) ~
224                 SALSA20_QUARTER_ROUND!(x10, x14, x02, x06) ~
225                 SALSA20_QUARTER_ROUND!(x15, x03, x07, x11) ~
226 
227                 SALSA20_QUARTER_ROUND!(x00, x01, x02, x03) ~
228                 SALSA20_QUARTER_ROUND!(x05, x06, x07, x04) ~
229                 SALSA20_QUARTER_ROUND!(x10, x11, x08, x09) ~
230                  SALSA20_QUARTER_ROUND!(x15, x12, x13, x14)
231               );
232     }
233     
234     storeLittleEndian(x00 + input[ 0], output.ptr + 4 *  0);
235     storeLittleEndian(x01 + input[ 1], output.ptr + 4 *  1);
236     storeLittleEndian(x02 + input[ 2], output.ptr + 4 *  2);
237     storeLittleEndian(x03 + input[ 3], output.ptr + 4 *  3);
238     storeLittleEndian(x04 + input[ 4], output.ptr + 4 *  4);
239     storeLittleEndian(x05 + input[ 5], output.ptr + 4 *  5);
240     storeLittleEndian(x06 + input[ 6], output.ptr + 4 *  6);
241     storeLittleEndian(x07 + input[ 7], output.ptr + 4 *  7);
242     storeLittleEndian(x08 + input[ 8], output.ptr + 4 *  8);
243     storeLittleEndian(x09 + input[ 9], output.ptr + 4 *  9);
244     storeLittleEndian(x10 + input[10], output.ptr + 4 * 10);
245     storeLittleEndian(x11 + input[11], output.ptr + 4 * 11);
246     storeLittleEndian(x12 + input[12], output.ptr + 4 * 12);
247     storeLittleEndian(x13 + input[13], output.ptr + 4 * 13);
248     storeLittleEndian(x14 + input[14], output.ptr + 4 * 14);
249     storeLittleEndian(x15 + input[15], output.ptr + 4 * 15);
250 }
251 
252 enum string SALSA20_QUARTER_ROUND(alias _x1, alias _x2, alias _x3, alias _x4) = q{
253     %2$s ^= rotateLeft(%1$s + %4$s,  7);
254     %3$s ^= rotateLeft(%2$s + %1$s,  9);
255     %4$s ^= rotateLeft(%3$s + %2$s, 13);
256     %1$s ^= rotateLeft(%4$s + %3$s, 18);
257 }.format(__traits(identifier, _x1), __traits(identifier, _x2), __traits(identifier, _x3), __traits(identifier, _x4));