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.TEA;
8 
9 private import tango.util.cipher.Cipher;
10 
11 /** Implementation of the TEA cipher designed by
12     David Wheeler and Roger Needham. */
13 class TEA : BlockCipher
14 {
15     private
16     {
17         enum uint ROUNDS = 32,
18                   KEY_SIZE = 16,
19                   BLOCK_SIZE = 8,
20                   DELTA = 0x9e3779b9u,
21                   DECRYPT_SUM = 0xc6ef3720u;
22         uint sk0, sk1, sk2, sk3, sum;
23     }
24 
25     this() {}
26     this(bool encrypt, ubyte[] key) {
27         this();
28         init(encrypt, key);
29     }
30 
31     final override void reset(){}
32     
33     @property final override const(char)[] name()
34     {
35         return "TEA";
36     }
37     
38     @property final override const uint blockSize()
39     {
40         return BLOCK_SIZE;
41     }
42     
43     final void init(bool encrypt, ubyte[] key)
44     {
45         _encrypt = encrypt;
46                     
47         if (key.length != KEY_SIZE)
48             invalid(name()~": Invalid key length (requires 16 bytes)");
49         
50         sk0 = ByteConverter.BigEndian.to!(uint)(key[0..4]);
51         sk1 = ByteConverter.BigEndian.to!(uint)(key[4..8]);
52         sk2 = ByteConverter.BigEndian.to!(uint)(key[8..12]);
53         sk3 = ByteConverter.BigEndian.to!(uint)(key[12..16]);
54 
55         _initialized = true;
56     }
57     
58     final override uint update(const(void[]) input_, void[] output_)
59     {
60         if (!_initialized)
61             invalid(name()~": Cipher not initialized");
62             
63         const(ubyte[]) input = cast(const(ubyte[])) input_;
64         ubyte[] output = cast(ubyte[]) output_;
65                     
66         if (input.length < BLOCK_SIZE)
67             invalid(name()~": Input buffer too short");
68             
69         if (output.length < BLOCK_SIZE)
70             invalid(name()~": Output buffer too short");
71         
72         uint v0 = ByteConverter.BigEndian.to!(uint)(input[0..4]),
73              v1 = ByteConverter.BigEndian.to!(uint)(input[4..8]);
74         
75         sum = _encrypt ? 0 : DECRYPT_SUM;
76         for (int i = 0; i < ROUNDS; i++)
77         {
78             if (_encrypt)
79             {
80                 sum += DELTA;
81                 v0 += ((v1 << 4) + sk0) ^ (v1 + sum) ^ ((v1 >> 5) + sk1);
82                 v1 += ((v0 << 4) + sk2) ^ (v0 + sum) ^ ((v0 >> 5) + sk3);
83             }
84             else
85             {
86                 v1 -= ((v0 << 4) + sk2) ^ (v0 + sum) ^ ((v0 >> 5) + sk3);
87                 v0 -= ((v1 << 4) + sk0) ^ (v1 + sum) ^ ((v1 >> 5) + sk1);
88                 sum -= DELTA;
89             }
90         }
91         
92         ByteConverter.BigEndian.from!(uint)(v0, output[0..4]);
93         ByteConverter.BigEndian.from!(uint)(v1, output[4..8]);
94         
95         return BLOCK_SIZE;
96     }
97     
98     /** Some TEA test vectors. */
99     debug (UnitTest)
100     {
101         unittest
102         {
103             __gshared immutable immutable(char)[][] test_keys = [
104                 "00000000000000000000000000000000",
105                 "00000000000000000000000000000000",
106                 "0123456712345678234567893456789a",
107                 "0123456712345678234567893456789a"
108             ];
109                  
110             __gshared immutable immutable(char)[][] test_plaintexts = [
111                 "0000000000000000",
112                 "0102030405060708",
113                 "0000000000000000",
114                 "0102030405060708"
115             ];
116                 
117             __gshared immutable immutable(char)[][] test_ciphertexts = [
118                 "41ea3a0a94baa940",
119                 "6a2f9cf3fccf3c55",
120                 "34e943b0900f5dcb",
121                 "773dc179878a81c0"
122             ];
123                 
124             
125             TEA t = new TEA();
126             foreach (uint i, string test_key; test_keys)
127             {
128                 ubyte[] buffer = new ubyte[t.blockSize];
129                 char[] result;
130                 auto key = ByteConverter.hexDecode(test_key);
131                 
132                 // Encryption
133                 t.init(true, key);
134                 t.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer);
135                 result = ByteConverter.hexEncode(buffer);
136                 assert(result == test_ciphertexts[i],
137                         t.name~": ("~result~") != ("~test_ciphertexts[i]~")");
138 
139                 // Decryption
140                 t.init(false, key);
141                 t.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer);
142                 result = ByteConverter.hexEncode(buffer);
143                 assert(result == test_plaintexts[i],
144                         t.name~": ("~result~") != ("~test_plaintexts[i]~")");
145             }
146         }
147     }
148 }