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.XTEA;
8 
9 private import tango.util.cipher.Cipher;
10 
11 /** Implementation of the XTEA cipher designed by
12     David Wheeler and Roger Needham. */
13 class XTEA : BlockCipher
14 {
15     private
16     {
17         enum uint ROUNDS = 32,
18                   KEY_SIZE = 16,
19                   BLOCK_SIZE = 8,
20                   DELTA = 0x9e3779b9u;
21         uint[] subkeys,
22                sum0,
23                sum1;
24     }
25 
26     this() {}
27     this(bool encrypt, ubyte[] key) {
28         this();
29         init(encrypt, key);
30     }
31 
32     final override void reset(){}
33     
34     @property final override const(char)[] name()
35     {
36         return "XTEA";
37     }
38     
39     @property final override const uint blockSize()
40     {
41         return BLOCK_SIZE;
42     }
43     
44     final void init(bool encrypt, ubyte[] key)
45     {
46         _encrypt = encrypt;
47                     
48         if (key.length != KEY_SIZE)
49             invalid(name()~": Invalid key length (requires 16 bytes)");
50         
51         subkeys = new uint[4];
52         sum0 = new uint[32];
53         sum1 = new uint[32];
54         
55         int i, j;
56         for (i = j = 0; i < 4; i++, j+=int.sizeof)
57             subkeys[i] = ByteConverter.BigEndian.to!(uint)(key[j..j+int.sizeof]);
58             
59         // Precompute the values of sum + k[] to speed up encryption
60         for (i = j = 0; i < ROUNDS; i++)
61         {
62             sum0[i] = (j + subkeys[j & 3]);
63             j += DELTA;
64             sum1[i] = (j + subkeys[j >> 11 & 3]);
65         }
66         
67         _initialized = true;
68     }
69     
70     final override uint update(const(void[]) input_, void[] output_)
71     {
72         if (!_initialized)
73             invalid(name()~": Cipher not initialized");
74             
75         const(ubyte[]) input = cast(const(ubyte[])) input_;
76         ubyte[] output = cast(ubyte[]) output_;
77                     
78         if (input.length < BLOCK_SIZE)
79             invalid(name()~": Input buffer too short");
80             
81         if (output.length < BLOCK_SIZE)
82             invalid(name()~": Output buffer too short");
83         
84         uint v0 = ByteConverter.BigEndian.to!(uint)(input[0..4]),
85              v1 = ByteConverter.BigEndian.to!(uint)(input[4..8]);
86              
87         if (_encrypt)
88         {
89             for (int i = 0; i < ROUNDS; i++)
90             {
91                 v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ sum0[i];
92                 v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ sum1[i];
93             }
94         }
95         else
96         {
97             for (int i = ROUNDS-1; i >= 0; i--)
98             {
99                 v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ sum1[i];
100                 v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ sum0[i];
101             }
102         }
103         
104         ByteConverter.BigEndian.from!(uint)(v0, output[0..4]);
105         ByteConverter.BigEndian.from!(uint)(v1, output[4..8]);
106         
107         return BLOCK_SIZE;
108     }
109     
110     /** Some XTEA test vectors. */
111     debug (UnitTest)
112     {
113         unittest
114         {
115             __gshared immutable immutable(char)[][] test_keys = [
116                 "00000000000000000000000000000000",
117                 "00000000000000000000000000000000",
118                 "0123456712345678234567893456789a",
119                 "0123456712345678234567893456789a",
120                 "00000000000000000000000000000001",
121                 "01010101010101010101010101010101",
122                 "0123456789abcdef0123456789abcdef",
123                 "0123456789abcdef0123456789abcdef",
124                 "00000000000000000000000000000000",
125                 "00000000000000000000000000000000"
126             ];
127                  
128             __gshared immutable immutable(char)[][] test_plaintexts = [
129                 "0000000000000000",
130                 "0102030405060708",
131                 "0000000000000000",
132                 "0102030405060708",
133                 "0000000000000001",
134                 "0101010101010101",
135                 "0123456789abcdef",
136                 "0000000000000000",
137                 "0123456789abcdef",
138                 "4141414141414141"
139             ];
140                 
141             __gshared immutable immutable(char)[][] test_ciphertexts = [
142                 "dee9d4d8f7131ed9",
143                 "065c1b8975c6a816",
144                 "1ff9a0261ac64264",
145                 "8c67155b2ef91ead",
146                 "9f25fa5b0f86b758",
147                 "c2eca7cec9b7f992",
148                 "27e795e076b2b537",
149                 "5c8eddc60a95b3e1",
150                 "7e66c71c88897221",
151                 "ed23375a821a8c2d"
152             ];
153                 
154             XTEA t = new XTEA();
155             foreach (uint i, immutable(char)[] test_key; test_keys)
156             {
157                 ubyte[] buffer = new ubyte[t.blockSize];
158                 char[] result;
159                 auto key = ByteConverter.hexDecode(test_key);
160                 
161                 // Encryption
162                 t.init(true, key);
163                 t.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer);
164                 result = ByteConverter.hexEncode(buffer);
165                 assert(result == test_ciphertexts[i],
166                         t.name~": ("~result~") != ("~test_ciphertexts[i]~")");
167     
168                 // Decryption
169                 t.init(false, key);
170                 t.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer);
171                 result = ByteConverter.hexEncode(buffer);
172                 assert(result == test_plaintexts[i],
173                         t.name~": ("~result~") != ("~test_plaintexts[i]~")");
174             }
175         }
176     }
177 }
178