blob: fe177fce3b59987371f45df8542571bfd6d59b38 [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(
Andrey Andreev8e202162014-02-05 18:59:55 +0200138 'mcrypt' => defined('MCRYPT_DEV_URANDOM'),
Andrey Andreev818aedb2014-02-03 11:30:25 +0200139 // 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 {
Andrey Andreev8e202162014-02-05 18:59:55 +0200191 $this->_driver = ($this->_drivers['openssl'] === TRUE)
192 ? 'openssl'
193 : 'mcrypt';
Andrey Andreev912831f2014-02-04 17:21:37 +0200194
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']))
Andrey Andreev8e202162014-02-05 18:59:55 +0200375 ? mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM)
Andrey Andreeve7516b02014-02-05 13:45:31 +0200376 : 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']))
Andrey Andreev8e202162014-02-05 18:59:55 +0200441 ? openssl_random_pseudo_bytes($iv_size)
Andrey Andreeve7516b02014-02-05 13:45:31 +0200442 : 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 Andreev29cade42014-02-04 14:05:58 +0200617 * Get params
Andrey Andreev818aedb2014-02-03 11:30:25 +0200618 *
619 * @param array $params Input parameters
620 * @return array
621 */
Andrey Andreev29cade42014-02-04 14:05:58 +0200622 protected function _get_params($params)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200623 {
624 if (empty($params))
625 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200626 return isset($this->_cipher, $this->_mode, $this->_key, $this->_handle)
Andrey Andreev29cade42014-02-04 14:05:58 +0200627 ? array(
628 'handle' => $this->_handle,
629 'cipher' => $this->_cipher,
630 'mode' => $this->_mode,
631 'key' => $this->_key,
632 'base64' => TRUE,
633 'hmac' => $this->_mode === 'gcm' ? FALSE : array('digest' => 'sha512', 'key' => NULL)
634 )
635 : FALSE;
636 }
637 elseif ( ! isset($params['cipher'], $params['mode'], $params['key']))
638 {
639 return FALSE;
640 }
641
642 if ($params['mode'] === 'gcm')
643 {
644 $params['hmac'] = FALSE;
645 }
646 elseif ( ! isset($params['hmac']) OR ( ! is_array($params['hmac']) && $params['hmac'] !== FALSE))
647 {
648 $params['hmac'] = array(
649 'digest' => 'sha512',
650 'key' => NULL
651 );
652 }
653 elseif (is_array($params['hmac']))
654 {
655 if (isset($params['hmac']['digest']) && ! isset($this->_digests[$params['hmac']['digest']]))
Andrey Andreev818aedb2014-02-03 11:30:25 +0200656 {
657 return FALSE;
658 }
659
Andrey Andreev29cade42014-02-04 14:05:58 +0200660 $params['hmac'] = array(
661 'digest' => isset($params['hmac']['digest']) ? $params['hmac']['digest'] : 'sha512',
662 'key' => isset($params['hmac']['key']) ? $params['hmac']['key'] : NULL
Andrey Andreev818aedb2014-02-03 11:30:25 +0200663 );
664 }
665
Andrey Andreevf4017672014-02-05 18:51:15 +0200666 if (isset($params['mode']))
667 {
668 $params['mode'] = strtolower($params['mode']);
669 if ( ! isset($this->_modes[$this->_driver][$params['mode']]))
670 {
671 return FALSE;
672 }
673 else
674 {
675 $params['mode'] = $this->_modes[$this->_driver][$params['mode']];
676 }
677 }
678
Andrey Andreev818aedb2014-02-03 11:30:25 +0200679 $params = array(
680 'handle' => NULL,
681 'cipher' => isset($params['cipher']) ? $params['cipher'] : $this->_cipher,
682 'mode' => isset($params['mode']) ? $params['mode'] : $this->_mode,
683 'key' => isset($params['key']) ? $params['key'] : $this->_key,
684 'iv' => isset($params['iv']) ? $params['iv'] : NULL,
Andrey Andreev29cade42014-02-04 14:05:58 +0200685 'base64' => isset($params['base64']) ? $params['base64'] : TRUE,
686 'hmac' => $params['hmac']
Andrey Andreev818aedb2014-02-03 11:30:25 +0200687 );
688
Andrey Andreev818aedb2014-02-03 11:30:25 +0200689 $this->_cipher_alias($params['cipher']);
Andrey Andreev29cade42014-02-04 14:05:58 +0200690 $params['handle'] = ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)
691 ? $this->{'_'.$this->_driver.'_get_handle'}($params['cipher'], $params['mode'])
692 : $this->_handle;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200693
694 return $params;
695 }
696
697 // --------------------------------------------------------------------
698
699 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200700 * Get MCrypt handle
Andrey Andreev818aedb2014-02-03 11:30:25 +0200701 *
Andrey Andreev29cade42014-02-04 14:05:58 +0200702 * @param string $cipher Cipher name
703 * @param string $mode Encryption mode
704 * @return resource
Andrey Andreev818aedb2014-02-03 11:30:25 +0200705 */
Andrey Andreev29cade42014-02-04 14:05:58 +0200706 protected function _mcrypt_get_handle($cipher, $mode)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200707 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200708 return mcrypt_module_open($cipher, '', $mode, '');
709 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200710
Andrey Andreev29cade42014-02-04 14:05:58 +0200711 // --------------------------------------------------------------------
Andrey Andreev818aedb2014-02-03 11:30:25 +0200712
Andrey Andreev29cade42014-02-04 14:05:58 +0200713 /**
714 * Get OpenSSL handle
715 *
716 * @param string $cipher Cipher name
717 * @param string $mode Encryption mode
718 * @return string
719 */
720 protected function _openssl_get_handle($cipher, $mode)
721 {
722 // OpenSSL methods aren't suffixed with '-stream' for this mode
723 return ($mode === 'stream')
724 ? $cipher
725 : $cipher.'-'.$mode;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200726 }
727
728 // --------------------------------------------------------------------
729
730 /**
731 * Cipher alias
732 *
733 * Tries to translate cipher names between MCrypt and OpenSSL's "dialects".
734 *
735 * @param string $cipher Cipher name
736 * @return void
737 */
738 protected function _cipher_alias(&$cipher)
739 {
740 static $dictionary;
741
742 if (empty($dictionary))
743 {
744 $dictionary = array(
745 'mcrypt' => array(
Andrey Andreev50ccc382014-02-04 23:30:06 +0200746 'aes-128' => 'rijndael-128',
747 'aes-192' => 'rijndael-128',
748 'aes-256' => 'rijndael-128',
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200749 'des3-ede3' => 'tripledes',
750 'bf' => 'blowfish',
Andrey Andreev818aedb2014-02-03 11:30:25 +0200751 ),
752 'openssl' => array(
Andrey Andreev50ccc382014-02-04 23:30:06 +0200753 'rijndael-128' => 'aes-128',
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200754 'tripledes' => 'des-ede3',
755 'blowfish' => 'bf'
Andrey Andreev818aedb2014-02-03 11:30:25 +0200756 )
757 );
758
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200759 // Notes:
760 //
761 // - Blowfish is said to be supporting key sizes between
762 // 4 and 56 bytes, but it appears that between MCrypt and
763 // OpenSSL, only those of 16 and more bytes are compatible.
764 //
765 // Other seemingly matching ciphers between MCrypt, OpenSSL:
Andrey Andreev818aedb2014-02-03 11:30:25 +0200766 //
767 // - DES is compatible, but doesn't need an alias
Andrey Andreev818aedb2014-02-03 11:30:25 +0200768 // - CAST-128/CAST5 is NOT compatible
769 // mcrypt: 'cast-128'
770 // openssl: 'cast5'
771 // - RC2 is NOT compatible
772 // mcrypt: 'rc2'
773 // openssl: 'rc2', 'rc2-40', 'rc2-64'
774 //
775 // To avoid any other confusion due to a popular (but incorrect)
776 // belief, it should also be noted that Rijndael-192/256 are NOT
777 // the same ciphers as AES-192/256 like Rijndael-128 and AES-256 is.
778 //
779 // All compatibility tests were done in CBC mode.
780 }
781
Andrey Andreev50ccc382014-02-04 23:30:06 +0200782 if (isset($dictionary[$this->_driver][$cipher]))
Andrey Andreev818aedb2014-02-03 11:30:25 +0200783 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200784 $cipher = $dictionary[$this->_driver][$cipher];
Andrey Andreev818aedb2014-02-03 11:30:25 +0200785 }
786 }
787
788 // --------------------------------------------------------------------
789
790 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200791 * HKDF
792 *
793 * @link https://tools.ietf.org/rfc/rfc5869.txt
794 * @param $key Input key
795 * @param $digest A SHA-2 hashing algorithm
796 * @param $salt Optional salt
797 * @param $info Optional context/application-specific info
798 * @param $length Output length (defaults to the selected digest size)
799 * @return string A pseudo-random key
800 */
801 public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')
802 {
803 if ( ! isset($this->_digests[$digest]))
804 {
805 return FALSE;
806 }
807
808 if (empty($length) OR ! is_int($length))
809 {
810 $length = $this->_digests[$digest];
811 }
812 elseif ($length > (255 * $this->_digests[$digest]))
813 {
814 return FALSE;
815 }
816
817 isset($salt) OR $salt = str_repeat("\0", $this->_digests[$digest]);
818
819 $prk = hash_hmac($digest, $key, $salt, TRUE);
820 $key = '';
821 for ($key_block = '', $block_index = 1; strlen($key) < $length; $block_index++)
822 {
823 $key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE);
824 $key .= $key_block;
825 }
826
827 return substr($key, 0, $length);
828 }
829
830 // --------------------------------------------------------------------
831
832 /**
Andrey Andreev818aedb2014-02-03 11:30:25 +0200833 * __get() magic
834 *
835 * @param string $key Property name
836 * @return mixed
837 */
838 public function __get($key)
839 {
Andrey Andreev912831f2014-02-04 17:21:37 +0200840 return in_array($key, array('cipher', 'mode', 'driver', 'drivers', 'digests'), TRUE)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200841 ? $this->{'_'.$key}
842 : NULL;
843 }
844
845}
846
847/* End of file Encryption.php */
848/* Location: ./system/libraries/Encryption.php */