1 module dcrypt.streamcipher.salsa;
2 
3 public import dcrypt.streamcipher.streamcipher;
4 
5 import dcrypt.bitmanip: rotl=rotateLeft;
6 import dcrypt.util: wipe;
7 
8 import dcrypt.bitmanip;
9 
10 import std.algorithm: min;
11 
12 // Test Salsa20
13 unittest {
14 	
15 	// test vectors generated with bouncycastle Salsa20 implementation
16 	string[] keys = [
17 		x"00000000000000000000000000000000",
18 		x"01010101010101010101010101010101",
19 		x"0202020202020202020202020202020202020202020202020202020202020202",
20 		x"0303030303030303030303030303030303030303030303030303030303030303",
21 	];
22 
23 	string[] ivs = [
24 		x"0101010101010101",
25 		x"0202020202020202",
26 		x"0303030303030303",
27 		x"0404040404040404",
28 	];
29 
30 	string[] plains = [
31 		x"0202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202",
32 		x"0303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303",
33 		x"0404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404",
34 		x"0505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505",
35 	];
36 
37 	string[] ciphers = [
38 		x"4280c83f61325a4b1c2ba27d5693c016a08b4eb5afe070991307a89a4f6787e734ef067bb35521ea44a4e07d90e64140959e10db4de238918956fc9a8b45245c6df266aed0f064f8be9d769380049b173f3ea0a665dcb46b85f5a9f01406e0cb7186fab2328ddc39051722caeaf2166c00a93e447df93eae9610b82af86c0ed9",
39 		x"3f1d4e1227cd3483068269ed5914a61dedce4e0fe2736d29e9dac4404a994423feb51748ba99e1f94599c53ee97f58d5b002c9bf04f9a0c2cc4d186620b7a23e312bfaf3d90b76f1fabaaa7a1526160d38fe64acb4af703e2d084424efcdf975825f5fcf39f3286375532ff7206b64b81ae65a0ada0005a5af6d035fb6b38221",
40 		x"e390de08ceb0eca5c373e7fbc62ae1a76eea14b3302bda97833801da174ef6464f1592d563c30b3da9c99c2b7850a6f93d036bdaf82788aa870a20e2fc384c61f33e45f52e03903a0ab55807b18920db7481bf5f4975faa4e0fc595dc3e8d23145b288e48ae4b895ce58198624b660867eb8226582f72879f4b90eb9a5e67067",
41 		x"a64037f78e86e53cade29afa3533719ea5b7b71162911bd06e351f172692602cc5c90b022652292f0a78530e4961004cd38b14a22c8a483f386987293c1c24fe1882cb13221d4ec6fa63082c586aab1e2d9fed293b17b1159f1c2f4fdc007df5c9a8c1b026d254262c6e28c0d2f0c8b3cfa83be5b3dbc29166f74123c7dfc54c",
42 	];
43 
44 	streamCipherTest(new Salsa20Engine, keys, plains, ciphers, ivs);
45 }
46 
47 // Test XSalsa
48 unittest {
49 	
50 	// test vectors generated with bouncycastle XSalsa20 implementation
51 	string[] keys = [
52 		x"86e2f31305b14bc42caf3f9c7fb4112cc7ae64cf43e0d429a27fa63b70d0939e",
53 		x"b96baefe0fa3144455926da4a8583643107bda7926b2ea4577776f9ca89a9d00",
54 		x"a5f519c06ede84d97d0d6dc8a9cfa52cfe532908e0dfdb03a875a948866cd77b",
55 		x"c758bac8a6a4a405aa057a9d90621afa9f23eeb5157f65474e0cdb11284bea77",
56 	];
57 	string[] ivs = [
58 		x"24eaeaac41c512e3eb77bc051c4e98ab565122ea4d274b4f",
59 		x"17af8f7d0a89e15c6587d3a4f6bb20a75a7ea9d70c96e01c",
60 		x"0a702d29a25362429c5a5f5e3dd86580c733fb94aff70037",
61 		x"4201c53a16675ee4b95a85572c59c6e6cdc2faad8c77bc49",
62 	];
63 	string[] plains = [
64 		x"dfe73782b7b40e000084dcd8170f95549180c8b546b8cce7823a38b11fd78f0fb94fa0720cffae5411e5108c9bd186b9f6ab7630477d8b2ca4160c9deeda271ded4a6dd782962b68315c0b9220b122a70bfee4d426eda7f1a44b562659c525bf23a5c692c8c79bb37d11e4386e5a8a096c333038ac590598ef59b6dd7b8969d6",
65 		x"1107c901f7330c80f24d02581bcd027c6a58cbc2809eebfd1c9182227875571da45c9db20e51421d2b970846e5c72ef1b5b8fdae6e7b59ad9d583dc87133c47686b123627b98e0422fb86495f73060c882302b9c20d310e4e0eddd5daca2f028952d925394afe04a9718f3e5fbe7879665c618a9e05c86bb286dc455898f53da",
66 		x"a2262bc781d520f799b31a18ad51501072670fa33e6d1c799c7e9c97322485b44800f33f81d9c85c750045f8acedfd61b31f064c4c36771586ea86b2441273936af2644644c3aa8a521ee03ddabdcb6a05177ebab78143ea0dcfd98ef3301f5b76f1f847fb24464f41bba616feb75c2cbb8629807b3dc9fe63bb7b4abcf94a60",
67 		x"a840111122d4db70f5fbfdea485a37f3ab621855ff44b29843b49d26499f6acbc809bd51d19f7ebab48d99265dddab8832795b526ab688048ce80a0f6b2e938e9568bdb7e90aae58f665f653a1c5b606b0cfac4a8fdb48e340a9c128a5aadea33c8258ea57d1660096cf0858c0e5bb98c7431121b7435e82c62df79ce11a99a1",
68 	];
69 	string[] ciphers = [
70 		x"7b64de1c7c6dec7eb70b79edf5c9d5812309a344e0f9ca53f18c922f03604d616a8008363bd82fc53341c32825ddb8ae371b242fa8eed90afdd38659a2304c13c774816e6c1b3022eebb8092971d3393406f8c70c8a02471146813906ac74e66751bb3dbb21a07913a69c1fcd8e9af0d3b23f13c74872da21eeef0a8578e5873",
71 		x"80d4be693aa49d763ec1dffa251a6bb0f83402902a8175f5759d40046bc2e0ced8a8239f5a2d2caf28846f8b0e8e0b471ec6d61ed19b268c5d4ed2aff87bb1f07adf0297d305767b70eda08a29c16f04825b7edefdcbc77fcfbd2c9fad63e0d8409dc7a661add37babf814d76aec15ad435b8d9393793189c76f3e51cce31e3b",
72 		x"8303327859df863abf0b932e3609b862b0e2399f277bbdc194fe19d9f6ad83685f0f2881db383677962d0ef5ae15e30c80cd03b994abc20a5e27a2b7c4c23ab2b045df862a315e5b5329e41183c98acfb2434ebccdf19005204b4d0c7541c3c517bbfc555c54c5d164be5b50ce22182dcb37b9e1a42a19390107683160e97c00",
73 		x"b8c77c22f789d71679afd50aeb51dcbca26066fc55cee32e5ce3647d89de1bc664f9760ca6ae3037104387ffd1ae6aaae76f7ea1a3c2cac7dc5e5fedf581f8ba3c5025c163cfe7f03337a5ada2e34c573da2149994e805101f829e774e91338e730f07ad870b94bf71a575af3dd029fabe8e874eb655843d8f37bc01a5cfc818",
74 	];
75 
76 	streamCipherTest(new XSalsa20Engine, keys, plains, ciphers, ivs);
77 }
78 
79 alias Salsa!20 Salsa20;
80 alias StreamCipherWrapper!Salsa20 Salsa20Engine;
81 
82 alias Salsa!(20, true) XSalsa20;
83 alias StreamCipherWrapper!XSalsa20 XSalsa20Engine;
84 
85 static assert(isStreamCipher!Salsa20, "Salsa20 is not a stream cipher!");
86 static assert(isStreamCipher!XSalsa20, "XSalsa20 is not a stream cipher!");
87 
88 ///
89 ///	implementation of the Salsa20/20 stream cipher
90 ///
91 /// Params:
92 /// rounds = Number of rounds. 12 and 20 are allowed. Default is 20.
93 ///
94 @safe
95 public struct Salsa(uint rounds = 20, bool xsalsa = false)
96 	if(rounds == 12 || rounds == 20)
97 {
98 
99 	@nogc nothrow:
100 
101 	static if(xsalsa) {
102 		public enum name = "XSalsa20/"~rounds;
103 	} else {
104 		public enum name = "Salsa20/"~rounds;
105 	}
106 
107 	private {
108 
109 		enum stateSize = 16; // 16, 32 bit ints = 64 bytes
110 		
111 		/*
112 		 * variables to hold the state of the engine
113 		 * during encryption and decryption
114 		 */
115 		uint				index = 0;
116 		uint[stateSize]		engineState; /// state
117 		ubyte[stateSize*4]	keyStream; /// expanded state, 64 bytes
118 		bool				initialized = false;
119 		
120 		/*
121 		 * internal counter
122 		 */
123 		uint cW0, cW1, cW2;
124 		
125 	}
126 
127 	@safe
128 	~this() {
129 		wipe(engineState);
130 		wipe(keyStream);
131 	}
132 
133 	/// Initialize the cipher.
134 	/// 
135 	/// Params:
136 	/// forEncryption = Not used because encryption and decryption is actually the same.
137 	/// key = secret key
138 	/// iv = Use a unique nonce per key.
139 	public void start(bool forEncryption, in ubyte[] key, in ubyte[] iv) nothrow @nogc
140 	in {
141 		static if(xsalsa) {
142 			assert(key.length == 32, "XSalsa requires a 256 bit key.");
143 			assert(iv.length == 24, "XSalsa needs a 192 bit nonce.");
144 		} else {
145 			assert(key.length == 16 || key.length == 32, "Salsa20 needs 128 or 256 bit keys.");
146 			assert(iv.length == 8, "Salsa20 needs a 8 byte IV.");
147 		}
148 	}
149 	body {
150 		static if(xsalsa) {
151 			// XSalsa
152 			ubyte[32] xkey = HSalsa(key, iv[0..16]);
153 			scope(exit) wipe(xkey);
154 			initState(engineState, xkey, 0, iv[16..24]);
155 		} else {
156 			// Salsa
157 			initState(engineState, key, 0, iv);
158 		}
159 		index = 0;
160 		resetCounter();
161 		initialized = true;
162 	}
163 
164 	/// Encrypt or decrypt input bytes (but no more than 2^70 bytes!).
165 	/// 
166 	/// Params:
167 	/// input = input bytes
168 	/// output = buffer for output bytes. length must match input length.
169 	/// 
170 	/// Returns: Slice pointing to processed data which might be smaller than `output`.
171 	/// 
172 	/// Throws: Error if limit of 2^70 bytes is exceeded.
173 	///
174 	public ubyte[] processBytes(in ubyte[] input, ubyte[] output)
175 	in {
176 		assert(output.length >= input.length, "output buffer too short");
177 		assert(initialized, "Salsa20Engine not initialized!");
178 	}
179 	body {
180 
181 		// can't encrypt more than 2^70 bytes per iv
182 		if (limitExceeded(input.length))
183 		{
184 			assert(false, "2^70 byte limit per IV would be exceeded. Change IV!");
185 		}
186 
187 		ubyte[] initialOutputSlice = output;
188 		const (ubyte)[] inp = input;
189 
190 		while(inp.length > 0) {
191 
192 			if (index == 0)
193 			{
194 				nextKeyStreamBlock();
195 			}
196 
197 			size_t len = min(keyStream.length-index, inp.length);
198 			output[0..len] = inp[0..len]^keyStream[index..index+len];
199 			index = (index + len) % keyStream.length;
200 			inp = inp[len..$];
201 			output = output[len..$];
202 		}
203 
204 		
205 		return initialOutputSlice[0..input.length];
206 	}
207 
208 	public ubyte processByte(in ubyte b)
209 	in {
210 		assert(initialized, "Salsa20Engine not initialized!");
211 	}
212 	body {
213 		
214 		// can't encrypt more than 2^70 bytes per iv
215 		if (limitExceeded(1))
216 		{
217 			assert(false, "2^70 byte limit per IV would be exceeded. Change IV!");
218 		}
219 
220 		if (index == 0)
221 		{
222 			nextKeyStreamBlock();
223 		}
224 		enum len = 1;
225 
226 		ubyte o = b^keyStream[index];
227 
228 		index = (index + len) % keyStream.length;
229 
230 		return o;
231 	}
232 
233 	/// Salsa20/rounds function
234 	///
235 	/// Params:
236 	/// rounds = number of rounds (20 in default implementation)
237 	/// input = input data
238 	/// x = output buffer where keystream gets written to   
239 	public static void block(uint rounds = 20)(in uint[] input, uint[] output) pure nothrow @nogc
240 		if(rounds % 2 == 0 || rounds > 0)
241 		in {
242 			assert(input.length == 16, "invalid input length");
243 			assert(output.length == 16, "invalid output buffer length");
244 		} body {
245 		
246 		uint[16] x = input;
247 		
248 		salsaDoubleRound!rounds(x);
249 		
250 		// element wise addition
251 		x[] += input[];
252 		output[] = x[];
253 	}
254 
255 	public static void block(uint rounds = 20)(in ref uint[16] input, ref uint[16] output) pure nothrow @nogc
256 		if(rounds % 2 == 0 || rounds > 0)
257 		in {
258 			assert(input.length == 16, "invalid input length");
259 			assert(output.length == 16, "invalid output buffer length");
260 		} body {
261 		
262 		uint[16] x = input;
263 		
264 		salsaDoubleRound!rounds(x);
265 		
266 		// element wise addition
267 		x[] += input[];
268 		output[] = x[];
269 	}
270 
271 	/// Params:
272 	/// keyBytes = key, 16 or 32 bytes.
273 	/// ivBytes = iv, exactly 8 bytes.
274 	public static void initState(ref uint[16] state, in ubyte[] keyBytes, in uint counter, in ubyte[] ivBytes) nothrow @nogc
275 	in {
276 		assert(keyBytes.length == 16 || keyBytes.length == 32, "invalid key length");
277 		assert(ivBytes.length == 8, "invalid iv length");
278 	}
279 	body {
280 
281 		uint[4] constants;
282 		
283 		// Key
284 		fromLittleEndian(keyBytes[0..16], state[1..5]);
285 		
286 		if (keyBytes.length == 32)
287 		{
288 			constants = sigma;
289 
290 			fromLittleEndian(keyBytes[16..32], state[11..15]);
291 		}
292 		else
293 		{
294 			// repeat the 128 bit key
295 			constants = tau;
296 			fromLittleEndian(keyBytes[0..16], state[11..15]);
297 		}
298 
299 		state[0] = constants[0];
300 		state[5] = constants[1];
301 		state[10] = constants[2];
302 		state[15] = constants[3];
303 
304 		// IV
305 		fromLittleEndian!uint(ivBytes[0..$], state[6..8]);
306 
307 		// counter
308 		state[8] = counter;
309 		state[9] = 0;
310 	}
311 
312 	
313 	//
314 	// Private implementation
315 	//
316 
317 private:
318 
319 	/// generate a block (64 bytes) of keystream
320 	void nextKeyStreamBlock() nothrow @nogc
321 	{
322 		uint[stateSize] x;
323 		block!rounds(engineState, x);
324 		toLittleEndian!uint(x, keyStream);
325 
326 		// increment counter
327 		// engineState[9] += ++engineState[8] == 0;
328 		if (++engineState[8] == 0)
329 		{
330 			++engineState[9];
331 		}
332 	}
333 
334 	void resetCounter() nothrow @nogc
335 	{
336 		cW0 = 0;
337 		cW1 = 0;
338 		cW2 = 0;
339 	}
340 
341 	bool limitExceeded() nothrow @nogc
342 	{
343 		if (++cW0 == 0)
344 		{
345 			if (++cW1 == 0)
346 			{
347 				return (++cW2 & 0x20) != 0;          // 2^(32 + 32 + 6)
348 			}
349 		}
350 		
351 		return false;
352 	}
353 
354 	/*
355 	 * test if limit will be exceeded for input of size len
356 	 */
357 	bool limitExceeded(size_t len) nothrow @nogc
358 	{
359 		cW0 += len;
360 		if (cW0 < len && cW0 >= 0)
361 		{
362 			if (++cW1 == 0)
363 			{
364 				return (++cW2 & 0x20) != 0;          // 2^(32 + 32 + 6)
365 			}
366 		}
367 		
368 		return false;
369 	}
370 }
371 
372 private {
373 	// constants
374 	
375 	enum uint[4] sigma	= [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]; //cast(ubyte[16])"expand 32-byte k";
376 	enum uint[4] tau	= [0x61707865, 0x3120646e, 0x79622d36, 0x6b206574]; //cast(ubyte[16])"expand 16-byte k";
377 
378 }
379 
380 /// Executes the double round function rounds/2 times.
381 ///
382 /// Params:
383 /// rounds = number of rounds.
384 /// x = the state. 
385 private void salsaDoubleRound(uint rounds)(uint[] x) pure nothrow @nogc
386 	if(rounds % 2 == 0 || rounds > 0)
387 	in {
388 		assert(x.length == 16, "invalid state length");
389 } body {
390 	foreach (i; 0..rounds/2)
391 	{
392 		x[ 4] ^= rotl((x[ 0]+x[12]), 7);
393 		x[ 8] ^= rotl((x[ 4]+x[ 0]), 9);
394 		x[12] ^= rotl((x[ 8]+x[ 4]),13);
395 		x[ 0] ^= rotl((x[12]+x[ 8]),18);
396 		x[ 9] ^= rotl((x[ 5]+x[ 1]), 7);
397 		x[13] ^= rotl((x[ 9]+x[ 5]), 9);
398 		x[ 1] ^= rotl((x[13]+x[ 9]),13);
399 		x[ 5] ^= rotl((x[ 1]+x[13]),18);
400 		x[14] ^= rotl((x[10]+x[ 6]), 7);
401 		x[ 2] ^= rotl((x[14]+x[10]), 9);
402 		x[ 6] ^= rotl((x[ 2]+x[14]),13);
403 		x[10] ^= rotl((x[ 6]+x[ 2]),18);
404 		x[ 3] ^= rotl((x[15]+x[11]), 7);
405 		x[ 7] ^= rotl((x[ 3]+x[15]), 9);
406 		x[11] ^= rotl((x[ 7]+x[ 3]),13);
407 		x[15] ^= rotl((x[11]+x[ 7]),18);
408 		x[ 1] ^= rotl((x[ 0]+x[ 3]), 7);
409 		x[ 2] ^= rotl((x[ 1]+x[ 0]), 9);
410 		x[ 3] ^= rotl((x[ 2]+x[ 1]),13);
411 		x[ 0] ^= rotl((x[ 3]+x[ 2]),18);
412 		x[ 6] ^= rotl((x[ 5]+x[ 4]), 7);
413 		x[ 7] ^= rotl((x[ 6]+x[ 5]), 9);
414 		x[ 4] ^= rotl((x[ 7]+x[ 6]),13);
415 		x[ 5] ^= rotl((x[ 4]+x[ 7]),18);
416 		x[11] ^= rotl((x[10]+x[ 9]), 7);
417 		x[ 8] ^= rotl((x[11]+x[10]), 9);
418 		x[ 9] ^= rotl((x[ 8]+x[11]),13);
419 		x[10] ^= rotl((x[ 9]+x[ 8]),18);
420 		x[12] ^= rotl((x[15]+x[14]), 7);
421 		x[13] ^= rotl((x[12]+x[15]), 9);
422 		x[14] ^= rotl((x[13]+x[12]),13);
423 		x[15] ^= rotl((x[14]+x[13]),18);
424 	}
425 }
426 
427 /// HSalsa as defined in http://cr.yp.to/snuffle/xsalsa-20110204.pdf
428 /// Params:
429 /// key = 32 byte key.
430 /// nonce = 24 byte nonce.
431 /// 
432 /// Returns: 256 bit value.
433 ubyte[32] HSalsa(uint rounds = 20)(in ubyte[] key, in ubyte[] nonce) pure nothrow @nogc
434 	if(rounds == 12 || rounds == 20)
435 	in {
436 		assert(key.length == 32, "HSalsa requires 256 bit key.");
437 		assert(nonce.length == 16, "HSalsa requires 128 bit nonce.");
438 } body {
439 	uint[16] x;
440 	uint[8] z;
441 
442 	scope(exit) {
443 		wipe(x);
444 		wipe(z);
445 	}
446 
447 	x[0] = sigma[0];
448 	x[5] = sigma[1];
449 	x[10] = sigma[2];
450 	x[15] = sigma[3];
451 
452 	fromLittleEndian!uint(key[0*4..4*4], x[1..5]);
453 	fromLittleEndian!uint(key[4*4..8*4], x[11..15]);
454 
455 	fromLittleEndian!uint(nonce, x[6..10]);
456 
457 	salsaDoubleRound!rounds(x);
458 
459 	z[0] = x[0];
460 	z[1] = x[5];
461 	z[2] = x[10];
462 	z[3] = x[15];
463 	z[4..8] = x[6..10];
464 
465 	ubyte[32] output;
466 	toLittleEndian!uint(z, output);
467 	return output;
468 }