1 /** 2 * Provides de-/encryption methods for Gravity's Mixcrypt 3 * algorithm. 4 * It uses a combination of a custom routine and their 5 * broken DES. 6 */ 7 module zgrf.crypto.mixcrypt; 8 9 import std.bitmanip; 10 11 import zgrf.crypto.desbroken; 12 import zgrf.bits; 13 14 /** 15 * Encrypts data using the key 0x00. Key parameter is ignored. 16 * 17 * Params: 18 * data = The data array to be encrypted 19 * key = Ignored. Should be empty 20 * 21 * Returns: An array of bytes containing the encrypted data 22 */ 23 ubyte[] encrypt(const(ubyte)[] data, const(ubyte)[] key = []) pure 24 { 25 size_t length = data.length; 26 const ubyte extraLength = length % 8; 27 const size_t paddedLength = (extraLength > 0) ? length + 8 - extraLength : length; 28 length -= extraLength; 29 30 auto subkeys = createSubkeys(key); 31 32 const auto cycle = getCycle(length); 33 34 ubyte[] encryptedData = new ubyte[paddedLength]; 35 36 auto j = 0; 37 for (auto i = 0, step = 0; i < length; i += 8) 38 { 39 step = i / 8; 40 if (step < 20 || step % cycle == 0) 41 { 42 const processedBlock = processBlock(data[i .. i + 8], subkeys); 43 encryptedData[i .. i + 8] = processedBlock; 44 } 45 else 46 { 47 if (j == 7) 48 { 49 const permBlock = permutateByteBlock(data[i .. i + 8], false); 50 encryptedData[i .. i + 8] = permBlock; 51 j = 0; 52 } 53 else 54 { 55 encryptedData[i .. i + 8] = data[i .. i + 8]; 56 } 57 j++; 58 } 59 } 60 61 if (extraLength > 0) 62 { 63 const ubyte padding = 8 - extraLength; 64 ubyte[] lastBlock = new ubyte[8]; 65 lastBlock[0 .. extraLength] = data[length .. length + extraLength]; 66 foreach (i; 0 .. padding) 67 { 68 lastBlock[extraLength + i] = padding; 69 } 70 const auto step = length / 8; 71 if (step < 20 || step % cycle == 0) 72 { 73 const processedBlock = processBlock(lastBlock, subkeys); 74 encryptedData[length .. length + 8] = processedBlock; 75 } 76 else 77 { 78 if (j == 7) 79 { 80 const permBlock = permutateByteBlock(lastBlock, false); 81 encryptedData[length .. length + 8] = permBlock; 82 } 83 else 84 { 85 encryptedData[length .. length + 8] = lastBlock; 86 } 87 } 88 } 89 90 return encryptedData; 91 } 92 93 /** 94 * Decrypts data using the key 0x00. Key parameter is ignored. 95 * 96 * The input data must be a multiple of 8 bytes long. 97 * 98 * Params: 99 * data = The data array to be decrypted 100 * key = Ignored. Should be empty 101 * unencryptedSize = The size/length of the unencrypted data. 102 * The returning data will be truncated to this size if it has padding. 103 * 104 * Returns: An array of bytes containing the decrypted data 105 */ 106 ubyte[] decrypt(const(ubyte)[] data, const(ubyte)[] key, const size_t unencryptedSize) pure 107 in (data.length % 8 == 0, "Data must be a multiple of 64 bits (8 bytes)") 108 { 109 const size_t length = data.length; 110 111 auto subkeys = createSubkeys(key); 112 113 const auto cycle = getCycle(unencryptedSize); 114 115 ubyte[] decryptedData = new ubyte[length]; 116 117 for (auto i = 0, j = 0, step = 0; i < length; i += 8) 118 { 119 step = i / 8; 120 if (step < 20 || step % cycle == 0) 121 { 122 const processedBlock = processBlock(data[i .. i + 8], subkeys); 123 decryptedData[i .. i + 8] = processedBlock; 124 } 125 else 126 { 127 if (j == 7) 128 { 129 const permBlock = permutateByteBlock(data[i .. i + 8]); 130 decryptedData[i .. i + 8] = permBlock; 131 j = 0; 132 } 133 else 134 { 135 decryptedData[i .. i + 8] = data[i .. i + 8]; 136 } 137 j++; 138 } 139 } 140 141 const long padding = decryptedData.length - unencryptedSize; 142 143 if (padding > 0) 144 { 145 return decryptedData[0 .. $ - padding]; 146 } 147 else 148 { 149 return decryptedData; 150 } 151 } 152 153 private int getCycle(const size_t unencryptedSize) pure 154 { 155 ubyte digits = 0; 156 size_t step = unencryptedSize; 157 while (step > 0) 158 { 159 step /= 10; 160 digits++; 161 } 162 if (digits == 0) 163 { 164 digits = 1; 165 } 166 167 int cycle; 168 if (digits < 3) 169 { 170 cycle = 1; 171 } 172 else if (digits < 5) 173 { 174 cycle = digits + 1; 175 } 176 else if (digits < 7) 177 { 178 cycle = digits + 9; 179 } 180 else 181 { 182 cycle = digits + 15; 183 } 184 185 return cycle; 186 } 187 188 private ubyte substituteLastByte(const ubyte lastByte) pure 189 { 190 ubyte result; 191 switch (lastByte) 192 { 193 case 0x77: 194 result = 0x48; 195 break; 196 case 0x48: 197 result = 0x77; 198 break; 199 case 0x00: 200 result = 0x2B; 201 break; 202 case 0x2B: 203 result = 0x00; 204 break; 205 case 0x01: 206 result = 0x68; 207 break; 208 case 0x68: 209 result = 0x01; 210 break; 211 case 0x60: 212 result = 0xFF; 213 break; 214 case 0xFF: 215 result = 0x60; 216 break; 217 case 0x6C: 218 result = 0x80; 219 break; 220 case 0x80: 221 result = 0x6C; 222 break; 223 case 0xB9: 224 result = 0xC0; 225 break; 226 case 0xC0: 227 result = 0xB9; 228 break; 229 case 0xEB: 230 result = 0xFE; 231 break; 232 case 0xFE: 233 result = 0xEB; 234 break; 235 default: 236 result = lastByte; 237 break; 238 } 239 return result; 240 } 241 242 private ubyte[8] permutateByteBlock(const ubyte[] data, bool isDecryption = true) pure 243 { 244 ubyte[8] block; 245 if (isDecryption) 246 { 247 block[0] = data[3]; 248 block[1] = data[4]; 249 block[2] = data[6]; 250 block[3] = data[0]; 251 block[4] = data[1]; 252 block[5] = data[2]; 253 block[6] = data[5]; 254 } 255 else 256 { 257 block[0] = data[3]; 258 block[1] = data[4]; 259 block[2] = data[5]; 260 block[3] = data[0]; 261 block[4] = data[1]; 262 block[5] = data[6]; 263 block[6] = data[2]; 264 } 265 266 const ubyte lastByte = substituteLastByte(data[7]); 267 block[7] = lastByte; 268 269 return block; 270 } 271 272 /// 273 unittest 274 { 275 const ubyte[] key = [0x0e, 0x32, 0x92, 0x32, 0xea, 0x6d, 0x0d, 0x73]; 276 const ubyte[] message = [0x64, 0x61, 0x74, 0x61, 0x5C, 0x61, 0x6C, 0x64, 0x65, 277 0x5F, 0x61, 0x6C, 0x63, 0x68, 0x65, 0x2E, 0x67, 0x61, 278 0x74, 0x00, 0x00, 0x00, 0x00, 0x00]; 279 const ubyte[] encoded = [0x30, 0x65, 0x25, 0x25, 0x08, 0x24, 0x39, 0x30, 0x64, 280 0x5B, 0x21, 0x6D, 0x37, 0x6D, 0x31, 0x7A, 0x63, 0x71, 281 0x65, 0x11, 0x51, 0x00, 0x55, 0x04]; 282 283 const ubyte[] actualDecoded = decrypt(encoded, key, 24); 284 const ubyte[] actualEncoded = encrypt(message, key); 285 286 assert(actualEncoded == encoded); 287 assert(actualDecoded == message); 288 } 289