Guest User

Encryption.php

a guest
Jan 20th, 2020
366
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 29.32 KB | None | 0 0
  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 - 2017, 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 - 2017, 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_overload flag
  139.      *
  140.      * @var    bool
  141.      */
  142.     protected static $func_overload;
  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.             'openssl' => extension_loaded('openssl')
  157.         );
  158.  
  159.         if ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl'])
  160.         {
  161.             show_error('Encryption: Unable to find an available encryption driver.');
  162.         }
  163.  
  164.         isset(self::$func_overload) OR self::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload'));
  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.             }
  685.             else
  686.             {
  687.                 $params['mode'] = $this->_modes[$this->_driver][$params['mode']];
  688.             }
  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
  910.      * @return    int
  911.      */
  912.     protected static function strlen($str)
  913.     {
  914.         return (self::$func_overload)
  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.     {
  931.         if (self::$func_overload)
  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. }
Add Comment
Please, Sign In to add comment