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