blob: d1aed739910b78c95607963baf2c59d5b2929f8e [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 /**
85 * List of supported HMAC algorightms
86 *
87 * name => digest size pairs
88 *
89 * @var array
90 */
91 protected $_digests = array(
92 'sha224' => 28,
93 'sha256' => 32,
94 'sha384' => 48,
95 'sha512' => 64
96 );
97
Andrey Andreev818aedb2014-02-03 11:30:25 +020098 // --------------------------------------------------------------------
99
100 /**
101 * Class constructor
102 *
103 * @param array $params Configuration parameters
104 * @return void
105 */
106 public function __construct(array $params = array())
107 {
108 $this->_drivers = array(
109 'mcrypt' => extension_loaded('mcrypt'),
110 // While OpenSSL is available for PHP 5.3.0, an IV parameter
111 // for the encrypt/decrypt functions is only available since 5.3.3
112 'openssl' => (is_php('5.3.3') && extension_loaded('openssl'))
113 );
114
115 if ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl'])
116 {
117 return show_error('Encryption: Unable to find an available encryption driver.');
118 }
Andrey Andreev29cade42014-02-04 14:05:58 +0200119 // Our configuration validates against the existence of MCRYPT_MODE_* constants,
120 // but MCrypt supports CTR mode without actually having a constant for it, so ...
121 elseif ($this->_drivers['mcrypt'] && ! defined('MCRYPT_MODE_CTR'))
122 {
123 define('MCRYPT_MODE_CTR', 'ctr');
124 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200125
126 $this->initialize($params);
127
Andrey Andreev818aedb2014-02-03 11:30:25 +0200128 isset($this->_key) OR $this->_key = config_item('encryption_key');
129 if (empty($this->_key))
130 {
131 return show_error('Encryption: You are required to set an encryption key in your configuration.');
132 }
133
134 log_message('debug', 'Encryption Class Initialized');
135 }
136
137 // --------------------------------------------------------------------
138
139 /**
140 * Initialize
141 *
142 * @param array $params Configuration parameters
143 * @return CI_Encryption
144 */
145 public function initialize(array $params)
146 {
147 if ( ! empty($params['driver']))
148 {
149 if (isset($this->_drivers[$params['driver']]))
150 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200151 if ($this->_drivers[$params['driver']])
Andrey Andreev818aedb2014-02-03 11:30:25 +0200152 {
153 $this->_driver = $params['driver'];
154 }
155 else
156 {
157 log_message('error', "Encryption: Driver '".$params['driver']."' is not available.");
158 }
159 }
160 else
161 {
162 log_message('error', "Encryption: Unknown driver '".$params['driver']."' cannot be configured.");
163 }
164 }
165
Andrey Andreev912831f2014-02-04 17:21:37 +0200166 if (empty($this->_driver))
167 {
168 $this->_driver = ($this->_drivers['mcrypt'] === TRUE)
169 ? 'mcrypt'
170 : 'openssl';
171
172 log_message('debug', "Encryption: Auto-configured driver '".$this->_driver."'.");
173 }
174
Andrey Andreev818aedb2014-02-03 11:30:25 +0200175 empty($params['key']) OR $this->_key = $params['key'];
176 $this->{'_'.$this->_driver.'_initialize'}($params);
177 return $this;
178 }
179
180 // --------------------------------------------------------------------
181
182 /**
183 * Initialize MCrypt
184 *
185 * @param array $params Configuration parameters
186 * @return void
187 */
188 protected function _mcrypt_initialize($params)
189 {
190 if ( ! empty($params['cipher']))
191 {
192 $params['cipher'] = strtolower($params['cipher']);
193 $this->_cipher_alias($params['cipher']);
194
195 if ( ! in_array($params['cipher'], mcrypt_list_algorithms(), TRUE))
196 {
197 log_message('error', 'Encryption: MCrypt cipher '.strtoupper($params['cipher']).' is not available.');
198 }
199 else
200 {
201 $this->_cipher = $params['cipher'];
202 }
203 }
204
205 if ( ! empty($params['mode']))
206 {
207 if ( ! defined('MCRYPT_MODE_'.$params['mode']))
208 {
209 log_message('error', 'Encryption: MCrypt mode '.strtotupper($params['mode']).' is not available.');
210 }
211 else
212 {
213 $this->_mode = constant('MCRYPT_MODE_'.$params['mode']);
214 }
215 }
216
217 if (isset($this->_cipher, $this->_mode))
218 {
219 if (is_resource($this->_handle)
220 && (strtolower(mcrypt_enc_get_algorithms_name($this->_handle)) !== $this->_cipher
221 OR strtolower(mcrypt_enc_get_modes_name($this->_handle)) !== $this->_mode)
222 )
223 {
224 mcrypt_module_close($this->_handle);
225 }
226
227 if ($this->_handle = mcrypt_module_open($this->_cipher, '', $this->_mode, ''))
228 {
229 log_message('debug', 'Encryption: MCrypt cipher '.strtoupper($this->_cipher).' initialized in '.strtoupper($this->_mode).' mode.');
230 }
231 else
232 {
233 log_message('error', 'Encryption: Unable to initialize MCrypt with cipher '.strtoupper($this->_cipher).' in '.strtoupper($this->_mode).' mode.');
234 }
235 }
236 }
237
238 // --------------------------------------------------------------------
239
240 /**
241 * Initialize OpenSSL
242 *
243 * @param array $params Configuration parameters
244 * @return void
245 */
246 protected function _openssl_initialize($params)
247 {
248 if ( ! empty($params['cipher']))
249 {
250 $params['cipher'] = strtolower($params['cipher']);
251 $this->_cipher_alias($params['cipher']);
252 $this->_cipher = $params['cipher'];
253 }
254
255 if ( ! empty($params['mode']))
256 {
257 $this->_mode = strtolower($params['mode']);
258 }
259
260 if (isset($this->_cipher, $this->_mode))
261 {
262 // OpenSSL methods aren't suffixed with '-stream' for this mode
263 $handle = ($this->_mode === 'stream')
264 ? $this->_cipher
265 : $this->_cipher.'-'.$this->_mode;
266
Andrey Andreev50ccc382014-02-04 23:30:06 +0200267 if ( ! in_array($handle, openssl_get_cipher_methods(), TRUE))
Andrey Andreev818aedb2014-02-03 11:30:25 +0200268 {
269 $this->_handle = NULL;
270 log_message('error', 'Encryption: Unable to initialize OpenSSL with method '.strtoupper($handle).'.');
271 }
272 else
273 {
274 $this->_handle = $handle;
275 log_message('debug', 'Encryption: OpenSSL initialized with method '.strtoupper($handle).'.');
276 }
277 }
278 }
279
280 // --------------------------------------------------------------------
281
282 /**
283 * Encrypt
284 *
285 * @param string $data Input data
286 * @param array $params Input parameters
287 * @return string
288 */
289 public function encrypt($data, array $params = NULL)
290 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200291 if (($params = $this->_get_params($params)) === FALSE)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200292 {
293 return FALSE;
294 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200295
296 if (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE)
297 {
298 return FALSE;
299 }
300
Andrey Andreev29cade42014-02-04 14:05:58 +0200301 if ($params['base64'])
302 {
303 $data = base64_encode($data);
304 }
305
306 if ($params['hmac'] !== FALSE)
307 {
308 if ( ! isset($params['hmac']['key']))
309 {
310 $params['hmac']['key'] = $this->hkdf(
311 $params['key'],
312 $params['hmac']['digest'],
313 NULL,
314 NULL,
315 'authentication'
316 );
317 }
318
Andrey Andreev177144f2014-02-04 18:07:34 +0200319 return hash_hmac($params['hmac']['digest'], $data, $params['hmac']['key'], ! $params['base64']).$data;
Andrey Andreev29cade42014-02-04 14:05:58 +0200320 }
321
322 return $data;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200323 }
324
325 // --------------------------------------------------------------------
326
327 /**
328 * Encrypt via MCrypt
329 *
330 * @param string $data Input data
331 * @param array $params Input parameters
332 * @return string
333 */
334 protected function _mcrypt_encrypt($data, $params)
335 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200336 if ( ! is_resource($params['handle']))
337 {
338 return FALSE;
339 }
Andrey Andreeve7516b02014-02-05 13:45:31 +0200340 elseif ( ! isset($params['iv']))
341 {
342 $params['iv'] = ($iv_size = mcrypt_enc_get_iv_size($params['handle']))
343 ? $this->_mcrypt_get_iv($iv_size)
344 : NULL;
345 }
346
347
348 if (mcrypt_generic_init($params['handle'], $params['key'], $params['iv']) < 0)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200349 {
350 if ($params['handle'] !== $this->_handle)
351 {
352 mcrypt_module_close($params['handle']);
353 }
354
355 return FALSE;
356 }
357
358 // Use PKCS#7 padding in order to ensure compatibility with OpenSSL
359 // and other implementations outside of PHP
360 $block_size = mcrypt_enc_get_block_size($params['handle']);
361 $pad = $block_size - (strlen($data) % $block_size);
362 $data .= str_repeat(chr($pad), $pad);
363
Andrey Andreeve7516b02014-02-05 13:45:31 +0200364 // Work-around for yet another strange behavior in MCrypt.
365 //
366 // When encrypting in ECB mode, the IV is ignored. Yet
367 // mcrypt_enc_get_iv_size() returns a value larger than 0
368 // even if ECB is used AND mcrypt_generic_init() complains
369 // if you don't pass an IV with length equal to the said
370 // return value.
371 //
372 // This probably would've been fine (even though still wasteful),
373 // but OpenSSL isn't that dumb and we need to make the process
374 // portable, so ...
375 $data = (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
376 ? $params['iv'].mcrypt_generic($params['handle'], $data)
377 : mcrypt_generic($params['handle'], $data);
Andrey Andreev818aedb2014-02-03 11:30:25 +0200378
379 mcrypt_generic_deinit($params['handle']);
380 if ($params['handle'] !== $this->_handle)
381 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200382 mcrypt_module_close($params['handle']);
Andrey Andreev818aedb2014-02-03 11:30:25 +0200383 }
384
385 return $data;
386 }
387
388 // --------------------------------------------------------------------
389
390 /**
391 * Encrypt via OpenSSL
392 *
393 * @param string $data Input data
394 * @param array $params Input parameters
395 * @return string
396 */
397 protected function _openssl_encrypt($data, $params)
398 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200399 if (empty($params['handle']))
400 {
401 return FALSE;
402 }
Andrey Andreeve7516b02014-02-05 13:45:31 +0200403 elseif ( ! isset($params['iv']))
404 {
405 $params['iv'] = ($iv_size = openssl_cipher_iv_length($params['handle']))
406 ? $this->_openssl_get_iv($iv_size)
407 : NULL;
408 }
Andrey Andreev29cade42014-02-04 14:05:58 +0200409
Andrey Andreev818aedb2014-02-03 11:30:25 +0200410 $data = openssl_encrypt(
411 $data,
412 $params['handle'],
413 $params['key'],
414 1, // DO NOT TOUCH!
415 $params['iv']
416 );
417
418 if ($data === FALSE)
419 {
420 return FALSE;
421 }
422
423 return $params['iv'].$data;
424 }
425
426 // --------------------------------------------------------------------
427
428 /**
429 * Decrypt
430 *
431 * @param string $data Encrypted data
432 * @param array $params Input parameters
433 * @return string
434 */
435 public function decrypt($data, array $params = NULL)
436 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200437 if (($params = $this->_get_params($params)) === FALSE)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200438 {
439 return FALSE;
440 }
Andrey Andreev29cade42014-02-04 14:05:58 +0200441
442 if ($params['hmac'] !== FALSE)
443 {
444 if ( ! isset($params['hmac']['key']))
445 {
446 $params['hmac']['key'] = $this->hkdf(
447 $params['key'],
448 $params['hmac']['digest'],
449 NULL,
450 NULL,
451 'authentication'
452 );
453 }
454
455 // This might look illogical, but it is done during encryption as well ...
Andrey Andreev177144f2014-02-04 18:07:34 +0200456 // The 'base64' value is effectively an inverted "raw data" parameter
Andrey Andreev29cade42014-02-04 14:05:58 +0200457 $digest_size = ($params['base64'])
458 ? $this->_digests[$params['hmac']['digest']] * 2
459 : $this->_digests[$params['hmac']['digest']];
460 $hmac = substr($data, 0, $digest_size);
461 $data = substr($data, $digest_size);
462
Andrey Andreev177144f2014-02-04 18:07:34 +0200463 if ($hmac !== hash_hmac($params['hmac']['digest'], $data, $params['hmac']['key'], ! $params['base64']))
Andrey Andreev29cade42014-02-04 14:05:58 +0200464 {
465 return FALSE;
466 }
467 }
468
469 if ($params['base64'])
Andrey Andreev818aedb2014-02-03 11:30:25 +0200470 {
471 $data = base64_decode($data);
472 }
473
Andrey Andreeve7516b02014-02-05 13:45:31 +0200474 if (isset($params['iv']) && strncmp($params['iv'], $data, $iv_size = strlen($params['iv'])) === 0)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200475 {
476 $data = substr($data, $iv_size);
477 }
478
479 return $this->{'_'.$this->_driver.'_decrypt'}($data, $params);
480 }
481
482 // --------------------------------------------------------------------
483
484 /**
485 * Decrypt via MCrypt
486 *
487 * @param string $data Encrypted data
488 * @param array $params Input parameters
489 * @return string
490 */
491 protected function _mcrypt_decrypt($data, $params)
492 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200493 if ( ! is_resource($params['handle']))
494 {
495 return FALSE;
496 }
Andrey Andreeve7516b02014-02-05 13:45:31 +0200497 elseif ( ! isset($params['iv']))
498 {
499 if ($iv_size = mcrypt_enc_get_iv_size($params['handle']))
500 {
501 if (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
502 {
503 $params['iv'] = substr($data, 0, $iv_size);
504 $data = substr($data, $iv_size);
505 }
506 else
507 {
508 // MCrypt is dumb and this is ignored, only size matters
509 $params['iv'] = str_repeat("\x0", $iv_size);
510 }
511 }
512 else
513 {
514 $params['iv'] = NULL;
515 }
516 }
517
518 if (mcrypt_generic_init($params['handle'], $params['key'], $params['iv']) < 0)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200519 {
520 if ($params['handle'] !== $this->_handle)
521 {
522 mcrypt_module_close($params['handle']);
523 }
524
525 return FALSE;
526 }
527
528 $data = mdecrypt_generic($params['handle'], $data);
529
530 mcrypt_generic_deinit($params['handle']);
531 if ($params['handle'] !== $this->_handle)
532 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200533 mcrypt_module_close($params['handle']);
Andrey Andreev818aedb2014-02-03 11:30:25 +0200534 }
535
536 // Remove PKCS#7 padding
537 return substr($data, 0, -ord($data[strlen($data)-1]));
538 }
539
540 // --------------------------------------------------------------------
541
542 /**
543 * Decrypt via OpenSSL
544 *
545 * @param string $data Encrypted data
546 * @param array $params Input parameters
547 * @return string
548 */
549 protected function _openssl_decrypt($data, $params)
550 {
Andrey Andreeve7516b02014-02-05 13:45:31 +0200551 if ( ! isset($params['iv']))
552 {
553 if ($iv_size = openssl_cipher_iv_length($params['handle']))
554 {
555 $params['iv'] = substr($data, 0, $iv_size);
556 $data = substr($data, $iv_size);
557 }
558 else
559 {
560 $params['iv'] = NULL;
561 }
562 }
563
Andrey Andreev29cade42014-02-04 14:05:58 +0200564 return empty($params['handle'])
565 ? FALSE
566 : openssl_decrypt(
567 $data,
568 $params['handle'],
569 $params['key'],
570 1, // DO NOT TOUCH!
571 $params['iv']
572 );
Andrey Andreev818aedb2014-02-03 11:30:25 +0200573 }
574
575 // --------------------------------------------------------------------
576
577 /**
Andrey Andreev818aedb2014-02-03 11:30:25 +0200578 * Get IV via MCrypt
579 *
580 * @param int $size
581 * @return int
582 */
583 protected function _mcrypt_get_iv($size)
584 {
585 // If /dev/urandom is available - use it, otherwise there's
586 // also /dev/random, but it is highly unlikely that it would
587 // be available while /dev/urandom is not and it is known to be
588 // blocking anyway.
589 if (defined(MCRYPT_DEV_URANDOM))
590 {
591 $source = MCRYPT_DEV_URANDOM;
592 }
593 else
594 {
595 $source = MCRYPT_RAND;
596 is_php('5.3') OR srand(microtime(TRUE));
597 }
598
599 return mcrypt_create_iv($size, $source);
600 }
601
602 // --------------------------------------------------------------------
603
604 /**
605 * Get IV via OpenSSL
606 *
607 * @param int $size IV size
608 * @return int
609 */
610 protected function _openssl_get_iv($size)
611 {
612 return openssl_random_pseudo_bytes($size);
613 }
614
615 // --------------------------------------------------------------------
616
617 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200618 * Get params
Andrey Andreev818aedb2014-02-03 11:30:25 +0200619 *
620 * @param array $params Input parameters
621 * @return array
622 */
Andrey Andreev29cade42014-02-04 14:05:58 +0200623 protected function _get_params($params)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200624 {
625 if (empty($params))
626 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200627 return isset($this->_cipher, $this->_mode, $this->_key, $this->_handle)
Andrey Andreev29cade42014-02-04 14:05:58 +0200628 ? array(
629 'handle' => $this->_handle,
630 'cipher' => $this->_cipher,
631 'mode' => $this->_mode,
632 'key' => $this->_key,
633 'base64' => TRUE,
634 'hmac' => $this->_mode === 'gcm' ? FALSE : array('digest' => 'sha512', 'key' => NULL)
635 )
636 : FALSE;
637 }
638 elseif ( ! isset($params['cipher'], $params['mode'], $params['key']))
639 {
640 return FALSE;
641 }
642
643 if ($params['mode'] === 'gcm')
644 {
645 $params['hmac'] = FALSE;
646 }
647 elseif ( ! isset($params['hmac']) OR ( ! is_array($params['hmac']) && $params['hmac'] !== FALSE))
648 {
649 $params['hmac'] = array(
650 'digest' => 'sha512',
651 'key' => NULL
652 );
653 }
654 elseif (is_array($params['hmac']))
655 {
656 if (isset($params['hmac']['digest']) && ! isset($this->_digests[$params['hmac']['digest']]))
Andrey Andreev818aedb2014-02-03 11:30:25 +0200657 {
658 return FALSE;
659 }
660
Andrey Andreev29cade42014-02-04 14:05:58 +0200661 $params['hmac'] = array(
662 'digest' => isset($params['hmac']['digest']) ? $params['hmac']['digest'] : 'sha512',
663 'key' => isset($params['hmac']['key']) ? $params['hmac']['key'] : NULL
Andrey Andreev818aedb2014-02-03 11:30:25 +0200664 );
665 }
666
667 $params = array(
668 'handle' => NULL,
669 'cipher' => isset($params['cipher']) ? $params['cipher'] : $this->_cipher,
670 'mode' => isset($params['mode']) ? $params['mode'] : $this->_mode,
671 'key' => isset($params['key']) ? $params['key'] : $this->_key,
672 'iv' => isset($params['iv']) ? $params['iv'] : NULL,
Andrey Andreev29cade42014-02-04 14:05:58 +0200673 'base64' => isset($params['base64']) ? $params['base64'] : TRUE,
674 'hmac' => $params['hmac']
Andrey Andreev818aedb2014-02-03 11:30:25 +0200675 );
676
Andrey Andreev818aedb2014-02-03 11:30:25 +0200677 $this->_cipher_alias($params['cipher']);
Andrey Andreev29cade42014-02-04 14:05:58 +0200678 $params['handle'] = ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)
679 ? $this->{'_'.$this->_driver.'_get_handle'}($params['cipher'], $params['mode'])
680 : $this->_handle;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200681
682 return $params;
683 }
684
685 // --------------------------------------------------------------------
686
687 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200688 * Get MCrypt handle
Andrey Andreev818aedb2014-02-03 11:30:25 +0200689 *
Andrey Andreev29cade42014-02-04 14:05:58 +0200690 * @param string $cipher Cipher name
691 * @param string $mode Encryption mode
692 * @return resource
Andrey Andreev818aedb2014-02-03 11:30:25 +0200693 */
Andrey Andreev29cade42014-02-04 14:05:58 +0200694 protected function _mcrypt_get_handle($cipher, $mode)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200695 {
Andrey Andreev29cade42014-02-04 14:05:58 +0200696 return mcrypt_module_open($cipher, '', $mode, '');
697 }
Andrey Andreev818aedb2014-02-03 11:30:25 +0200698
Andrey Andreev29cade42014-02-04 14:05:58 +0200699 // --------------------------------------------------------------------
Andrey Andreev818aedb2014-02-03 11:30:25 +0200700
Andrey Andreev29cade42014-02-04 14:05:58 +0200701 /**
702 * Get OpenSSL handle
703 *
704 * @param string $cipher Cipher name
705 * @param string $mode Encryption mode
706 * @return string
707 */
708 protected function _openssl_get_handle($cipher, $mode)
709 {
710 // OpenSSL methods aren't suffixed with '-stream' for this mode
711 return ($mode === 'stream')
712 ? $cipher
713 : $cipher.'-'.$mode;
Andrey Andreev818aedb2014-02-03 11:30:25 +0200714 }
715
716 // --------------------------------------------------------------------
717
718 /**
719 * Cipher alias
720 *
721 * Tries to translate cipher names between MCrypt and OpenSSL's "dialects".
722 *
723 * @param string $cipher Cipher name
724 * @return void
725 */
726 protected function _cipher_alias(&$cipher)
727 {
728 static $dictionary;
729
730 if (empty($dictionary))
731 {
732 $dictionary = array(
733 'mcrypt' => array(
Andrey Andreev50ccc382014-02-04 23:30:06 +0200734 'aes-128' => 'rijndael-128',
735 'aes-192' => 'rijndael-128',
736 'aes-256' => 'rijndael-128',
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200737 'des3-ede3' => 'tripledes',
738 'bf' => 'blowfish',
Andrey Andreev818aedb2014-02-03 11:30:25 +0200739 ),
740 'openssl' => array(
Andrey Andreev50ccc382014-02-04 23:30:06 +0200741 'rijndael-128' => 'aes-128',
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200742 'tripledes' => 'des-ede3',
743 'blowfish' => 'bf'
Andrey Andreev818aedb2014-02-03 11:30:25 +0200744 )
745 );
746
Andrey Andreevd9a48da2014-02-05 14:10:28 +0200747 // Notes:
748 //
749 // - Blowfish is said to be supporting key sizes between
750 // 4 and 56 bytes, but it appears that between MCrypt and
751 // OpenSSL, only those of 16 and more bytes are compatible.
752 //
753 // Other seemingly matching ciphers between MCrypt, OpenSSL:
Andrey Andreev818aedb2014-02-03 11:30:25 +0200754 //
755 // - DES is compatible, but doesn't need an alias
Andrey Andreev818aedb2014-02-03 11:30:25 +0200756 // - CAST-128/CAST5 is NOT compatible
757 // mcrypt: 'cast-128'
758 // openssl: 'cast5'
759 // - RC2 is NOT compatible
760 // mcrypt: 'rc2'
761 // openssl: 'rc2', 'rc2-40', 'rc2-64'
762 //
763 // To avoid any other confusion due to a popular (but incorrect)
764 // belief, it should also be noted that Rijndael-192/256 are NOT
765 // the same ciphers as AES-192/256 like Rijndael-128 and AES-256 is.
766 //
767 // All compatibility tests were done in CBC mode.
768 }
769
Andrey Andreev50ccc382014-02-04 23:30:06 +0200770 if (isset($dictionary[$this->_driver][$cipher]))
Andrey Andreev818aedb2014-02-03 11:30:25 +0200771 {
Andrey Andreev50ccc382014-02-04 23:30:06 +0200772 $cipher = $dictionary[$this->_driver][$cipher];
Andrey Andreev818aedb2014-02-03 11:30:25 +0200773 }
774 }
775
776 // --------------------------------------------------------------------
777
778 /**
Andrey Andreev29cade42014-02-04 14:05:58 +0200779 * HKDF
780 *
781 * @link https://tools.ietf.org/rfc/rfc5869.txt
782 * @param $key Input key
783 * @param $digest A SHA-2 hashing algorithm
784 * @param $salt Optional salt
785 * @param $info Optional context/application-specific info
786 * @param $length Output length (defaults to the selected digest size)
787 * @return string A pseudo-random key
788 */
789 public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')
790 {
791 if ( ! isset($this->_digests[$digest]))
792 {
793 return FALSE;
794 }
795
796 if (empty($length) OR ! is_int($length))
797 {
798 $length = $this->_digests[$digest];
799 }
800 elseif ($length > (255 * $this->_digests[$digest]))
801 {
802 return FALSE;
803 }
804
805 isset($salt) OR $salt = str_repeat("\0", $this->_digests[$digest]);
806
807 $prk = hash_hmac($digest, $key, $salt, TRUE);
808 $key = '';
809 for ($key_block = '', $block_index = 1; strlen($key) < $length; $block_index++)
810 {
811 $key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE);
812 $key .= $key_block;
813 }
814
815 return substr($key, 0, $length);
816 }
817
818 // --------------------------------------------------------------------
819
820 /**
Andrey Andreev818aedb2014-02-03 11:30:25 +0200821 * __get() magic
822 *
823 * @param string $key Property name
824 * @return mixed
825 */
826 public function __get($key)
827 {
Andrey Andreev912831f2014-02-04 17:21:37 +0200828 return in_array($key, array('cipher', 'mode', 'driver', 'drivers', 'digests'), TRUE)
Andrey Andreev818aedb2014-02-03 11:30:25 +0200829 ? $this->{'_'.$key}
830 : NULL;
831 }
832
833}
834
835/* End of file Encryption.php */
836/* Location: ./system/libraries/Encryption.php */