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