Subversion-Projekte lars-tiefland.ci

Revision

Revision 2242 | Revision 2257 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | 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
 *
2254 lars 9
 * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
68 lars 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/)
2254 lars 32
 * @copyright	Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
68 lars 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
	/**
2107 lars 138
	 * mbstring.func_overload flag
68 lars 139
	 *
140
	 * @var	bool
141
	 */
2107 lars 142
	protected static $func_overload;
68 lars 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(
1257 lars 155
			'mcrypt'  => defined('MCRYPT_DEV_URANDOM'),
156
			'openssl' => extension_loaded('openssl')
68 lars 157
		);
158
 
159
		if ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl'])
160
		{
161
			show_error('Encryption: Unable to find an available encryption driver.');
162
		}
163
 
2107 lars 164
		isset(self::$func_overload) OR self::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload'));
68 lars 165
		$this->initialize($params);
166
 
167
		if ( ! isset($this->_key) && self::strlen($key = config_item('encryption_key')) > 0)
168
		{
169
			$this->_key = $key;
170
		}
171
 
172
		log_message('info', 'Encryption Class Initialized');
173
	}
174
 
175
	// --------------------------------------------------------------------
176
 
177
	/**
178
	 * Initialize
179
	 *
180
	 * @param	array	$params	Configuration parameters
181
	 * @return	CI_Encryption
182
	 */
183
	public function initialize(array $params)
184
	{
185
		if ( ! empty($params['driver']))
186
		{
187
			if (isset($this->_drivers[$params['driver']]))
188
			{
189
				if ($this->_drivers[$params['driver']])
190
				{
191
					$this->_driver = $params['driver'];
192
				}
193
				else
194
				{
195
					log_message('error', "Encryption: Driver '".$params['driver']."' is not available.");
196
				}
197
			}
198
			else
199
			{
200
				log_message('error', "Encryption: Unknown driver '".$params['driver']."' cannot be configured.");
201
			}
202
		}
203
 
204
		if (empty($this->_driver))
205
		{
206
			$this->_driver = ($this->_drivers['openssl'] === TRUE)
207
				? 'openssl'
208
				: 'mcrypt';
209
 
210
			log_message('debug', "Encryption: Auto-configured driver '".$this->_driver."'.");
211
		}
212
 
213
		empty($params['cipher']) && $params['cipher'] = $this->_cipher;
214
		empty($params['key']) OR $this->_key = $params['key'];
215
		$this->{'_'.$this->_driver.'_initialize'}($params);
216
		return $this;
217
	}
218
 
219
	// --------------------------------------------------------------------
220
 
221
	/**
222
	 * Initialize MCrypt
223
	 *
224
	 * @param	array	$params	Configuration parameters
225
	 * @return	void
226
	 */
227
	protected function _mcrypt_initialize($params)
228
	{
229
		if ( ! empty($params['cipher']))
230
		{
231
			$params['cipher'] = strtolower($params['cipher']);
232
			$this->_cipher_alias($params['cipher']);
233
 
234
			if ( ! in_array($params['cipher'], mcrypt_list_algorithms(), TRUE))
235
			{
236
				log_message('error', 'Encryption: MCrypt cipher '.strtoupper($params['cipher']).' is not available.');
237
			}
238
			else
239
			{
240
				$this->_cipher = $params['cipher'];
241
			}
242
		}
243
 
244
		if ( ! empty($params['mode']))
245
		{
246
			$params['mode'] = strtolower($params['mode']);
247
			if ( ! isset($this->_modes['mcrypt'][$params['mode']]))
248
			{
249
				log_message('error', 'Encryption: MCrypt mode '.strtoupper($params['mode']).' is not available.');
250
			}
251
			else
252
			{
253
				$this->_mode = $this->_modes['mcrypt'][$params['mode']];
254
			}
255
		}
256
 
257
		if (isset($this->_cipher, $this->_mode))
258
		{
259
			if (is_resource($this->_handle)
260
				&& (strtolower(mcrypt_enc_get_algorithms_name($this->_handle)) !== $this->_cipher
261
					OR strtolower(mcrypt_enc_get_modes_name($this->_handle)) !== $this->_mode)
262
			)
263
			{
264
				mcrypt_module_close($this->_handle);
265
			}
266
 
267
			if ($this->_handle = mcrypt_module_open($this->_cipher, '', $this->_mode, ''))
268
			{
269
				log_message('info', 'Encryption: MCrypt cipher '.strtoupper($this->_cipher).' initialized in '.strtoupper($this->_mode).' mode.');
270
			}
271
			else
272
			{
273
				log_message('error', 'Encryption: Unable to initialize MCrypt with cipher '.strtoupper($this->_cipher).' in '.strtoupper($this->_mode).' mode.');
274
			}
275
		}
276
	}
277
 
278
	// --------------------------------------------------------------------
279
 
280
	/**
281
	 * Initialize OpenSSL
282
	 *
283
	 * @param	array	$params	Configuration parameters
284
	 * @return	void
285
	 */
286
	protected function _openssl_initialize($params)
287
	{
288
		if ( ! empty($params['cipher']))
289
		{
290
			$params['cipher'] = strtolower($params['cipher']);
291
			$this->_cipher_alias($params['cipher']);
292
			$this->_cipher = $params['cipher'];
293
		}
294
 
295
		if ( ! empty($params['mode']))
296
		{
297
			$params['mode'] = strtolower($params['mode']);
298
			if ( ! isset($this->_modes['openssl'][$params['mode']]))
299
			{
300
				log_message('error', 'Encryption: OpenSSL mode '.strtoupper($params['mode']).' is not available.');
301
			}
302
			else
303
			{
304
				$this->_mode = $this->_modes['openssl'][$params['mode']];
305
			}
306
		}
307
 
308
		if (isset($this->_cipher, $this->_mode))
309
		{
310
			// This is mostly for the stream mode, which doesn't get suffixed in OpenSSL
311
			$handle = empty($this->_mode)
312
				? $this->_cipher
313
				: $this->_cipher.'-'.$this->_mode;
314
 
315
			if ( ! in_array($handle, openssl_get_cipher_methods(), TRUE))
316
			{
317
				$this->_handle = NULL;
318
				log_message('error', 'Encryption: Unable to initialize OpenSSL with method '.strtoupper($handle).'.');
319
			}
320
			else
321
			{
322
				$this->_handle = $handle;
323
				log_message('info', 'Encryption: OpenSSL initialized with method '.strtoupper($handle).'.');
324
			}
325
		}
326
	}
327
 
328
	// --------------------------------------------------------------------
329
 
330
	/**
331
	 * Create a random key
332
	 *
333
	 * @param	int	$length	Output length
334
	 * @return	string
335
	 */
336
	public function create_key($length)
337
	{
338
		if (function_exists('random_bytes'))
339
		{
340
			try
341
			{
342
				return random_bytes((int) $length);
343
			}
344
			catch (Exception $e)
345
			{
346
				log_message('error', $e->getMessage());
347
				return FALSE;
348
			}
349
		}
350
		elseif (defined('MCRYPT_DEV_URANDOM'))
351
		{
352
			return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
353
		}
354
 
355
		$is_secure = NULL;
356
		$key = openssl_random_pseudo_bytes($length, $is_secure);
357
		return ($is_secure === TRUE)
358
			? $key
359
			: FALSE;
360
	}
361
 
362
	// --------------------------------------------------------------------
363
 
364
	/**
365
	 * Encrypt
366
	 *
367
	 * @param	string	$data	Input data
368
	 * @param	array	$params	Input parameters
369
	 * @return	string
370
	 */
371
	public function encrypt($data, array $params = NULL)
372
	{
373
		if (($params = $this->_get_params($params)) === FALSE)
374
		{
375
			return FALSE;
376
		}
377
 
378
		isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');
379
 
380
		if (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE)
381
		{
382
			return FALSE;
383
		}
384
 
385
		$params['base64'] && $data = base64_encode($data);
386
 
387
		if (isset($params['hmac_digest']))
388
		{
389
			isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
390
			return hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']).$data;
391
		}
392
 
393
		return $data;
394
	}
395
 
396
	// --------------------------------------------------------------------
397
 
398
	/**
399
	 * Encrypt via MCrypt
400
	 *
401
	 * @param	string	$data	Input data
402
	 * @param	array	$params	Input parameters
403
	 * @return	string
404
	 */
405
	protected function _mcrypt_encrypt($data, $params)
406
	{
407
		if ( ! is_resource($params['handle']))
408
		{
409
			return FALSE;
410
		}
411
 
412
		// The greater-than-1 comparison is mostly a work-around for a bug,
413
		// where 1 is returned for ARCFour instead of 0.
414
		$iv = (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)
415
			? $this->create_key($iv_size)
416
			: NULL;
417
 
418
		if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)
419
		{
420
			if ($params['handle'] !== $this->_handle)
421
			{
422
				mcrypt_module_close($params['handle']);
423
			}
424
 
425
			return FALSE;
426
		}
427
 
428
		// Use PKCS#7 padding in order to ensure compatibility with OpenSSL
429
		// and other implementations outside of PHP.
430
		if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
431
		{
432
			$block_size = mcrypt_enc_get_block_size($params['handle']);
433
			$pad = $block_size - (self::strlen($data) % $block_size);
434
			$data .= str_repeat(chr($pad), $pad);
435
		}
436
 
437
		// Work-around for yet another strange behavior in MCrypt.
438
		//
439
		// When encrypting in ECB mode, the IV is ignored. Yet
440
		// mcrypt_enc_get_iv_size() returns a value larger than 0
441
		// even if ECB is used AND mcrypt_generic_init() complains
442
		// if you don't pass an IV with length equal to the said
443
		// return value.
444
		//
445
		// This probably would've been fine (even though still wasteful),
446
		// but OpenSSL isn't that dumb and we need to make the process
447
		// portable, so ...
448
		$data = (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
449
			? $iv.mcrypt_generic($params['handle'], $data)
450
			: mcrypt_generic($params['handle'], $data);
451
 
452
		mcrypt_generic_deinit($params['handle']);
453
		if ($params['handle'] !== $this->_handle)
454
		{
455
			mcrypt_module_close($params['handle']);
456
		}
457
 
458
		return $data;
459
	}
460
 
461
	// --------------------------------------------------------------------
462
 
463
	/**
464
	 * Encrypt via OpenSSL
465
	 *
466
	 * @param	string	$data	Input data
467
	 * @param	array	$params	Input parameters
468
	 * @return	string
469
	 */
470
	protected function _openssl_encrypt($data, $params)
471
	{
472
		if (empty($params['handle']))
473
		{
474
			return FALSE;
475
		}
476
 
477
		$iv = ($iv_size = openssl_cipher_iv_length($params['handle']))
478
			? $this->create_key($iv_size)
479
			: NULL;
480
 
481
		$data = openssl_encrypt(
482
			$data,
483
			$params['handle'],
484
			$params['key'],
485
			1, // DO NOT TOUCH!
486
			$iv
487
		);
488
 
489
		if ($data === FALSE)
490
		{
491
			return FALSE;
492
		}
493
 
494
		return $iv.$data;
495
	}
496
 
497
	// --------------------------------------------------------------------
498
 
499
	/**
500
	 * Decrypt
501
	 *
502
	 * @param	string	$data	Encrypted data
503
	 * @param	array	$params	Input parameters
504
	 * @return	string
505
	 */
506
	public function decrypt($data, array $params = NULL)
507
	{
508
		if (($params = $this->_get_params($params)) === FALSE)
509
		{
510
			return FALSE;
511
		}
512
 
513
		if (isset($params['hmac_digest']))
514
		{
515
			// This might look illogical, but it is done during encryption as well ...
516
			// The 'base64' value is effectively an inverted "raw data" parameter
517
			$digest_size = ($params['base64'])
518
				? $this->_digests[$params['hmac_digest']] * 2
519
				: $this->_digests[$params['hmac_digest']];
520
 
521
			if (self::strlen($data) <= $digest_size)
522
			{
523
				return FALSE;
524
			}
525
 
526
			$hmac_input = self::substr($data, 0, $digest_size);
527
			$data = self::substr($data, $digest_size);
528
 
529
			isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
530
			$hmac_check = hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']);
531
 
532
			// Time-attack-safe comparison
533
			$diff = 0;
534
			for ($i = 0; $i < $digest_size; $i++)
535
			{
536
				$diff |= ord($hmac_input[$i]) ^ ord($hmac_check[$i]);
537
			}
538
 
539
			if ($diff !== 0)
540
			{
541
				return FALSE;
542
			}
543
		}
544
 
545
		if ($params['base64'])
546
		{
547
			$data = base64_decode($data);
548
		}
549
 
550
		isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');
551
 
552
		return $this->{'_'.$this->_driver.'_decrypt'}($data, $params);
553
	}
554
 
555
	// --------------------------------------------------------------------
556
 
557
	/**
558
	 * Decrypt via MCrypt
559
	 *
560
	 * @param	string	$data	Encrypted data
561
	 * @param	array	$params	Input parameters
562
	 * @return	string
563
	 */
564
	protected function _mcrypt_decrypt($data, $params)
565
	{
566
		if ( ! is_resource($params['handle']))
567
		{
568
			return FALSE;
569
		}
570
 
571
		// The greater-than-1 comparison is mostly a work-around for a bug,
572
		// where 1 is returned for ARCFour instead of 0.
573
		if (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)
574
		{
575
			if (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
576
			{
577
				$iv = self::substr($data, 0, $iv_size);
578
				$data = self::substr($data, $iv_size);
579
			}
580
			else
581
			{
582
				// MCrypt is dumb and this is ignored, only size matters
583
				$iv = str_repeat("\x0", $iv_size);
584
			}
585
		}
586
		else
587
		{
588
			$iv = NULL;
589
		}
590
 
591
		if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)
592
		{
593
			if ($params['handle'] !== $this->_handle)
594
			{
595
				mcrypt_module_close($params['handle']);
596
			}
597
 
598
			return FALSE;
599
		}
600
 
601
		$data = mdecrypt_generic($params['handle'], $data);
602
		// Remove PKCS#7 padding, if necessary
603
		if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
604
		{
605
			$data = self::substr($data, 0, -ord($data[self::strlen($data)-1]));
606
		}
607
 
608
		mcrypt_generic_deinit($params['handle']);
609
		if ($params['handle'] !== $this->_handle)
610
		{
611
			mcrypt_module_close($params['handle']);
612
		}
613
 
614
		return $data;
615
	}
616
 
617
	// --------------------------------------------------------------------
618
 
619
	/**
620
	 * Decrypt via OpenSSL
621
	 *
622
	 * @param	string	$data	Encrypted data
623
	 * @param	array	$params	Input parameters
624
	 * @return	string
625
	 */
626
	protected function _openssl_decrypt($data, $params)
627
	{
628
		if ($iv_size = openssl_cipher_iv_length($params['handle']))
629
		{
630
			$iv = self::substr($data, 0, $iv_size);
631
			$data = self::substr($data, $iv_size);
632
		}
633
		else
634
		{
635
			$iv = NULL;
636
		}
637
 
638
		return empty($params['handle'])
639
			? FALSE
640
			: openssl_decrypt(
641
				$data,
642
				$params['handle'],
643
				$params['key'],
644
				1, // DO NOT TOUCH!
645
				$iv
646
			);
647
	}
648
 
649
	// --------------------------------------------------------------------
650
 
651
	/**
652
	 * Get params
653
	 *
654
	 * @param	array	$params	Input parameters
655
	 * @return	array
656
	 */
657
	protected function _get_params($params)
658
	{
659
		if (empty($params))
660
		{
661
			return isset($this->_cipher, $this->_mode, $this->_key, $this->_handle)
662
				? array(
663
					'handle' => $this->_handle,
664
					'cipher' => $this->_cipher,
665
					'mode' => $this->_mode,
666
					'key' => NULL,
667
					'base64' => TRUE,
668
					'hmac_digest' => 'sha512',
669
					'hmac_key' => NULL
670
				)
671
				: FALSE;
672
		}
673
		elseif ( ! isset($params['cipher'], $params['mode'], $params['key']))
674
		{
675
			return FALSE;
676
		}
677
 
678
		if (isset($params['mode']))
679
		{
680
			$params['mode'] = strtolower($params['mode']);
681
			if ( ! isset($this->_modes[$this->_driver][$params['mode']]))
682
			{
683
				return FALSE;
684
			}
2254 lars 685
			else
686
			{
687
				$params['mode'] = $this->_modes[$this->_driver][$params['mode']];
688
			}
68 lars 689
		}
690
 
691
		if (isset($params['hmac']) && $params['hmac'] === FALSE)
692
		{
693
			$params['hmac_digest'] = $params['hmac_key'] = NULL;
694
		}
695
		else
696
		{
697
			if ( ! isset($params['hmac_key']))
698
			{
699
				return FALSE;
700
			}
701
			elseif (isset($params['hmac_digest']))
702
			{
703
				$params['hmac_digest'] = strtolower($params['hmac_digest']);
704
				if ( ! isset($this->_digests[$params['hmac_digest']]))
705
				{
706
					return FALSE;
707
				}
708
			}
709
			else
710
			{
711
				$params['hmac_digest'] = 'sha512';
712
			}
713
		}
714
 
715
		$params = array(
716
			'handle' => NULL,
717
			'cipher' => $params['cipher'],
718
			'mode' => $params['mode'],
719
			'key' => $params['key'],
720
			'base64' => isset($params['raw_data']) ? ! $params['raw_data'] : FALSE,
721
			'hmac_digest' => $params['hmac_digest'],
722
			'hmac_key' => $params['hmac_key']
723
		);
724
 
725
		$this->_cipher_alias($params['cipher']);
726
		$params['handle'] = ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)
727
			? $this->{'_'.$this->_driver.'_get_handle'}($params['cipher'], $params['mode'])
728
			: $this->_handle;
729
 
730
		return $params;
731
	}
732
 
733
	// --------------------------------------------------------------------
734
 
735
	/**
736
	 * Get MCrypt handle
737
	 *
738
	 * @param	string	$cipher	Cipher name
739
	 * @param	string	$mode	Encryption mode
740
	 * @return	resource
741
	 */
742
	protected function _mcrypt_get_handle($cipher, $mode)
743
	{
744
		return mcrypt_module_open($cipher, '', $mode, '');
745
	}
746
 
747
	// --------------------------------------------------------------------
748
 
749
	/**
750
	 * Get OpenSSL handle
751
	 *
752
	 * @param	string	$cipher	Cipher name
753
	 * @param	string	$mode	Encryption mode
754
	 * @return	string
755
	 */
756
	protected function _openssl_get_handle($cipher, $mode)
757
	{
758
		// OpenSSL methods aren't suffixed with '-stream' for this mode
759
		return ($mode === 'stream')
760
			? $cipher
761
			: $cipher.'-'.$mode;
762
	}
763
 
764
	// --------------------------------------------------------------------
765
 
766
	/**
767
	 * Cipher alias
768
	 *
769
	 * Tries to translate cipher names between MCrypt and OpenSSL's "dialects".
770
	 *
771
	 * @param	string	$cipher	Cipher name
772
	 * @return	void
773
	 */
774
	protected function _cipher_alias(&$cipher)
775
	{
776
		static $dictionary;
777
 
778
		if (empty($dictionary))
779
		{
780
			$dictionary = array(
781
				'mcrypt' => array(
782
					'aes-128' => 'rijndael-128',
783
					'aes-192' => 'rijndael-128',
784
					'aes-256' => 'rijndael-128',
785
					'des3-ede3' => 'tripledes',
786
					'bf' => 'blowfish',
787
					'cast5' => 'cast-128',
788
					'rc4' => 'arcfour',
789
					'rc4-40' => 'arcfour'
790
				),
791
				'openssl' => array(
792
					'rijndael-128' => 'aes-128',
793
					'tripledes' => 'des-ede3',
794
					'blowfish' => 'bf',
795
					'cast-128' => 'cast5',
796
					'arcfour' => 'rc4-40',
797
					'rc4' => 'rc4-40'
798
				)
799
			);
800
 
801
			// Notes:
802
			//
803
			// - Rijndael-128 is, at the same time all three of AES-128,
804
			//   AES-192 and AES-256. The only difference between them is
805
			//   the key size. Rijndael-192, Rijndael-256 on the other hand
806
			//   also have different block sizes and are NOT AES-compatible.
807
			//
808
			// - Blowfish is said to be supporting key sizes between
809
			//   4 and 56 bytes, but it appears that between MCrypt and
810
			//   OpenSSL, only those of 16 and more bytes are compatible.
811
			//   Also, don't know what MCrypt's 'blowfish-compat' is.
812
			//
813
			// - CAST-128/CAST5 produces a longer cipher when encrypted via
814
			//   OpenSSL, but (strangely enough) can be decrypted by either
815
			//   extension anyway.
816
			//   Also, it appears that OpenSSL uses 16 rounds regardless of
817
			//   the key size, while RFC2144 says that for key sizes lower
818
			//   than 11 bytes, only 12 rounds should be used. This makes
819
			//   it portable only with keys of between 11 and 16 bytes.
820
			//
821
			// - RC4 (ARCFour) has a strange implementation under OpenSSL.
822
			//   Its 'rc4-40' cipher method seems to work flawlessly, yet
823
			//   there's another one, 'rc4' that only works with a 16-byte key.
824
			//
825
			// - DES is compatible, but doesn't need an alias.
826
			//
827
			// Other seemingly matching ciphers between MCrypt, OpenSSL:
828
			//
829
			// - RC2 is NOT compatible and only an obscure forum post
830
			//   confirms that it is MCrypt's fault.
831
		}
832
 
833
		if (isset($dictionary[$this->_driver][$cipher]))
834
		{
835
			$cipher = $dictionary[$this->_driver][$cipher];
836
		}
837
	}
838
 
839
	// --------------------------------------------------------------------
840
 
841
	/**
842
	 * HKDF
843
	 *
844
	 * @link	https://tools.ietf.org/rfc/rfc5869.txt
845
	 * @param	$key	Input key
846
	 * @param	$digest	A SHA-2 hashing algorithm
847
	 * @param	$salt	Optional salt
848
	 * @param	$length	Output length (defaults to the selected digest size)
849
	 * @param	$info	Optional context/application-specific info
850
	 * @return	string	A pseudo-random key
851
	 */
852
	public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')
853
	{
854
		if ( ! isset($this->_digests[$digest]))
855
		{
856
			return FALSE;
857
		}
858
 
859
		if (empty($length) OR ! is_int($length))
860
		{
861
			$length = $this->_digests[$digest];
862
		}
863
		elseif ($length > (255 * $this->_digests[$digest]))
864
		{
865
			return FALSE;
866
		}
867
 
868
		self::strlen($salt) OR $salt = str_repeat("\0", $this->_digests[$digest]);
869
 
870
		$prk = hash_hmac($digest, $key, $salt, TRUE);
871
		$key = '';
872
		for ($key_block = '', $block_index = 1; self::strlen($key) < $length; $block_index++)
873
		{
874
			$key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE);
875
			$key .= $key_block;
876
		}
877
 
878
		return self::substr($key, 0, $length);
879
	}
880
 
881
	// --------------------------------------------------------------------
882
 
883
	/**
884
	 * __get() magic
885
	 *
886
	 * @param	string	$key	Property name
887
	 * @return	mixed
888
	 */
889
	public function __get($key)
890
	{
891
		// Because aliases
892
		if ($key === 'mode')
893
		{
894
			return array_search($this->_mode, $this->_modes[$this->_driver], TRUE);
895
		}
896
		elseif (in_array($key, array('cipher', 'driver', 'drivers', 'digests'), TRUE))
897
		{
898
			return $this->{'_'.$key};
899
		}
900
 
901
		return NULL;
902
	}
903
 
904
	// --------------------------------------------------------------------
905
 
906
	/**
907
	 * Byte-safe strlen()
908
	 *
909
	 * @param	string	$str
1257 lars 910
	 * @return	int
68 lars 911
	 */
912
	protected static function strlen($str)
913
	{
2107 lars 914
		return (self::$func_overload)
68 lars 915
			? mb_strlen($str, '8bit')
916
			: strlen($str);
917
	}
918
 
919
	// --------------------------------------------------------------------
920
 
921
	/**
922
	 * Byte-safe substr()
923
	 *
924
	 * @param	string	$str
925
	 * @param	int	$start
926
	 * @param	int	$length
927
	 * @return	string
928
	 */
929
	protected static function substr($str, $start, $length = NULL)
930
	{
2107 lars 931
		if (self::$func_overload)
68 lars 932
		{
933
			// mb_substr($str, $start, null, '8bit') returns an empty
934
			// string on PHP 5.3
935
			isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start);
936
			return mb_substr($str, $start, $length, '8bit');
937
		}
938
 
939
		return isset($length)
940
			? substr($str, $start, $length)
941
			: substr($str, $start);
942
	}
943
}