Chamilo is a learning management system focused on ease of use and accessibility
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
chamilo-lms/plugin/advancedsubscription/resources/phpcrypt/Core.php

622 lines
17 KiB

<?php
/*
* Author: Ryan Gilfether
* URL: http://www.gilfether.com/phpCrypt
* Date: March 26, 2013
* Copyright (C) 2013 Ryan Gilfether
*
* This file is part of phpCrypt
*
* phpCrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
namespace PHP_Crypt;
/**
* A base class that should not be used directly. It is intended as a base
* object that should be extended and provides tools that child objects may use.
*
* @author Ryan Gilfether
* @link http://www.gilfether.com/phpcrypt
* @copyright 2013 Ryan Gilfether
*/
class Core
{
/** @type integer HASH_LEN The length of md5() hash string */
const HASH_LEN = 16;
/**
* Constructor
*
*/
protected function __construct()
{
}
/**
* Destructor
*
*/
protected function __destruct()
{
}
/**
* Convert hexidecimal to a binary string (ex: "00110110")
*
* @param string $hex A string containing a hexidecimal number
* @return string A string representation of a binary
*/
public static function hex2Bin($hex)
{
// if we do not have an even number of hex characters
// append a 0 to the beginning to make it even
if(strlen($hex) % 2)
$hex = "0$hex";
$parts = str_split($hex, 2);
$parts = array_map(function($v) {
$v = base_convert($v, 16, 2);
return str_pad($v, 8, "0", STR_PAD_LEFT);
}, $parts);
return implode("", $parts);
}
/**
* Convert hex to a string
*
* @param string $hex A string representation of Hex (IE: "1a2b3c" not 0x1a2b3c)
* @return string a string
*/
public static function hex2Str($hex)
{
// php version >= 5.4 have a hex2bin function, use it
// if it exists
if(function_exists("hex2bin"))
return hex2bin($hex);
$parts = str_split($hex, 2);
$parts = array_map(function($v) {
return chr(Core::hex2Dec($v));
}, $parts);
return implode("", $parts);
}
/**
* Converts Hex to Decimal
* This function just calls php's hexdec() function, but I
* encapsulated it in this function to keep things uniform
* and have all possible conversion function available in
* the Cipher class
*
* @param string $hex A hex number to convert to decimal
* @return integer A decimal number
*/
public static function hex2Dec($hex)
{
return hexdec($hex);
}
/**
* Convert binary string (ie 00110110) to hex
*
* @param string $bin A binary string
* @return string A string representation of hexidecimal number
*/
public static function bin2Hex($bin)
{
$parts = str_split($bin, 8);
$parts = array_map(function($v) {
$v = str_pad($v, 8, "0", STR_PAD_LEFT);
$v = dechex(bindec($v));
return str_pad($v, 2, "0", STR_PAD_LEFT);
}, $parts);
return implode("", $parts);
}
/**
* Converts a binary representation (ie 01101011) back to a string
*
* @param string $bin a binary representation string
* @return string A string of characters representing the binary
*/
public static function bin2Str($bin)
{
$hex = self::bin2Hex($bin);
return self::hex2Str($hex);
}
/**
* Convert a binary string (ie: 01101011) to a decimal number
*
* @param string A string representation of a binary number
* @return integer The number converted from the binary string
*/
public static function bin2Dec($bin)
{
return bindec($bin);
}
/**
* Convert a string to hex
* This function calls the PHP bin2hex(), and is here
* for consistency with the other string functions
*
* @param string $str A string
* @return string A string representation of hexidecimal number
*/
public static function str2Hex($str)
{
return bin2hex($str);
}
/**
* Convert a string of characters to a decimal number
*
* @param string $str The string to convert to decimal
* @return integer The integer converted from the string
*/
public static function str2Dec($str)
{
$hex = self::str2Hex($str);
return self::hex2Dec($hex);
}
/**
* Converts a string to binary representation (ie 01101011)
*
* @param string $str A string
* @return string A binary representation of the the string
*/
public static function str2Bin($str)
{
$hex = self::str2Hex($str);
$parts = str_split($hex, 2);
$parts = array_map(function($v) {
return Core::hex2Bin($v);
}, $parts);
return implode("", $parts);
}
/**
* Converts Decimal to Hex
* This function just calls php's dechex() function, but I
* encapsulated it in this function to keep things uniform
* and have all possible conversion function available in
* the Cipher class
*
* The parameter $req_bytes will pad the return hex with NULL (00)
* until the hex represents the number of bytes given to $req_bytes
* This is because dechex() drops null bytes from the Hex, which may
* be needed in some cases
*
* @param integer $dec A decimal number to convert
* @param integer $req_bytes Optional, forces the string to be at least
* $req_bytes in size, this is needed because on occasion left most null bytes
* are dropped in dechex(), causing the string to have a shorter byte
* size than the initial integer.
* @return string A hexidecimal representation of the decimal number
*/
public static function dec2Hex($dec, $req_bytes = 0)
{
$hex = dechex($dec);
// if we do not have an even number of hex characters
// append a 0 to the beginning. dechex() drops leading 0's
if(strlen($hex) % 2)
$hex = "0$hex";
// if the number of bytes in the hex is less than
// what we need it to be, add null bytes to the
// front of the hex to padd it to the required size
if(($req_bytes * 2) > strlen($hex))
$hex = str_pad($hex, ($req_bytes * 2), "0", STR_PAD_LEFT);
return $hex;
}
/**
* Converts Decimal to Binary
* This function just calls php's decbin() function, but I
* encapsulated it in this function to keep things uniform
* and have all possible conversion function available in
* the Cipher class
*
* @param integer $dec A decimal number to convert
* @param integer $req_bytes Optional, forces the string to be at least
* $req_bytes in size, this is needed because on occasion left most null bytes
* are dropped in dechex(), causing the string to have a shorter byte
* size than the initial integer.
* @return string A binary representation of the decimal number
*/
public static function dec2Bin($dec, $req_bytes = 0)
{
$hex = self::dec2Hex($dec, $req_bytes);
return self::hex2Bin($hex);
}
/**
* Convert a decimal to a string of bytes
*
* @param integer $dec A decimal number
* @param integer $req_bytes Optional, forces the string to be at least
* $req_bytes in size, this is needed because on occasion left most null bytes
* are dropped in dechex(), causing the string to have a shorter byte
* size than the initial integer.
* @return string A string with the number of bytes equal to $dec
*/
public static function dec2Str($dec, $req_bytes = 0)
{
$hex = self::dec2Hex($dec, $req_bytes);
return self::hex2Str($hex);
}
/**
* XORs two binary strings (representation of binary, ie 01101011),
* assumed to be equal length
*
* @param string $a A string that represents binary
* @param string $b A string that represents binary
* @return string A representation of binary
*/
public static function xorBin($a, $b)
{
$len_a = strlen($a);
$len_b = strlen($b);
$width = $len_a;
// first determine if the two binary strings are the same length,
// and if not get them to the same length
if($len_a > $len_b)
{
$width = $len_a;
$b = str_pad($b, $width, "0", STR_PAD_LEFT);
}
else if($len_a < $len_b)
{
$width = $len_b;
$a = str_pad($a, $width, "0", STR_PAD_LEFT);
}
// fortunately PHP knows how to XOR each byte in a string
// so we don't have to loop to do it
$bin = self::bin2Str($a) ^ self::bin2Str($b);
return self::str2Bin($bin);
}
/**
* ExclusiveOR hex values. Supports an unlimited number of parameters.
* The values are string representations of hex values
* IE: "0a1b2c3d" not 0x0a1b2c3d
*
* @param string Unlimited number parameters, each a string representation of hex
* @return string A string representation of the result in Hex
*/
public static function xorHex()
{
$hex = func_get_args();
$count = func_num_args();
// we need a minimum of 2 values
if($count < 2)
return false;
// first get all hex values to an even number
array_walk($hex, function(&$val, $i){
if(strlen($val) % 2)
$val = "0".$val;
});
$res = 0;
for($i = 0; $i < $count; ++$i)
{
// if this is the first loop, set the 'result' to the first
// hex value
if($i == 0)
$res = $hex[0];
else
{
// to make the code easier to follow
$h1 = $res;
$h2 = $hex[$i];
// get lengths
$len1 = strlen($h1);
$len2 = strlen($h2);
// now check that both hex values are the same length,
// if not pad them with 0's until they are
if($len1 > $len2)
$h2 = str_pad($h2, $len1, "0", STR_PAD_LEFT);
else if($len1 < $len2)
$h1 = str_pad($h1, $len2, "0", STR_PAD_LEFT);
// PHP knows how to XOR each byte in a string, so convert the
// hex to a string, XOR, and convert back
$res = self::hex2Str($h1) ^ self::hex2Str($h2);
$res = self::str2Hex($res);
}
}
return $res;
}
/**
* Forces an integer to be signed
*
* @param integer $int An integer
* @return integer An signed integer
*/
public static function sInt($int)
{
$arr = unpack("i", pack("i", $int));
return $arr[1];
}
/**
* Forces an integer to be unsigned
*
* @param integer $int A signed integer
* @return integer An unsigned integer
*/
public static function uInt($int)
{
$arr = unpack("I", pack("I", $int));
$ret = $arr[1];
// On 32 bit platforms unpack() and pack() do not convert
// from signed to unsigned properly all the time, it will return
// the same negative number given to it, the work around is
// to use sprintf().
// Tested with php 5.3.x on Windows XP & Linux 32bit
if($ret < 0)
$ret = sprintf("%u", $ret) + 0; // convert from string to int
return $ret;
}
/**
* Forces an integer to be a 32 bit signed integer
*
* @param integer $int An integer
* @return integer An signed 32 bit integer
*/
public static function sInt32($int)
{
if(PHP_INT_SIZE === 4) // 32 bit
return self::sInt($int);
else // PHP_INT_SIZE === 8 // 64 bit
{
$arr = unpack("l", pack("l", $int));
return $arr[1];
}
}
/**
* Force an integer to be a 32 bit unsigned integer
*
* @param integer $int An integer
* @return integer An unsigned 32 bit integer
*/
public static function uInt32($int)
{
if(PHP_INT_SIZE === 4) // 32 bit
return self::uInt($int);
else // PHP_INT_SIZE === 8 // 64 bit
{
$arr = unpack("L", pack("L", $int));
return $arr[1];
}
}
/**
* Converts an integer to the value for an signed char
*
* @param integer $int The integer to convert to a signed char
* @return integer A signed integer, representing a signed char
*/
public static function sChar($int)
{
$arr = unpack("c", pack("c", $int));
return $arr[1];
}
/**
* Converts an integer to the value for an unsigned char
*
* @param integer $int The integer to convert to a unsigned char
* @return integer An unsigned integer, representing a unsigned char
*/
public static function uChar($int)
{
$arr = unpack("C", pack("C", $int));
return $arr[1];
}
/**
* Rotates bits Left, appending the bits pushed off the left onto the right
*
* @param integer $n The integer to rotate bits to the left
* @param integer $shifts The number of shifts left to make
* @return integer The resulting value from the rotation
*/
public static function rotBitsLeft32($i, $shifts)
{
if($shifts <= 0)
return $i;
$shifts &= 0x1f; /* higher rotates would not bring anything */
// this is causing problems on 32 bit platform
//return self::uInt32(($i << $shifts) | ($i >> (32 - $shifts)));
// so lets cheat: convert to binary string, rotate left, and
// convert back to decimal
$i = self::dec2Bin(self::uInt32($i), 4);
$i = substr($i, $shifts).substr($i, 0, $shifts);
return self::bin2Dec($i);
}
/**
* Rotates bits right, appending the bits pushed off the right onto the left
*
* @param integer $n The integer to rotate bits to the right
* @param integer $shifts The number of shifts right to make
* @return integer The resulting value from the rotation
*/
public static function rotBitsRight32($i, $shifts)
{
if($shifts <= 0)
return $i;
$shifts &= 0x1f; /* higher rotates would not bring anything */
// this might cause problems on 32 bit platforms since rotBitsLeft32 was
// having a problem with some bit shifts on 32 bits
// return self::uInt32(($i >> $shifts) | ($i << (32 - $shifts)));
// so lets cheat: convert to binary string, rotate right,
// and convert back to decimal
$i = self::dec2Bin($i, 4);
$i = substr($i, (-1 * $shifts)).substr($i, 0, (-1 * $shifts));
return self::bin2Dec($i);
}
/**
* Create a string of random bytes, used for creating an IV
* and a random key. See PHP_Crypt::createKey() and PHP_Crypt::createIV()
* There are 4 ways to auto generate random bytes by setting $src parameter
* PHP_Crypt::RAND - Default, uses mt_rand()
* PHP_Crypt::RAND_DEV_RAND - Unix only, uses /dev/random
* PHP_Crypt::RAND_DEV_URAND - Unix only, uses /dev/urandom
* PHP_Crypt::RAND_WIN_COM - Windows only, uses Microsoft's CAPICOM SDK
*
* @param string $src Optional, Use the $src to create the random bytes
* by default PHP_Crypt::RAND is used when $src is not specified
* @param integer $byte_len The length of the byte string to create
* @return string A random string of bytes
*/
public static function randBytes($src = PHP_Crypt::RAND, $byte_len = PHP_Crypt::RAND_DEFAULT_SZ)
{
$bytes = "";
$err_msg = "";
if($src == PHP_Crypt::RAND_DEV_RAND)
{
if(file_exists(PHP_Crypt::RAND_DEV_RAND))
$bytes = file_get_contents(PHP_CRYPT::RAND_DEV_RAND, false, null, 0, $byte_len);
else
$err_msg = PHP_Crypt::RAND_DEV_RAND." not found";
}
else if($src == PHP_Crypt::RAND_DEV_URAND)
{
if(file_exists(PHP_Crypt::RAND_DEV_URAND))
$bytes = file_get_contents(PHP_CRYPT::RAND_DEV_URAND, false, null, 0, $byte_len);
else
$err_msg = PHP_Crypt::RAND_DEV_URAND." not found";
}
else if($src == PHP_Crypt::RAND_WIN_COM)
{
if(extension_loaded('com_dotnet'))
{
// http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx
try
{
// request a random number in $byte_len bytes, returned
// as base_64 encoded string. This is because PHP munges the
// binary data on Windows
$com = @new \COM("CAPICOM.Utilities.1");
$bytes = $com->GetRandom($byte_len, 0);
}
catch(Exception $e)
{
$err_msg = "Windows COM exception: ".$e->getMessage();
}
if(!$bytes)
$err_msg = "Windows COM failed to create random string of bytes";
}
else
$err_msg = "The COM_DOTNET extension is not loaded";
}
// trigger a warning if something went wrong
if($err_msg != "")
trigger_error("$err_msg. Defaulting to PHP_Crypt::RAND", E_USER_WARNING);
// if the random bytes where not created properly or PHP_Crypt::RAND was
// passed as the $src param, create the bytes using mt_rand(). It's not
// the most secure option but we have no other choice
if(strlen($bytes) < $byte_len)
{
$bytes = "";
// md5() hash a random number to get a 16 byte string, keep looping
// until we have a string as long or longer than the ciphers block size
for($i = 0; ($i * self::HASH_LEN) < $byte_len; ++$i)
$bytes .= md5(mt_rand(), true);
}
// because $bytes may have come from mt_rand() or /dev/urandom which are not
// cryptographically secure, lets add another layer of 'randomness' before
// the final md5() below
$bytes = str_shuffle($bytes);
// md5() the $bytes to add extra randomness. Since md5() only returns
// 16 bytes, we may need to loop to generate a string of $bytes big enough for
// some ciphers which have a block size larger than 16 bytes
$tmp = "";
$loop = ceil(strlen($bytes) / self::HASH_LEN);
for($i = 0; $i < $loop; ++$i)
$tmp .= md5(substr($bytes, ($i * self::HASH_LEN), self::HASH_LEN), true);
// grab the number of bytes equal to the requested $byte_len
return substr($tmp, 0, $byte_len);
}
}
?>