1 module xtea.XteaCrypto; 2 @safe: 3 nothrow: 4 pure: 5 6 ubyte[] encrypt(XTEA crypto, const(ubyte)[] m) 7 { 8 auto ans = m.dup; 9 crypto.Encrypt(ans); 10 return ans; 11 } 12 @nogc unittest 13 { 14 15 enum sourceData = cast(ubyte[])[6, 2, 87, 66, 77, 289, 623, 39823]; 16 enum encrypted = XTEA([1,2,3,4], 64).encrypt(sourceData); 17 import std.algorithm:equal; 18 static assert(encrypted.equal([49, 144, 90, 128, 35, 56, 194, 0])); 19 } 20 21 ubyte[] decrypt(XTEA crypto, const(ubyte)[] m) { 22 auto ans = m.dup; 23 crypto.Decrypt(ans); 24 return ans; 25 } 26 unittest 27 { 28 enum sourceData = cast(ubyte[])[6, 2, 87, 66, 77, 289, 623, 39823]; 29 enum encrypted = cast(ubyte[])[49, 144, 90, 128, 35, 56, 194, 0]; 30 enum dencrypted = XTEA([1,2,3,4], 64).decrypt(encrypted); 31 import std.algorithm:equal; 32 static assert(dencrypted.equal(sourceData)); 33 } 34 // Coverage % 35 unittest { 36 enum sourceData = cast(ubyte[])[6, 2, 87, 66, 77, 289, 623, 39823]; 37 enum crypto = XTEA([1,2,3,4], 64); 38 auto runtime = crypto.decrypt(crypto.encrypt(sourceData)); 39 import std.algorithm:equal; 40 assert(runtime.equal(sourceData)); 41 } 42 43 /++ 44 + XTEA helper type 45 + see: http://en.wikipedia.org/wiki/XTEA 46 +/ 47 public struct XTEA 48 { 49 @safe: 50 nothrow: 51 pure: 52 @nogc: 53 54 /// XTEA delta constant 55 private enum int DELTA = cast(int)0x9E3779B9; 56 57 /// Key - 4 integer 58 private int[4] m_key; 59 60 /// Round to go - 64 are commonly used 61 private int m_rounds; 62 63 /// c'tor 64 public this(int[4] _key, int _rounds) 65 { 66 m_key = _key; 67 m_rounds = _rounds; 68 } 69 70 ~this() @trusted 71 { 72 version (LDC) if (!__ctfe) 73 { 74 import ldc.intrinsics : llvm_memset; 75 // llvm_memset with the last parameter set to "true" is guaranteed 76 // not to be elided by the compiler even if the compiler determines 77 // the new values are never read. 78 static if (is(typeof(llvm_memset(array.ptr, 0, array.length * T.sizeof, true)))) // LLVM 7+ 79 llvm_memset(&m_key, 0, m_key.sizeof, true); 80 else // Pre-LLVM 7 81 llvm_memset(&m_key, 0, m_key.sizeof, m_key.alignof, true); 82 return; 83 } 84 m_key[] = 0; 85 } 86 87 /// Encrypt given ubyte array (length to be crypted must be 8 ubyte aligned) 88 public alias Crypt!(EncryptBlock) Encrypt; 89 /// Decrypt given ubyte array (length to be crypted must be 8 ubyte aligned) 90 public alias Crypt!(DecryptBlock) Decrypt; 91 92 /// 93 private const void Crypt(alias T)(ubyte[] _ubytes, size_t _offset=0, long _count=-1) 94 { 95 if(_count == -1) 96 _count = cast(long)(_ubytes.length - _offset); 97 98 assert(_count % 8 == 0); 99 100 for (size_t i = _offset; i < (_offset+_count); i += 8) 101 T(_ubytes, i); 102 } 103 104 /// Encrypt given block of 8 ubytes 105 private const void EncryptBlock(ubyte[] _ubytes, size_t _offset) 106 { 107 auto v0 = ReadInt(_ubytes, _offset); 108 auto v1 = ReadInt(_ubytes, _offset + 4); 109 110 int sum = 0; 111 112 foreach (i; 0..m_rounds) 113 { 114 v0 += ((v1 << 4 ^ cast(int)(cast(uint)v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]); 115 sum += DELTA; 116 v1 += ((v0 << 4 ^ cast(int)(cast(uint)v0 >> 5)) + v0) ^ (sum + m_key[cast(int)(cast(uint)sum >> 11) & 3]); 117 } 118 119 StoreInt(v0, _ubytes, _offset); 120 StoreInt(v1, _ubytes, _offset + 4); 121 } 122 123 /// Decrypt given block of 8 ubytes 124 private const void DecryptBlock(ubyte[] _ubytes, size_t _offset) 125 { 126 auto v0 = ReadInt(_ubytes, _offset); 127 auto v1 = ReadInt(_ubytes, _offset + 4); 128 129 auto sum = cast(int)(cast(uint)DELTA * cast(uint)m_rounds); 130 131 foreach (i; 0..m_rounds) 132 { 133 v1 -= ((v0 << 4 ^ cast(int)(cast(uint)v0 >> 5)) + v0) ^ (sum + m_key[cast(int)(cast(uint)sum >> 11) & 3]); 134 sum -= DELTA; 135 v0 -= ((v1 << 4 ^ cast(int)(cast(uint)v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]); 136 } 137 138 StoreInt(v0, _ubytes, _offset); 139 StoreInt(v1, _ubytes, _offset + 4); 140 } 141 142 /// Read 32 bit int from buffer 143 private static int ReadInt(ubyte[] _ubytes, size_t _offset) 144 { 145 return (((_ubytes[_offset++] & 0xff) << 0) 146 | ((_ubytes[_offset++] & 0xff) << 8) 147 | ((_ubytes[_offset++] & 0xff) << 16) 148 | ((_ubytes[_offset] & 0xff) << 24)); 149 } 150 151 /// Write 32 bit int from buffer 152 private static void StoreInt(int _value, ubyte[] _ubytes, size_t _offset) 153 { 154 auto unsignedValue = cast(uint)_value; 155 _ubytes[_offset++] = cast(ubyte)(unsignedValue >> 0); 156 _ubytes[_offset++] = cast(ubyte)(unsignedValue >> 8); 157 _ubytes[_offset++] = cast(ubyte)(unsignedValue >> 16); 158 _ubytes[_offset] = cast(ubyte)(unsignedValue >> 24); 159 } 160 } 161 162 @nogc unittest 163 { 164 ubyte[16] sourceData = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; 165 ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; 166 167 auto crypto = XTEA([1,2,3,4], 64); 168 169 crypto.Encrypt(data); 170 crypto.Decrypt(data); 171 assert(sourceData == data); 172 }