1 module buffer.packet; 2 3 import std.meta : AliasSeq, staticIndexOf; 4 import std.variant; 5 import std.bitmanip; 6 import std.traits; 7 import std.typecons; 8 import std.conv : to; 9 import std.exception; 10 11 import crypto.aes; 12 import crypto.tea; 13 import crypto.rsa; 14 15 import buffer.utils; 16 17 /// All encryption supported. 18 enum CryptType 19 { 20 NONE = 0, 21 XTEA = 1, 22 AES = 2, 23 RSA = 3, 24 RSA_XTEA_MIXIN = 4 25 } 26 27 package: 28 29 /// These two items must correspond one by one. 30 alias supportedBuiltinTypes = AliasSeq!( byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real, bool, char, string); 31 immutable byte[] supportedBuiltinTypeNos = [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x20, 0x21, 0x22, 0x30, 0x40, 0x41 ]; 32 33 /// Convert Type to TypeNo. 34 template TypeNo(T) 35 { 36 enum idx = staticIndexOf!(T, supportedBuiltinTypes); 37 static assert(idx != -1, "Data types that are not supported: " ~ typeid(T)); 38 enum TypeNo = supportedBuiltinTypeNos[idx]; 39 } 40 41 alias write = std.bitmanip.write; 42 43 class Packet 44 { 45 static ubyte[] build(const ushort magic, const CryptType crypt, const string key, Nullable!RSAKeyInfo rsaKey, 46 const string name, const string method, Variant[] params) 47 { 48 assert(name.length <= 255, "Paramter name cannot be greater than 255 characters."); 49 assert(method.length <= 255, "Paramter method cannot be greater than 255 characters."); 50 51 ubyte[] tlv; 52 BufferBuilder bb = new BufferBuilder(&tlv); 53 54 foreach (v; params) 55 { 56 bool typeValid; 57 static foreach (T; supportedBuiltinTypes) 58 { 59 if (v.type == typeid(T)) 60 { 61 static if (is(T == string)) 62 bb.put!T(v.get!T, true, true, 4); 63 else 64 bb.put!T(v.get!T, true, false, 0); 65 typeValid = true; 66 } 67 else if (v.type == typeid(ConstOf!T)) 68 { 69 static if (is(Unique!T == string)) 70 bb.put!T(v.get!(const T), true, true, 4); 71 else 72 bb.put!T(v.get!(const T), true, false, 0); 73 typeValid = true; 74 } 75 else if (v.type == typeid(SharedOf!T)) 76 { 77 static if (is(Unique!T == string)) 78 bb.put!T(v.get!(shared T), true, true, 4); 79 else 80 bb.put!T(v.get!(shared T), true, false, 0); 81 typeValid = true; 82 } 83 else if (v.type == typeid(SharedConstOf!T)) 84 { 85 static if (is(Unique!T == string)) 86 bb.put!T(v.get!(shared const T), true, true, 4); 87 else 88 bb.put!T(v.get!(shared const T), true, false, 0); 89 typeValid = true; 90 } 91 else if (v.type == typeid(ImmutableOf!T)) 92 { 93 static if (is(Unique!T == string)) 94 bb.put!T(v.get!(immutable T), true, true, 4); 95 else 96 bb.put!T(v.get!(immutable T), true, false, 0); 97 typeValid = true; 98 } 99 } 100 101 assert(typeValid, "Data types id that are not supported: " ~ v.type.toString); 102 } 103 104 final switch (crypt) 105 { 106 case CryptType.NONE: 107 break; 108 case CryptType.XTEA: 109 tlv = Xtea.encrypt(tlv, key, 64, PaddingMode.PKCS5); 110 break; 111 case CryptType.AES: 112 tlv = AESUtils.encrypt!AES128(tlv, key, iv, PaddingMode.PKCS5); 113 break; 114 case CryptType.RSA: 115 tlv = RSA.encrypt(rsaKey.get, tlv); 116 break; 117 case CryptType.RSA_XTEA_MIXIN: 118 tlv = RSA.encrypt(rsaKey.get, tlv, true); 119 } 120 121 ubyte[] buffer; 122 bb = new BufferBuilder(&buffer); 123 bb.put!ushort(magic, false, false, 0); 124 bb.put!int(0, false, false, 0); // length, seize a seat. 125 bb.put!string(name, false, true, 2); 126 bb.put!string(method, false, true, 2); 127 buffer ~= tlv; 128 buffer.write!int(cast(int)(buffer.length - 2 - 4 + 2), 2); 129 buffer ~= strToByte_hex(MD5(buffer)[0 .. 4]); 130 131 return buffer; 132 } 133 134 static size_t parseInfo(const ubyte[] buffer, out string name, out string method) 135 { 136 enforce(buffer != null && buffer.length >= 10, "Incorrect buffer length."); 137 138 ushort len1 = buffer.peek!ushort(6); 139 if (len1 > 0) 140 { 141 name = cast(string) buffer[8 .. 8 + len1]; 142 } 143 144 ushort len2 = buffer.peek!ushort(8 + len1); 145 if (len2 > 0) 146 { 147 method = cast(string) buffer[10 + len1 .. 10 + len1 + len2]; 148 } 149 150 return 10 + len1 + len2; 151 } 152 153 static Variant[] parse(const ubyte[] buffer, const ushort magic, const CryptType crypt, const string key, Nullable!RSAKeyInfo rsaKey, out string name, out string method) 154 { 155 enforce(buffer != null && buffer.length >= 10, "Incorrect buffer length."); 156 157 ushort t_magic; 158 int t_len; 159 t_magic = buffer.peek!ushort(0); 160 t_len = buffer.peek!int(2); 161 162 if ((t_magic != magic) || (t_len > cast(int)buffer.length - 6)) 163 { 164 return null; 165 } 166 167 ubyte[] buf = cast(ubyte[]) buffer[0 .. t_len + 6]; 168 if (strToByte_hex(MD5(buf[0 .. $ - 2])[0 .. 4]) != buf[$ - 2 .. $]) 169 { 170 return null; 171 } 172 173 size_t tlv_pos = parseInfo(buf, name, method); 174 buf = buf[tlv_pos .. $ - 2]; 175 176 final switch (crypt) 177 { 178 case CryptType.NONE: 179 break; 180 case CryptType.XTEA: 181 buf = Xtea.decrypt(buf, key, 64, PaddingMode.PKCS5); 182 break; 183 case CryptType.AES: 184 buf = AESUtils.decrypt!AES128(buf, key, iv, PaddingMode.PKCS5); 185 break; 186 case CryptType.RSA: 187 buf = RSA.decrypt(rsaKey.get, buf); 188 break; 189 case CryptType.RSA_XTEA_MIXIN: 190 buf = RSA.decrypt(rsaKey.get, buf, true); 191 break; 192 } 193 194 ubyte typeNo; 195 int pos; 196 Variant[] ret; 197 198 while (pos < buf.length) 199 { 200 typeNo = buf[pos]; 201 pos++; 202 203 bool typeValid; 204 static foreach (idx, T; supportedBuiltinTypes) 205 { 206 if (typeNo == supportedBuiltinTypeNos[idx]) 207 { 208 typeValid = true; 209 210 static if (is(T == real)) 211 { 212 //get!real; 213 ret ~= Variant(ubytesToReal(buf[pos .. pos + real.sizeof])); 214 pos += real.sizeof; 215 } 216 else static if (is(T == string)) 217 { 218 immutable temp = buf.peek!int(pos); 219 pos += 4; 220 ret ~= Variant(cast(string) buf[pos .. pos + temp]); // !! May be: invalid UTF-8 sequence. 221 pos += temp; 222 } 223 else 224 { 225 ret ~= Variant(buf.peek!T(pos)); 226 pos += T.sizeof; 227 } 228 } 229 } 230 assert(typeValid, "Data types id that are not supported: " ~ typeNo.to!string); 231 } 232 233 return ret; 234 } 235 236 private: 237 238 static ubyte[] iv = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 239 } 240 241 class BufferBuilder 242 { 243 public ubyte[]* buffer; 244 245 this(ubyte[]* buffer) 246 { 247 this.buffer = buffer; 248 } 249 250 size_t put(T)(const T value, const bool isWriteTypeInfo, const bool isWriteLengthInfo, const int lengthBytes) 251 { 252 assert(lengthBytes == 0 || lengthBytes == 2 || lengthBytes == 4); 253 254 ubyte[] buf_data; 255 size_t len; 256 257 if (isWriteTypeInfo) 258 { 259 *buffer ~= TypeNo!T; 260 } 261 262 static if (is(Unqual!T == string)) 263 { 264 buf_data = cast(ubyte[])value; 265 len = buf_data.length; 266 } 267 else static if (is(Unqual!T == real)) 268 { 269 buf_data = realToUBytes(value); 270 len = real.sizeof; 271 } 272 else 273 { 274 buf_data = new ubyte[T.sizeof]; 275 buf_data.write!T(value, 0); 276 len = T.sizeof; 277 } 278 279 if (isWriteLengthInfo && lengthBytes > 0) 280 { 281 ubyte[] buf_len = new ubyte[lengthBytes]; 282 if (lengthBytes == 2) 283 buf_len.write!ushort(cast(ushort)len, 0); 284 else 285 buf_len.write!int(cast(int)len, 0); 286 287 *buffer ~= buf_len; 288 } 289 290 *buffer ~= buf_data; 291 292 return len; 293 } 294 }