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