blob: 36975e58526d0fb72edffddc04c09aba38b9b877 [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 Andreev8d33a9a2014-02-05 23:11:23 +0200327 elseif ( ! isset($params['key']))
328 {
329 if ( ! isset($this->_key))
330 {
331 return show_error('Encryption: You are required to set an encryption key in your configuration.');
332 }
333
334 $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, strlen($this->_key), 'encryption');
335 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200336
337 if (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE)
338 {
339 return FALSE;
340 }
341
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200342 $params['base64'] && $data = base64_encode($data);
Andrey Andreev29cade42014-02-04 14:05:58 +0200343
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200344 if (isset($params['hmac_digest']))
Andrey Andreev29cade42014-02-04 14:05:58 +0200345 {
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200346 isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
347 return hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']).$data;
Andrey Andreev29cade42014-02-04 14:05:58 +0200348 }
349
350 return $data;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200351 }
352
353 // --------------------------------------------------------------------
354
355 /**
356 * Encrypt via MCrypt
357 *
358 * @param string $data Input data
359 * @param array $params Input parameters
360 * @return string
361 */
362 protected function _mcrypt_encrypt($data, $params)
363 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200364 if ( ! is_resource($params['handle']))
365 {
366 return FALSE;
367 }
Andrey Andreeve7516b02014-02-05 13:45:31 +0200368 elseif ( ! isset($params['iv']))
369 {
370 $params['iv'] = ($iv_size = mcrypt_enc_get_iv_size($params['handle']))
Andrey Andreev8e202162014-02-05 18:59:55 +0200371 ? mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM)
Andrey Andreeve7516b02014-02-05 13:45:31 +0200372 : NULL;
373 }
374
375
376 if (mcrypt_generic_init($params['handle'], $params['key'], $params['iv']) < 0)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200377 {
378 if ($params['handle'] !== $this->_handle)
379 {
380 mcrypt_module_close($params['handle']);
381 }
382
383 return FALSE;
384 }
385
386 // Use PKCS#7 padding in order to ensure compatibility with OpenSSL
Andrey Andreevf4017672014-02-05 18:51:15 +0200387 // and other implementations outside of PHP.
388 if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
389 {
390 $block_size = mcrypt_enc_get_block_size($params['handle']);
391 $pad = $block_size - (strlen($data) % $block_size);
392 $data .= str_repeat(chr($pad), $pad);
393 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200394
Andrey Andreeve7516b02014-02-05 13:45:31 +0200395 // Work-around for yet another strange behavior in MCrypt.
396 //
397 // When encrypting in ECB mode, the IV is ignored. Yet
398 // mcrypt_enc_get_iv_size() returns a value larger than 0
399 // even if ECB is used AND mcrypt_generic_init() complains
400 // if you don't pass an IV with length equal to the said
401 // return value.
402 //
403 // This probably would've been fine (even though still wasteful),
404 // but OpenSSL isn't that dumb and we need to make the process
405 // portable, so ...
406 $data = (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
407 ? $params['iv'].mcrypt_generic($params['handle'], $data)
408 : mcrypt_generic($params['handle'], $data);
Andrey Andreev818aedb2014-02-03 11:30:25 +0200409
410 mcrypt_generic_deinit($params['handle']);
411 if ($params['handle'] !== $this->_handle)
412 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200413 mcrypt_module_close($params['handle']);
Andrey Andreev818aedb2014-02-03 11:30:25 +0200414 }
415
416 return $data;
417 }
418
419 // --------------------------------------------------------------------
420
421 /**
422 * Encrypt via OpenSSL
423 *
424 * @param string $data Input data
425 * @param array $params Input parameters
426 * @return string
427 */
428 protected function _openssl_encrypt($data, $params)
429 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200430 if (empty($params['handle']))
431 {
432 return FALSE;
433 }
Andrey Andreeve7516b02014-02-05 13:45:31 +0200434 elseif ( ! isset($params['iv']))
435 {
436 $params['iv'] = ($iv_size = openssl_cipher_iv_length($params['handle']))
Andrey Andreev8e202162014-02-05 18:59:55 +0200437 ? openssl_random_pseudo_bytes($iv_size)
Andrey Andreeve7516b02014-02-05 13:45:31 +0200438 : NULL;
439 }
Andrey Andreev29cade42014-02-04 14:05:58 +0200440
Andrey Andreev818aedb2014-02-03 11:30:25 +0200441 $data = openssl_encrypt(
442 $data,
443 $params['handle'],
444 $params['key'],
445 1, // DO NOT TOUCH!
446 $params['iv']
447 );
448
449 if ($data === FALSE)
450 {
451 return FALSE;
452 }
453
454 return $params['iv'].$data;
455 }
456
457 // --------------------------------------------------------------------
458
459 /**
460 * Decrypt
461 *
462 * @param string $data Encrypted data
463 * @param array $params Input parameters
464 * @return string
465 */
466 public function decrypt($data, array $params = NULL)
467 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200468 if (($params = $this->_get_params($params)) === FALSE)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200469 {
470 return FALSE;
471 }
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200472 elseif ( ! isset($params['key']))
Andrey Andreev29cade42014-02-04 14:05:58 +0200473 {
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200474 if ( ! isset($this->_key))
Andrey Andreev29cade42014-02-04 14:05:58 +0200475 {
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200476 return show_error('Encryption: You are required to set an encryption key in your configuration.');
Andrey Andreev29cade42014-02-04 14:05:58 +0200477 }
478
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200479 $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, strlen($this->_key), 'encryption');
480 }
481
482 if (isset($params['hmac_digest']))
483 {
484 isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
485
Andrey Andreev29cade42014-02-04 14:05:58 +0200486 // This might look illogical, but it is done during encryption as well ...
Andrey Andreev177144f2014-02-04 18:07:34 +0200487 // The 'base64' value is effectively an inverted "raw data" parameter
Andrey Andreev29cade42014-02-04 14:05:58 +0200488 $digest_size = ($params['base64'])
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200489 ? $this->_digests[$params['hmac_digest']] * 2
490 : $this->_digests[$params['hmac_digest']];
Andrey Andreev29cade42014-02-04 14:05:58 +0200491 $hmac = substr($data, 0, $digest_size);
492 $data = substr($data, $digest_size);
493
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200494 if ($hmac !== hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']))
Andrey Andreev29cade42014-02-04 14:05:58 +0200495 {
496 return FALSE;
497 }
498 }
499
500 if ($params['base64'])
Andrey Andreev818aedb2014-02-03 11:30:25 +0200501 {
502 $data = base64_decode($data);
503 }
504
Andrey Andreeve7516b02014-02-05 13:45:31 +0200505 if (isset($params['iv']) && strncmp($params['iv'], $data, $iv_size = strlen($params['iv'])) === 0)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200506 {
507 $data = substr($data, $iv_size);
508 }
509
510 return $this->{'_'.$this->_driver.'_decrypt'}($data, $params);
511 }
512
513 // --------------------------------------------------------------------
514
515 /**
516 * Decrypt via MCrypt
517 *
518 * @param string $data Encrypted data
519 * @param array $params Input parameters
520 * @return string
521 */
522 protected function _mcrypt_decrypt($data, $params)
523 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200524 if ( ! is_resource($params['handle']))
525 {
526 return FALSE;
527 }
Andrey Andreeve7516b02014-02-05 13:45:31 +0200528 elseif ( ! isset($params['iv']))
529 {
530 if ($iv_size = mcrypt_enc_get_iv_size($params['handle']))
531 {
532 if (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
533 {
534 $params['iv'] = substr($data, 0, $iv_size);
535 $data = substr($data, $iv_size);
536 }
537 else
538 {
539 // MCrypt is dumb and this is ignored, only size matters
540 $params['iv'] = str_repeat("\x0", $iv_size);
541 }
542 }
543 else
544 {
545 $params['iv'] = NULL;
546 }
547 }
548
549 if (mcrypt_generic_init($params['handle'], $params['key'], $params['iv']) < 0)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200550 {
551 if ($params['handle'] !== $this->_handle)
552 {
553 mcrypt_module_close($params['handle']);
554 }
555
556 return FALSE;
557 }
558
559 $data = mdecrypt_generic($params['handle'], $data);
Andrey Andreevf4017672014-02-05 18:51:15 +0200560 // Remove PKCS#7 padding, if necessary
561 if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
562 {
563 $data = substr($data, 0, -ord($data[strlen($data)-1]));
564 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200565
566 mcrypt_generic_deinit($params['handle']);
567 if ($params['handle'] !== $this->_handle)
568 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200569 mcrypt_module_close($params['handle']);
Andrey Andreev818aedb2014-02-03 11:30:25 +0200570 }
571
Andrey Andreevf4017672014-02-05 18:51:15 +0200572 return $data;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200573 }
574
575 // --------------------------------------------------------------------
576
577 /**
578 * Decrypt via OpenSSL
579 *
580 * @param string $data Encrypted data
581 * @param array $params Input parameters
582 * @return string
583 */
584 protected function _openssl_decrypt($data, $params)
585 {
Andrey Andreeve7516b02014-02-05 13:45:31 +0200586 if ( ! isset($params['iv']))
587 {
588 if ($iv_size = openssl_cipher_iv_length($params['handle']))
589 {
590 $params['iv'] = substr($data, 0, $iv_size);
591 $data = substr($data, $iv_size);
592 }
593 else
594 {
595 $params['iv'] = NULL;
596 }
597 }
598
Andrey Andreev29cade42014-02-04 14:05:58 +0200599 return empty($params['handle'])
600 ? FALSE
601 : openssl_decrypt(
602 $data,
603 $params['handle'],
604 $params['key'],
605 1, // DO NOT TOUCH!
606 $params['iv']
607 );
Andrey Andreev818aedb2014-02-03 11:30:25 +0200608 }
609
610 // --------------------------------------------------------------------
611
612 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200613 * Get params
Andrey Andreev818aedb2014-02-03 11:30:25 +0200614 *
615 * @param array $params Input parameters
616 * @return array
617 */
Andrey Andreev29cade42014-02-04 14:05:58 +0200618 protected function _get_params($params)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200619 {
620 if (empty($params))
621 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200622 return isset($this->_cipher, $this->_mode, $this->_key, $this->_handle)
Andrey Andreev29cade42014-02-04 14:05:58 +0200623 ? array(
624 'handle' => $this->_handle,
625 'cipher' => $this->_cipher,
626 'mode' => $this->_mode,
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200627 'key' => NULL,
Andrey Andreev29cade42014-02-04 14:05:58 +0200628 'base64' => TRUE,
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200629 'hmac_digest' => ($this->_mode !== 'gcm' ? 'sha512' : NULL),
630 'hmac_key' => NULL
Andrey Andreev29cade42014-02-04 14:05:58 +0200631 )
632 : FALSE;
633 }
634 elseif ( ! isset($params['cipher'], $params['mode'], $params['key']))
635 {
636 return FALSE;
637 }
638
Andrey Andreevf4017672014-02-05 18:51:15 +0200639 if (isset($params['mode']))
640 {
641 $params['mode'] = strtolower($params['mode']);
642 if ( ! isset($this->_modes[$this->_driver][$params['mode']]))
643 {
644 return FALSE;
645 }
646 else
647 {
648 $params['mode'] = $this->_modes[$this->_driver][$params['mode']];
649 }
650 }
651
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200652 if ($params['mode'] === 'gcm' OR isset($params['hmac']) && $params['hmac'] === FALSE)
653 {
654 $params['hmac_digest'] = $params['hmac_key'] = NULL;
655 }
656 else
657 {
658 if ( ! isset($params['hmac_key']))
659 {
660 return FALSE;
661 }
662 elseif (isset($params['hmac_digest']))
663 {
664 $params['hmac_digest'] = strtolower($params['hmac_digest']);
665 if ( ! isset($this->_digests[$params['hmac_digest']]))
666 {
667 return FALSE;
668 }
669 }
670 else
671 {
672 $params['hmac_digest'] = 'sha512';
673 }
674 }
675
Andrey Andreev818aedb2014-02-03 11:30:25 +0200676 $params = array(
677 'handle' => NULL,
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200678 'cipher' => $params['cipher'],
679 'mode' => $params['mode'],
680 'key' => $params['key'],
Andrey Andreev818aedb2014-02-03 11:30:25 +0200681 'iv' => isset($params['iv']) ? $params['iv'] : NULL,
Andrey Andreev29cade42014-02-04 14:05:58 +0200682 'base64' => isset($params['base64']) ? $params['base64'] : TRUE,
Andrey Andreev8d33a9a2014-02-05 23:11:23 +0200683 'hmac_digest' => $params['hmac_digest'],
684 'hmac_key' => $params['hmac_key']
Andrey Andreev818aedb2014-02-03 11:30:25 +0200685 );
686
Andrey Andreev818aedb2014-02-03 11:30:25 +0200687 $this->_cipher_alias($params['cipher']);
Andrey Andreev29cade42014-02-04 14:05:58 +0200688 $params['handle'] = ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)
689 ? $this->{'_'.$this->_driver.'_get_handle'}($params['cipher'], $params['mode'])
690 : $this->_handle;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200691
692 return $params;
693 }
694
695 // --------------------------------------------------------------------
696
697 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200698 * Get MCrypt handle
Andrey Andreev818aedb2014-02-03 11:30:25 +0200699 *
Andrey Andreev29cade42014-02-04 14:05:58 +0200700 * @param string $cipher Cipher name
701 * @param string $mode Encryption mode
702 * @return resource
Andrey Andreev818aedb2014-02-03 11:30:25 +0200703 */
Andrey Andreev29cade42014-02-04 14:05:58 +0200704 protected function _mcrypt_get_handle($cipher, $mode)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200705 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200706 return mcrypt_module_open($cipher, '', $mode, '');
707 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200708
Andrey Andreev29cade42014-02-04 14:05:58 +0200709 // --------------------------------------------------------------------
Andrey Andreev818aedb2014-02-03 11:30:25 +0200710
Andrey Andreev29cade42014-02-04 14:05:58 +0200711 /**
712 * Get OpenSSL handle
713 *
714 * @param string $cipher Cipher name
715 * @param string $mode Encryption mode
716 * @return string
717 */
718 protected function _openssl_get_handle($cipher, $mode)
719 {
720 // OpenSSL methods aren't suffixed with '-stream' for this mode
721 return ($mode === 'stream')
722 ? $cipher
723 : $cipher.'-'.$mode;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200724 }
725
726 // --------------------------------------------------------------------
727
728 /**
729 * Cipher alias
730 *
731 * Tries to translate cipher names between MCrypt and OpenSSL's "dialects".
732 *
733 * @param string $cipher Cipher name
734 * @return void
735 */
736 protected function _cipher_alias(&$cipher)
737 {
738 static $dictionary;
739
740 if (empty($dictionary))
741 {
742 $dictionary = array(
743 'mcrypt' => array(
Andrey Andreev50ccc382014-02-04 23:30:06 +0200744 'aes-128' => 'rijndael-128',
745 'aes-192' => 'rijndael-128',
746 'aes-256' => 'rijndael-128',
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200747 'des3-ede3' => 'tripledes',
748 'bf' => 'blowfish',
Andrey Andreev818aedb2014-02-03 11:30:25 +0200749 ),
750 'openssl' => array(
Andrey Andreev50ccc382014-02-04 23:30:06 +0200751 'rijndael-128' => 'aes-128',
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200752 'tripledes' => 'des-ede3',
753 'blowfish' => 'bf'
Andrey Andreev818aedb2014-02-03 11:30:25 +0200754 )
755 );
756
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200757 // Notes:
758 //
759 // - Blowfish is said to be supporting key sizes between
760 // 4 and 56 bytes, but it appears that between MCrypt and
761 // OpenSSL, only those of 16 and more bytes are compatible.
762 //
763 // Other seemingly matching ciphers between MCrypt, OpenSSL:
Andrey Andreev818aedb2014-02-03 11:30:25 +0200764 //
765 // - DES is compatible, but doesn't need an alias
Andrey Andreev818aedb2014-02-03 11:30:25 +0200766 // - CAST-128/CAST5 is NOT compatible
767 // mcrypt: 'cast-128'
768 // openssl: 'cast5'
769 // - RC2 is NOT compatible
770 // mcrypt: 'rc2'
771 // openssl: 'rc2', 'rc2-40', 'rc2-64'
772 //
773 // To avoid any other confusion due to a popular (but incorrect)
774 // belief, it should also be noted that Rijndael-192/256 are NOT
775 // the same ciphers as AES-192/256 like Rijndael-128 and AES-256 is.
776 //
777 // All compatibility tests were done in CBC mode.
778 }
779
Andrey Andreev50ccc382014-02-04 23:30:06 +0200780 if (isset($dictionary[$this->_driver][$cipher]))
Andrey Andreev818aedb2014-02-03 11:30:25 +0200781 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200782 $cipher = $dictionary[$this->_driver][$cipher];
Andrey Andreev818aedb2014-02-03 11:30:25 +0200783 }
784 }
785
786 // --------------------------------------------------------------------
787
788 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200789 * HKDF
790 *
791 * @link https://tools.ietf.org/rfc/rfc5869.txt
792 * @param $key Input key
793 * @param $digest A SHA-2 hashing algorithm
794 * @param $salt Optional salt
795 * @param $info Optional context/application-specific info
796 * @param $length Output length (defaults to the selected digest size)
797 * @return string A pseudo-random key
798 */
799 public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')
800 {
801 if ( ! isset($this->_digests[$digest]))
802 {
803 return FALSE;
804 }
805
806 if (empty($length) OR ! is_int($length))
807 {
808 $length = $this->_digests[$digest];
809 }
810 elseif ($length > (255 * $this->_digests[$digest]))
811 {
812 return FALSE;
813 }
814
815 isset($salt) OR $salt = str_repeat("\0", $this->_digests[$digest]);
816
817 $prk = hash_hmac($digest, $key, $salt, TRUE);
818 $key = '';
819 for ($key_block = '', $block_index = 1; strlen($key) < $length; $block_index++)
820 {
821 $key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE);
822 $key .= $key_block;
823 }
824
825 return substr($key, 0, $length);
826 }
827
828 // --------------------------------------------------------------------
829
830 /**
Andrey Andreev818aedb2014-02-03 11:30:25 +0200831 * __get() magic
832 *
833 * @param string $key Property name
834 * @return mixed
835 */
836 public function __get($key)
837 {
Andrey Andreev912831f2014-02-04 17:21:37 +0200838 return in_array($key, array('cipher', 'mode', 'driver', 'drivers', 'digests'), TRUE)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200839 ? $this->{'_'.$key}
840 : NULL;
841 }
842
843}
844
845/* End of file Encryption.php */
846/* Location: ./system/libraries/Encryption.php */