blob: 51e390c91c31b7133d310f98b13cf20c144c40ae [file] [log] [blame]
Andrey Andreev818aedb2014-02-03 11:30:25 +02001<?php
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.2.4 or newer
6 *
7 * 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 *
19 * @package CodeIgniter
20 * @author EllisLab Dev Team
21 * @copyright Copyright (c) 2008 - 2013, EllisLab, Inc. (http://ellislab.com/)
22 * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
23 * @link http://codeigniter.com
24 * @since Version 3.0
25 * @filesource
26 */
27defined('BASEPATH') OR exit('No direct script access allowed');
28
29/**
30 * CodeIgniter Encryption Class
31 *
32 * Provides two-way keyed encryption via PHP's MCrypt and/or OpenSSL extensions.
33 *
34 * @package CodeIgniter
35 * @subpackage Libraries
36 * @category Libraries
37 * @author Andrey Andreev
38 * @link http://codeigniter.com/user_guide/libraries/encryption.html
39 */
40class CI_Encryption {
41
42 /**
43 * Encryption cipher
44 *
45 * @var string
46 */
47 protected $_cipher = 'rijndael-128';
48
49 /**
50 * Cipher mode
51 *
52 * @var string
53 */
54 protected $_mode = 'cbc';
55
56 /**
57 * Cipher handle
58 *
59 * @var mixed
60 */
61 protected $_handle;
62
63 /**
64 * Encryption key
65 *
66 * @var string
67 */
68 protected $_key;
69
70 /**
71 * PHP extension to be used
72 *
73 * @var string
74 */
75 protected $_driver;
76
77 /**
78 * List of usable drivers (PHP extensions)
79 *
80 * @var array
81 */
82 protected $_drivers = array();
83
Andrey Andreev29cade42014-02-04 14:05:58 +020084 /**
Andrey Andreevf4017672014-02-05 18:51:15 +020085 * List of available modes
86 *
87 * @var array
88 */
89 protected $_modes = array(
90 'mcrypt' => array(
91 'cbc' => 'cbc',
92 'ecb' => 'ecb',
93 'ofb' => 'nofb',
94 'ofb8' => 'ofb',
95 'cfb' => 'ncfb',
96 'cfb8' => 'cfb',
97 'ctr' => 'ctr',
98 'stream' => 'stream'
99 ),
100 'openssl' => array(
101 'cbc' => 'cbc',
102 'ecb' => 'ecb',
103 'ofb' => 'ofb',
104 'cfb' => 'cfb',
105 'cfb8' => 'cfb8',
106 'ctr' => 'ctr',
107 'stream' => '',
108 'gcm' => 'gcm',
109 'xts' => 'xts'
110 )
111 );
112
113 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200114 * List of supported HMAC algorightms
115 *
116 * name => digest size pairs
117 *
118 * @var array
119 */
120 protected $_digests = array(
121 'sha224' => 28,
122 'sha256' => 32,
123 'sha384' => 48,
124 'sha512' => 64
125 );
126
Andrey Andreev818aedb2014-02-03 11:30:25 +0200127 // --------------------------------------------------------------------
128
129 /**
130 * Class constructor
131 *
132 * @param array $params Configuration parameters
133 * @return void
134 */
135 public function __construct(array $params = array())
136 {
137 $this->_drivers = array(
138 'mcrypt' => extension_loaded('mcrypt'),
139 // While OpenSSL is available for PHP 5.3.0, an IV parameter
140 // for the encrypt/decrypt functions is only available since 5.3.3
141 'openssl' => (is_php('5.3.3') && extension_loaded('openssl'))
142 );
143
144 if ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl'])
145 {
146 return show_error('Encryption: Unable to find an available encryption driver.');
147 }
148
149 $this->initialize($params);
150
Andrey Andreev818aedb2014-02-03 11:30:25 +0200151 isset($this->_key) OR $this->_key = config_item('encryption_key');
152 if (empty($this->_key))
153 {
154 return show_error('Encryption: You are required to set an encryption key in your configuration.');
155 }
156
157 log_message('debug', 'Encryption Class Initialized');
158 }
159
160 // --------------------------------------------------------------------
161
162 /**
163 * Initialize
164 *
165 * @param array $params Configuration parameters
166 * @return CI_Encryption
167 */
168 public function initialize(array $params)
169 {
170 if ( ! empty($params['driver']))
171 {
172 if (isset($this->_drivers[$params['driver']]))
173 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200174 if ($this->_drivers[$params['driver']])
Andrey Andreev818aedb2014-02-03 11:30:25 +0200175 {
176 $this->_driver = $params['driver'];
177 }
178 else
179 {
180 log_message('error', "Encryption: Driver '".$params['driver']."' is not available.");
181 }
182 }
183 else
184 {
185 log_message('error', "Encryption: Unknown driver '".$params['driver']."' cannot be configured.");
186 }
187 }
188
Andrey Andreev912831f2014-02-04 17:21:37 +0200189 if (empty($this->_driver))
190 {
191 $this->_driver = ($this->_drivers['mcrypt'] === TRUE)
192 ? 'mcrypt'
193 : 'openssl';
194
195 log_message('debug', "Encryption: Auto-configured driver '".$this->_driver."'.");
196 }
197
Andrey Andreev818aedb2014-02-03 11:30:25 +0200198 empty($params['key']) OR $this->_key = $params['key'];
199 $this->{'_'.$this->_driver.'_initialize'}($params);
200 return $this;
201 }
202
203 // --------------------------------------------------------------------
204
205 /**
206 * Initialize MCrypt
207 *
208 * @param array $params Configuration parameters
209 * @return void
210 */
211 protected function _mcrypt_initialize($params)
212 {
213 if ( ! empty($params['cipher']))
214 {
215 $params['cipher'] = strtolower($params['cipher']);
216 $this->_cipher_alias($params['cipher']);
217
218 if ( ! in_array($params['cipher'], mcrypt_list_algorithms(), TRUE))
219 {
220 log_message('error', 'Encryption: MCrypt cipher '.strtoupper($params['cipher']).' is not available.');
221 }
222 else
223 {
224 $this->_cipher = $params['cipher'];
225 }
226 }
227
228 if ( ! empty($params['mode']))
229 {
Andrey Andreevf4017672014-02-05 18:51:15 +0200230 $params['mode'] = strtolower($params['mode']);
231 if ( ! isset($this->_modes['mcrypt'][$params['mode']]))
Andrey Andreev818aedb2014-02-03 11:30:25 +0200232 {
233 log_message('error', 'Encryption: MCrypt mode '.strtotupper($params['mode']).' is not available.');
234 }
235 else
236 {
Andrey Andreevf4017672014-02-05 18:51:15 +0200237 $this->_mode = $this->_modes['mcrypt'][$params['mode']];
Andrey Andreev818aedb2014-02-03 11:30:25 +0200238 }
239 }
240
241 if (isset($this->_cipher, $this->_mode))
242 {
243 if (is_resource($this->_handle)
244 && (strtolower(mcrypt_enc_get_algorithms_name($this->_handle)) !== $this->_cipher
245 OR strtolower(mcrypt_enc_get_modes_name($this->_handle)) !== $this->_mode)
246 )
247 {
248 mcrypt_module_close($this->_handle);
249 }
250
251 if ($this->_handle = mcrypt_module_open($this->_cipher, '', $this->_mode, ''))
252 {
253 log_message('debug', 'Encryption: MCrypt cipher '.strtoupper($this->_cipher).' initialized in '.strtoupper($this->_mode).' mode.');
254 }
255 else
256 {
257 log_message('error', 'Encryption: Unable to initialize MCrypt with cipher '.strtoupper($this->_cipher).' in '.strtoupper($this->_mode).' mode.');
258 }
259 }
260 }
261
262 // --------------------------------------------------------------------
263
264 /**
265 * Initialize OpenSSL
266 *
267 * @param array $params Configuration parameters
268 * @return void
269 */
270 protected function _openssl_initialize($params)
271 {
272 if ( ! empty($params['cipher']))
273 {
274 $params['cipher'] = strtolower($params['cipher']);
275 $this->_cipher_alias($params['cipher']);
276 $this->_cipher = $params['cipher'];
277 }
278
279 if ( ! empty($params['mode']))
280 {
Andrey Andreevf4017672014-02-05 18:51:15 +0200281 $params['mode'] = strtolower($params['mode']);
282 if ( ! isset($this->_modes['openssl'][$params['mode']]))
283 {
284 log_message('error', 'Encryption: OpenSSL mode '.strtotupper($params['mode']).' is not available.');
285 }
286 else
287 {
288 $this->_mode = $this->_modes['openssl'][$params['mode']];
289 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200290 }
291
292 if (isset($this->_cipher, $this->_mode))
293 {
Andrey Andreevf4017672014-02-05 18:51:15 +0200294 // This is mostly for the stream mode, which doesn't get suffixed in OpenSSL
295 $handle = empty($this->_mode)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200296 ? $this->_cipher
297 : $this->_cipher.'-'.$this->_mode;
298
Andrey Andreev50ccc382014-02-04 23:30:06 +0200299 if ( ! in_array($handle, openssl_get_cipher_methods(), TRUE))
Andrey Andreev818aedb2014-02-03 11:30:25 +0200300 {
301 $this->_handle = NULL;
302 log_message('error', 'Encryption: Unable to initialize OpenSSL with method '.strtoupper($handle).'.');
303 }
304 else
305 {
306 $this->_handle = $handle;
307 log_message('debug', 'Encryption: OpenSSL initialized with method '.strtoupper($handle).'.');
308 }
309 }
310 }
311
312 // --------------------------------------------------------------------
313
314 /**
315 * Encrypt
316 *
317 * @param string $data Input data
318 * @param array $params Input parameters
319 * @return string
320 */
321 public function encrypt($data, array $params = NULL)
322 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200323 if (($params = $this->_get_params($params)) === FALSE)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200324 {
325 return FALSE;
326 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200327
328 if (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE)
329 {
330 return FALSE;
331 }
332
Andrey Andreev29cade42014-02-04 14:05:58 +0200333 if ($params['base64'])
334 {
335 $data = base64_encode($data);
336 }
337
338 if ($params['hmac'] !== FALSE)
339 {
340 if ( ! isset($params['hmac']['key']))
341 {
342 $params['hmac']['key'] = $this->hkdf(
343 $params['key'],
344 $params['hmac']['digest'],
345 NULL,
346 NULL,
347 'authentication'
348 );
349 }
350
Andrey Andreev177144f2014-02-04 18:07:34 +0200351 return hash_hmac($params['hmac']['digest'], $data, $params['hmac']['key'], ! $params['base64']).$data;
Andrey Andreev29cade42014-02-04 14:05:58 +0200352 }
353
354 return $data;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200355 }
356
357 // --------------------------------------------------------------------
358
359 /**
360 * Encrypt via MCrypt
361 *
362 * @param string $data Input data
363 * @param array $params Input parameters
364 * @return string
365 */
366 protected function _mcrypt_encrypt($data, $params)
367 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200368 if ( ! is_resource($params['handle']))
369 {
370 return FALSE;
371 }
Andrey Andreeve7516b02014-02-05 13:45:31 +0200372 elseif ( ! isset($params['iv']))
373 {
374 $params['iv'] = ($iv_size = mcrypt_enc_get_iv_size($params['handle']))
375 ? $this->_mcrypt_get_iv($iv_size)
376 : NULL;
377 }
378
379
380 if (mcrypt_generic_init($params['handle'], $params['key'], $params['iv']) < 0)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200381 {
382 if ($params['handle'] !== $this->_handle)
383 {
384 mcrypt_module_close($params['handle']);
385 }
386
387 return FALSE;
388 }
389
390 // Use PKCS#7 padding in order to ensure compatibility with OpenSSL
Andrey Andreevf4017672014-02-05 18:51:15 +0200391 // and other implementations outside of PHP.
392 if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
393 {
394 $block_size = mcrypt_enc_get_block_size($params['handle']);
395 $pad = $block_size - (strlen($data) % $block_size);
396 $data .= str_repeat(chr($pad), $pad);
397 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200398
Andrey Andreeve7516b02014-02-05 13:45:31 +0200399 // Work-around for yet another strange behavior in MCrypt.
400 //
401 // When encrypting in ECB mode, the IV is ignored. Yet
402 // mcrypt_enc_get_iv_size() returns a value larger than 0
403 // even if ECB is used AND mcrypt_generic_init() complains
404 // if you don't pass an IV with length equal to the said
405 // return value.
406 //
407 // This probably would've been fine (even though still wasteful),
408 // but OpenSSL isn't that dumb and we need to make the process
409 // portable, so ...
410 $data = (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
411 ? $params['iv'].mcrypt_generic($params['handle'], $data)
412 : mcrypt_generic($params['handle'], $data);
Andrey Andreev818aedb2014-02-03 11:30:25 +0200413
414 mcrypt_generic_deinit($params['handle']);
415 if ($params['handle'] !== $this->_handle)
416 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200417 mcrypt_module_close($params['handle']);
Andrey Andreev818aedb2014-02-03 11:30:25 +0200418 }
419
420 return $data;
421 }
422
423 // --------------------------------------------------------------------
424
425 /**
426 * Encrypt via OpenSSL
427 *
428 * @param string $data Input data
429 * @param array $params Input parameters
430 * @return string
431 */
432 protected function _openssl_encrypt($data, $params)
433 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200434 if (empty($params['handle']))
435 {
436 return FALSE;
437 }
Andrey Andreeve7516b02014-02-05 13:45:31 +0200438 elseif ( ! isset($params['iv']))
439 {
440 $params['iv'] = ($iv_size = openssl_cipher_iv_length($params['handle']))
441 ? $this->_openssl_get_iv($iv_size)
442 : NULL;
443 }
Andrey Andreev29cade42014-02-04 14:05:58 +0200444
Andrey Andreev818aedb2014-02-03 11:30:25 +0200445 $data = openssl_encrypt(
446 $data,
447 $params['handle'],
448 $params['key'],
449 1, // DO NOT TOUCH!
450 $params['iv']
451 );
452
453 if ($data === FALSE)
454 {
455 return FALSE;
456 }
457
458 return $params['iv'].$data;
459 }
460
461 // --------------------------------------------------------------------
462
463 /**
464 * Decrypt
465 *
466 * @param string $data Encrypted data
467 * @param array $params Input parameters
468 * @return string
469 */
470 public function decrypt($data, array $params = NULL)
471 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200472 if (($params = $this->_get_params($params)) === FALSE)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200473 {
474 return FALSE;
475 }
Andrey Andreev29cade42014-02-04 14:05:58 +0200476
477 if ($params['hmac'] !== FALSE)
478 {
479 if ( ! isset($params['hmac']['key']))
480 {
481 $params['hmac']['key'] = $this->hkdf(
482 $params['key'],
483 $params['hmac']['digest'],
484 NULL,
485 NULL,
486 'authentication'
487 );
488 }
489
490 // This might look illogical, but it is done during encryption as well ...
Andrey Andreev177144f2014-02-04 18:07:34 +0200491 // The 'base64' value is effectively an inverted "raw data" parameter
Andrey Andreev29cade42014-02-04 14:05:58 +0200492 $digest_size = ($params['base64'])
493 ? $this->_digests[$params['hmac']['digest']] * 2
494 : $this->_digests[$params['hmac']['digest']];
495 $hmac = substr($data, 0, $digest_size);
496 $data = substr($data, $digest_size);
497
Andrey Andreev177144f2014-02-04 18:07:34 +0200498 if ($hmac !== hash_hmac($params['hmac']['digest'], $data, $params['hmac']['key'], ! $params['base64']))
Andrey Andreev29cade42014-02-04 14:05:58 +0200499 {
500 return FALSE;
501 }
502 }
503
504 if ($params['base64'])
Andrey Andreev818aedb2014-02-03 11:30:25 +0200505 {
506 $data = base64_decode($data);
507 }
508
Andrey Andreeve7516b02014-02-05 13:45:31 +0200509 if (isset($params['iv']) && strncmp($params['iv'], $data, $iv_size = strlen($params['iv'])) === 0)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200510 {
511 $data = substr($data, $iv_size);
512 }
513
514 return $this->{'_'.$this->_driver.'_decrypt'}($data, $params);
515 }
516
517 // --------------------------------------------------------------------
518
519 /**
520 * Decrypt via MCrypt
521 *
522 * @param string $data Encrypted data
523 * @param array $params Input parameters
524 * @return string
525 */
526 protected function _mcrypt_decrypt($data, $params)
527 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200528 if ( ! is_resource($params['handle']))
529 {
530 return FALSE;
531 }
Andrey Andreeve7516b02014-02-05 13:45:31 +0200532 elseif ( ! isset($params['iv']))
533 {
534 if ($iv_size = mcrypt_enc_get_iv_size($params['handle']))
535 {
536 if (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
537 {
538 $params['iv'] = substr($data, 0, $iv_size);
539 $data = substr($data, $iv_size);
540 }
541 else
542 {
543 // MCrypt is dumb and this is ignored, only size matters
544 $params['iv'] = str_repeat("\x0", $iv_size);
545 }
546 }
547 else
548 {
549 $params['iv'] = NULL;
550 }
551 }
552
553 if (mcrypt_generic_init($params['handle'], $params['key'], $params['iv']) < 0)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200554 {
555 if ($params['handle'] !== $this->_handle)
556 {
557 mcrypt_module_close($params['handle']);
558 }
559
560 return FALSE;
561 }
562
563 $data = mdecrypt_generic($params['handle'], $data);
Andrey Andreevf4017672014-02-05 18:51:15 +0200564 // Remove PKCS#7 padding, if necessary
565 if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
566 {
567 $data = substr($data, 0, -ord($data[strlen($data)-1]));
568 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200569
570 mcrypt_generic_deinit($params['handle']);
571 if ($params['handle'] !== $this->_handle)
572 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200573 mcrypt_module_close($params['handle']);
Andrey Andreev818aedb2014-02-03 11:30:25 +0200574 }
575
Andrey Andreevf4017672014-02-05 18:51:15 +0200576 return $data;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200577 }
578
579 // --------------------------------------------------------------------
580
581 /**
582 * Decrypt via OpenSSL
583 *
584 * @param string $data Encrypted data
585 * @param array $params Input parameters
586 * @return string
587 */
588 protected function _openssl_decrypt($data, $params)
589 {
Andrey Andreeve7516b02014-02-05 13:45:31 +0200590 if ( ! isset($params['iv']))
591 {
592 if ($iv_size = openssl_cipher_iv_length($params['handle']))
593 {
594 $params['iv'] = substr($data, 0, $iv_size);
595 $data = substr($data, $iv_size);
596 }
597 else
598 {
599 $params['iv'] = NULL;
600 }
601 }
602
Andrey Andreev29cade42014-02-04 14:05:58 +0200603 return empty($params['handle'])
604 ? FALSE
605 : openssl_decrypt(
606 $data,
607 $params['handle'],
608 $params['key'],
609 1, // DO NOT TOUCH!
610 $params['iv']
611 );
Andrey Andreev818aedb2014-02-03 11:30:25 +0200612 }
613
614 // --------------------------------------------------------------------
615
616 /**
Andrey Andreev818aedb2014-02-03 11:30:25 +0200617 * Get IV via MCrypt
618 *
619 * @param int $size
620 * @return int
621 */
622 protected function _mcrypt_get_iv($size)
623 {
624 // If /dev/urandom is available - use it, otherwise there's
625 // also /dev/random, but it is highly unlikely that it would
626 // be available while /dev/urandom is not and it is known to be
627 // blocking anyway.
628 if (defined(MCRYPT_DEV_URANDOM))
629 {
630 $source = MCRYPT_DEV_URANDOM;
631 }
632 else
633 {
634 $source = MCRYPT_RAND;
635 is_php('5.3') OR srand(microtime(TRUE));
636 }
637
638 return mcrypt_create_iv($size, $source);
639 }
640
641 // --------------------------------------------------------------------
642
643 /**
644 * Get IV via OpenSSL
645 *
646 * @param int $size IV size
647 * @return int
648 */
649 protected function _openssl_get_iv($size)
650 {
651 return openssl_random_pseudo_bytes($size);
652 }
653
654 // --------------------------------------------------------------------
655
656 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200657 * Get params
Andrey Andreev818aedb2014-02-03 11:30:25 +0200658 *
659 * @param array $params Input parameters
660 * @return array
661 */
Andrey Andreev29cade42014-02-04 14:05:58 +0200662 protected function _get_params($params)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200663 {
664 if (empty($params))
665 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200666 return isset($this->_cipher, $this->_mode, $this->_key, $this->_handle)
Andrey Andreev29cade42014-02-04 14:05:58 +0200667 ? array(
668 'handle' => $this->_handle,
669 'cipher' => $this->_cipher,
670 'mode' => $this->_mode,
671 'key' => $this->_key,
672 'base64' => TRUE,
673 'hmac' => $this->_mode === 'gcm' ? FALSE : array('digest' => 'sha512', 'key' => NULL)
674 )
675 : FALSE;
676 }
677 elseif ( ! isset($params['cipher'], $params['mode'], $params['key']))
678 {
679 return FALSE;
680 }
681
682 if ($params['mode'] === 'gcm')
683 {
684 $params['hmac'] = FALSE;
685 }
686 elseif ( ! isset($params['hmac']) OR ( ! is_array($params['hmac']) && $params['hmac'] !== FALSE))
687 {
688 $params['hmac'] = array(
689 'digest' => 'sha512',
690 'key' => NULL
691 );
692 }
693 elseif (is_array($params['hmac']))
694 {
695 if (isset($params['hmac']['digest']) && ! isset($this->_digests[$params['hmac']['digest']]))
Andrey Andreev818aedb2014-02-03 11:30:25 +0200696 {
697 return FALSE;
698 }
699
Andrey Andreev29cade42014-02-04 14:05:58 +0200700 $params['hmac'] = array(
701 'digest' => isset($params['hmac']['digest']) ? $params['hmac']['digest'] : 'sha512',
702 'key' => isset($params['hmac']['key']) ? $params['hmac']['key'] : NULL
Andrey Andreev818aedb2014-02-03 11:30:25 +0200703 );
704 }
705
Andrey Andreevf4017672014-02-05 18:51:15 +0200706 if (isset($params['mode']))
707 {
708 $params['mode'] = strtolower($params['mode']);
709 if ( ! isset($this->_modes[$this->_driver][$params['mode']]))
710 {
711 return FALSE;
712 }
713 else
714 {
715 $params['mode'] = $this->_modes[$this->_driver][$params['mode']];
716 }
717 }
718
Andrey Andreev818aedb2014-02-03 11:30:25 +0200719 $params = array(
720 'handle' => NULL,
721 'cipher' => isset($params['cipher']) ? $params['cipher'] : $this->_cipher,
722 'mode' => isset($params['mode']) ? $params['mode'] : $this->_mode,
723 'key' => isset($params['key']) ? $params['key'] : $this->_key,
724 'iv' => isset($params['iv']) ? $params['iv'] : NULL,
Andrey Andreev29cade42014-02-04 14:05:58 +0200725 'base64' => isset($params['base64']) ? $params['base64'] : TRUE,
726 'hmac' => $params['hmac']
Andrey Andreev818aedb2014-02-03 11:30:25 +0200727 );
728
Andrey Andreev818aedb2014-02-03 11:30:25 +0200729 $this->_cipher_alias($params['cipher']);
Andrey Andreev29cade42014-02-04 14:05:58 +0200730 $params['handle'] = ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)
731 ? $this->{'_'.$this->_driver.'_get_handle'}($params['cipher'], $params['mode'])
732 : $this->_handle;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200733
734 return $params;
735 }
736
737 // --------------------------------------------------------------------
738
739 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200740 * Get MCrypt handle
Andrey Andreev818aedb2014-02-03 11:30:25 +0200741 *
Andrey Andreev29cade42014-02-04 14:05:58 +0200742 * @param string $cipher Cipher name
743 * @param string $mode Encryption mode
744 * @return resource
Andrey Andreev818aedb2014-02-03 11:30:25 +0200745 */
Andrey Andreev29cade42014-02-04 14:05:58 +0200746 protected function _mcrypt_get_handle($cipher, $mode)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200747 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200748 return mcrypt_module_open($cipher, '', $mode, '');
749 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200750
Andrey Andreev29cade42014-02-04 14:05:58 +0200751 // --------------------------------------------------------------------
Andrey Andreev818aedb2014-02-03 11:30:25 +0200752
Andrey Andreev29cade42014-02-04 14:05:58 +0200753 /**
754 * Get OpenSSL handle
755 *
756 * @param string $cipher Cipher name
757 * @param string $mode Encryption mode
758 * @return string
759 */
760 protected function _openssl_get_handle($cipher, $mode)
761 {
762 // OpenSSL methods aren't suffixed with '-stream' for this mode
763 return ($mode === 'stream')
764 ? $cipher
765 : $cipher.'-'.$mode;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200766 }
767
768 // --------------------------------------------------------------------
769
770 /**
771 * Cipher alias
772 *
773 * Tries to translate cipher names between MCrypt and OpenSSL's "dialects".
774 *
775 * @param string $cipher Cipher name
776 * @return void
777 */
778 protected function _cipher_alias(&$cipher)
779 {
780 static $dictionary;
781
782 if (empty($dictionary))
783 {
784 $dictionary = array(
785 'mcrypt' => array(
Andrey Andreev50ccc382014-02-04 23:30:06 +0200786 'aes-128' => 'rijndael-128',
787 'aes-192' => 'rijndael-128',
788 'aes-256' => 'rijndael-128',
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200789 'des3-ede3' => 'tripledes',
790 'bf' => 'blowfish',
Andrey Andreev818aedb2014-02-03 11:30:25 +0200791 ),
792 'openssl' => array(
Andrey Andreev50ccc382014-02-04 23:30:06 +0200793 'rijndael-128' => 'aes-128',
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200794 'tripledes' => 'des-ede3',
795 'blowfish' => 'bf'
Andrey Andreev818aedb2014-02-03 11:30:25 +0200796 )
797 );
798
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200799 // Notes:
800 //
801 // - Blowfish is said to be supporting key sizes between
802 // 4 and 56 bytes, but it appears that between MCrypt and
803 // OpenSSL, only those of 16 and more bytes are compatible.
804 //
805 // Other seemingly matching ciphers between MCrypt, OpenSSL:
Andrey Andreev818aedb2014-02-03 11:30:25 +0200806 //
807 // - DES is compatible, but doesn't need an alias
Andrey Andreev818aedb2014-02-03 11:30:25 +0200808 // - CAST-128/CAST5 is NOT compatible
809 // mcrypt: 'cast-128'
810 // openssl: 'cast5'
811 // - RC2 is NOT compatible
812 // mcrypt: 'rc2'
813 // openssl: 'rc2', 'rc2-40', 'rc2-64'
814 //
815 // To avoid any other confusion due to a popular (but incorrect)
816 // belief, it should also be noted that Rijndael-192/256 are NOT
817 // the same ciphers as AES-192/256 like Rijndael-128 and AES-256 is.
818 //
819 // All compatibility tests were done in CBC mode.
820 }
821
Andrey Andreev50ccc382014-02-04 23:30:06 +0200822 if (isset($dictionary[$this->_driver][$cipher]))
Andrey Andreev818aedb2014-02-03 11:30:25 +0200823 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200824 $cipher = $dictionary[$this->_driver][$cipher];
Andrey Andreev818aedb2014-02-03 11:30:25 +0200825 }
826 }
827
828 // --------------------------------------------------------------------
829
830 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200831 * HKDF
832 *
833 * @link https://tools.ietf.org/rfc/rfc5869.txt
834 * @param $key Input key
835 * @param $digest A SHA-2 hashing algorithm
836 * @param $salt Optional salt
837 * @param $info Optional context/application-specific info
838 * @param $length Output length (defaults to the selected digest size)
839 * @return string A pseudo-random key
840 */
841 public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')
842 {
843 if ( ! isset($this->_digests[$digest]))
844 {
845 return FALSE;
846 }
847
848 if (empty($length) OR ! is_int($length))
849 {
850 $length = $this->_digests[$digest];
851 }
852 elseif ($length > (255 * $this->_digests[$digest]))
853 {
854 return FALSE;
855 }
856
857 isset($salt) OR $salt = str_repeat("\0", $this->_digests[$digest]);
858
859 $prk = hash_hmac($digest, $key, $salt, TRUE);
860 $key = '';
861 for ($key_block = '', $block_index = 1; strlen($key) < $length; $block_index++)
862 {
863 $key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE);
864 $key .= $key_block;
865 }
866
867 return substr($key, 0, $length);
868 }
869
870 // --------------------------------------------------------------------
871
872 /**
Andrey Andreev818aedb2014-02-03 11:30:25 +0200873 * __get() magic
874 *
875 * @param string $key Property name
876 * @return mixed
877 */
878 public function __get($key)
879 {
Andrey Andreev912831f2014-02-04 17:21:37 +0200880 return in_array($key, array('cipher', 'mode', 'driver', 'drivers', 'digests'), TRUE)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200881 ? $this->{'_'.$key}
882 : NULL;
883 }
884
885}
886
887/* End of file Encryption.php */
888/* Location: ./system/libraries/Encryption.php */