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 }