1 module crypto.tea; 2 3 import std.bitmanip; 4 import std.exception; 5 6 public import crypto.padding; 7 8 package class TEA 9 { 10 private enum int DELTA = cast(int) 0x9E3779B9; 11 private int[4] m_key; 12 private int m_rounds; 13 14 public this(const int[4] key) 15 { 16 m_key = key.dup; 17 m_rounds = 32; 18 } 19 20 ~this() 21 { 22 import crypto.utils : secureZeroMemory; 23 secureZeroMemory(cast(void[]) m_key); 24 } 25 26 /// Encrypt given ubyte array (length to be crypted must be 8 ubyte aligned) 27 public alias encrypt = crypt!(encryptBlock); 28 /// Decrypt given ubyte array (length to be crypted must be 8 ubyte aligned) 29 public alias decrypt = crypt!(decryptBlock); 30 31 private void crypt(alias T)(ubyte[] _ubytes, size_t _offset = 0, long _count = -1) 32 { 33 if (_count == -1) 34 { 35 _count = cast(long)(_ubytes.length - _offset); 36 } 37 38 enforce(_count % 8 == 0); 39 40 for (size_t i = _offset; i < (_offset + _count); i += 8) 41 { 42 T(_ubytes, i); 43 } 44 } 45 46 /// Encrypt given block of 8 ubytes 47 private void encryptBlock(ubyte[] _ubytes, size_t _offset) 48 { 49 auto v0 = _ubytes.peek!(int, Endian.littleEndian)(_offset); 50 auto v1 = _ubytes.peek!(int, Endian.littleEndian)(_offset + 4); 51 52 int sum = 0; 53 54 foreach (i; 0 .. m_rounds) 55 { 56 sum += DELTA; 57 v0 += ((v1 << 4) + m_key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + m_key[1]); 58 v1 += ((v0 << 4) + m_key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + m_key[3]); 59 } 60 61 _ubytes.write!(int, Endian.littleEndian)(v0, _offset); 62 _ubytes.write!(int, Endian.littleEndian)(v1, _offset + 4); 63 } 64 65 /// Decrypt given block of 8 ubytes 66 private void decryptBlock(ubyte[] _ubytes, size_t _offset) 67 { 68 auto v0 = _ubytes.peek!(int, Endian.littleEndian)(_offset); 69 auto v1 = _ubytes.peek!(int, Endian.littleEndian)(_offset + 4); 70 71 auto sum = cast(int)(cast(uint) DELTA * cast(uint) m_rounds); //0xC6EF3720 72 73 foreach (i; 0 .. m_rounds) 74 { 75 v1 -= ((v0 << 4) + m_key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + m_key[3]); 76 v0 -= ((v1 << 4) + m_key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + m_key[1]); 77 sum -= DELTA; 78 } 79 80 _ubytes.write!(int, Endian.littleEndian)(v0, _offset); 81 _ubytes.write!(int, Endian.littleEndian)(v1, _offset + 4); 82 } 83 } 84 85 /// 86 class Tea 87 { 88 public static ubyte[] encrypt(const ubyte[] input, const char[] key, PaddingMode paddingMode = PaddingMode.NoPadding) 89 { 90 ubyte[] buf = cast(ubyte[])key; 91 int[4] bkey = [buf[0], buf[1], buf[2], buf[3]]; 92 93 return encrypt(input, bkey, paddingMode); 94 } 95 96 public static ubyte[] encrypt(const ubyte[] input, const int[4] key, PaddingMode paddingMode = PaddingMode.NoPadding) 97 { 98 ubyte[] data = Padding.padding(input, 8, paddingMode); 99 100 TEA tea = new TEA(key); 101 tea.encrypt(data); 102 103 return data; 104 } 105 106 public static ubyte[] decrypt(const ubyte[] input, const char[] key, PaddingMode paddingMode = PaddingMode.NoPadding) 107 { 108 ubyte[] buf = cast(ubyte[])key; 109 int[4] bkey = [buf[0], buf[1], buf[2], buf[3]]; 110 111 return decrypt(input, bkey, paddingMode); 112 } 113 114 public static ubyte[] decrypt(const ubyte[] input, const int[4] key, PaddingMode paddingMode = PaddingMode.NoPadding) 115 { 116 auto data = input.dup; 117 TEA tea = new TEA(key); 118 tea.decrypt(data); 119 120 return Padding.unpadding(data, 8, paddingMode); 121 } 122 } 123 124 unittest 125 { 126 ubyte[] data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; 127 int[4] key = [1, 2, 3, 4]; 128 129 ubyte[] buf = Tea.encrypt(data, key, PaddingMode.PKCS5); 130 buf = Tea.decrypt(buf, key, PaddingMode.PKCS5); 131 assert(data == buf); 132 } 133 134 package class XTEA 135 { 136 private enum int DELTA = cast(int) 0x9E3779B9; 137 private int[4] m_key; 138 private int m_rounds; 139 140 public this(const int[4] key, const int rounds) 141 { 142 m_key = key.dup; 143 m_rounds = rounds; 144 } 145 146 ~this() 147 { 148 import crypto.utils : secureZeroMemory; 149 secureZeroMemory(cast(void[]) m_key); 150 } 151 152 /// Encrypt given ubyte array (length to be crypted must be 8 ubyte aligned) 153 public alias encrypt = crypt!(encryptBlock); 154 /// Decrypt given ubyte array (length to be crypted must be 8 ubyte aligned) 155 public alias decrypt = crypt!(decryptBlock); 156 157 private void crypt(alias T)(ubyte[] _ubytes, size_t _offset = 0, long _count = -1) 158 { 159 if (_count == -1) 160 { 161 _count = cast(long)(_ubytes.length - _offset); 162 } 163 164 enforce(_count % 8 == 0); 165 166 for (size_t i = _offset; i < (_offset + _count); i += 8) 167 { 168 T(_ubytes, i); 169 } 170 } 171 172 /// Encrypt given block of 8 ubytes 173 private void encryptBlock(ubyte[] _ubytes, size_t _offset) 174 { 175 auto v0 = _ubytes.peek!(int, Endian.littleEndian)(_offset); 176 auto v1 = _ubytes.peek!(int, Endian.littleEndian)(_offset + 4); 177 178 int sum = 0; 179 180 foreach (i; 0 .. m_rounds) 181 { 182 v0 += ((v1 << 4 ^ cast(int)(cast(uint) v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]); 183 sum += DELTA; 184 v1 += ((v0 << 4 ^ cast(int)(cast(uint) v0 >> 5)) + v0) ^ (sum + m_key[cast(int)(cast(uint) sum >> 11) & 3]); 185 } 186 187 _ubytes.write!(int, Endian.littleEndian)(v0, _offset); 188 _ubytes.write!(int, Endian.littleEndian)(v1, _offset + 4); 189 } 190 191 /// Decrypt given block of 8 ubytes 192 private void decryptBlock(ubyte[] _ubytes, size_t _offset) 193 { 194 auto v0 = _ubytes.peek!(int, Endian.littleEndian)(_offset); 195 auto v1 = _ubytes.peek!(int, Endian.littleEndian)(_offset + 4); 196 197 auto sum = cast(int)(cast(uint) DELTA * cast(uint) m_rounds); 198 199 foreach (i; 0 .. m_rounds) 200 { 201 v1 -= ((v0 << 4 ^ cast(int)(cast(uint) v0 >> 5)) + v0) ^ (sum + m_key[cast(int)(cast(uint) sum >> 11) & 3]); 202 sum -= DELTA; 203 v0 -= ((v1 << 4 ^ cast(int)(cast(uint) v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]); 204 } 205 206 _ubytes.write!(int, Endian.littleEndian)(v0, _offset); 207 _ubytes.write!(int, Endian.littleEndian)(v1, _offset + 4); 208 } 209 } 210 211 /// 212 class Xtea 213 { 214 public static ubyte[] encrypt(const ubyte[] input, const char[] key, const int rounds = 64, PaddingMode paddingMode = PaddingMode.NoPadding) 215 { 216 ubyte[] buf = cast(ubyte[])key; 217 int[4] bkey = [buf[0], buf[1], buf[2], buf[3]]; 218 219 return encrypt(input, bkey, rounds, paddingMode); 220 } 221 222 public static ubyte[] encrypt(const ubyte[] input, const int[4] key, const int rounds = 64, PaddingMode paddingMode = PaddingMode.NoPadding) 223 { 224 ubyte[] data = Padding.padding(input, 8, paddingMode); 225 226 XTEA xtea = new XTEA(key, rounds); 227 xtea.encrypt(data); 228 229 return data; 230 } 231 232 public static ubyte[] decrypt(const ubyte[] input, const char[] key, const int rounds = 64, PaddingMode paddingMode = PaddingMode.NoPadding) 233 { 234 ubyte[] buf = cast(ubyte[])key; 235 int[4] bkey = [buf[0], buf[1], buf[2], buf[3]]; 236 237 return decrypt(input, bkey, rounds, paddingMode); 238 } 239 240 public static ubyte[] decrypt(const ubyte[] input, const int[4] key, const int rounds = 64, PaddingMode paddingMode = PaddingMode.NoPadding) 241 { 242 auto data = input.dup; 243 XTEA xtea = new XTEA(key, rounds); 244 xtea.decrypt(data); 245 246 return Padding.unpadding(data, 8, paddingMode); 247 } 248 } 249 250 unittest 251 { 252 ubyte[] data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; 253 int[4] key = [1, 2, 3, 4]; 254 immutable int rounds = 64; 255 256 ubyte[] buf = Xtea.encrypt(data, key, rounds, PaddingMode.PKCS5); 257 buf = Xtea.decrypt(buf, key, rounds, PaddingMode.PKCS5); 258 assert(data == buf); 259 }