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