blob: 4edfaa78e2397cf5b8fade49fb3efd919ce952c7 [file] [log] [blame]
Derek Allard2067d1a2008-11-13 22:59:24 +00001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
9 * @copyright Copyright (c) 2008, EllisLab, Inc.
10 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
12 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * CodeIgniter Encryption Class
20 *
21 * Provides two-way keyed encoding using XOR Hashing and Mcrypt
22 *
23 * @package CodeIgniter
24 * @subpackage Libraries
25 * @category Libraries
26 * @author ExpressionEngine Dev Team
27 * @link http://codeigniter.com/user_guide/libraries/encryption.html
28 */
29class CI_Encrypt {
30
31 var $CI;
32 var $encryption_key = '';
33 var $_hash_type = 'sha1';
34 var $_mcrypt_exists = FALSE;
35 var $_mcrypt_cipher;
36 var $_mcrypt_mode;
37
38 /**
39 * Constructor
40 *
41 * Simply determines whether the mcrypt library exists.
42 *
43 */
44 function CI_Encrypt()
45 {
46 $this->CI =& get_instance();
47 $this->_mcrypt_exists = ( ! function_exists('mcrypt_encrypt')) ? FALSE : TRUE;
48 log_message('debug', "Encrypt Class Initialized");
49 }
50
51 // --------------------------------------------------------------------
52
53 /**
54 * Fetch the encryption key
55 *
56 * Returns it as MD5 in order to have an exact-length 128 bit key.
57 * Mcrypt is sensitive to keys that are not the correct length
58 *
59 * @access public
60 * @param string
61 * @return string
62 */
63 function get_key($key = '')
64 {
65 if ($key == '')
66 {
67 if ($this->encryption_key != '')
68 {
69 return $this->encryption_key;
70 }
71
72 $CI =& get_instance();
73 $key = $CI->config->item('encryption_key');
74
75 if ($key === FALSE)
76 {
77 show_error('In order to use the encryption class requires that you set an encryption key in your config file.');
78 }
79 }
80
81 return md5($key);
82 }
83
84 // --------------------------------------------------------------------
85
86 /**
87 * Set the encryption key
88 *
89 * @access public
90 * @param string
91 * @return void
92 */
93 function set_key($key = '')
94 {
95 $this->encryption_key = $key;
96 }
97
98 // --------------------------------------------------------------------
99
100 /**
101 * Encode
102 *
103 * Encodes the message string using bitwise XOR encoding.
104 * The key is combined with a random hash, and then it
105 * too gets converted using XOR. The whole thing is then run
106 * through mcrypt (if supported) using the randomized key.
107 * The end result is a double-encrypted message string
108 * that is randomized with each call to this function,
109 * even if the supplied message and key are the same.
110 *
111 * @access public
112 * @param string the string to encode
113 * @param string the key
114 * @return string
115 */
116 function encode($string, $key = '')
117 {
118 $key = $this->get_key($key);
119 $enc = $this->_xor_encode($string, $key);
120
121 if ($this->_mcrypt_exists === TRUE)
122 {
123 $enc = $this->mcrypt_encode($enc, $key);
124 }
125 return base64_encode($enc);
126 }
127
128 // --------------------------------------------------------------------
129
130 /**
131 * Decode
132 *
133 * Reverses the above process
134 *
135 * @access public
136 * @param string
137 * @param string
138 * @return string
139 */
140 function decode($string, $key = '')
141 {
142 $key = $this->get_key($key);
143
144 if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string))
145 {
146 return FALSE;
147 }
148
149 $dec = base64_decode($string);
150
151 if ($this->_mcrypt_exists === TRUE)
152 {
153 if (($dec = $this->mcrypt_decode($dec, $key)) === FALSE)
154 {
155 return FALSE;
156 }
157 }
158
159 return $this->_xor_decode($dec, $key);
160 }
161
162 // --------------------------------------------------------------------
163
164 /**
165 * XOR Encode
166 *
167 * Takes a plain-text string and key as input and generates an
168 * encoded bit-string using XOR
169 *
170 * @access private
171 * @param string
172 * @param string
173 * @return string
174 */
175 function _xor_encode($string, $key)
176 {
177 $rand = '';
178 while (strlen($rand) < 32)
179 {
180 $rand .= mt_rand(0, mt_getrandmax());
181 }
182
183 $rand = $this->hash($rand);
184
185 $enc = '';
186 for ($i = 0; $i < strlen($string); $i++)
187 {
188 $enc .= substr($rand, ($i % strlen($rand)), 1).(substr($rand, ($i % strlen($rand)), 1) ^ substr($string, $i, 1));
189 }
190
191 return $this->_xor_merge($enc, $key);
192 }
193
194 // --------------------------------------------------------------------
195
196 /**
197 * XOR Decode
198 *
199 * Takes an encoded string and key as input and generates the
200 * plain-text original message
201 *
202 * @access private
203 * @param string
204 * @param string
205 * @return string
206 */
207 function _xor_decode($string, $key)
208 {
209 $string = $this->_xor_merge($string, $key);
210
211 $dec = '';
212 for ($i = 0; $i < strlen($string); $i++)
213 {
214 $dec .= (substr($string, $i++, 1) ^ substr($string, $i, 1));
215 }
216
217 return $dec;
218 }
219
220 // --------------------------------------------------------------------
221
222 /**
223 * XOR key + string Combiner
224 *
225 * Takes a string and key as input and computes the difference using XOR
226 *
227 * @access private
228 * @param string
229 * @param string
230 * @return string
231 */
232 function _xor_merge($string, $key)
233 {
234 $hash = $this->hash($key);
235 $str = '';
236 for ($i = 0; $i < strlen($string); $i++)
237 {
238 $str .= substr($string, $i, 1) ^ substr($hash, ($i % strlen($hash)), 1);
239 }
240
241 return $str;
242 }
243
244 // --------------------------------------------------------------------
245
246 /**
247 * Encrypt using Mcrypt
248 *
249 * @access public
250 * @param string
251 * @param string
252 * @return string
253 */
254 function mcrypt_encode($data, $key)
255 {
256 $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode());
257 $init_vect = mcrypt_create_iv($init_size, MCRYPT_RAND);
258 return $this->_add_cipher_noise($init_vect.mcrypt_encrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), $key);
259 }
260
261 // --------------------------------------------------------------------
262
263 /**
264 * Decrypt using Mcrypt
265 *
266 * @access public
267 * @param string
268 * @param string
269 * @return string
270 */
271 function mcrypt_decode($data, $key)
272 {
273 $data = $this->_remove_cipher_noise($data, $key);
274 $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode());
275
276 if ($init_size > strlen($data))
277 {
278 return FALSE;
279 }
280
281 $init_vect = substr($data, 0, $init_size);
282 $data = substr($data, $init_size);
283 return rtrim(mcrypt_decrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), "\0");
284 }
285
286 // --------------------------------------------------------------------
287
288 /**
289 * Adds permuted noise to the IV + encrypted data to protect
290 * against Man-in-the-middle attacks on CBC mode ciphers
291 * http://www.ciphersbyritter.com/GLOSSARY.HTM#IV
292 *
293 * Function description
294 *
295 * @access private
296 * @param string
297 * @param string
298 * @return string
299 */
300 function _add_cipher_noise($data, $key)
301 {
302 $keyhash = $this->hash($key);
303 $keylen = strlen($keyhash);
304 $str = '';
305
306 for ($i = 0, $j = 0, $len = strlen($data); $i < $len; ++$i, ++$j)
307 {
308 if ($j >= $keylen)
309 {
310 $j = 0;
311 }
312
313 $str .= chr((ord($data[$i]) + ord($keyhash[$j])) % 256);
314 }
315
316 return $str;
317 }
318
319 // --------------------------------------------------------------------
320
321 /**
322 * Removes permuted noise from the IV + encrypted data, reversing
323 * _add_cipher_noise()
324 *
325 * Function description
326 *
327 * @access public
328 * @param type
329 * @return type
330 */
331 function _remove_cipher_noise($data, $key)
332 {
333 $keyhash = $this->hash($key);
334 $keylen = strlen($keyhash);
335 $str = '';
336
337 for ($i = 0, $j = 0, $len = strlen($data); $i < $len; ++$i, ++$j)
338 {
339 if ($j >= $keylen)
340 {
341 $j = 0;
342 }
343
344 $temp = ord($data[$i]) - ord($keyhash[$j]);
345
346 if ($temp < 0)
347 {
348 $temp = $temp + 256;
349 }
350
351 $str .= chr($temp);
352 }
353
354 return $str;
355 }
356
357 // --------------------------------------------------------------------
358
359 /**
360 * Set the Mcrypt Cipher
361 *
362 * @access public
363 * @param constant
364 * @return string
365 */
366 function set_cipher($cipher)
367 {
368 $this->_mcrypt_cipher = $cipher;
369 }
370
371 // --------------------------------------------------------------------
372
373 /**
374 * Set the Mcrypt Mode
375 *
376 * @access public
377 * @param constant
378 * @return string
379 */
380 function set_mode($mode)
381 {
382 $this->_mcrypt_mode = $mode;
383 }
384
385 // --------------------------------------------------------------------
386
387 /**
388 * Get Mcrypt cipher Value
389 *
390 * @access private
391 * @return string
392 */
393 function _get_cipher()
394 {
395 if ($this->_mcrypt_cipher == '')
396 {
397 $this->_mcrypt_cipher = MCRYPT_RIJNDAEL_256;
398 }
399
400 return $this->_mcrypt_cipher;
401 }
402
403 // --------------------------------------------------------------------
404
405 /**
406 * Get Mcrypt Mode Value
407 *
408 * @access private
409 * @return string
410 */
411 function _get_mode()
412 {
413 if ($this->_mcrypt_mode == '')
414 {
415 $this->_mcrypt_mode = MCRYPT_MODE_ECB;
416 }
417
418 return $this->_mcrypt_mode;
419 }
420
421 // --------------------------------------------------------------------
422
423 /**
424 * Set the Hash type
425 *
426 * @access public
427 * @param string
428 * @return string
429 */
430 function set_hash($type = 'sha1')
431 {
432 $this->_hash_type = ($type != 'sha1' AND $type != 'md5') ? 'sha1' : $type;
433 }
434
435 // --------------------------------------------------------------------
436
437 /**
438 * Hash encode a string
439 *
440 * @access public
441 * @param string
442 * @return string
443 */
444 function hash($str)
445 {
446 return ($this->_hash_type == 'sha1') ? $this->sha1($str) : md5($str);
447 }
448
449 // --------------------------------------------------------------------
450
451 /**
452 * Generate an SHA1 Hash
453 *
454 * @access public
455 * @param string
456 * @return string
457 */
458 function sha1($str)
459 {
460 if ( ! function_exists('sha1'))
461 {
462 if ( ! function_exists('mhash'))
463 {
464 require_once(BASEPATH.'libraries/Sha1'.EXT);
465 $SH = new CI_SHA;
466 return $SH->generate($str);
467 }
468 else
469 {
470 return bin2hex(mhash(MHASH_SHA1, $str));
471 }
472 }
473 else
474 {
475 return sha1($str);
476 }
477 }
478
479}
480
481// END CI_Encrypt class
482
483/* End of file Encrypt.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000484/* Location: ./system/libraries/Encrypt.php */