1 module dcrypt.crypto.engines.salsa20;
2 
3 public import dcrypt.crypto.streamcipher;
4 public import dcrypt.crypto.params.keyparameter;
5 
6 import dcrypt.util.bitmanip: rotl=rotateLeft;
7 
8 import dcrypt.util.pack;
9 import dcrypt.exceptions;
10 
11 // test different keys, ivs and plain texts
12 unittest {
13 	
14 	// test vectors generated with bouncycastle Salsa20 implementation
15 	string[] keys = [
16 		x"00000000000000000000000000000000",
17 		x"01010101010101010101010101010101",
18 		x"02020202020202020202020202020202",
19 		x"03030303030303030303030303030303",
20 		x"04040404040404040404040404040404",
21 		x"05050505050505050505050505050505",
22 		x"0606060606060606060606060606060606060606060606060606060606060606",
23 		x"0707070707070707070707070707070707070707070707070707070707070707",
24 		x"0808080808080808080808080808080808080808080808080808080808080808",
25 		x"0909090909090909090909090909090909090909090909090909090909090909",
26 		x"0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a",
27 		x"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
28 	];
29 
30 	string[] ivs = [
31 		x"0101010101010101",
32 		x"0202020202020202",
33 		x"0303030303030303",
34 		x"0404040404040404",
35 		x"0505050505050505",
36 		x"0606060606060606",
37 		x"0707070707070707",
38 		x"0808080808080808",
39 		x"0909090909090909",
40 		x"0a0a0a0a0a0a0a0a",
41 		x"0b0b0b0b0b0b0b0b",
42 		x"0c0c0c0c0c0c0c0c",
43 	];
44 
45 	string[] plains = [
46 		x"0202020202020202020202020202020202020202020202020202020202020202",
47 		x"0303030303030303030303030303030303030303030303030303030303030303",
48 		x"0404040404040404040404040404040404040404040404040404040404040404",
49 		x"0505050505050505050505050505050505050505050505050505050505050505",
50 		x"0606060606060606060606060606060606060606060606060606060606060606",
51 		x"0707070707070707070707070707070707070707070707070707070707070707",
52 		x"0808080808080808080808080808080808080808080808080808080808080808",
53 		x"0909090909090909090909090909090909090909090909090909090909090909",
54 		x"0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a",
55 		x"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
56 		x"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
57 		x"0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d",
58 	];
59 
60 	string[] ciphers = [
61 		x"4280c83f61325a4b1c2ba27d5693c016a08b4eb5afe070991307a89a4f6787e7",
62 		x"3f1d4e1227cd3483068269ed5914a61dedce4e0fe2736d29e9dac4404a994423",
63 		x"364dc45c17c762f274fcfb1ec04a2c56105e1af3f487f3e72f8dd7d6a9f4d625",
64 		x"75f0241f020912c83572024d36fa27a97e357de3177e0b66c5855d0492262145",
65 		x"1a336b436f2fbc172944cb70309b5b683f0348e7dba08536fa7b452bb54ba8b9",
66 		x"e850390fd95ac10048e85ac9443b2247f72e55215a865f21742ff1a62645d64d",
67 		x"002d2a162c7143b5c74c4c928101d1479791c1d475beec08d361afdf92e82416",
68 		x"6955d342676abe02a858d4b0edba48b5a015987643c5bedce850670f7fb5f0bc",
69 		x"9034ddf6d47e56c8d1b29283794464485390d22669569a58e271b85fe75f62e7",
70 		x"d39db03f960056879dcc76169e165cb49e9be7c73abead205ec9eeaf0997896e",
71 		x"b9b544b4299c75723ddaee3f8781748ccf89cfef17a8468000e0e7d2d5359c22",
72 		x"27760fc0a53dae75e8df893f0281e1ab7f8740bdc52d0527db2fa0d2d4273b0c",
73 	];
74 
75 	streamCipherTest(new Salsa20Engine, keys, plains, ciphers, ivs);
76 }
77 
78 alias StreamCipherWrapper!Salsa20 Salsa20Engine;
79 
80 static assert(isStreamCipher!Salsa20, "Salsa20 is not a stream cipher!");
81 
82 ///
83 ///	implementation of the Salsa20/20 stream cipher
84 ///
85 @safe
86 public struct Salsa20 {
87 
88 	public enum stateSize = 16; // 16, 32 bit ints = 64 bytes
89 	public enum name = "Salsa20/20";
90 
91 	private {
92 		// constants
93 
94 		immutable static {
95 			ubyte[]
96 			sigma = cast(immutable ubyte[])"expand 32-byte k",
97 				tau   = cast(immutable ubyte[])"expand 16-byte k";
98 		}
99 
100 		/*
101 		 * variables to hold the state of the engine
102 		 * during encryption and decryption
103 		 */
104 		uint					index = 0;
105 		uint[stateSize]		engineState; /// state
106 		uint[stateSize]		x  ; /// internal buffer
107 		ubyte[stateSize*4]	keyStream; /// expanded state, 64 bytes
108 		ubyte[32]			workingKey;
109 		size_t				workingKeyLength = 32;
110 		ubyte[8]			workingIV;
111 		bool				initialized = false;
112 		
113 		/*
114 		 * internal counter
115 		 */
116 		uint cW0, cW1, cW2;
117 
118 		invariant {
119 			assert(workingKeyLength == 16 || workingKeyLength == 32, "invalid workingKey length: must be 16 or 32 bytes");
120 		}
121 		
122 	}
123 
124 	@safe @nogc nothrow
125 	~this() {
126 		import dcrypt.util.util: wipe;
127 		
128 		wipe(engineState);
129 		wipe(x);
130 		wipe(keyStream);
131 		wipe(workingKey);
132 		wipe(workingIV);
133 	}
134 
135 	/// Initialize the cipher.
136 	/// 
137 	/// Params:
138 	/// forEncryption = Not used because encryption and decryption is actually the same.
139 	/// key = secret key
140 	/// iv = Use a unique nonce per key.
141 	public void start(bool forEncryption, in ubyte[] key, in ubyte[] iv) nothrow @nogc
142 	in {
143 		assert(key.length == 16 || key.length == 32, "Salsa20 needs 128 or 256 bit keys.");
144 		assert(iv.length == 8, "Salsa20 needs a 8 byte IV.");
145 	}
146 	body {
147 		setKey(key, iv);
148 	}
149 
150 	/// 
151 	/// Throws: MaxBytesExceededException = if limit of 2^70 bytes is exceeded
152 	///
153 	public ubyte returnByte(ubyte input)
154 	{
155 		if (limitExceeded())
156 		{
157 			throw new MaxBytesExceededException("2^70 byte limit per IV. Change IV");
158 		}
159 		
160 		if (index == 0)
161 		{
162 			generateKeyStream(keyStream);
163 			
164 			if (++engineState[8] == 0)
165 			{
166 				++engineState[9];
167 			}
168 		}
169 		
170 		ubyte output = keyStream[index]^input;
171 		index = (index + 1) & 63;
172 		
173 		return output;
174 	}
175 
176 	///
177 	/// encrypt or decrypt input bytes but no more than 2^70!
178 	/// 
179 	/// Params:
180 	/// input = input bytes
181 	/// output = buffer for output bytes. length must match input length.
182 	/// 
183 	/// Throws: MaxBytesExceededException = if limit of 2^70 bytes is exceeded
184 	///
185 	public void processBytes(in ubyte[] input, ubyte[] output)
186 	in {
187 		assert(output.length >= input.length, "output buffer too short");
188 		assert(initialized, "Salsa20Engine not initialized!");
189 	}
190 	body {
191 
192 		// can't encrypt more than 2^70 bytes per iv
193 		if (limitExceeded(input.length))
194 		{
195 			throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded. Change IV!");
196 		}
197 
198 		for (size_t i = 0; i < input.length; i++)
199 		{
200 			if (index == 0)
201 			{
202 				generateKeyStream(keyStream);
203 				
204 				if (++engineState[8] == 0)
205 				{
206 					++engineState[9];
207 				}
208 			}
209 			
210 			output[i] = (keyStream[index]^input[i]);
211 			index = (index + 1) & 63;
212 		}
213 	}
214 
215 	/// reset the cipher to its initial state
216 	public void reset() nothrow @nogc
217 	in {
218 		assert(initialized, "not yet initialized");
219 	}
220 	body {
221 		setKey(workingKey, workingIV);
222 	}
223 
224 	
225 	/**
226 	 * Salsa20 function
227 	 *
228 	 * Params:
229 	 * rounds = number of rounds (20 in default implementation)
230 	 * input = input data
231 	 * x = output buffer where keystream gets written to
232 	 */    
233 	public final static void salsaCore(uint rounds)(in uint[] input, uint[] x) pure nothrow @nogc
234 	in {
235 		assert(input.length == stateSize, "invalid input length");
236 		assert(x.length == stateSize, "x: invalid length");
237 
238 	}
239 	body {
240 
241 		static assert(rounds % 2 == 0 || rounds > 0, "rounds must be a even number and > 0");
242 
243 		x[] = input[];
244 		
245 		for (int i = rounds; i > 0; i -= 2)
246 		{
247 			x[ 4] ^= rotl((x[ 0]+x[12]), 7);
248 			x[ 8] ^= rotl((x[ 4]+x[ 0]), 9);
249 			x[12] ^= rotl((x[ 8]+x[ 4]),13);
250 			x[ 0] ^= rotl((x[12]+x[ 8]),18);
251 			x[ 9] ^= rotl((x[ 5]+x[ 1]), 7);
252 			x[13] ^= rotl((x[ 9]+x[ 5]), 9);
253 			x[ 1] ^= rotl((x[13]+x[ 9]),13);
254 			x[ 5] ^= rotl((x[ 1]+x[13]),18);
255 			x[14] ^= rotl((x[10]+x[ 6]), 7);
256 			x[ 2] ^= rotl((x[14]+x[10]), 9);
257 			x[ 6] ^= rotl((x[ 2]+x[14]),13);
258 			x[10] ^= rotl((x[ 6]+x[ 2]),18);
259 			x[ 3] ^= rotl((x[15]+x[11]), 7);
260 			x[ 7] ^= rotl((x[ 3]+x[15]), 9);
261 			x[11] ^= rotl((x[ 7]+x[ 3]),13);
262 			x[15] ^= rotl((x[11]+x[ 7]),18);
263 			x[ 1] ^= rotl((x[ 0]+x[ 3]), 7);
264 			x[ 2] ^= rotl((x[ 1]+x[ 0]), 9);
265 			x[ 3] ^= rotl((x[ 2]+x[ 1]),13);
266 			x[ 0] ^= rotl((x[ 3]+x[ 2]),18);
267 			x[ 6] ^= rotl((x[ 5]+x[ 4]), 7);
268 			x[ 7] ^= rotl((x[ 6]+x[ 5]), 9);
269 			x[ 4] ^= rotl((x[ 7]+x[ 6]),13);
270 			x[ 5] ^= rotl((x[ 4]+x[ 7]),18);
271 			x[11] ^= rotl((x[10]+x[ 9]), 7);
272 			x[ 8] ^= rotl((x[11]+x[10]), 9);
273 			x[ 9] ^= rotl((x[ 8]+x[11]),13);
274 			x[10] ^= rotl((x[ 9]+x[ 8]),18);
275 			x[12] ^= rotl((x[15]+x[14]), 7);
276 			x[13] ^= rotl((x[12]+x[15]), 9);
277 			x[14] ^= rotl((x[13]+x[12]),13);
278 			x[15] ^= rotl((x[14]+x[13]),18);
279 		}
280 		
281 		// element wise addition
282 		x[] += input[];
283 		
284 	}
285 
286 	//
287 	// Private implementation
288 	//
289 
290 private:
291 
292 	/// Params:
293 	/// keyBytes = key, 16 or 32 bytes
294 	/// ivBytes = iv, exactly 8 bytes
295 	void setKey(in ubyte[] keyBytes, in ubyte[] ivBytes) nothrow @nogc
296 	in {
297 		assert(keyBytes.length == 16 || keyBytes.length == 32, "invalid key length");
298 		assert(ivBytes.length == 8, "invalid iv length");
299 	}
300 	body {
301 		workingKeyLength = keyBytes.length;
302 		workingKey[0..workingKeyLength] = keyBytes[];
303 		workingIV[]  = ivBytes[];
304 
305 		index = 0;
306 		resetCounter();
307 		uint offset = 0;
308 		ubyte[sigma.length] constants;
309 		
310 		// Key
311 		fromLittleEndian(workingKey[0..16], engineState[1..5]);
312 		
313 		if (workingKeyLength == 32)
314 		{
315 			constants[] = sigma[];
316 			offset = 16;
317 		}
318 		else
319 		{
320 			constants[] = tau[];
321 		}
322 
323 		fromLittleEndian(workingKey[offset..offset+16], engineState[11..11+4]);
324 
325 		engineState[0] = fromLittleEndian!uint(constants[0..$]);
326 		engineState[5] = fromLittleEndian!uint(constants[4..$]);
327 		engineState[10] = fromLittleEndian!uint(constants[8..$]);
328 		engineState[15] = fromLittleEndian!uint(constants[12..$]);
329 
330 		// IV
331 
332 		engineState[6] = fromLittleEndian!uint(workingIV[0..$]);
333 		engineState[7] = fromLittleEndian!uint(workingIV[4..$]);
334 		engineState[8] = 0;
335 		engineState[9] = 0;
336 
337 		initialized = true;
338 	}
339 
340 	/// generate a block (64 bytes) of keystream
341 	void generateKeyStream(ubyte[] output) nothrow @nogc
342 	in {
343 		assert(output.length == stateSize*4, "invalid length of output buffer: 64 bytes required");
344 	}
345 	body {
346 		salsaCore!20(engineState, x);
347 		toLittleEndian(x, output);
348 	}
349 
350 	void resetCounter() nothrow @nogc
351 	{
352 		cW0 = 0;
353 		cW1 = 0;
354 		cW2 = 0;
355 	}
356 	
357 	bool limitExceeded() nothrow @nogc
358 	{
359 		if (++cW0 == 0)
360 		{
361 			if (++cW1 == 0)
362 			{
363 				return (++cW2 & 0x20) != 0;          // 2^(32 + 32 + 6)
364 			}
365 		}
366 		
367 		return false;
368 	}
369 	
370 	/*
371 	 * test if limit will be exceeded for input of size len
372 	 */
373 	bool limitExceeded(size_t len) nothrow @nogc
374 	{
375 		cW0 += len;
376 		if (cW0 < len && cW0 >= 0)
377 		{
378 			if (++cW1 == 0)
379 			{
380 				return (++cW2 & 0x20) != 0;          // 2^(32 + 32 + 6)
381 			}
382 		}
383 		
384 		return false;
385 	}
386 }
387