0
|
1 /**
|
|
2 * This file is part of the dcrypt project.
|
|
3 *
|
|
4 * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved.
|
|
5 * License: MIT
|
|
6 * Authors: Thomas Dixon
|
|
7 */
|
|
8
|
|
9 module dcrypt.crypto.ciphers.XTEA;
|
|
10
|
|
11 import dcrypt.misc.Util;
|
|
12 import dcrypt.crypto.BlockCipher;
|
|
13
|
|
14 /** Implementation of the XTEA cipher designed by
|
|
15 David Wheeler and Roger Needham. */
|
|
16 class XTEA : BlockCipher {
|
|
17 private const uint ROUNDS = 32,
|
|
18 KEY_SIZE = 16,
|
|
19 BLOCK_SIZE = 8,
|
|
20 DELTA = 0x9e3779b9;
|
|
21 private uint[] subkeys,
|
|
22 sum0,
|
|
23 sum1;
|
|
24 private bool initialized,
|
|
25 encrypt;
|
|
26
|
|
27 void reset(){}
|
|
28
|
|
29 char[] name() {
|
|
30 return "XTEA";
|
|
31 }
|
|
32
|
|
33 uint blockSize() {
|
|
34 return BLOCK_SIZE;
|
|
35 }
|
|
36
|
|
37 void init(bool encrypt, CipherParameters params) {
|
|
38 SymmetricKey keyParams = cast(SymmetricKey)params;
|
|
39 if (!keyParams)
|
|
40 throw new InvalidParameterError(
|
|
41 name()~": Invalid parameter passed to init");
|
|
42 this.encrypt = encrypt;
|
|
43
|
|
44 if (keyParams.key.length != KEY_SIZE)
|
|
45 throw new InvalidKeyError(
|
|
46 name()~": Invalid key length (requires 16 bytes)");
|
|
47
|
|
48 subkeys = new uint[4];
|
|
49 sum0 = new uint[32];
|
|
50 sum1 = new uint[32];
|
|
51
|
|
52 int i, j;
|
|
53 for (i = j = 0; i < 4; i++, j+=4)
|
|
54 subkeys[i] = Util.ubytesToUintBig(keyParams.key, j);
|
|
55
|
|
56 // Precompute the values of sum + k[] to speed up encryption
|
|
57 for (i = j = 0; i < ROUNDS; i++) {
|
|
58 sum0[i] = (j + subkeys[j & 3]);
|
|
59 j += DELTA;
|
|
60 sum1[i] = (j + subkeys[j >> 11 & 3]);
|
|
61 }
|
|
62 initialized = true;
|
|
63 }
|
|
64
|
|
65 uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
|
|
66 if (!initialized)
|
|
67 throw new NotInitializedError(name()~": Cipher not initialized");
|
|
68
|
|
69 ubyte[] input = cast(ubyte[]) input_;
|
|
70 ubyte[] output = cast(ubyte[]) output_;
|
|
71
|
|
72 if ((inOff + BLOCK_SIZE) > input.length)
|
|
73 throw new ShortBufferError(name()~": Input buffer too short");
|
|
74
|
|
75 if ((outOff + BLOCK_SIZE) > output.length)
|
|
76 throw new ShortBufferError(name()~": Output buffer too short");
|
|
77
|
|
78 uint v0 = Util.ubytesToUintBig(input, inOff),
|
|
79 v1 = Util.ubytesToUintBig(input, inOff+4);
|
|
80
|
|
81 if (encrypt) {
|
|
82 for (int i = 0; i < ROUNDS; i++) {
|
|
83 v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ sum0[i];
|
|
84 v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ sum1[i];
|
|
85 }
|
|
86 } else {
|
|
87 for (int i = ROUNDS-1; i >= 0; i--) {
|
|
88 v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ sum1[i];
|
|
89 v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ sum0[i];
|
|
90 }
|
|
91 }
|
|
92
|
|
93 Util.uintToUbytesBig(v0, output, outOff);
|
|
94 Util.uintToUbytesBig(v1, output, outOff+4);
|
|
95
|
|
96 return BLOCK_SIZE;
|
|
97 }
|
|
98
|
|
99 /** Some XTEA test vectors. */
|
|
100 version (UnitTest) {
|
|
101 unittest {
|
|
102 static const char[][] test_keys = [
|
|
103 "00000000000000000000000000000000",
|
|
104 "00000000000000000000000000000000",
|
|
105 "0123456712345678234567893456789a",
|
|
106 "0123456712345678234567893456789a",
|
|
107 "00000000000000000000000000000001",
|
|
108 "01010101010101010101010101010101",
|
|
109 "0123456789abcdef0123456789abcdef",
|
|
110 "0123456789abcdef0123456789abcdef",
|
|
111 "00000000000000000000000000000000",
|
|
112 "00000000000000000000000000000000"
|
|
113 ];
|
|
114
|
|
115 static const char[][] test_plaintexts = [
|
|
116 "0000000000000000",
|
|
117 "0102030405060708",
|
|
118 "0000000000000000",
|
|
119 "0102030405060708",
|
|
120 "0000000000000001",
|
|
121 "0101010101010101",
|
|
122 "0123456789abcdef",
|
|
123 "0000000000000000",
|
|
124 "0123456789abcdef",
|
|
125 "4141414141414141"
|
|
126 ];
|
|
127
|
|
128 static const char[][] test_ciphertexts = [
|
|
129 "dee9d4d8f7131ed9",
|
|
130 "065c1b8975c6a816",
|
|
131 "1ff9a0261ac64264",
|
|
132 "8c67155b2ef91ead",
|
|
133 "9f25fa5b0f86b758",
|
|
134 "c2eca7cec9b7f992",
|
|
135 "27e795e076b2b537",
|
|
136 "5c8eddc60a95b3e1",
|
|
137 "7e66c71c88897221",
|
|
138 "ed23375a821a8c2d"
|
|
139 ];
|
|
140
|
|
141 XTEA t = new XTEA();
|
|
142 foreach (uint i, char[] test_key; test_keys) {
|
|
143 ubyte[] buffer = new ubyte[t.blockSize];
|
|
144 char[] result;
|
|
145 SymmetricKey key = new SymmetricKey(Util.hexToUbytes(test_key));
|
|
146
|
|
147 // Encryption
|
|
148 t.init(true, key);
|
|
149 t.processBlock(Util.hexToUbytes(test_plaintexts[i]), 0, buffer, 0);
|
|
150 result = Util.ubytesToHex(buffer);
|
|
151 assert(result == test_ciphertexts[i],
|
|
152 t.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
|
|
153
|
|
154 // Decryption
|
|
155 t.init(false, key);
|
|
156 t.processBlock(Util.hexToUbytes(test_ciphertexts[i]), 0, buffer, 0);
|
|
157 result = Util.ubytesToHex(buffer);
|
|
158 assert(result == test_plaintexts[i],
|
|
159 t.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
|
|
160 }
|
|
161 }
|
|
162 }
|
|
163 }
|