| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 | /** * Copyright: Copyright (C) Thomas Dixon 2008. All rights reserved. * License: BSD style: $(LICENSE) * Authors: Thomas Dixon */ module tango.util.cipher.XTEA; private import tango.util.cipher.Cipher; /** Implementation of the XTEA cipher designed by David Wheeler and Roger Needham. */ class XTEA : BlockCipher { private { enum uint ROUNDS = 32, KEY_SIZE = 16, BLOCK_SIZE = 8, DELTA = 0x9e3779b9u; uint[] subkeys, sum0, sum1; } this() {} this(bool encrypt, ubyte[] key) { this(); init(encrypt, key); } final override void reset(){} @property final override const(char)[] name() { return "XTEA"; } @property final override const uint blockSize() { return BLOCK_SIZE; } final void init(bool encrypt, ubyte[] key) { _encrypt = encrypt; if (key.length != KEY_SIZE) invalid(name()~": Invalid key length (requires 16 bytes)"); subkeys = new uint[4]; sum0 = new uint[32]; sum1 = new uint[32]; int i, j; for (i = j = 0; i < 4; i++, j+=int.sizeof) subkeys[i] = ByteConverter.BigEndian.to!(uint)(key[j..j+int.sizeof]); // Precompute the values of sum + k[] to speed up encryption for (i = j = 0; i < ROUNDS; i++) { sum0[i] = (j + subkeys[j & 3]); j += DELTA; sum1[i] = (j + subkeys[j >> 11 & 3]); } _initialized = true; } final override uint update(const(void[]) input_, void[] output_) { if (!_initialized) invalid(name()~": Cipher not initialized"); const(ubyte[]) input = cast(const(ubyte[])) input_; ubyte[] output = cast(ubyte[]) output_; if (input.length < BLOCK_SIZE) invalid(name()~": Input buffer too short"); if (output.length < BLOCK_SIZE) invalid(name()~": Output buffer too short"); uint v0 = ByteConverter.BigEndian.to!(uint)(input[0..4]), v1 = ByteConverter.BigEndian.to!(uint)(input[4..8]); if (_encrypt) { for (int i = 0; i < ROUNDS; i++) { v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ sum0[i]; v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ sum1[i]; } } else { for (int i = ROUNDS-1; i >= 0; i--) { v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ sum1[i]; v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ sum0[i]; } } ByteConverter.BigEndian.from!(uint)(v0, output[0..4]); ByteConverter.BigEndian.from!(uint)(v1, output[4..8]); return BLOCK_SIZE; } /** Some XTEA test vectors. */ debug (UnitTest) { unittest { enum immutable(char)[][] test_keys = [ "00000000000000000000000000000000", "00000000000000000000000000000000", "0123456712345678234567893456789a", "0123456712345678234567893456789a", "00000000000000000000000000000001", "01010101010101010101010101010101", "0123456789abcdef0123456789abcdef", "0123456789abcdef0123456789abcdef", "00000000000000000000000000000000", "00000000000000000000000000000000" ]; enum immutable(char)[][] test_plaintexts = [ "0000000000000000", "0102030405060708", "0000000000000000", "0102030405060708", "0000000000000001", "0101010101010101", "0123456789abcdef", "0000000000000000", "0123456789abcdef", "4141414141414141" ]; enum immutable(char)[][] test_ciphertexts = [ "dee9d4d8f7131ed9", "065c1b8975c6a816", "1ff9a0261ac64264", "8c67155b2ef91ead", "9f25fa5b0f86b758", "c2eca7cec9b7f992", "27e795e076b2b537", "5c8eddc60a95b3e1", "7e66c71c88897221", "ed23375a821a8c2d" ]; XTEA t = new XTEA(); foreach (uint i, immutable(char)[] test_key; test_keys) { ubyte[] buffer = new ubyte[t.blockSize]; char[] result; auto key = ByteConverter.hexDecode(test_key); // Encryption t.init(true, key); t.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer); result = ByteConverter.hexEncode(buffer); assert(result == test_ciphertexts[i], t.name~": ("~result~") != ("~test_ciphertexts[i]~")"); // Decryption t.init(false, key); t.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer); result = ByteConverter.hexEncode(buffer); assert(result == test_plaintexts[i], t.name~": ("~result~") != ("~test_plaintexts[i]~")"); } } } } |