Source for file crypt.php

Documentation is available at crypt.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Platform
  4.  * @subpackage  Crypt
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM') or die;
  11.  
  12. /**
  13.  * JCrypt is a Joomla Platform class for handling basic encryption/decryption of data.
  14.  *
  15.  * @package     Joomla.Platform
  16.  * @subpackage  Crypt
  17.  * @since       12.1
  18.  */
  19. class JCrypt
  20. {
  21.     /**
  22.      * @var    JCryptCipher  The encryption cipher object.
  23.      * @since  12.1
  24.      */
  25.     private $_cipher;
  26.  
  27.     /**
  28.      * @var    JCryptKey  The encryption key[/pair)].
  29.      * @since  12.1
  30.      */
  31.     private $_key;
  32.  
  33.     /**
  34.      * Object Constructor takes an optional key to be used for encryption/decryption. If no key is given then the
  35.      * secret word from the configuration object is used.
  36.      *
  37.      * @param   JCryptCipher  $cipher  The encryption cipher object.
  38.      * @param   JCryptKey     $key     The encryption key[/pair)].
  39.      *
  40.      * @since   12.1
  41.      */
  42.     public function __construct(JCryptCipher $cipher = null, JCryptKey $key = null)
  43.     {
  44.         // Set the encryption key[/pair)].
  45.         $this->_key = $key;
  46.  
  47.         // Set the encryption cipher.
  48.         $this->_cipher = isset($cipher) ? $cipher : new JCryptCipherSimple;
  49.     }
  50.  
  51.     /**
  52.      * Method to decrypt a data string.
  53.      *
  54.      * @param   string  $data  The encrypted string to decrypt.
  55.      *
  56.      * @return  string  The decrypted data string.
  57.      *
  58.      * @since   12.1
  59.      * @throws  InvalidArgumentException
  60.      */
  61.     public function decrypt($data)
  62.     {
  63.         try
  64.         {
  65.             return $this->_cipher->decrypt($data, $this->_key);
  66.         }
  67.         catch (InvalidArgumentException $e)
  68.         {
  69.             return false;
  70.         }
  71.     }
  72.  
  73.     /**
  74.      * Method to encrypt a data string.
  75.      *
  76.      * @param   string  $data  The data string to encrypt.
  77.      *
  78.      * @return  string  The encrypted data string.
  79.      *
  80.      * @since   12.1
  81.      */
  82.     public function encrypt($data)
  83.     {
  84.         return $this->_cipher->encrypt($data, $this->_key);
  85.     }
  86.  
  87.     /**
  88.      * Method to generate a new encryption key[/pair] object.
  89.      *
  90.      * @param   array  $options  Key generation options.
  91.      *
  92.      * @return  JCryptKey 
  93.      *
  94.      * @since   12.1
  95.      */
  96.     public function generateKey(array $options = array())
  97.     {
  98.         return $this->_cipher->generateKey($options);
  99.     }
  100.  
  101.     /**
  102.      * Method to set the encryption key[/pair] object.
  103.      *
  104.      * @param   JCryptKey  $key  The key object to set.
  105.      *
  106.      * @return  JCrypt 
  107.      *
  108.      * @since   12.1
  109.      */
  110.     public function setKey(JCryptKey $key)
  111.     {
  112.         $this->_key = $key;
  113.  
  114.         return $this;
  115.     }
  116.  
  117.     /**
  118.      * Generate random bytes.
  119.      *
  120.      * @param   integer  $length  Length of the random data to generate
  121.      *
  122.      * @return  string  Random binary data
  123.      *
  124.      * @since  12.1
  125.      */
  126.     public static function genRandomBytes($length = 16)
  127.     {
  128.         $length = (int) $length;
  129.         $sslStr = '';
  130.  
  131.         /*
  132.          * If a secure randomness generator exists and we don't
  133.          * have a buggy PHP version use it.
  134.          */
  135.         if (function_exists('openssl_random_pseudo_bytes')
  136.             && (version_compare(PHP_VERSION, '5.3.4') >= 0 || IS_WIN))
  137.         {
  138.             $sslStr = openssl_random_pseudo_bytes($length, $strong);
  139.  
  140.             if ($strong)
  141.             {
  142.                 return $sslStr;
  143.             }
  144.         }
  145.  
  146.         /*
  147.          * Collect any entropy available in the system along with a number
  148.          * of time measurements of operating system randomness.
  149.          */
  150.         $bitsPerRound = 2;
  151.         $maxTimeMicro = 400;
  152.         $shaHashLength = 20;
  153.         $randomStr = '';
  154.         $total = $length;
  155.  
  156.         // Check if we can use /dev/urandom.
  157.         $urandom = false;
  158.         $handle = null;
  159.  
  160.         // This is PHP 5.3.3 and up
  161.         if (function_exists('stream_set_read_buffer') && @is_readable('/dev/urandom'))
  162.         {
  163.             $handle = @fopen('/dev/urandom', 'rb');
  164.  
  165.             if ($handle)
  166.             {
  167.                 $urandom = true;
  168.             }
  169.         }
  170.  
  171.         while ($length > strlen($randomStr))
  172.         {
  173.             $bytes = ($total > $shaHashLength)? $shaHashLength : $total;
  174.             $total -= $bytes;
  175.  
  176.             /*
  177.              * Collect any entropy available from the PHP system and filesystem.
  178.              * If we have ssl data that isn't strong, we use it once.
  179.              */
  180.             $entropy = rand() . uniqid(mt_rand(), true) . $sslStr;
  181.             $entropy .= implode('', @fstat(fopen(__FILE__, 'r')));
  182.             $entropy .= memory_get_usage();
  183.             $sslStr = '';
  184.  
  185.             if ($urandom)
  186.             {
  187.                 stream_set_read_buffer($handle, 0);
  188.                 $entropy .= @fread($handle, $bytes);
  189.             }
  190.             else
  191.             {
  192.                 /*
  193.                  * There is no external source of entropy so we repeat calls
  194.                  * to mt_rand until we are assured there's real randomness in
  195.                  * the result.
  196.                  *
  197.                  * Measure the time that the operations will take on average.
  198.                  */
  199.                 $samples = 3;
  200.                 $duration = 0;
  201.  
  202.                 for ($pass = 0; $pass < $samples; ++$pass)
  203.                 {
  204.                     $microStart = microtime(true) * 1000000;
  205.                     $hash = sha1(mt_rand(), true);
  206.  
  207.                     for ($count = 0; $count < 50; ++$count)
  208.                     {
  209.                         $hash = sha1($hash, true);
  210.                     }
  211.  
  212.                     $microEnd = microtime(true) * 1000000;
  213.                     $entropy .= $microStart . $microEnd;
  214.  
  215.                     if ($microStart >= $microEnd)
  216.                     {
  217.                         $microEnd += 1000000;
  218.                     }
  219.  
  220.                     $duration += $microEnd - $microStart;
  221.                 }
  222.  
  223.                 $duration = $duration / $samples;
  224.  
  225.                 /*
  226.                  * Based on the average time, determine the total rounds so that
  227.                  * the total running time is bounded to a reasonable number.
  228.                  */
  229.                 $rounds = (int) (($maxTimeMicro / $duration) * 50);
  230.  
  231.                 /*
  232.                  * Take additional measurements. On average we can expect
  233.                  * at least $bitsPerRound bits of entropy from each measurement.
  234.                  */
  235.                 $iter = $bytes * (int) ceil(8 / $bitsPerRound);
  236.  
  237.                 for ($pass = 0; $pass < $iter; ++$pass)
  238.                 {
  239.                     $microStart = microtime(true);
  240.                     $hash = sha1(mt_rand(), true);
  241.  
  242.                     for ($count = 0; $count < $rounds; ++$count)
  243.                     {
  244.                         $hash = sha1($hash, true);
  245.                     }
  246.  
  247.                     $entropy .= $microStart . microtime(true);
  248.                 }
  249.             }
  250.  
  251.             $randomStr .= sha1($entropy, true);
  252.         }
  253.  
  254.         if ($urandom)
  255.         {
  256.             @fclose($handle);
  257.         }
  258.  
  259.         return substr($randomStr, 0, $length);
  260.     }
  261.  
  262.     /**
  263.      * A timing safe comparison method. This defeats hacking
  264.      * attempts that use timing based attack vectors.
  265.      *
  266.      * @param   string  $known    A known string to check against.
  267.      * @param   string  $unknown  An unknown string to check.
  268.      *
  269.      * @return  boolean  True if the two strings are exactly the same.
  270.      *
  271.      * @since   3.2
  272.      */
  273.     public static function timingSafeCompare($known, $unknown)
  274.     {
  275.         // Prevent issues if string length is 0
  276.         $known .= chr(0);
  277.         $unknown .= chr(0);
  278.  
  279.         $knownLength = strlen($known);
  280.         $unknownLength = strlen($unknown);
  281.  
  282.         // Set the result to the difference between the lengths
  283.         $result = $knownLength - $unknownLength;
  284.  
  285.         // Note that we ALWAYS iterate over the user-supplied length to prevent leaking length info.
  286.         for ($i = 0; $i < $unknownLength; $i++)
  287.         {
  288.             // Using % here is a trick to prevent notices. It's safe, since if the lengths are different, $result is already non-0
  289.             $result |= (ord($known[$i % $knownLength]) ^ ord($unknown[$i]));
  290.         }
  291.  
  292.         // They are only identical strings if $result is exactly 0...
  293.         return $result === 0;
  294.     }
  295.  
  296.     /**
  297.      * Tests for the availability of updated crypt().
  298.      * Based on a method by Anthony Ferrera
  299.      *
  300.      * @return  boolean  True if updated crypt() is available.
  301.      *
  302.      * @note    To be removed when PHP 5.3.7 or higher is the minimum supported version.
  303.      * @see     https://github.com/ircmaxell/password_compat/blob/master/version-test.php
  304.      * @since   3.2
  305.      */
  306.     public static function hasStrongPasswordSupport()
  307.     {
  308.         static $pass = null;
  309.  
  310.         if (is_null($pass))
  311.         {
  312.             // Check to see whether crypt() is supported.
  313.             if (version_compare(PHP_VERSION, '5.3.7', '>=') === true)
  314.             {
  315.                 // We have safe PHP version.
  316.                 $pass = true;
  317.             }
  318.             else
  319.             {
  320.                 // We need to test if we have patched PHP version.
  321.                 jimport('compat.password.lib.version_test');
  322.                 $test = new version_test;
  323.                 $pass = $test->version_test();
  324.             }
  325.  
  326.             if ($pass && !defined('PASSWORD_DEFAULT'))
  327.             {
  328.                 // Always make sure that the password hashing API has been defined.
  329.                 include_once JPATH_ROOT . '/libraries/compat/password/lib/password.php';
  330.             }
  331.         }
  332.  
  333.         return $pass;
  334.     }
  335. }

Documentation generated on Tue, 19 Nov 2013 14:57:39 +0100 by phpDocumentor 1.4.3