1<?PHP
2/*The contents of this document are free for use by anyone for any purpose they choose.
3no warranty is implied, nor will one be honored.  The author assumes no liability for
4any consequences that may arise from the use of contents of this document.  Events not
5covered include but are not limited to: system crashes, system slowdown, system failure,
6fires, explosions, floods, earthquakes, boiling seas, intestinal inflammation, cold coffee,
7and total protonic inversion.
8Any questions, comments, or improvements are appreciated and can be directed to:
9
10Tim Thorpe
11blushift@netins.net
12*/
13
14/*
15This script implements the 56-bit DES encryption algorithm,
16created from scratch based on interpretation of the original
17specification document.
18
19des_encrypt_ecb("my key", "my text");
20and
21mcrypt_ecb (MCRYPT_DES, "my key", "my text", MCRYPT_ENCRYPT, str_pad("", 8, chr(0x00)));
22are functionally identical.
23
24Because PHP is lacking in bitwise operators, this set of routines
25makes use of data that has been expanded so that a byte becomes an 8-byte string.
26
27The script performs the transformations, and then condenses the data back to a usable form.
28Apologies for a lack of comments in the bulk of the functions.
29*/////////////////////////////////////////////////////////////////////////////
30
31function des_encrypt_ecb($key, $clearText) {
32//Function to implement Electronic Code Book encoding
33//I haven't had occasion to add any other methods or a decoder for that matter,
34//but all of the underlying functions were written to be universal to all encoding methods,
35//so adding other methods should be easy if you need them.
36
37    if (strlen($key) < 7)
38        $key = str_pad(substr($key, 0, 7), 7, chr(0x00));  //We need 7 bytes for a key; no more, no less
39
40    if (strlen($key) == 7) $key = des_add_parity($key);
41
42	$keys = des_make_subkeys (des_bits_to_bytes ($key));  //Chew the key into the subkeys needed for DES
43	$blockCount = (int) (strlen($clearText) / 8);  //Figure out how many blocks of 8 bytes we need to encode
44	if (strlen ($clearText) % 8) {  //Check to see if there are any leftovers
45		$blockCount++;  //Add another block for them
46		$clearText = str_pad($clearText, $blockCount * 8, chr(0x00));  //Pad it out with zeroes
47	}
48	for ($i = 0; $i < $blockCount; $i++) {  //Cycle through the blocks
49		$clearBlock = substr($clearText, $i * 8, 8);  //Grab a block from the input
50		$cypherBlock = des_block_encode(des_bits_to_bytes ($clearBlock), $keys);  //Encrypt it
51		$cypherText .= des_bytes_to_bits ($cypherBlock);  //Convert the result back to a useful form
52	}
53	return ($cypherText);  //Cough it up
54}
55
56function des_block_encode($clearText, $subKeys) {
57	$ip_table = array(58, 50, 42, 34, 26, 18, 10,  2,
58			  60, 52, 44, 36, 28, 20, 12,  4,
59			  62, 54, 46, 38, 30, 22, 14,  6,
60			  64, 56, 48, 40, 32, 24, 16,  8,
61			  57, 49, 41, 33, 25, 17,  9,  1,
62			  59, 51, 43, 35, 27, 19, 11,  3,
63			  61, 53, 45, 37, 29, 21, 13,  5,
64			  63, 55, 47, 39, 31, 23, 15,  7);
65
66	$ip1_table = array(40,  8, 48, 16, 56, 24, 64, 32,
67			   39,  7, 47, 15, 55, 23, 63, 31,
68			   38,  6, 46, 14, 54, 22, 62, 30,
69			   37,  5, 45, 13, 53, 21, 61, 29,
70			   36,  4, 44, 12, 52, 20, 60, 28,
71			   35,  3, 43, 11, 51, 19, 59, 27,
72			   34,  2, 42, 10, 50, 18, 58, 26,
73			   33,  1, 41,  9, 49, 17, 57, 25);
74
75	foreach ($ip_table as $bit) {
76		$ip .= $clearText[$bit - 1];
77	}
78	$l[0] = substr($ip, 0, 32);
79	$r[0] = substr($ip, 32);
80	for ($index = 1; $index <= 16; $index++) {
81		$l[$index] = $r[$index - 1];
82		$r[$index] = des_xor($l[$index - 1], des_transform($r[$index - 1], $subKeys[$index]));
83	}
84
85	foreach ($ip1_table as $bit) {
86		$concat	= $r[16] . $l[16];
87		$cypherBlock .= $concat[$bit - 1];
88	}
89	return ($cypherBlock);
90}
91
92function des_make_subkeys($key) {
93	$pc1 = array(57, 49, 41, 33, 25, 17,  9,
94		      1, 58, 50, 42, 34, 26, 18,
95		     10,  2, 59, 51, 43, 35, 27,
96		     19, 11,  3, 60, 52, 44, 36,
97		     63, 55, 47, 39, 31, 23, 15,
98		      7, 62, 54, 46, 38, 30, 22,
99		     14,  6, 61, 53, 45, 37, 29,
100		     21, 13,  5, 28, 20, 12,  4);
101
102	$pc2 = array(14, 17, 11, 24,  1,  5,
103		      3, 28, 15,  6, 21, 10,
104		     23, 19, 12,  4, 26,  8,
105		     16,  7, 27, 20, 13,  2,
106		     41, 52, 31, 37, 47, 55,
107		     30, 40, 51, 45, 33, 48,
108		     44, 49, 39, 56, 34, 53,
109		     46, 42, 50, 36, 29, 32);
110
111	$shifts = array(1, 1, 2, 2,
112			2, 2, 2, 2,
113			1, 2, 2, 2,
114			2, 2, 2, 1);
115	foreach ($pc1 as $bit) {
116		$k .= $key[$bit - 1];
117	}
118	$index = 0;
119	$c[$index] = substr($k, 0, 28);
120	$d[$index] = substr($k, 28);
121	unset ($k);
122	foreach ($shifts as $positions) {
123		$index++;
124		$c[$index] = des_rotate_left ($c[$index - 1], $positions);
125		$d[$index] = des_rotate_left ($d[$index - 1], $positions);
126	}
127	for ($i = 1; $i <= 16; $i++) {
128		foreach ($pc2 as $bit) {
129			$concat = $c[$i] . $d[$i];
130			$k[$i] .= $concat[$bit - 1];
131		}
132	}
133	return ($k);
134}
135
136function des_transform($data, $key) {
137	$e_table = array(32,  1,  2,  3,  4,  5,
138			  4,  5,  6,  7,  8,  9,
139			  8,  9, 10, 11, 12, 13,
140			 12, 13, 14, 15, 16, 17,
141			 16, 17, 18, 19, 20, 21,
142			 20, 21, 22, 23, 24, 25,
143			 24, 25, 26, 27, 28, 29,
144			 28, 29, 30, 31, 32,  1);
145
146	$s = array(array(14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7,
147			  0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8,
148			  4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,
149			 15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13),
150		   array(15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10,
151			  3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5,
152			  0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,
153			 13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9),
154		   array(10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8,
155			 13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1,
156			 13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,
157			  1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12),
158		   array( 7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15,
159			 13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9,
160			 10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,
161			  3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14),
162		   array( 2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9,
163			 14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6,
164			  4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,
165			 11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3),
166		   array(12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11,
167			 10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8,
168			  9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,
169			  4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13),
170		   array( 4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1,
171			 13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6,
172			  1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,
173			  6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12),
174		   array(13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0,  12,  7,
175			  1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2,
176			  7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13,  15, 3,  5,  8,
177			  2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11));
178
179	$p_table = array(16,  7, 20, 21,
180                         29, 12, 28, 17,
181                          1, 15, 23, 26,
182                          5, 18, 31, 10,
183                          2,  8, 24, 14,
184                         32, 27,  3,  9,
185                         19, 13, 30,  6,
186                         22, 11,  4, 25);
187
188	$nybbles = array(chr(0x00).chr(0x00).chr(0x00).chr(0x00), chr(0x00).chr(0x00).chr(0x00).chr(0x01),
189			 chr(0x00).chr(0x00).chr(0x01).chr(0x00), chr(0x00).chr(0x00).chr(0x01).chr(0x01),
190			 chr(0x00).chr(0x01).chr(0x00).chr(0x00), chr(0x00).chr(0x01).chr(0x00).chr(0x01),
191			 chr(0x00).chr(0x01).chr(0x01).chr(0x00), chr(0x00).chr(0x01).chr(0x01).chr(0x01),
192			 chr(0x01).chr(0x00).chr(0x00).chr(0x00), chr(0x01).chr(0x00).chr(0x00).chr(0x01),
193			 chr(0x01).chr(0x00).chr(0x01).chr(0x00), chr(0x01).chr(0x00).chr(0x01).chr(0x01),
194			 chr(0x01).chr(0x01).chr(0x00).chr(0x00), chr(0x01).chr(0x01).chr(0x00).chr(0x01),
195			 chr(0x01).chr(0x01).chr(0x01).chr(0x00), chr(0x01).chr(0x01).chr(0x01).chr(0x01));
196
197
198	foreach ($e_table as $bit) {
199		$e .= $data[$bit - 1];
200	}
201	$ek = des_xor($e, $key);
202	for ($i = 0; $i < 8; $i++) {
203		$offset = $i * 6;
204		$sAddress = ord($ek[$offset]) * 0x20 +
205			    ord($ek[$offset + 1]) * 0x08 +
206			    ord($ek[$offset + 2]) * 0x04 +
207			    ord($ek[$offset + 3]) * 0x02 +
208			    ord($ek[$offset + 4]) +
209			    ord($ek[$offset + 5]) * 0x10;
210		$sResult .= $nybbles[$s[$i][$sAddress]];
211	}
212	foreach ($p_table as $bit) {
213		$p .= $sResult[$bit - 1];
214	}
215	return ($p);
216}
217
218function des_bits_to_bytes($bitStream) {
219	for ($i = 0; $i < strlen($bitStream); $i++) {
220		$val = ord($bitStream[$i]);
221		if ($val & 0x80) {$byteStream .= chr(0x01);} else {$byteStream .= chr(0x00);}
222		if ($val & 0x40) {$byteStream .= chr(0x01);} else {$byteStream .= chr(0x00);}
223		if ($val & 0x20) {$byteStream .= chr(0x01);} else {$byteStream .= chr(0x00);}
224		if ($val & 0x10) {$byteStream .= chr(0x01);} else {$byteStream .= chr(0x00);}
225		if ($val & 0x08) {$byteStream .= chr(0x01);} else {$byteStream .= chr(0x00);}
226		if ($val & 0x04) {$byteStream .= chr(0x01);} else {$byteStream .= chr(0x00);}
227		if ($val & 0x02) {$byteStream .= chr(0x01);} else {$byteStream .= chr(0x00);}
228		if ($val & 0x01) {$byteStream .= chr(0x01);} else {$byteStream .= chr(0x00);}
229	}
230	return ($byteStream);
231}
232
233function des_bytes_to_bits($byteStream) {
234	for ($i = 0; $i < (strlen($byteStream) / 8); $i++) {
235		$offset	= $i * 8;
236		$value = ord($byteStream[$offset]) * 0x80 +
237			 ord($byteStream[$offset + 1]) * 0x40 +
238			 ord($byteStream[$offset + 2]) * 0x20 +
239			 ord($byteStream[$offset + 3]) * 0x10 +
240			 ord($byteStream[$offset + 4]) * 0x08 +
241			 ord($byteStream[$offset + 5]) * 0x04 +
242			 ord($byteStream[$offset + 6]) * 0x02 +
243			 ord($byteStream[$offset + 7]);
244		$bitStream .= chr($value);
245
246	}
247	return ($bitStream);
248}
249
250function des_rotate_left($input, $positions) {
251	return substr($input, $positions) . substr($input, 0, $positions);
252}
253
254function des_xor($a, $b) {
255	for ($i = 0; $i < strlen($a); $i++) {
256		$xor .= $a[$i] ^ $b[$i];
257	}
258	return $xor;
259}
260
261// DES helper function
262// input: 7-Bytes Key without parity
263// ouput: 8-Bytes Key with parity
264function des_add_parity($key)
265{
266    static $odd_parity = array(
267        1,  1,  2,  2,  4,  4,  7,  7,  8,  8, 11, 11, 13, 13, 14, 14,
268        16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
269        32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
270        49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
271        64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
272        81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
273        97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
274        112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
275        128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
276        145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
277        161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
278        176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
279        193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
280        208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
281        224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
282        241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254);
283
284    for ($i = 0; $i < strlen($key); $i++) {
285        $bin .= sprintf('%08s', decbin(ord($key{$i})));
286    }
287
288    $str1 = explode('-', substr(chunk_split($bin, 7, '-'), 0, -1));
289    foreach($str1 as $s) {
290        $x .= sprintf('%02s', dechex($odd_parity[bindec($s . '0')]));
291    }
292
293    return pack('H*', $x);
294
295}
296