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