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 }