1 /**
2  * Broken Data Encryption Standard (DES) implementation.
3  *
4  * From the author of grftool:
5  * > GRAVITY's DES implementation is broken in that it uses
6  * > a bitwise AND instead of a bitwise OR while creating the keyschedule
7  * > causing the keyschedule to always be 0x80 bytes of 0.
8  * Additionally only 1 round is being used when processing a 64 bit block.
9  */
10 module zgrf.crypto.desbroken;
11 
12 import std.bitmanip;
13 
14 import zgrf.crypto.des;
15 import zgrf.bits;
16 
17 /**
18  * Encrypts data using the key 0x00. Key parameter is ignored.
19  *
20  * Params:
21  *     data =  The data array to be encrypted
22  *     key =   Ignored. Should be empty
23  *
24  * Returns: An array of bytes containing the encrypted data
25 */
26 ubyte[] encrypt(const(ubyte)[] data, const(ubyte)[] key = []) pure
27 {
28     return zgrf.crypto.des.encrypt2(data, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], &createSubkeys,
29             &processBlock);
30 }
31 
32 /**
33  * Decrypts data using the key 0x00. Key parameter is ignored.
34  *
35  * Calls [zgrf.crypto.des.decrypt2] with the subkeys function [createSubkeys] and
36  * the process function [processBlock].
37  *
38  * The data must be a multiple of 8 bytes long.
39  *
40  * Params:
41  *     data =  The data array to be decrypted
42  *     key =   Ignored. Should be empty
43  *     unencryptedSize = The size/length of the unencrypted data.
44  *                       The returning data will be truncated to this size if it has padding.
45  *
46  * Returns: An array of bytes containing the decrypted data
47  */
48 ubyte[] decrypt(const(ubyte)[] data, const(ubyte)[] key, const size_t unencryptedSize) pure
49 in (data.length % 8 == 0, "Data must be a multiple of 64 bits (8 bytes)")
50 {
51     return zgrf.crypto.des.decrypt2(data, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], unencryptedSize,
52             &createSubkeys, &processBlock);
53 }
54 
55 /**
56  * Processes one block (8 bytes) of data.
57  *
58  * Unlike the original DES it only uses 1 round.
59  *
60  * Params:
61  *  block = The block to process (must be 8 bytes long)
62  *  subkeys = The subkeys to use for processing
63  *
64  * Returns:
65  *  The processed block
66  */
67 ubyte[] processBlock(const(ubyte)[] block, const BitArray[16] subkeys) pure
68 {
69     return zgrf.crypto.des.processBlock(block, subkeys, 1);
70 }
71 
72 /**
73  * Creates 16 subkeys all of them being 0x00.
74  *
75  * They key parameter is unused.
76  *
77  * Params:
78  *  key = Ignored. Should be empty
79  *
80  * Returns:
81  *  16 BitArrays that contain the subkeys
82  */
83 BitArray[16] createSubkeys(const(ubyte)[] key) pure
84 {
85     const bool[48] bits = 0;
86     BitArray[16] keys;
87     foreach (i; 0 .. 16)
88     {
89         keys[i] = BitArray(bits);
90     }
91 
92     return keys;
93 }
94 
95 ///
96 unittest
97 {
98     const ubyte[] message = [0x48, 0x69, 0x67, 0x68, 0x20, 0x50, 0x72, 0x69, 0x65, 0x73, 0x74];
99     const ubyte[] encodingKey = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
100     const ubyte[] decodingKey = [0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7];
101     const ubyte[] encoded = [0x4D, 0x7D, 0x36, 0x69, 0x30, 0x11, 0x26, 0x7D,
102         0x61, 0x77, 0x65, 0x54, 0x54, 0x05, 0x50, 0x11];
103 
104     const ubyte[] actualEncoded = encrypt(message, encodingKey);
105     const ubyte[] actualDecoded = decrypt(encoded, decodingKey, message.length);
106 
107     assert(actualEncoded == encoded);
108     assert(actualDecoded == message);
109 }