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