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 }