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 }