Subversion-Projekte lars-tiefland.ci

Revision

Revision 1257 | Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2016, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 3.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
/**
41
 * CodeIgniter Encryption Class
42
 *
43
 * Provides two-way keyed encryption via PHP's MCrypt and/or OpenSSL extensions.
44
 *
45
 * @package		CodeIgniter
46
 * @subpackage	Libraries
47
 * @category	Libraries
48
 * @author		Andrey Andreev
49
 * @link		https://codeigniter.com/user_guide/libraries/encryption.html
50
 */
51
class CI_Encryption {
52
 
53
	/**
54
	 * Encryption cipher
55
	 *
56
	 * @var	string
57
	 */
58
	protected $_cipher = 'aes-128';
59
 
60
	/**
61
	 * Cipher mode
62
	 *
63
	 * @var	string
64
	 */
65
	protected $_mode = 'cbc';
66
 
67
	/**
68
	 * Cipher handle
69
	 *
70
	 * @var	mixed
71
	 */
72
	protected $_handle;
73
 
74
	/**
75
	 * Encryption key
76
	 *
77
	 * @var	string
78
	 */
79
	protected $_key;
80
 
81
	/**
82
	 * PHP extension to be used
83
	 *
84
	 * @var	string
85
	 */
86
	protected $_driver;
87
 
88
	/**
89
	 * List of usable drivers (PHP extensions)
90
	 *
91
	 * @var	array
92
	 */
93
	protected $_drivers = array();
94
 
95
	/**
96
	 * List of available modes
97
	 *
98
	 * @var	array
99
	 */
100
	protected $_modes = array(
101
		'mcrypt' => array(
102
			'cbc' => 'cbc',
103
			'ecb' => 'ecb',
104
			'ofb' => 'nofb',
105
			'ofb8' => 'ofb',
106
			'cfb' => 'ncfb',
107
			'cfb8' => 'cfb',
108
			'ctr' => 'ctr',
109
			'stream' => 'stream'
110
		),
111
		'openssl' => array(
112
			'cbc' => 'cbc',
113
			'ecb' => 'ecb',
114
			'ofb' => 'ofb',
115
			'cfb' => 'cfb',
116
			'cfb8' => 'cfb8',
117
			'ctr' => 'ctr',
118
			'stream' => '',
119
			'xts' => 'xts'
120
		)
121
	);
122
 
123
	/**
124
	 * List of supported HMAC algorithms
125
	 *
126
	 * name => digest size pairs
127
	 *
128
	 * @var	array
129
	 */
130
	protected $_digests = array(
131
		'sha224' => 28,
132
		'sha256' => 32,
133
		'sha384' => 48,
134
		'sha512' => 64
135
	);
136
 
137
	/**
138
	 * mbstring.func_override flag
139
	 *
140
	 * @var	bool
141
	 */
142
	protected static $func_override;
143
 
144
	// --------------------------------------------------------------------
145
 
146
	/**
147
	 * Class constructor
148
	 *
149
	 * @param	array	$params	Configuration parameters
150
	 * @return	void
151
	 */
152
	public function __construct(array $params = array())
153
	{
154
		$this->_drivers = array(
155
			'mcrypt' => defined('MCRYPT_DEV_URANDOM'),
156
			// While OpenSSL is available for PHP 5.3.0, an IV parameter
157
			// for the encrypt/decrypt functions is only available since 5.3.3
158
			'openssl' => (is_php('5.3.3') && extension_loaded('openssl'))
159
		);
160
 
161
		if ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl'])
162
		{
163
			show_error('Encryption: Unable to find an available encryption driver.');
164
		}
165
 
166
		isset(self::$func_override) OR self::$func_override = (extension_loaded('mbstring') && ini_get('mbstring.func_override'));
167
		$this->initialize($params);
168
 
169
		if ( ! isset($this->_key) && self::strlen($key = config_item('encryption_key')) > 0)
170
		{
171
			$this->_key = $key;
172
		}
173
 
174
		log_message('info', 'Encryption Class Initialized');
175
	}
176
 
177
	// --------------------------------------------------------------------
178
 
179
	/**
180
	 * Initialize
181
	 *
182
	 * @param	array	$params	Configuration parameters
183
	 * @return	CI_Encryption
184
	 */
185
	public function initialize(array $params)
186
	{
187
		if ( ! empty($params['driver']))
188
		{
189
			if (isset($this->_drivers[$params['driver']]))
190
			{
191
				if ($this->_drivers[$params['driver']])
192
				{
193
					$this->_driver = $params['driver'];
194
				}
195
				else
196
				{
197
					log_message('error', "Encryption: Driver '".$params['driver']."' is not available.");
198
				}
199
			}
200
			else
201
			{
202
				log_message('error', "Encryption: Unknown driver '".$params['driver']."' cannot be configured.");
203
			}
204
		}
205
 
206
		if (empty($this->_driver))
207
		{
208
			$this->_driver = ($this->_drivers['openssl'] === TRUE)
209
				? 'openssl'
210
				: 'mcrypt';
211
 
212
			log_message('debug', "Encryption: Auto-configured driver '".$this->_driver."'.");
213
		}
214
 
215
		empty($params['cipher']) && $params['cipher'] = $this->_cipher;
216
		empty($params['key']) OR $this->_key = $params['key'];
217
		$this->{'_'.$this->_driver.'_initialize'}($params);
218
		return $this;
219
	}
220
 
221
	// --------------------------------------------------------------------
222
 
223
	/**
224
	 * Initialize MCrypt
225
	 *
226
	 * @param	array	$params	Configuration parameters
227
	 * @return	void
228
	 */
229
	protected function _mcrypt_initialize($params)
230
	{
231
		if ( ! empty($params['cipher']))
232
		{
233
			$params['cipher'] = strtolower($params['cipher']);
234
			$this->_cipher_alias($params['cipher']);
235
 
236
			if ( ! in_array($params['cipher'], mcrypt_list_algorithms(), TRUE))
237
			{
238
				log_message('error', 'Encryption: MCrypt cipher '.strtoupper($params['cipher']).' is not available.');
239
			}
240
			else
241
			{
242
				$this->_cipher = $params['cipher'];
243
			}
244
		}
245
 
246
		if ( ! empty($params['mode']))
247
		{
248
			$params['mode'] = strtolower($params['mode']);
249
			if ( ! isset($this->_modes['mcrypt'][$params['mode']]))
250
			{
251
				log_message('error', 'Encryption: MCrypt mode '.strtoupper($params['mode']).' is not available.');
252
			}
253
			else
254
			{
255
				$this->_mode = $this->_modes['mcrypt'][$params['mode']];
256
			}
257
		}
258
 
259
		if (isset($this->_cipher, $this->_mode))
260
		{
261
			if (is_resource($this->_handle)
262
				&& (strtolower(mcrypt_enc_get_algorithms_name($this->_handle)) !== $this->_cipher
263
					OR strtolower(mcrypt_enc_get_modes_name($this->_handle)) !== $this->_mode)
264
			)
265
			{
266
				mcrypt_module_close($this->_handle);
267
			}
268
 
269
			if ($this->_handle = mcrypt_module_open($this->_cipher, '', $this->_mode, ''))
270
			{
271
				log_message('info', 'Encryption: MCrypt cipher '.strtoupper($this->_cipher).' initialized in '.strtoupper($this->_mode).' mode.');
272
			}
273
			else
274
			{
275
				log_message('error', 'Encryption: Unable to initialize MCrypt with cipher '.strtoupper($this->_cipher).' in '.strtoupper($this->_mode).' mode.');
276
			}
277
		}
278
	}
279
 
280
	// --------------------------------------------------------------------
281
 
282
	/**
283
	 * Initialize OpenSSL
284
	 *
285
	 * @param	array	$params	Configuration parameters
286
	 * @return	void
287
	 */
288
	protected function _openssl_initialize($params)
289
	{
290
		if ( ! empty($params['cipher']))
291
		{
292
			$params['cipher'] = strtolower($params['cipher']);
293
			$this->_cipher_alias($params['cipher']);
294
			$this->_cipher = $params['cipher'];
295
		}
296
 
297
		if ( ! empty($params['mode']))
298
		{
299
			$params['mode'] = strtolower($params['mode']);
300
			if ( ! isset($this->_modes['openssl'][$params['mode']]))
301
			{
302
				log_message('error', 'Encryption: OpenSSL mode '.strtoupper($params['mode']).' is not available.');
303
			}
304
			else
305
			{
306
				$this->_mode = $this->_modes['openssl'][$params['mode']];
307
			}
308
		}
309
 
310
		if (isset($this->_cipher, $this->_mode))
311
		{
312
			// This is mostly for the stream mode, which doesn't get suffixed in OpenSSL
313
			$handle = empty($this->_mode)
314
				? $this->_cipher
315
				: $this->_cipher.'-'.$this->_mode;
316
 
317
			if ( ! in_array($handle, openssl_get_cipher_methods(), TRUE))
318
			{
319
				$this->_handle = NULL;
320
				log_message('error', 'Encryption: Unable to initialize OpenSSL with method '.strtoupper($handle).'.');
321
			}
322
			else
323
			{
324
				$this->_handle = $handle;
325
				log_message('info', 'Encryption: OpenSSL initialized with method '.strtoupper($handle).'.');
326
			}
327
		}
328
	}
329
 
330
	// --------------------------------------------------------------------
331
 
332
	/**
333
	 * Create a random key
334
	 *
335
	 * @param	int	$length	Output length
336
	 * @return	string
337
	 */
338
	public function create_key($length)
339
	{
340
		if (function_exists('random_bytes'))
341
		{
342
			try
343
			{
344
				return random_bytes((int) $length);
345
			}
346
			catch (Exception $e)
347
			{
348
				log_message('error', $e->getMessage());
349
				return FALSE;
350
			}
351
		}
352
		elseif (defined('MCRYPT_DEV_URANDOM'))
353
		{
354
			return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
355
		}
356
 
357
		$is_secure = NULL;
358
		$key = openssl_random_pseudo_bytes($length, $is_secure);
359
		return ($is_secure === TRUE)
360
			? $key
361
			: FALSE;
362
	}
363
 
364
	// --------------------------------------------------------------------
365
 
366
	/**
367
	 * Encrypt
368
	 *
369
	 * @param	string	$data	Input data
370
	 * @param	array	$params	Input parameters
371
	 * @return	string
372
	 */
373
	public function encrypt($data, array $params = NULL)
374
	{
375
		if (($params = $this->_get_params($params)) === FALSE)
376
		{
377
			return FALSE;
378
		}
379
 
380
		isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');
381
 
382
		if (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE)
383
		{
384
			return FALSE;
385
		}
386
 
387
		$params['base64'] && $data = base64_encode($data);
388
 
389
		if (isset($params['hmac_digest']))
390
		{
391
			isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
392
			return hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']).$data;
393
		}
394
 
395
		return $data;
396
	}
397
 
398
	// --------------------------------------------------------------------
399
 
400
	/**
401
	 * Encrypt via MCrypt
402
	 *
403
	 * @param	string	$data	Input data
404
	 * @param	array	$params	Input parameters
405
	 * @return	string
406
	 */
407
	protected function _mcrypt_encrypt($data, $params)
408
	{
409
		if ( ! is_resource($params['handle']))
410
		{
411
			return FALSE;
412
		}
413
 
414
		// The greater-than-1 comparison is mostly a work-around for a bug,
415
		// where 1 is returned for ARCFour instead of 0.
416
		$iv = (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)
417
			? $this->create_key($iv_size)
418
			: NULL;
419
 
420
		if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)
421
		{
422
			if ($params['handle'] !== $this->_handle)
423
			{
424
				mcrypt_module_close($params['handle']);
425
			}
426
 
427
			return FALSE;
428
		}
429
 
430
		// Use PKCS#7 padding in order to ensure compatibility with OpenSSL
431
		// and other implementations outside of PHP.
432
		if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
433
		{
434
			$block_size = mcrypt_enc_get_block_size($params['handle']);
435
			$pad = $block_size - (self::strlen($data) % $block_size);
436
			$data .= str_repeat(chr($pad), $pad);
437
		}
438
 
439
		// Work-around for yet another strange behavior in MCrypt.
440
		//
441
		// When encrypting in ECB mode, the IV is ignored. Yet
442
		// mcrypt_enc_get_iv_size() returns a value larger than 0
443
		// even if ECB is used AND mcrypt_generic_init() complains
444
		// if you don't pass an IV with length equal to the said
445
		// return value.
446
		//
447
		// This probably would've been fine (even though still wasteful),
448
		// but OpenSSL isn't that dumb and we need to make the process
449
		// portable, so ...
450
		$data = (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
451
			? $iv.mcrypt_generic($params['handle'], $data)
452
			: mcrypt_generic($params['handle'], $data);
453
 
454
		mcrypt_generic_deinit($params['handle']);
455
		if ($params['handle'] !== $this->_handle)
456
		{
457
			mcrypt_module_close($params['handle']);
458
		}
459
 
460
		return $data;
461
	}
462
 
463
	// --------------------------------------------------------------------
464
 
465
	/**
466
	 * Encrypt via OpenSSL
467
	 *
468
	 * @param	string	$data	Input data
469
	 * @param	array	$params	Input parameters
470
	 * @return	string
471
	 */
472
	protected function _openssl_encrypt($data, $params)
473
	{
474
		if (empty($params['handle']))
475
		{
476
			return FALSE;
477
		}
478
 
479
		$iv = ($iv_size = openssl_cipher_iv_length($params['handle']))
480
			? $this->create_key($iv_size)
481
			: NULL;
482
 
483
		$data = openssl_encrypt(
484
			$data,
485
			$params['handle'],
486
			$params['key'],
487
			1, // DO NOT TOUCH!
488
			$iv
489
		);
490
 
491
		if ($data === FALSE)
492
		{
493
			return FALSE;
494
		}
495
 
496
		return $iv.$data;
497
	}
498
 
499
	// --------------------------------------------------------------------
500
 
501
	/**
502
	 * Decrypt
503
	 *
504
	 * @param	string	$data	Encrypted data
505
	 * @param	array	$params	Input parameters
506
	 * @return	string
507
	 */
508
	public function decrypt($data, array $params = NULL)
509
	{
510
		if (($params = $this->_get_params($params)) === FALSE)
511
		{
512
			return FALSE;
513
		}
514
 
515
		if (isset($params['hmac_digest']))
516
		{
517
			// This might look illogical, but it is done during encryption as well ...
518
			// The 'base64' value is effectively an inverted "raw data" parameter
519
			$digest_size = ($params['base64'])
520
				? $this->_digests[$params['hmac_digest']] * 2
521
				: $this->_digests[$params['hmac_digest']];
522
 
523
			if (self::strlen($data) <= $digest_size)
524
			{
525
				return FALSE;
526
			}
527
 
528
			$hmac_input = self::substr($data, 0, $digest_size);
529
			$data = self::substr($data, $digest_size);
530
 
531
			isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
532
			$hmac_check = hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']);
533
 
534
			// Time-attack-safe comparison
535
			$diff = 0;
536
			for ($i = 0; $i < $digest_size; $i++)
537
			{
538
				$diff |= ord($hmac_input[$i]) ^ ord($hmac_check[$i]);
539
			}
540
 
541
			if ($diff !== 0)
542
			{
543
				return FALSE;
544
			}
545
		}
546
 
547
		if ($params['base64'])
548
		{
549
			$data = base64_decode($data);
550
		}
551
 
552
		isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');
553
 
554
		return $this->{'_'.$this->_driver.'_decrypt'}($data, $params);
555
	}
556
 
557
	// --------------------------------------------------------------------
558
 
559
	/**
560
	 * Decrypt via MCrypt
561
	 *
562
	 * @param	string	$data	Encrypted data
563
	 * @param	array	$params	Input parameters
564
	 * @return	string
565
	 */
566
	protected function _mcrypt_decrypt($data, $params)
567
	{
568
		if ( ! is_resource($params['handle']))
569
		{
570
			return FALSE;
571
		}
572
 
573
		// The greater-than-1 comparison is mostly a work-around for a bug,
574
		// where 1 is returned for ARCFour instead of 0.
575
		if (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)
576
		{
577
			if (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
578
			{
579
				$iv = self::substr($data, 0, $iv_size);
580
				$data = self::substr($data, $iv_size);
581
			}
582
			else
583
			{
584
				// MCrypt is dumb and this is ignored, only size matters
585
				$iv = str_repeat("\x0", $iv_size);
586
			}
587
		}
588
		else
589
		{
590
			$iv = NULL;
591
		}
592
 
593
		if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)
594
		{
595
			if ($params['handle'] !== $this->_handle)
596
			{
597
				mcrypt_module_close($params['handle']);
598
			}
599
 
600
			return FALSE;
601
		}
602
 
603
		$data = mdecrypt_generic($params['handle'], $data);
604
		// Remove PKCS#7 padding, if necessary
605
		if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
606
		{
607
			$data = self::substr($data, 0, -ord($data[self::strlen($data)-1]));
608
		}
609
 
610
		mcrypt_generic_deinit($params['handle']);
611
		if ($params['handle'] !== $this->_handle)
612
		{
613
			mcrypt_module_close($params['handle']);
614
		}
615
 
616
		return $data;
617
	}
618
 
619
	// --------------------------------------------------------------------
620
 
621
	/**
622
	 * Decrypt via OpenSSL
623
	 *
624
	 * @param	string	$data	Encrypted data
625
	 * @param	array	$params	Input parameters
626
	 * @return	string
627
	 */
628
	protected function _openssl_decrypt($data, $params)
629
	{
630
		if ($iv_size = openssl_cipher_iv_length($params['handle']))
631
		{
632
			$iv = self::substr($data, 0, $iv_size);
633
			$data = self::substr($data, $iv_size);
634
		}
635
		else
636
		{
637
			$iv = NULL;
638
		}
639
 
640
		return empty($params['handle'])
641
			? FALSE
642
			: openssl_decrypt(
643
				$data,
644
				$params['handle'],
645
				$params['key'],
646
				1, // DO NOT TOUCH!
647
				$iv
648
			);
649
	}
650
 
651
	// --------------------------------------------------------------------
652
 
653
	/**
654
	 * Get params
655
	 *
656
	 * @param	array	$params	Input parameters
657
	 * @return	array
658
	 */
659
	protected function _get_params($params)
660
	{
661
		if (empty($params))
662
		{
663
			return isset($this->_cipher, $this->_mode, $this->_key, $this->_handle)
664
				? array(
665
					'handle' => $this->_handle,
666
					'cipher' => $this->_cipher,
667
					'mode' => $this->_mode,
668
					'key' => NULL,
669
					'base64' => TRUE,
670
					'hmac_digest' => 'sha512',
671
					'hmac_key' => NULL
672
				)
673
				: FALSE;
674
		}
675
		elseif ( ! isset($params['cipher'], $params['mode'], $params['key']))
676
		{
677
			return FALSE;
678
		}
679
 
680
		if (isset($params['mode']))
681
		{
682
			$params['mode'] = strtolower($params['mode']);
683
			if ( ! isset($this->_modes[$this->_driver][$params['mode']]))
684
			{
685
				return FALSE;
686
			}
687
			else
688
			{
689
				$params['mode'] = $this->_modes[$this->_driver][$params['mode']];
690
			}
691
		}
692
 
693
		if (isset($params['hmac']) && $params['hmac'] === FALSE)
694
		{
695
			$params['hmac_digest'] = $params['hmac_key'] = NULL;
696
		}
697
		else
698
		{
699
			if ( ! isset($params['hmac_key']))
700
			{
701
				return FALSE;
702
			}
703
			elseif (isset($params['hmac_digest']))
704
			{
705
				$params['hmac_digest'] = strtolower($params['hmac_digest']);
706
				if ( ! isset($this->_digests[$params['hmac_digest']]))
707
				{
708
					return FALSE;
709
				}
710
			}
711
			else
712
			{
713
				$params['hmac_digest'] = 'sha512';
714
			}
715
		}
716
 
717
		$params = array(
718
			'handle' => NULL,
719
			'cipher' => $params['cipher'],
720
			'mode' => $params['mode'],
721
			'key' => $params['key'],
722
			'base64' => isset($params['raw_data']) ? ! $params['raw_data'] : FALSE,
723
			'hmac_digest' => $params['hmac_digest'],
724
			'hmac_key' => $params['hmac_key']
725
		);
726
 
727
		$this->_cipher_alias($params['cipher']);
728
		$params['handle'] = ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)
729
			? $this->{'_'.$this->_driver.'_get_handle'}($params['cipher'], $params['mode'])
730
			: $this->_handle;
731
 
732
		return $params;
733
	}
734
 
735
	// --------------------------------------------------------------------
736
 
737
	/**
738
	 * Get MCrypt handle
739
	 *
740
	 * @param	string	$cipher	Cipher name
741
	 * @param	string	$mode	Encryption mode
742
	 * @return	resource
743
	 */
744
	protected function _mcrypt_get_handle($cipher, $mode)
745
	{
746
		return mcrypt_module_open($cipher, '', $mode, '');
747
	}
748
 
749
	// --------------------------------------------------------------------
750
 
751
	/**
752
	 * Get OpenSSL handle
753
	 *
754
	 * @param	string	$cipher	Cipher name
755
	 * @param	string	$mode	Encryption mode
756
	 * @return	string
757
	 */
758
	protected function _openssl_get_handle($cipher, $mode)
759
	{
760
		// OpenSSL methods aren't suffixed with '-stream' for this mode
761
		return ($mode === 'stream')
762
			? $cipher
763
			: $cipher.'-'.$mode;
764
	}
765
 
766
	// --------------------------------------------------------------------
767
 
768
	/**
769
	 * Cipher alias
770
	 *
771
	 * Tries to translate cipher names between MCrypt and OpenSSL's "dialects".
772
	 *
773
	 * @param	string	$cipher	Cipher name
774
	 * @return	void
775
	 */
776
	protected function _cipher_alias(&$cipher)
777
	{
778
		static $dictionary;
779
 
780
		if (empty($dictionary))
781
		{
782
			$dictionary = array(
783
				'mcrypt' => array(
784
					'aes-128' => 'rijndael-128',
785
					'aes-192' => 'rijndael-128',
786
					'aes-256' => 'rijndael-128',
787
					'des3-ede3' => 'tripledes',
788
					'bf' => 'blowfish',
789
					'cast5' => 'cast-128',
790
					'rc4' => 'arcfour',
791
					'rc4-40' => 'arcfour'
792
				),
793
				'openssl' => array(
794
					'rijndael-128' => 'aes-128',
795
					'tripledes' => 'des-ede3',
796
					'blowfish' => 'bf',
797
					'cast-128' => 'cast5',
798
					'arcfour' => 'rc4-40',
799
					'rc4' => 'rc4-40'
800
				)
801
			);
802
 
803
			// Notes:
804
			//
805
			// - Rijndael-128 is, at the same time all three of AES-128,
806
			//   AES-192 and AES-256. The only difference between them is
807
			//   the key size. Rijndael-192, Rijndael-256 on the other hand
808
			//   also have different block sizes and are NOT AES-compatible.
809
			//
810
			// - Blowfish is said to be supporting key sizes between
811
			//   4 and 56 bytes, but it appears that between MCrypt and
812
			//   OpenSSL, only those of 16 and more bytes are compatible.
813
			//   Also, don't know what MCrypt's 'blowfish-compat' is.
814
			//
815
			// - CAST-128/CAST5 produces a longer cipher when encrypted via
816
			//   OpenSSL, but (strangely enough) can be decrypted by either
817
			//   extension anyway.
818
			//   Also, it appears that OpenSSL uses 16 rounds regardless of
819
			//   the key size, while RFC2144 says that for key sizes lower
820
			//   than 11 bytes, only 12 rounds should be used. This makes
821
			//   it portable only with keys of between 11 and 16 bytes.
822
			//
823
			// - RC4 (ARCFour) has a strange implementation under OpenSSL.
824
			//   Its 'rc4-40' cipher method seems to work flawlessly, yet
825
			//   there's another one, 'rc4' that only works with a 16-byte key.
826
			//
827
			// - DES is compatible, but doesn't need an alias.
828
			//
829
			// Other seemingly matching ciphers between MCrypt, OpenSSL:
830
			//
831
			// - RC2 is NOT compatible and only an obscure forum post
832
			//   confirms that it is MCrypt's fault.
833
		}
834
 
835
		if (isset($dictionary[$this->_driver][$cipher]))
836
		{
837
			$cipher = $dictionary[$this->_driver][$cipher];
838
		}
839
	}
840
 
841
	// --------------------------------------------------------------------
842
 
843
	/**
844
	 * HKDF
845
	 *
846
	 * @link	https://tools.ietf.org/rfc/rfc5869.txt
847
	 * @param	$key	Input key
848
	 * @param	$digest	A SHA-2 hashing algorithm
849
	 * @param	$salt	Optional salt
850
	 * @param	$length	Output length (defaults to the selected digest size)
851
	 * @param	$info	Optional context/application-specific info
852
	 * @return	string	A pseudo-random key
853
	 */
854
	public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')
855
	{
856
		if ( ! isset($this->_digests[$digest]))
857
		{
858
			return FALSE;
859
		}
860
 
861
		if (empty($length) OR ! is_int($length))
862
		{
863
			$length = $this->_digests[$digest];
864
		}
865
		elseif ($length > (255 * $this->_digests[$digest]))
866
		{
867
			return FALSE;
868
		}
869
 
870
		self::strlen($salt) OR $salt = str_repeat("\0", $this->_digests[$digest]);
871
 
872
		$prk = hash_hmac($digest, $key, $salt, TRUE);
873
		$key = '';
874
		for ($key_block = '', $block_index = 1; self::strlen($key) < $length; $block_index++)
875
		{
876
			$key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE);
877
			$key .= $key_block;
878
		}
879
 
880
		return self::substr($key, 0, $length);
881
	}
882
 
883
	// --------------------------------------------------------------------
884
 
885
	/**
886
	 * __get() magic
887
	 *
888
	 * @param	string	$key	Property name
889
	 * @return	mixed
890
	 */
891
	public function __get($key)
892
	{
893
		// Because aliases
894
		if ($key === 'mode')
895
		{
896
			return array_search($this->_mode, $this->_modes[$this->_driver], TRUE);
897
		}
898
		elseif (in_array($key, array('cipher', 'driver', 'drivers', 'digests'), TRUE))
899
		{
900
			return $this->{'_'.$key};
901
		}
902
 
903
		return NULL;
904
	}
905
 
906
	// --------------------------------------------------------------------
907
 
908
	/**
909
	 * Byte-safe strlen()
910
	 *
911
	 * @param	string	$str
912
	 * @return	integer
913
	 */
914
	protected static function strlen($str)
915
	{
916
		return (self::$func_override)
917
			? mb_strlen($str, '8bit')
918
			: strlen($str);
919
	}
920
 
921
	// --------------------------------------------------------------------
922
 
923
	/**
924
	 * Byte-safe substr()
925
	 *
926
	 * @param	string	$str
927
	 * @param	int	$start
928
	 * @param	int	$length
929
	 * @return	string
930
	 */
931
	protected static function substr($str, $start, $length = NULL)
932
	{
933
		if (self::$func_override)
934
		{
935
			// mb_substr($str, $start, null, '8bit') returns an empty
936
			// string on PHP 5.3
937
			isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start);
938
			return mb_substr($str, $start, $length, '8bit');
939
		}
940
 
941
		return isset($length)
942
			? substr($str, $start, $length)
943
			: substr($str, $start);
944
	}
945
}