1 module dcrypt.blockcipher.rc6;
2 
3 import dcrypt.blockcipher.blockcipher;
4 import dcrypt.bitmanip;
5 
6 /// test the RC6 block cipher engine
7 unittest {
8 
9 	// 160, 224 bit keys commented out
10 	string[] test_keys = [
11 		x"01010101010101010101010101010101",
12 		//x"0101010101010101010101010101010101010101",
13 		x"010101010101010101010101010101010101010101010101",
14 		//x"01010101010101010101010101010101010101010101010101010101",
15 		x"0101010101010101010101010101010101010101010101010101010101010101",
16 	];
17 	string[] test_plaintexts = [
18 		x"01010101010101010101010101010101",
19 		//x"01010101010101010101010101010101",
20 		x"01010101010101010101010101010101",
21 		//x"01010101010101010101010101010101",
22 		x"01010101010101010101010101010101",
23 	];
24 	string[] test_ciphertexts = [
25 		x"efb2c7dd69614683dab0bc607036c425",
26 		//x"ff59d24608b42833e9292f4be0a239a5",
27 		x"142aa9f25dd64a8a6444304735aa6641",
28 		//x"26e64b9c2bb63e93494d0d803994bdfd",
29 		x"41ecee06dda0946c816f528a767c0ef6",
30 	];
31 	
32 	RC6Engine t = new RC6Engine();
33 	blockCipherTest(t, test_keys, test_plaintexts, test_ciphertexts);
34 }
35 
36 alias BlockCipherWrapper!RC6 RC6Engine;
37 
38 /**
39  * An RC6 engine.
40  */
41 @safe
42 public struct RC6
43 {
44 	public {
45 
46 		enum name = "RC6";
47 		enum blockSize = 4*bytesPerWord;
48 
49 		
50 		/// Params:
51 		/// forEncryption = `false`: decrypt, `true`: encrypt
52 		/// userKey = Secret key.
53 		/// iv = Not used.
54 		/// Throws: Error if key has unsupported size.	
55 		void start(bool forEncryption, in ubyte[] userKey, in ubyte[] iv = null) nothrow @nogc
56 		{
57 			this.forEncryption = forEncryption;
58 			setKey(userKey);
59 		}
60 
61 		uint processBlock(in ubyte[]  input, ubyte[]  output) nothrow @nogc
62 		in {
63 			assert(initialized, "RC6 engine not initialized");
64 			assert(blockSize <= input.length, "input buffer too short");
65 			assert(blockSize <= output.length, "output buffer too short");
66 		}
67 		body {
68 			return forEncryption ? encryptBlock(input, output) : decryptBlock(input, output);
69 		}
70 
71 		void reset() nothrow @nogc
72 		{
73 		}
74 
75 		
76 		private nothrow @nogc:
77 
78 		private {
79 			enum wordSize = 32;
80 			enum bytesPerWord = wordSize / 8;
81 
82 			/*
83 			 * the number of rounds to perform
84 			 */
85 			enum _noRounds = 20;
86 
87 			/*
88 			 * our "magic constants" for wordSize 32
89 			 *
90 			 * Pw = Odd((e-2) * 2^wordsize)
91 			 * Qw = Odd((o-2) * 2^wordsize)
92 			 *
93 			 * where e is the base of natural logarithms (2.718281828...)
94 			 * and o is the golden ratio (1.61803398...)
95 			 */
96 			enum uint    P32 = 0xb7e15163;
97 			enum uint    Q32 = 0x9e3779b9;
98 
99 			enum    LGW = 5;        // log2(32)
100 
101 			/*
102 			 * the expanded key array of size 2*(rounds + 1)
103 			 */
104 			uint[2+2*_noRounds+2] _S;
105 			bool forEncryption;
106 			bool initialized = false; // set to true in setKey()
107 
108 		}
109 		/**
110 		 * Re-key the cipher.
111 		 * Params:  key  the key to be used
112 		 */
113 		private void setKey(in ubyte[] key) nothrow @nogc
114 		in {
115 			size_t len = key.length;
116 			assert(len == 16 || len == 24 || len == 32, "RC6: Unsupported key length. Should be 128, 192, or 256 bits."); 
117 		}
118 		body {
119 			
120 			enum maxKeyLength = 32;
121 			
122 			//
123 			// KEY EXPANSION:
124 			//
125 			// There are 3 phases to the key expansion.
126 			//
127 			// Phase 1:
128 			//   Copy the secret key K[0...b-1] into an array L[0..c-1] of
129 			//   c = ceil(b/u), where u = wordSize/8 in little-endian order.
130 			//   In other words, we fill up L using u consecutive key bytes
131 			//   of K. Any unfilled byte positions in L are zeroed. In the
132 			//   case that b = c = 0, set c = 1 and L[0] = 0.
133 			//
134 			// compute number of dwords
135 			size_t c = (key.length + (bytesPerWord - 1)) / bytesPerWord;
136 			if (c == 0)
137 			{
138 				c = 1;
139 			}
140 			
141 			uint[(maxKeyLength + bytesPerWord - 1) / bytesPerWord] L;	///	Static length is hight enough to support 256 bit keys.
142 			immutable size_t Llength = (key.length + bytesPerWord - 1) / bytesPerWord;	/// Holds the actual length of L.
143 			
144 			// load all key bytes into array of key dwords
145 			
146 			foreach(size_t i; 0..key.length) {
147 				L[i / bytesPerWord] += key[i] << (8*i);
148 			}
149 			
150 			//
151 			// Phase 2:
152 			//   Key schedule is placed in a array of 2+2*ROUNDS+2 = 44 dwords.
153 			//   Initialize S to a particular fixed pseudo-random bit pattern
154 			//   using an arithmetic progression modulo 2^wordsize determined
155 			//   by the magic numbers, Pw & Qw.
156 			//
157 			//        _S            = new uint[2+2*_noRounds+2];
158 			
159 			_S[0] = P32;
160 			foreach (size_t i; 1.._S.length)
161 			{
162 				_S[i] = (_S[i-1] + Q32);
163 			}
164 			
165 			//
166 			// Phase 3:
167 			//   Mix in the user's secret key in 3 passes over the arrays S & L.
168 			//   The max of the arrays sizes is used as the loop control
169 			//
170 			size_t iter;
171 			
172 			if (Llength > _S.length)
173 			{
174 				iter = 3 * Llength;
175 			}
176 			else
177 			{
178 				iter = 3 * _S.length;
179 			}
180 			
181 			uint A = 0;
182 			uint B = 0;
183 			uint i = 0, j = 0;
184 			
185 			foreach (k; 0..iter)
186 			{
187 				A = _S[i] = rotateLeft(_S[i] + A + B, 3);
188 				B =  L[j] = rotateLeft(L[j] + A + B, A+B);
189 				i = (i+1) % _S.length;
190 				j = (j+1) %  Llength;
191 			}
192 			
193 			initialized = true;
194 		}
195 	}
196 
197 	@nogc
198 	private uint encryptBlock(in ubyte[]  input, ubyte[]  output) nothrow
199 	{
200 		// load A,B,C and D registers from in.
201 		uint A = fromLittleEndian!uint(input);
202 		uint B = fromLittleEndian!uint(input[bytesPerWord..$]);
203 		uint C = fromLittleEndian!uint(input[bytesPerWord*2..$]);
204 		uint D = fromLittleEndian!uint(input[bytesPerWord*3..$]);
205 
206 		// Do pseudo-round #0: pre-whitening of B and D
207 		B += _S[0];
208 		D += _S[1];
209 
210 		// perform round #1,#2 ... #ROUNDS of encryption
211 		foreach (uint i; 1.._noRounds+1)
212 		{
213 			uint t = 0,u = 0;
214 
215 			t = B*(2*B+1);
216 			t = rotateLeft(t,5);
217 
218 			u = D*(2*D+1);
219 			u = rotateLeft(u,5);
220 
221 			A ^= t;
222 			A = rotateLeft(A,u);
223 			A += _S[2*i];
224 
225 			C ^= u;
226 			C = rotateLeft(C,t);
227 			C += _S[2*i+1];
228 
229 			uint temp = A;
230 			A = B;
231 			B = C;
232 			C = D;
233 			D = temp;
234 		}
235 		// do pseudo-round #(ROUNDS+1) : post-whitening of A and C
236 		A += _S[2*_noRounds+2];
237 		C += _S[2*_noRounds+3];
238 
239 		// store A, B, C and D registers to out
240 		toLittleEndian(A, output);
241 		toLittleEndian(B, output[bytesPerWord..$]);
242 		toLittleEndian(C, output[bytesPerWord*2..$]);
243 		toLittleEndian(D, output[bytesPerWord*3..$]);
244 
245 		return 4 * bytesPerWord;
246 	}
247 
248 	@nogc
249 	private uint decryptBlock(in ubyte[]  input, ubyte[]  output) nothrow
250 	{
251 		// load A,B,C and D registers from out.
252 		uint A = fromLittleEndian!uint(input);
253 		uint B = fromLittleEndian!uint(input[bytesPerWord..$]);
254 		uint C = fromLittleEndian!uint(input[bytesPerWord*2..$]);
255 		uint D = fromLittleEndian!uint(input[bytesPerWord*3..$]);
256 
257 		// Undo pseudo-round #(ROUNDS+1) : post whitening of A and C
258 		C -= _S[2*_noRounds+3];
259 		A -= _S[2*_noRounds+2];
260 
261 		// Undo round #ROUNDS, .., #2,#1 of encryption
262 		for (uint i = _noRounds; i >= 1; i--)
263 		{
264 			uint t=0,u = 0;
265 
266 			uint temp = D;
267 			D = C;
268 			C = B;
269 			B = A;
270 			A = temp;
271 
272 			t = B*(2*B+1);
273 			t = rotateLeft(t, LGW);
274 
275 			u = D*(2*D+1);
276 			u = rotateLeft(u, LGW);
277 
278 			C -= _S[2*i+1];
279 			C = rotateRight(C,t);
280 			C ^= u;
281 
282 			A -= _S[2*i];
283 			A = rotateRight(A,u);
284 			A ^= t;
285 
286 		}
287 		// Undo pseudo-round #0: pre-whitening of B and D
288 		D -= _S[1];
289 		B -= _S[0];
290 
291 		toLittleEndian(A, output);
292 		toLittleEndian(B, output[bytesPerWord..$]);
293 		toLittleEndian(C, output[bytesPerWord*2..$]);
294 		toLittleEndian(D, output[bytesPerWord*3..$]);
295 
296 		return 4 * bytesPerWord;
297 	}
298 }