1 /**
2  * Copyright: Copyright (C) Thomas Dixon 2009. All rights reserved.
3  * License:   BSD style: $(LICENSE)
4  * Authors:   Thomas Dixon
5  */
6 
7 module tango.util.cipher.Salsa20;
8 
9 private import tango.util.cipher.Cipher;
10 
11 /** Implementation of Salsa20 designed by Daniel J. Bernstein. */
12 class Salsa20 : StreamCipher
13 {
14     protected
15     {
16         // Constants
17         __gshared immutable immutable(ubyte)[] sigma = cast(immutable(ubyte)[])"expand 32-byte k";
18         __gshared immutable immutable(ubyte)[] tau = cast(immutable(ubyte)[])"expand 16-byte k";
19         
20         // Counter indexes (added for ChaCha)            
21         uint i0, i1;
22                       
23         // Internal state              
24         uint[] state;
25         
26         // Keystream and index marker
27         ubyte[] keyStream;
28         uint index;
29         
30         // Internal copies of the key and IV for resetting the cipher
31         const(ubyte)[] workingKey,
32                        workingIV;
33     }
34     
35     this()
36     {
37         state = new uint[16];
38         
39         // State expanded into bytes
40         keyStream = new ubyte[64];
41         
42         i0 = 8;
43         i1 = 9;
44     }
45 
46     this(bool encrypt, ubyte[] key, ubyte[] iv) {
47         this();
48         init(encrypt, key, iv);
49     }
50 
51     void init(bool encrypt, ubyte[] key, ubyte[] iv)
52     {
53         if (key)
54         {
55             if (key.length != 16 && key.length != 32)
56                 invalid(name()~": Invalid key length. (requires 16 or 32 bytes)");
57             
58             workingKey = key;
59             keySetup();
60             
61             index = 0;
62         }
63         
64         if (!workingKey)
65             invalid(name()~": Key not set.");
66             
67         if (!iv || iv.length != 8)
68             invalid(name()~": 8 byte IV required.");
69             
70         workingIV = iv;
71         ivSetup();
72         
73         _encrypt = _initialized = true;
74     }
75     
76     @property override const(char[]) name()
77     {
78         return "Salsa20";
79     }
80     
81     override ubyte returnByte(ubyte input)
82     {
83         if (!_initialized)
84             invalid (name()~": Cipher not initialized");
85             
86         if (index == 0) {
87             salsa20WordToByte(state, keyStream);
88             state[i0]++;
89             if (!state[i0])
90                 state[i1]++;
91             // As in djb's, changing the IV after 2^70 bytes is the user's responsibility
92             // lol glwt
93         }
94         
95         ubyte result = (keyStream[index]^input);
96         index = (index + 1) & 0x3f;
97         
98         return result;
99     }
100     
101     override uint update(const(void[]) input_, void[] output_)
102     {
103         if (!_initialized)
104             invalid(name()~": Cipher not initialized");
105             
106         const(ubyte[]) input = cast(const(ubyte[])) input_;
107         ubyte[] output = cast(ubyte[]) output_;
108             
109         if (input.length > output.length)
110             invalid(name()~": Output buffer too short");
111             
112         for (int i = 0; i < input.length; i++)
113         {
114             if (index == 0)
115             {
116                 salsa20WordToByte(state, keyStream);
117                 state[i0]++;
118                 if (!state[i0])
119                     state[i1]++;
120                 // As in djb's, changing the IV after 2^70 bytes is the user's responsibility
121                 // lol glwt
122             }
123             output[i] = (keyStream[index]^input[i]);
124             index = (index + 1) & 0x3f; 
125         }
126         
127         return cast(uint)input.length;
128     }
129     
130     override void reset()
131     {
132         keySetup();
133         ivSetup();
134         index = 0;
135     }
136     
137     protected void keySetup()
138     {
139         uint offset;
140         const(ubyte)[] constants;
141         
142         state[1] = ByteConverter.LittleEndian.to!(uint)(workingKey[0..4]);
143         state[2] = ByteConverter.LittleEndian.to!(uint)(workingKey[4..8]);
144         state[3] = ByteConverter.LittleEndian.to!(uint)(workingKey[8..12]);
145         state[4] = ByteConverter.LittleEndian.to!(uint)(workingKey[12..16]);
146         
147         if (workingKey.length == 32)
148         {
149             constants = sigma;
150             offset = 16;
151         } else
152             constants = tau;
153             
154         state[11] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset..offset+4]);
155         state[12] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+4..offset+8]);
156         state[13] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+8..offset+12]);
157         state[14] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+12..offset+16]);
158         state[ 0] = ByteConverter.LittleEndian.to!(uint)(constants[0..4]);
159         state[ 5] = ByteConverter.LittleEndian.to!(uint)(constants[4..8]);
160         state[10] = ByteConverter.LittleEndian.to!(uint)(constants[8..12]);
161         state[15] = ByteConverter.LittleEndian.to!(uint)(constants[12..16]);
162     }
163     
164     protected void ivSetup()
165     {
166         state[6] = ByteConverter.LittleEndian.to!(uint)(workingIV[0..4]);
167         state[7] = ByteConverter.LittleEndian.to!(uint)(workingIV[4..8]);
168         state[8] = state[9] = 0;
169     }
170     
171     protected void salsa20WordToByte(const(uint[]) input, ref ubyte[] output)
172     {
173         uint[] x = new uint[16];
174         x[] = input[0..16];
175         
176         int i;
177         for (i = 0; i < 10; i++)
178         {
179             x[ 4] ^= Bitwise.rotateLeft(x[ 0]+x[12],  7u);
180             x[ 8] ^= Bitwise.rotateLeft(x[ 4]+x[ 0],  9u);
181             x[12] ^= Bitwise.rotateLeft(x[ 8]+x[ 4], 13u);
182             x[ 0] ^= Bitwise.rotateLeft(x[12]+x[ 8], 18u);
183             x[ 9] ^= Bitwise.rotateLeft(x[ 5]+x[ 1],  7u);
184             x[13] ^= Bitwise.rotateLeft(x[ 9]+x[ 5],  9u);
185             x[ 1] ^= Bitwise.rotateLeft(x[13]+x[ 9], 13u);
186             x[ 5] ^= Bitwise.rotateLeft(x[ 1]+x[13], 18u);
187             x[14] ^= Bitwise.rotateLeft(x[10]+x[ 6],  7u);
188             x[ 2] ^= Bitwise.rotateLeft(x[14]+x[10],  9u);
189             x[ 6] ^= Bitwise.rotateLeft(x[ 2]+x[14], 13u);
190             x[10] ^= Bitwise.rotateLeft(x[ 6]+x[ 2], 18u);
191             x[ 3] ^= Bitwise.rotateLeft(x[15]+x[11],  7u);
192             x[ 7] ^= Bitwise.rotateLeft(x[ 3]+x[15],  9u);
193             x[11] ^= Bitwise.rotateLeft(x[ 7]+x[ 3], 13u);
194             x[15] ^= Bitwise.rotateLeft(x[11]+x[ 7], 18u);
195             x[ 1] ^= Bitwise.rotateLeft(x[ 0]+x[ 3],  7u);
196             x[ 2] ^= Bitwise.rotateLeft(x[ 1]+x[ 0],  9u);
197             x[ 3] ^= Bitwise.rotateLeft(x[ 2]+x[ 1], 13u);
198             x[ 0] ^= Bitwise.rotateLeft(x[ 3]+x[ 2], 18u);
199             x[ 6] ^= Bitwise.rotateLeft(x[ 5]+x[ 4],  7u);
200             x[ 7] ^= Bitwise.rotateLeft(x[ 6]+x[ 5],  9u);
201             x[ 4] ^= Bitwise.rotateLeft(x[ 7]+x[ 6], 13u);
202             x[ 5] ^= Bitwise.rotateLeft(x[ 4]+x[ 7], 18u);
203             x[11] ^= Bitwise.rotateLeft(x[10]+x[ 9],  7u);
204             x[ 8] ^= Bitwise.rotateLeft(x[11]+x[10],  9u);
205             x[ 9] ^= Bitwise.rotateLeft(x[ 8]+x[11], 13u);
206             x[10] ^= Bitwise.rotateLeft(x[ 9]+x[ 8], 18u);
207             x[12] ^= Bitwise.rotateLeft(x[15]+x[14],  7u);
208             x[13] ^= Bitwise.rotateLeft(x[12]+x[15],  9u);
209             x[14] ^= Bitwise.rotateLeft(x[13]+x[12], 13u);
210             x[15] ^= Bitwise.rotateLeft(x[14]+x[13], 18u);
211         }
212         
213         for (i = 0; i < 16; i++)
214             x[i] += input[i];
215             
216         int j;    
217         for (i = j = 0; i < x.length; i++,j+=int.sizeof)
218             ByteConverter.LittleEndian.from!(uint)(x[i], output[j..j+int.sizeof]);
219     }
220     
221     /** Salsa20 test vectors */
222     debug (UnitTest)
223     {
224         unittest
225         {
226             __gshared immutable immutable(char)[][] test_keys = [
227                 "80000000000000000000000000000000", 
228                 "0053a6f94c9ff24598eb3e91e4378add",
229                 "00002000000000000000000000000000"~
230                 "00000000000000000000000000000000",
231                 "0f62b5085bae0154a7fa4da0f34699ec"~
232                 "3f92e5388bde3184d72a7dd02376c91c"
233                 
234             ];
235             
236             __gshared immutable immutable(char)[][] test_ivs = [
237                 "0000000000000000",            
238                 "0d74db42a91077de",
239                 "0000000000000000",
240                 "288ff65dc42b92f9"
241             ];
242                  
243             __gshared immutable immutable(char)[][] test_plaintexts = [
244                 "00000000000000000000000000000000"~
245                 "00000000000000000000000000000000"~
246                 "00000000000000000000000000000000"~
247                 "00000000000000000000000000000000",
248                 
249                 "00000000000000000000000000000000"~
250                 "00000000000000000000000000000000"~
251                 "00000000000000000000000000000000"~
252                 "00000000000000000000000000000000",
253                 
254                 "00000000000000000000000000000000"~
255                 "00000000000000000000000000000000"~
256                 "00000000000000000000000000000000"~
257                 "00000000000000000000000000000000",
258                 
259                 "00000000000000000000000000000000"~
260                 "00000000000000000000000000000000"~
261                 "00000000000000000000000000000000"~
262                 "00000000000000000000000000000000"
263                 
264                 
265             ];
266                  
267             __gshared immutable immutable(char)[][] test_ciphertexts = [
268                 "4dfa5e481da23ea09a31022050859936"~ // Expected output
269                 "da52fcee218005164f267cb65f5cfd7f"~
270                 "2b4f97e0ff16924a52df269515110a07"~
271                 "f9e460bc65ef95da58f740b7d1dbb0aa",
272                          
273                 "05e1e7beb697d999656bf37c1b978806"~
274                 "735d0b903a6007bd329927efbe1b0e2a"~
275                 "8137c1ae291493aa83a821755bee0b06"~
276                 "cd14855a67e46703ebf8f3114b584cba",
277                  
278                 "c29ba0da9ebebfacdebbdd1d16e5f598"~
279                 "7e1cb12e9083d437eaaaa4ba0cdc909e"~
280                 "53d052ac387d86acda8d956ba9e6f654"~
281                 "3065f6912a7df710b4b57f27809bafe3",
282                 
283                 "5e5e71f90199340304abb22a37b6625b"~
284                 "f883fb89ce3b21f54a10b81066ef87da"~
285                 "30b77699aa7379da595c77dd59542da2"~
286                 "08e5954f89e40eb7aa80a84a6176663f"
287             ];
288 
289             Salsa20 s20 = new Salsa20();
290             ubyte[] buffer = new ubyte[64];
291             char[] result;
292             for (int i = 0; i < test_keys.length; i++)
293             {
294                 auto key = ByteConverter.hexDecode(test_keys[i]);
295                 auto params = ByteConverter.hexDecode(test_ivs[i]);
296                 
297                 // Encryption
298                 s20.init(true, key, params);
299                 s20.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer);
300                 result = ByteConverter.hexEncode(buffer);
301                 assert(result == test_ciphertexts[i],
302                         s20.name()~": ("~result~") != ("~test_ciphertexts[i]~")");           
303                 
304                 // Decryption
305                 s20.init(false, key, params);
306                 s20.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer);
307                 result = ByteConverter.hexEncode(buffer);
308                 assert(result == test_plaintexts[i],
309                         s20.name()~": ("~result~") != ("~test_plaintexts[i]~")");
310             }   
311         }
312     }
313 }