blob: c2cb808dd61a7e3367038bccbac4c219ac35b534 [file] [log] [blame]
Derek Jones37f4b9c2011-07-01 17:56:50 -05001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
Derek Allard2067d1a2008-11-13 22:59:24 +00002/**
3 * CodeIgniter
4 *
Greg Aker741de1c2010-11-10 14:52:57 -06005 * An open source application development framework for PHP 5.1.6 or newer
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05007 * NOTICE OF LICENSE
8 *
9 * Licensed under the Open Software License version 3.0
10 *
11 * This source file is subject to the Open Software License (OSL 3.0) that is
12 * bundled with this package in the files license.txt / license.rst. It is
13 * also available through the world wide web at this URL:
14 * http://opensource.org/licenses/OSL-3.0
15 * If you did not receive a copy of the license and are unable to obtain it
16 * through the world wide web, please send an email to
17 * licensing@ellislab.com so we can send you a copy immediately.
18 *
Derek Allard2067d1a2008-11-13 22:59:24 +000019 * @package CodeIgniter
Derek Jonesf4a4bd82011-10-20 12:18:42 -050020 * @author EllisLab Dev Team
21 * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc. (http://ellislab.com/)
22 * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
Derek Allard2067d1a2008-11-13 22:59:24 +000023 * @link http://codeigniter.com
24 * @since Version 1.0
25 * @filesource
26 */
27
28// ------------------------------------------------------------------------
29
30/**
31 * CodeIgniter Encryption Class
32 *
33 * Provides two-way keyed encoding using XOR Hashing and Mcrypt
34 *
35 * @package CodeIgniter
36 * @subpackage Libraries
37 * @category Libraries
Derek Jonesf4a4bd82011-10-20 12:18:42 -050038 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000039 * @link http://codeigniter.com/user_guide/libraries/encryption.html
40 */
41class CI_Encrypt {
42
43 var $CI;
44 var $encryption_key = '';
45 var $_hash_type = 'sha1';
46 var $_mcrypt_exists = FALSE;
47 var $_mcrypt_cipher;
48 var $_mcrypt_mode;
49
50 /**
51 * Constructor
52 *
53 * Simply determines whether the mcrypt library exists.
54 *
55 */
Greg Akera9263282010-11-10 15:26:43 -060056 public function __construct()
Derek Allard2067d1a2008-11-13 22:59:24 +000057 {
58 $this->CI =& get_instance();
59 $this->_mcrypt_exists = ( ! function_exists('mcrypt_encrypt')) ? FALSE : TRUE;
60 log_message('debug', "Encrypt Class Initialized");
61 }
62
63 // --------------------------------------------------------------------
64
65 /**
66 * Fetch the encryption key
67 *
68 * Returns it as MD5 in order to have an exact-length 128 bit key.
69 * Mcrypt is sensitive to keys that are not the correct length
70 *
71 * @access public
72 * @param string
73 * @return string
74 */
75 function get_key($key = '')
76 {
77 if ($key == '')
78 {
79 if ($this->encryption_key != '')
80 {
81 return $this->encryption_key;
82 }
83
84 $CI =& get_instance();
85 $key = $CI->config->item('encryption_key');
86
Derek Jones7284f062010-08-31 00:30:21 -050087 if ($key == FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +000088 {
89 show_error('In order to use the encryption class requires that you set an encryption key in your config file.');
90 }
91 }
92
93 return md5($key);
94 }
95
96 // --------------------------------------------------------------------
97
98 /**
99 * Set the encryption key
100 *
101 * @access public
102 * @param string
103 * @return void
104 */
105 function set_key($key = '')
106 {
107 $this->encryption_key = $key;
108 }
109
110 // --------------------------------------------------------------------
111
112 /**
113 * Encode
114 *
115 * Encodes the message string using bitwise XOR encoding.
116 * The key is combined with a random hash, and then it
117 * too gets converted using XOR. The whole thing is then run
118 * through mcrypt (if supported) using the randomized key.
119 * The end result is a double-encrypted message string
120 * that is randomized with each call to this function,
121 * even if the supplied message and key are the same.
122 *
123 * @access public
124 * @param string the string to encode
125 * @param string the key
126 * @return string
127 */
128 function encode($string, $key = '')
129 {
130 $key = $this->get_key($key);
Derek Jones09c77932010-08-31 13:17:10 -0500131
Derek Allard2067d1a2008-11-13 22:59:24 +0000132 if ($this->_mcrypt_exists === TRUE)
133 {
Derek Jones09c77932010-08-31 13:17:10 -0500134 $enc = $this->mcrypt_encode($string, $key);
Derek Allard2067d1a2008-11-13 22:59:24 +0000135 }
Derek Jones09c77932010-08-31 13:17:10 -0500136 else
137 {
138 $enc = $this->_xor_encode($string, $key);
139 }
140
Derek Allard2067d1a2008-11-13 22:59:24 +0000141 return base64_encode($enc);
142 }
143
144 // --------------------------------------------------------------------
145
146 /**
147 * Decode
148 *
149 * Reverses the above process
150 *
151 * @access public
152 * @param string
153 * @param string
154 * @return string
155 */
156 function decode($string, $key = '')
157 {
158 $key = $this->get_key($key);
Barry Mienydd671972010-10-04 16:33:58 +0200159
Derek Allard2067d1a2008-11-13 22:59:24 +0000160 if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string))
161 {
162 return FALSE;
163 }
164
165 $dec = base64_decode($string);
166
167 if ($this->_mcrypt_exists === TRUE)
168 {
169 if (($dec = $this->mcrypt_decode($dec, $key)) === FALSE)
170 {
171 return FALSE;
172 }
173 }
Derek Jones09c77932010-08-31 13:17:10 -0500174 else
175 {
176 $dec = $this->_xor_decode($dec, $key);
177 }
Barry Mienydd671972010-10-04 16:33:58 +0200178
Derek Jones09c77932010-08-31 13:17:10 -0500179 return $dec;
Derek Allard2067d1a2008-11-13 22:59:24 +0000180 }
181
182 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200183
Derek Jones09c77932010-08-31 13:17:10 -0500184 /**
185 * Encode from Legacy
186 *
187 * Takes an encoded string from the original Encryption class algorithms and
188 * returns a newly encoded string using the improved method added in 2.0.0
189 * This allows for backwards compatibility and a method to transition to the
190 * new encryption algorithms.
Barry Mienydd671972010-10-04 16:33:58 +0200191 *
Derek Jones09c77932010-08-31 13:17:10 -0500192 * For more details, see http://codeigniter.com/user_guide/installation/upgrade_200.html#encryption
193 *
194 * @access public
195 * @param string
196 * @param int (mcrypt mode constant)
197 * @param string
198 * @return string
199 */
200 function encode_from_legacy($string, $legacy_mode = MCRYPT_MODE_ECB, $key = '')
201 {
202 if ($this->_mcrypt_exists === FALSE)
203 {
204 log_message('error', 'Encoding from legacy is available only when Mcrypt is in use.');
205 return FALSE;
206 }
Barry Mienydd671972010-10-04 16:33:58 +0200207
Derek Jones09c77932010-08-31 13:17:10 -0500208 // decode it first
209 // set mode temporarily to what it was when string was encoded with the legacy
Barry Mienydd671972010-10-04 16:33:58 +0200210 // algorithm - typically MCRYPT_MODE_ECB
Derek Jones09c77932010-08-31 13:17:10 -0500211 $current_mode = $this->_get_mode();
212 $this->set_mode($legacy_mode);
Barry Mienydd671972010-10-04 16:33:58 +0200213
Derek Jones09c77932010-08-31 13:17:10 -0500214 $key = $this->get_key($key);
Barry Mienydd671972010-10-04 16:33:58 +0200215
Derek Jones09c77932010-08-31 13:17:10 -0500216 if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string))
217 {
218 return FALSE;
219 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000220
Derek Jones09c77932010-08-31 13:17:10 -0500221 $dec = base64_decode($string);
Barry Mienydd671972010-10-04 16:33:58 +0200222
Derek Jones09c77932010-08-31 13:17:10 -0500223 if (($dec = $this->mcrypt_decode($dec, $key)) === FALSE)
224 {
225 return FALSE;
226 }
227
228 $dec = $this->_xor_decode($dec, $key);
229
230 // set the mcrypt mode back to what it should be, typically MCRYPT_MODE_CBC
Derek Jones092103e2010-09-02 11:11:58 -0500231 $this->set_mode($current_mode);
Derek Jones09c77932010-08-31 13:17:10 -0500232
233 // and re-encode
234 return base64_encode($this->mcrypt_encode($dec, $key));
235 }
236
237 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200238
Derek Allard2067d1a2008-11-13 22:59:24 +0000239 /**
240 * XOR Encode
241 *
242 * Takes a plain-text string and key as input and generates an
243 * encoded bit-string using XOR
244 *
245 * @access private
246 * @param string
247 * @param string
248 * @return string
249 */
250 function _xor_encode($string, $key)
251 {
252 $rand = '';
253 while (strlen($rand) < 32)
254 {
255 $rand .= mt_rand(0, mt_getrandmax());
256 }
257
258 $rand = $this->hash($rand);
259
260 $enc = '';
261 for ($i = 0; $i < strlen($string); $i++)
Barry Mienydd671972010-10-04 16:33:58 +0200262 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000263 $enc .= substr($rand, ($i % strlen($rand)), 1).(substr($rand, ($i % strlen($rand)), 1) ^ substr($string, $i, 1));
264 }
265
266 return $this->_xor_merge($enc, $key);
267 }
268
269 // --------------------------------------------------------------------
270
271 /**
272 * XOR Decode
273 *
274 * Takes an encoded string and key as input and generates the
275 * plain-text original message
276 *
277 * @access private
278 * @param string
279 * @param string
280 * @return string
281 */
282 function _xor_decode($string, $key)
283 {
284 $string = $this->_xor_merge($string, $key);
285
286 $dec = '';
287 for ($i = 0; $i < strlen($string); $i++)
288 {
289 $dec .= (substr($string, $i++, 1) ^ substr($string, $i, 1));
290 }
291
292 return $dec;
293 }
294
295 // --------------------------------------------------------------------
296
297 /**
298 * XOR key + string Combiner
299 *
300 * Takes a string and key as input and computes the difference using XOR
301 *
302 * @access private
303 * @param string
304 * @param string
305 * @return string
306 */
307 function _xor_merge($string, $key)
308 {
309 $hash = $this->hash($key);
310 $str = '';
311 for ($i = 0; $i < strlen($string); $i++)
312 {
313 $str .= substr($string, $i, 1) ^ substr($hash, ($i % strlen($hash)), 1);
314 }
315
316 return $str;
317 }
318
319 // --------------------------------------------------------------------
320
321 /**
322 * Encrypt using Mcrypt
323 *
324 * @access public
325 * @param string
326 * @param string
327 * @return string
328 */
329 function mcrypt_encode($data, $key)
330 {
331 $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode());
332 $init_vect = mcrypt_create_iv($init_size, MCRYPT_RAND);
333 return $this->_add_cipher_noise($init_vect.mcrypt_encrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), $key);
334 }
335
336 // --------------------------------------------------------------------
337
338 /**
339 * Decrypt using Mcrypt
340 *
341 * @access public
342 * @param string
343 * @param string
344 * @return string
345 */
346 function mcrypt_decode($data, $key)
347 {
348 $data = $this->_remove_cipher_noise($data, $key);
349 $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode());
350
351 if ($init_size > strlen($data))
352 {
353 return FALSE;
354 }
355
356 $init_vect = substr($data, 0, $init_size);
357 $data = substr($data, $init_size);
358 return rtrim(mcrypt_decrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), "\0");
359 }
360
361 // --------------------------------------------------------------------
362
363 /**
364 * Adds permuted noise to the IV + encrypted data to protect
365 * against Man-in-the-middle attacks on CBC mode ciphers
366 * http://www.ciphersbyritter.com/GLOSSARY.HTM#IV
367 *
368 * Function description
369 *
370 * @access private
371 * @param string
372 * @param string
373 * @return string
374 */
375 function _add_cipher_noise($data, $key)
376 {
377 $keyhash = $this->hash($key);
378 $keylen = strlen($keyhash);
379 $str = '';
380
381 for ($i = 0, $j = 0, $len = strlen($data); $i < $len; ++$i, ++$j)
382 {
383 if ($j >= $keylen)
384 {
385 $j = 0;
386 }
387
388 $str .= chr((ord($data[$i]) + ord($keyhash[$j])) % 256);
389 }
390
391 return $str;
392 }
393
394 // --------------------------------------------------------------------
395
396 /**
397 * Removes permuted noise from the IV + encrypted data, reversing
398 * _add_cipher_noise()
399 *
400 * Function description
401 *
402 * @access public
403 * @param type
404 * @return type
405 */
406 function _remove_cipher_noise($data, $key)
407 {
408 $keyhash = $this->hash($key);
409 $keylen = strlen($keyhash);
410 $str = '';
411
412 for ($i = 0, $j = 0, $len = strlen($data); $i < $len; ++$i, ++$j)
413 {
414 if ($j >= $keylen)
415 {
416 $j = 0;
417 }
418
419 $temp = ord($data[$i]) - ord($keyhash[$j]);
420
421 if ($temp < 0)
422 {
423 $temp = $temp + 256;
424 }
Barry Mienydd671972010-10-04 16:33:58 +0200425
Derek Allard2067d1a2008-11-13 22:59:24 +0000426 $str .= chr($temp);
427 }
428
429 return $str;
430 }
431
432 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200433
Derek Allard2067d1a2008-11-13 22:59:24 +0000434 /**
435 * Set the Mcrypt Cipher
436 *
437 * @access public
438 * @param constant
439 * @return string
440 */
441 function set_cipher($cipher)
442 {
443 $this->_mcrypt_cipher = $cipher;
444 }
445
446 // --------------------------------------------------------------------
447
448 /**
449 * Set the Mcrypt Mode
450 *
451 * @access public
452 * @param constant
453 * @return string
454 */
455 function set_mode($mode)
456 {
457 $this->_mcrypt_mode = $mode;
458 }
459
460 // --------------------------------------------------------------------
461
462 /**
463 * Get Mcrypt cipher Value
464 *
465 * @access private
466 * @return string
467 */
468 function _get_cipher()
469 {
470 if ($this->_mcrypt_cipher == '')
471 {
472 $this->_mcrypt_cipher = MCRYPT_RIJNDAEL_256;
473 }
474
475 return $this->_mcrypt_cipher;
476 }
477
478 // --------------------------------------------------------------------
479
480 /**
481 * Get Mcrypt Mode Value
482 *
483 * @access private
484 * @return string
485 */
486 function _get_mode()
487 {
488 if ($this->_mcrypt_mode == '')
489 {
Derek Jones09c77932010-08-31 13:17:10 -0500490 $this->_mcrypt_mode = MCRYPT_MODE_CBC;
Derek Allard2067d1a2008-11-13 22:59:24 +0000491 }
Barry Mienydd671972010-10-04 16:33:58 +0200492
Derek Allard2067d1a2008-11-13 22:59:24 +0000493 return $this->_mcrypt_mode;
494 }
495
496 // --------------------------------------------------------------------
497
498 /**
499 * Set the Hash type
500 *
501 * @access public
502 * @param string
503 * @return string
504 */
505 function set_hash($type = 'sha1')
506 {
507 $this->_hash_type = ($type != 'sha1' AND $type != 'md5') ? 'sha1' : $type;
508 }
509
510 // --------------------------------------------------------------------
511
512 /**
513 * Hash encode a string
514 *
515 * @access public
516 * @param string
517 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200518 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000519 function hash($str)
520 {
521 return ($this->_hash_type == 'sha1') ? $this->sha1($str) : md5($str);
522 }
523
524 // --------------------------------------------------------------------
525
526 /**
527 * Generate an SHA1 Hash
528 *
529 * @access public
530 * @param string
531 * @return string
532 */
533 function sha1($str)
534 {
535 if ( ! function_exists('sha1'))
536 {
537 if ( ! function_exists('mhash'))
538 {
Greg Aker3a746652011-04-19 10:59:47 -0500539 require_once(BASEPATH.'libraries/Sha1.php');
Derek Allard2067d1a2008-11-13 22:59:24 +0000540 $SH = new CI_SHA;
541 return $SH->generate($str);
542 }
543 else
544 {
545 return bin2hex(mhash(MHASH_SHA1, $str));
546 }
547 }
548 else
549 {
550 return sha1($str);
551 }
552 }
553
554}
555
556// END CI_Encrypt class
557
558/* End of file Encrypt.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000559/* Location: ./system/libraries/Encrypt.php */