1 /** 2 * Copyright: Copyright (C) Thomas Dixon 2008. All rights reserved. 3 * License: BSD style: $(LICENSE) 4 * Authors: Thomas Dixon 5 */ 6 7 module tango.util.cipher.RC6; 8 9 private import tango.util.cipher.Cipher; 10 11 /** 12 * Implementation of the RC6-32/20/b cipher designed by 13 * Ron Rivest et al. of RSA Security. 14 * 15 * It should be noted that this algorithm is very similar to RC5. 16 * Currently there are no plans to implement RC5, but should that change 17 * in the future, it may be wise to rewrite both RC5 and RC6 to use some 18 * kind of template or base class. 19 * 20 * This algorithm is patented and trademarked. 21 * 22 * References: http://people.csail.mit.edu/rivest/Rc6.pdf 23 */ 24 class RC6 : BlockCipher 25 { 26 private 27 { 28 enum uint ROUNDS = 20, 29 BLOCK_SIZE = 16, 30 // Magic constants for a 32 bit word size 31 P = 0xb7e15163, 32 Q = 0x9e3779b9; 33 uint[] S; 34 const(ubyte)[] workingKey; 35 } 36 37 this() {} 38 this(bool encrypt, ubyte[] key) { 39 this(); 40 init(encrypt, key); 41 } 42 43 @property final override const(char)[] name() 44 { 45 return "RC6"; 46 } 47 48 @property final override const uint blockSize() 49 { 50 return BLOCK_SIZE; 51 } 52 53 final void init(bool encrypt, ubyte[] key) 54 { 55 _encrypt = encrypt; 56 57 auto len = key.length; 58 if (len != 16 && len != 24 && len != 32) 59 invalid(name()~": Invalid key length (requires 16/24/32 bytes)"); 60 61 S = new uint[2*ROUNDS+4]; 62 63 workingKey = key; 64 setup(workingKey); 65 66 _initialized = true; 67 } 68 69 final override uint update(const(void[]) input_, void[] output_) { 70 if (!_initialized) 71 invalid(name()~": Cipher not initialized"); 72 73 const(ubyte[]) input = cast(const(ubyte[])) input_; 74 ubyte[] output = cast(ubyte[]) output_; 75 76 if (input.length < BLOCK_SIZE) 77 invalid(name()~": Input buffer too short"); 78 79 if (output.length < BLOCK_SIZE) 80 invalid(name()~": Output buffer too short"); 81 82 uint A = ByteConverter.LittleEndian.to!(uint)(input[0..4]), 83 B = ByteConverter.LittleEndian.to!(uint)(input[4..8]), 84 C = ByteConverter.LittleEndian.to!(uint)(input[8..12]), 85 D = ByteConverter.LittleEndian.to!(uint)(input[12..16]), 86 t, 87 u; 88 89 if (_encrypt) 90 { 91 B += S[0]; 92 D += S[1]; 93 94 for (int i = 1; i <= ROUNDS; i++) 95 { 96 t = Bitwise.rotateLeft(B*((B<<1)+1), 5u); 97 u = Bitwise.rotateLeft(D*((D<<1)+1), 5u); 98 A = Bitwise.rotateLeft(A^t, u) + S[i<<1]; 99 C = Bitwise.rotateLeft(C^u, t) + S[(i<<1)+1]; 100 t = A; 101 A = B; 102 B = C; 103 C = D; 104 D = t; 105 } 106 107 A += S[2*ROUNDS+2]; 108 C += S[2*ROUNDS+3]; 109 } 110 else 111 { 112 C -= S[2*ROUNDS+3]; 113 A -= S[2*ROUNDS+2]; 114 115 for (int i = ROUNDS; i >= 1; i--) 116 { 117 t = D; 118 D = C; 119 C = B; 120 B = A; 121 A = t; 122 u = Bitwise.rotateLeft(D*((D<<1)+1), 5u); 123 t = Bitwise.rotateLeft(B*((B<<1)+1), 5u); 124 C = Bitwise.rotateRight(C-S[(i<<1)+1], t) ^ u; 125 A = Bitwise.rotateRight(A-S[i<<1], u) ^ t; 126 } 127 128 D -= S[1]; 129 B -= S[0]; 130 } 131 132 ByteConverter.LittleEndian.from!(uint)(A, output[0..4]); 133 ByteConverter.LittleEndian.from!(uint)(B, output[4..8]); 134 ByteConverter.LittleEndian.from!(uint)(C, output[8..12]); 135 ByteConverter.LittleEndian.from!(uint)(D, output[12..16]); 136 137 return BLOCK_SIZE; 138 } 139 140 final override void reset() 141 { 142 setup(workingKey); 143 } 144 145 private void setup(const(ubyte)[] key) 146 { 147 size_t c = key.length/4; 148 uint[] L = new uint[c]; 149 for (int i = 0, j = 0; i < c; i++, j+=4) 150 L[i] = ByteConverter.LittleEndian.to!(uint)(key[j..j+int.sizeof]); 151 152 S[0] = P; 153 for (int i = 1; i <= 2*ROUNDS+3; i++) 154 S[i] = S[i-1] + Q; 155 156 uint A, B, i, j, v = 3*(2*ROUNDS+4); // Relying on ints initializing to 0 157 for (int s = 1; s <= v; s++) 158 { 159 A = S[i] = Bitwise.rotateLeft(S[i]+A+B, 3u); 160 B = L[j] = Bitwise.rotateLeft(L[j]+A+B, A+B); 161 i = (i + 1) % (2*ROUNDS+4); 162 j = (j + 1) % c; 163 } 164 } 165 166 /** Some RC6 test vectors from the spec. */ 167 debug (UnitTest) 168 { 169 unittest 170 { 171 __gshared immutable immutable(char)[][] test_keys = [ 172 "00000000000000000000000000000000", 173 "0123456789abcdef0112233445566778", 174 "00000000000000000000000000000000"~ 175 "0000000000000000", 176 "0123456789abcdef0112233445566778"~ 177 "899aabbccddeeff0", 178 "00000000000000000000000000000000"~ 179 "00000000000000000000000000000000", 180 "0123456789abcdef0112233445566778"~ 181 "899aabbccddeeff01032547698badcfe" 182 ]; 183 184 __gshared immutable immutable(char)[][] test_plaintexts = [ 185 "00000000000000000000000000000000", 186 "02132435465768798a9bacbdcedfe0f1", 187 "00000000000000000000000000000000", 188 "02132435465768798a9bacbdcedfe0f1", 189 "00000000000000000000000000000000", 190 "02132435465768798a9bacbdcedfe0f1" 191 ]; 192 193 __gshared immutable immutable(char)[][] test_ciphertexts = [ 194 "8fc3a53656b1f778c129df4e9848a41e", 195 "524e192f4715c6231f51f6367ea43f18", 196 "6cd61bcb190b30384e8a3f168690ae82", 197 "688329d019e505041e52e92af95291d4", 198 "8f5fbd0510d15fa893fa3fda6e857ec2", 199 "c8241816f0d7e48920ad16a1674e5d48" 200 ]; 201 202 RC6 t = new RC6(); 203 foreach (uint i, immutable(char)[] test_key; test_keys) 204 { 205 ubyte[] buffer = new ubyte[t.blockSize]; 206 char[] result; 207 auto key = ByteConverter.hexDecode(test_key); 208 209 // Encryption 210 t.init(true, key); 211 t.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer); 212 result = ByteConverter.hexEncode(buffer); 213 assert(result == test_ciphertexts[i], 214 t.name~": ("~result~") != ("~test_ciphertexts[i]~")"); 215 216 // Decryption 217 t.init(false, key); 218 t.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer); 219 result = ByteConverter.hexEncode(buffer); 220 assert(result == test_plaintexts[i], 221 t.name~": ("~result~") != ("~test_plaintexts[i]~")"); 222 } 223 } 224 } 225 }