commit 578fb50dd7f32c478d921ab237f4fe7d401c0602
parent e7446c9274a073cb5c0239c624a7c331dea0bfd6
Author: Jan Dankert <devnull@localhost>
Date: Sun, 3 Dec 2017 03:33:57 +0100
Refactoring: Security-Funktionen in ein eigenes "Modul" ausgelagert.
Diffstat:
10 files changed, 575 insertions(+), 526 deletions(-)
diff --git a/action/UserAction.class.php b/action/UserAction.class.php
@@ -226,7 +226,7 @@ class UserAction extends Action
array('totpSecretUrl' => "otpauth://totp/{$issuer}:{$account}?secret={$secret}&issuer={$issuer}",
'hotpSecretUrl' => "otpauth://hotp/{$issuer}:{$account}?secret={$secret}&issuer={$issuer}&counter={$counter}"
)
- + array('totpToken'=>$this->user->getTOTPCode())
+ + array('totpToken'=>Password::getTOTPCode($this->user->otpSecret))
);
$this->setTemplateVar( 'allstyles',$this->user->getAvailableStyles() );
diff --git a/auth/InternalAuth.class.php b/auth/InternalAuth.class.php
@@ -70,7 +70,7 @@ SQL
{
$user = new User($row_user['id']);
$user->load();
- if ( $user->getTOTPCode() == $token )
+ if ( Password::getTOTPCode($user->otpSecret) == $token )
return true;
else
return OR_AUTH_STATUS_TOKEN_NEEDED;
diff --git a/init.php b/init.php
@@ -117,7 +117,9 @@ require_once( OR_SERVICECLASSES_DIR."include.inc.".PHP_EXT );
require_once( OR_AUTHCLASSES_DIR."include.inc.".PHP_EXT );
+require_once( OR_MODULES_DIR."security/require.".PHP_EXT );
require_once( OR_MODULES_DIR."template-engine/require.".PHP_EXT );
+require_once( OR_MODULES_DIR."util/require.".PHP_EXT );
?>
\ No newline at end of file
diff --git a/modules/security/Base2n.class.php b/modules/security/Base2n.class.php
@@ -0,0 +1,303 @@
+<?php
+/**
+ * Binary-to-text PHP Utilities
+ *
+ * @package binary-to-text-php
+ * @link https://github.com/ademarre/binary-to-text-php
+ * @author Andre DeMarre
+ * @copyright 2009-2013 Andre DeMarre
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+/**
+ * Class for binary-to-text encoding with a base of 2^n
+ *
+ * The Base2n class is for binary-to-text conversion. It employs a
+ * generalization of the algorithms used by many encoding schemes that
+ * use a fixed number of bits to encode each character. In other words,
+ * the base is a power of 2.
+ *
+ * Earlier versions of this class were named
+ * FixedBitNotation and FixedBitEncoding.
+ *
+ * @package binary-to-text-php
+ */
+class Base2n
+{
+ protected $_chars;
+ protected $_bitsPerCharacter;
+ protected $_radix;
+ protected $_rightPadFinalBits;
+ protected $_padFinalGroup;
+ protected $_padCharacter;
+ protected $_caseSensitive;
+ protected $_charmap;
+
+ /**
+ * Constructor
+ *
+ * @param integer $bitsPerCharacter Bits to use for each encoded character
+ * @param string $chars Base character alphabet
+ * @param boolean $caseSensitive To decode in a case-sensitive manner
+ * @param boolean $rightPadFinalBits How to encode last character
+ * @param boolean $padFinalGroup Add padding to end of encoded output
+ * @param string $padCharacter Character to use for padding
+ *
+ * @throws InvalidArgumentException for incompatible parameters
+ */
+ public function __construct(
+ $bitsPerCharacter,
+ $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_',
+ $caseSensitive = TRUE, $rightPadFinalBits = FALSE,
+ $padFinalGroup = FALSE, $padCharacter = '=')
+ {
+ // Ensure validity of $chars
+ if (!is_string($chars) || ($charLength = strlen($chars)) < 2) {
+ throw new InvalidArgumentException('$chars must be a string of at least two characters');
+ }
+
+ // Ensure validity of $padCharacter
+ if ($padFinalGroup) {
+ if (!is_string($padCharacter) || !isset($padCharacter[0])) {
+ throw new InvalidArgumentException('$padCharacter must be a string of one character');
+ }
+
+ if ($caseSensitive) {
+ $padCharFound = strpos($chars, $padCharacter[0]);
+ } else {
+ $padCharFound = stripos($chars, $padCharacter[0]);
+ }
+
+ if ($padCharFound !== FALSE) {
+ throw new InvalidArgumentException('$padCharacter can not be a member of $chars');
+ }
+ }
+
+ // Ensure validity of $bitsPerCharacter
+ if (!is_int($bitsPerCharacter)) {
+ throw new InvalidArgumentException('$bitsPerCharacter must be an integer');
+ }
+
+ if ($bitsPerCharacter < 1) {
+ // $bitsPerCharacter must be at least 1
+ throw new InvalidArgumentException('$bitsPerCharacter can not be less than 1');
+
+ } elseif ($charLength < 1 << $bitsPerCharacter) {
+ // Character length of $chars is too small for $bitsPerCharacter
+ // Find greatest acceptable value of $bitsPerCharacter
+ $bitsPerCharacter = 1;
+ $radix = 2;
+
+ while ($charLength >= ($radix <<= 1) && $bitsPerCharacter < 8) {
+ $bitsPerCharacter++;
+ }
+
+ $radix >>= 1;
+ throw new InvalidArgumentException(
+ '$bitsPerCharacter can not be more than ' . $bitsPerCharacter
+ . ' given $chars length of ' . $charLength
+ . ' (max radix ' . $radix . ')');
+
+ } elseif ($bitsPerCharacter > 8) {
+ // $bitsPerCharacter must not be greater than 8
+ throw new InvalidArgumentException('$bitsPerCharacter can not be greater than 8');
+
+ } else {
+ $radix = 1 << $bitsPerCharacter;
+ }
+
+ $this->_chars = $chars;
+ $this->_bitsPerCharacter = $bitsPerCharacter;
+ $this->_radix = $radix;
+ $this->_rightPadFinalBits = $rightPadFinalBits;
+ $this->_padFinalGroup = $padFinalGroup;
+ $this->_padCharacter = $padCharacter[0];
+ $this->_caseSensitive = $caseSensitive;
+ }
+
+ /**
+ * Encode a string
+ *
+ * @param string $rawString Binary data to encode
+ * @return string
+ */
+ public function encode($rawString)
+ {
+ // Unpack string into an array of bytes
+ $bytes = unpack('C*', $rawString);
+ $byteCount = count($bytes);
+
+ $encodedString = '';
+ $byte = array_shift($bytes);
+ $bitsRead = 0;
+ $oldBits = 0;
+
+ $chars = $this->_chars;
+ $bitsPerCharacter = $this->_bitsPerCharacter;
+ $rightPadFinalBits = $this->_rightPadFinalBits;
+ $padFinalGroup = $this->_padFinalGroup;
+ $padCharacter = $this->_padCharacter;
+
+ $charsPerByte = 8 / $bitsPerCharacter;
+ $encodedLength = $byteCount * $charsPerByte;
+
+ // Generate encoded output; each loop produces one encoded character
+ for ($c = 0; $c < $encodedLength; $c++) {
+
+ // Get the bits needed for this encoded character
+ if ($bitsRead + $bitsPerCharacter > 8) {
+ // Not enough bits remain in this byte for the current character
+ // Save the remaining bits before getting the next byte
+ $oldBitCount = 8 - $bitsRead;
+ $oldBits = $byte ^ ($byte >> $oldBitCount << $oldBitCount);
+ $newBitCount = $bitsPerCharacter - $oldBitCount;
+
+ if (!$bytes) {
+ // Last bits; match final character and exit loop
+ if ($rightPadFinalBits) $oldBits <<= $newBitCount;
+ $encodedString .= $chars[$oldBits];
+
+ if ($padFinalGroup) {
+ // Array of the lowest common multiples of $bitsPerCharacter and 8, divided by 8
+ $lcmMap = array(1 => 1, 2 => 1, 3 => 3, 4 => 1, 5 => 5, 6 => 3, 7 => 7, 8 => 1);
+ $bytesPerGroup = $lcmMap[$bitsPerCharacter];
+ $pads = $bytesPerGroup * $charsPerByte - ceil((strlen($rawString) % $bytesPerGroup) * $charsPerByte);
+ $encodedString .= str_repeat($padCharacter, $pads);
+ }
+
+ break;
+ }
+
+ // Get next byte
+ $byte = array_shift($bytes);
+ $bitsRead = 0;
+
+ } else {
+ $oldBitCount = 0;
+ $newBitCount = $bitsPerCharacter;
+ }
+
+ // Read only the needed bits from this byte
+ $bits = $byte >> 8 - ($bitsRead + ($newBitCount));
+ $bits ^= $bits >> $newBitCount << $newBitCount;
+ $bitsRead += $newBitCount;
+
+ if ($oldBitCount) {
+ // Bits come from seperate bytes, add $oldBits to $bits
+ $bits = ($oldBits << $newBitCount) | $bits;
+ }
+
+ $encodedString .= $chars[$bits];
+ }
+
+ return $encodedString;
+ }
+
+ /**
+ * Decode a string
+ *
+ * @param string $encodedString Data to decode
+ * @param boolean $strict Returns NULL if $encodedString contains an undecodable character
+ * @return string
+ */
+ public function decode($encodedString, $strict = FALSE)
+ {
+ if (!$encodedString || !is_string($encodedString)) {
+ // Empty string, nothing to decode
+ return '';
+ }
+
+ $chars = $this->_chars;
+ $bitsPerCharacter = $this->_bitsPerCharacter;
+ $radix = $this->_radix;
+ $rightPadFinalBits = $this->_rightPadFinalBits;
+ $padFinalGroup = $this->_padFinalGroup;
+ $padCharacter = $this->_padCharacter;
+ $caseSensitive = $this->_caseSensitive;
+
+ // Get index of encoded characters
+ if ($this->_charmap) {
+ $charmap = $this->_charmap;
+
+ } else {
+ $charmap = array();
+
+ for ($i = 0; $i < $radix; $i++) {
+ $charmap[$chars[$i]] = $i;
+ }
+
+ $this->_charmap = $charmap;
+ }
+
+ // The last encoded character is $encodedString[$lastNotatedIndex]
+ $lastNotatedIndex = strlen($encodedString) - 1;
+
+ // Remove trailing padding characters
+ if ($padFinalGroup) {
+ while ($encodedString[$lastNotatedIndex] === $padCharacter) {
+ $encodedString = substr($encodedString, 0, $lastNotatedIndex);
+ $lastNotatedIndex--;
+ }
+ }
+
+ $rawString = '';
+ $byte = 0;
+ $bitsWritten = 0;
+
+ // Convert each encoded character to a series of unencoded bits
+ for ($c = 0; $c <= $lastNotatedIndex; $c++) {
+
+ if (!$caseSensitive && !isset($charmap[$encodedString[$c]])) {
+ // Encoded character was not found; try other case
+ if (isset($charmap[$cUpper = strtoupper($encodedString[$c])])) {
+ $charmap[$encodedString[$c]] = $charmap[$cUpper];
+
+ } elseif (isset($charmap[$cLower = strtolower($encodedString[$c])])) {
+ $charmap[$encodedString[$c]] = $charmap[$cLower];
+ }
+ }
+
+ if (isset($charmap[$encodedString[$c]])) {
+ $bitsNeeded = 8 - $bitsWritten;
+ $unusedBitCount = $bitsPerCharacter - $bitsNeeded;
+
+ // Get the new bits ready
+ if ($bitsNeeded > $bitsPerCharacter) {
+ // New bits aren't enough to complete a byte; shift them left into position
+ $newBits = $charmap[$encodedString[$c]] << $bitsNeeded - $bitsPerCharacter;
+ $bitsWritten += $bitsPerCharacter;
+
+ } elseif ($c !== $lastNotatedIndex || $rightPadFinalBits) {
+ // Zero or more too many bits to complete a byte; shift right
+ $newBits = $charmap[$encodedString[$c]] >> $unusedBitCount;
+ $bitsWritten = 8; //$bitsWritten += $bitsNeeded;
+
+ } else {
+ // Final bits don't need to be shifted
+ $newBits = $charmap[$encodedString[$c]];
+ $bitsWritten = 8;
+ }
+
+ $byte |= $newBits;
+
+ if ($bitsWritten === 8 || $c === $lastNotatedIndex) {
+ // Byte is ready to be written
+ $rawString .= pack('C', $byte);
+
+ if ($c !== $lastNotatedIndex) {
+ // Start the next byte
+ $bitsWritten = $unusedBitCount;
+ $byte = ($charmap[$encodedString[$c]] ^ ($newBits << $unusedBitCount)) << 8 - $bitsWritten;
+ }
+ }
+
+ } elseif ($strict) {
+ // Unable to decode character; abort
+ return NULL;
+ }
+ }
+
+ return $rawString;
+ }
+}
+?>+
\ No newline at end of file
diff --git a/modules/security/Password.class.php b/modules/security/Password.class.php
@@ -0,0 +1,253 @@
+<?php
+
+define('OR_PASSWORD_ALGO_PLAIN',0);
+define('OR_PASSWORD_ALGO_CRYPT',1);
+define('OR_PASSWORD_ALGO_MD5' ,2);
+define('OR_PASSWORD_ALGO_PHP_PASSWORD_HASH',3);
+define('OR_PASSWORD_ALGO_SHA1' ,4);
+
+
+/**
+ * Sicherheitsfunktionen für Passwörter.
+ *
+ * @author dankert
+ *
+ */
+class Password
+{
+ /**
+ * Ermittelt den bestverfügbarsten hash-Algorhytmus.
+ */
+ static public function bestAlgoAvailable()
+ {
+ if ( function_exists('password_hash') )
+ {
+ return OR_PASSWORD_ALGO_PHP_PASSWORD_HASH;
+ }
+ elseif ( function_exists('crypt') && defined('CRYPT_BLOWFISH') && CRYPT_BLOWFISH == 1 )
+ {
+ return OR_PASSWORD_ALGO_CRYPT;
+ }
+ elseif ( function_exists('sha1') )
+ {
+ return OR_PASSWORD_ALGO_SHA1;
+ }
+ elseif ( function_exists('md5') )
+ {
+ return OR_PASSWORD_ALGO_MD5;
+ }
+ else
+ {
+ return OR_PASSWORD_ALGO_PLAIN;
+ }
+ }
+
+
+
+ /**
+ * Hashen eines Kennwortes mit Bcrypt (bzw. MD5).
+ * @param $password
+ * @param $algo int Algo
+ * @param $cost Kostenfaktor: Eine Ganzzahl von 4 bis 31.
+ */
+ static public function hash( $password,$algo,$cost=10 )
+ {
+ switch( $algo )
+ {
+ case OR_PASSWORD_ALGO_PHP_PASSWORD_HASH:
+
+ return password_hash( $password, PASSWORD_BCRYPT,array('cost'=>$cost) );
+
+ case OR_PASSWORD_ALGO_CRYPT:
+
+ $salt = Password::randomHexString(10); // this should be cryptographically safe.
+
+ if ( version_compare(PHP_VERSION, '5.3.7') >= 0 )
+ $algo = '2y';
+ else
+ $algo = '2a';
+
+ // Kostenfaktor muss zwischen '04' und '31' (jeweils einschließlich) liegen.
+ $cost = max(min($cost,31),4);
+ $cost = str_pad($cost, 2, '0', STR_PAD_LEFT);
+
+ return crypt($password,'$'.$algo.'$'.$cost.'$'.$salt.'$');
+
+ case OR_PASSWORD_ALGO_MD5:
+ return md5($password); // ooold.
+
+ case OR_PASSWORD_ALGO_SHA1:
+ return sha1($password); //
+
+ case OR_PASSWORD_ALGO_PLAIN:
+ return $password; // you want it, you get it.
+ }
+ }
+
+ /**
+ * Prüft das Kennwort gegen einen gespeicherten Hashwert.
+ *
+ * @param String $password Passwort
+ * @param String $hash Hash
+ * @return boolean true, falls das Passwort dem Hashwert entspricht.
+ */
+ static public function check( $password,$hash,$algo )
+ {
+ switch( $algo )
+ {
+ case OR_PASSWORD_ALGO_PHP_PASSWORD_HASH:
+ // This is 'timing attack safe' as the documentation says.
+ return password_verify($password,$hash);
+
+ case OR_PASSWORD_ALGO_CRYPT:
+
+ if ( function_exists('crypt') )
+ {
+ // Workaround: Die Spalte 'password' war frueher nur 50 Stellen lang, daher
+ // wird der mit crypt() erzeugte Hash auf die Länge des gespeicherten Hashes
+ // gekürzt. Mit aktuellen Versionen gespeicherte Hashes haben die volle Länge.
+ return Password::equals( $hash, substr(crypt($password,$hash),0,strlen($hash)) );
+ }
+ else
+ {
+ throw new LogicException("Modular crypt format is not supported by this PHP version (no function 'crypt()')");
+ }
+
+ case OR_PASSWORD_ALGO_SHA1:
+ return Password::equals( $hash, sha1($password) );
+
+ case OR_PASSWORD_ALGO_MD5:
+ return Password::equals( $hash, md5($password) );
+
+ case OR_PASSWORD_ALGO_PLAIN:
+ return Password::equals( $hash, $password );
+ }
+ }
+
+
+ /**
+ * Creates cryptographic safe random bytes in HEX format.
+ * @param int $bytesCount
+ * @return string HEX
+ */
+ static public function randomHexString( $bytesCount )
+ {
+ return bin2hex( Password::randomBytes($bytesCount) );
+ }
+
+
+ /**
+ * Creates a cryptographic safe number.
+ * @param int $bytesCount
+ * @return int
+ */
+ static public function randomNumber( $bytesCount )
+ {
+ return bindec( Password::randomBytes($bytesCount) );
+ }
+
+
+ /**
+ * Creates cryptographic safe random bytes.
+ * @param int $bytesCount
+ * @return string Binary bytes
+ */
+ static public function randomBytes( $bytesCount )
+ {
+ if ( function_exists('random_bytes') )
+ {
+ return random_bytes($bytesCount);
+ }
+ elseif ( function_exists('openssl_random_pseudo_bytes') )
+ {
+ return openssl_random_pseudo_bytes($bytesCount);
+ }
+ else
+ {
+ // This fallback is NOT cryptographic safe!
+ $buf = '';
+ for ($i = 0; $i < $length; ++$i)
+ {
+ $buf .= chr(mt_rand(0, 255));
+ }
+ return $buf;
+ }
+ }
+
+
+ /**
+ * Time-safe Compare of 2 strings.
+ *
+ * @param String $known
+ * @param String $user
+ * @return boolean true, if equal
+ */
+ static private function equals( $known, $user )
+ {
+ if( function_exists('hash_equals'))
+ {
+ return hash_equals($known, $user);
+ }
+ else
+ {
+ if ( strlen($known) != strlen($user) )
+ {
+ return false;
+ }
+ else
+ {
+ $res = $known ^ $user;
+ $ret = 0;
+ for($i = strlen($res) - 1; $i >= 0; $i--) $ret |= ord($res[$i]);
+ return !$ret;
+ }
+ }
+ }
+
+
+ /**
+ * Cryptographic delay of execution.
+ * Delay is from 0 to 168 milliseconds, Steps of 10 nanoseconds(!), which would be very heavy to attack over a network.
+ */
+ static public function delay()
+ {
+ time_nanosleep(0, Password::randomNumber(3)*10); // delay: 0-167772150ns (= 0-~168ms)
+ }
+
+
+
+
+
+ /**
+ * Calculate the code, with given secret and point in time.
+ *
+ * @param string $secret
+ * @param int|null $timeSlice
+ *
+ * @return string
+ */
+ public static function getTOTPCode( $secret )
+ {
+ $codeLength = 6;
+ $timeSlice = floor(time() / 30);
+ $secretkey = @hex2bin($secret);
+ // Pack time into binary string
+ $time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice);
+ // Hash it with users secret key
+ $hm = hash_hmac('SHA1', $time, $secretkey, true);
+ // Use last nipple of result as index/offset
+ $offset = ord(substr($hm, -1)) & 0x0F;
+ // grab 4 bytes of the result
+ $hashpart = substr($hm, $offset, 4);
+ // Unpak binary value
+ $value = unpack('N', $hashpart);
+ $value = $value[1];
+ // Only 32 bits
+ $value = $value & 0x7FFFFFFF;
+ $modulo = pow(10, $codeLength);
+ return str_pad($value % $modulo, $codeLength, '0', STR_PAD_LEFT);
+ }
+
+
+}
+?>+
\ No newline at end of file
diff --git a/modules/security/require.php b/modules/security/require.php
@@ -0,0 +1,6 @@
+<?php
+
+include( dirname(__FILE__) . '/Base2n.class.php');
+include( dirname(__FILE__) . '/Password.class.php');
+
+?>+
\ No newline at end of file
diff --git a/modules/util/require.php b/modules/util/require.php
@@ -0,0 +1,5 @@
+<?php
+
+//include( dirname(__FILE__) . '/.class.php');
+
+?>+
\ No newline at end of file
diff --git a/util/Base2n.class.php b/util/Base2n.class.php
@@ -1,303 +0,0 @@
-<?php
-/**
- * Binary-to-text PHP Utilities
- *
- * @package binary-to-text-php
- * @link https://github.com/ademarre/binary-to-text-php
- * @author Andre DeMarre
- * @copyright 2009-2013 Andre DeMarre
- * @license http://opensource.org/licenses/MIT MIT
- */
-
-/**
- * Class for binary-to-text encoding with a base of 2^n
- *
- * The Base2n class is for binary-to-text conversion. It employs a
- * generalization of the algorithms used by many encoding schemes that
- * use a fixed number of bits to encode each character. In other words,
- * the base is a power of 2.
- *
- * Earlier versions of this class were named
- * FixedBitNotation and FixedBitEncoding.
- *
- * @package binary-to-text-php
- */
-class Base2n
-{
- protected $_chars;
- protected $_bitsPerCharacter;
- protected $_radix;
- protected $_rightPadFinalBits;
- protected $_padFinalGroup;
- protected $_padCharacter;
- protected $_caseSensitive;
- protected $_charmap;
-
- /**
- * Constructor
- *
- * @param integer $bitsPerCharacter Bits to use for each encoded character
- * @param string $chars Base character alphabet
- * @param boolean $caseSensitive To decode in a case-sensitive manner
- * @param boolean $rightPadFinalBits How to encode last character
- * @param boolean $padFinalGroup Add padding to end of encoded output
- * @param string $padCharacter Character to use for padding
- *
- * @throws InvalidArgumentException for incompatible parameters
- */
- public function __construct(
- $bitsPerCharacter,
- $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_',
- $caseSensitive = TRUE, $rightPadFinalBits = FALSE,
- $padFinalGroup = FALSE, $padCharacter = '=')
- {
- // Ensure validity of $chars
- if (!is_string($chars) || ($charLength = strlen($chars)) < 2) {
- throw new InvalidArgumentException('$chars must be a string of at least two characters');
- }
-
- // Ensure validity of $padCharacter
- if ($padFinalGroup) {
- if (!is_string($padCharacter) || !isset($padCharacter[0])) {
- throw new InvalidArgumentException('$padCharacter must be a string of one character');
- }
-
- if ($caseSensitive) {
- $padCharFound = strpos($chars, $padCharacter[0]);
- } else {
- $padCharFound = stripos($chars, $padCharacter[0]);
- }
-
- if ($padCharFound !== FALSE) {
- throw new InvalidArgumentException('$padCharacter can not be a member of $chars');
- }
- }
-
- // Ensure validity of $bitsPerCharacter
- if (!is_int($bitsPerCharacter)) {
- throw new InvalidArgumentException('$bitsPerCharacter must be an integer');
- }
-
- if ($bitsPerCharacter < 1) {
- // $bitsPerCharacter must be at least 1
- throw new InvalidArgumentException('$bitsPerCharacter can not be less than 1');
-
- } elseif ($charLength < 1 << $bitsPerCharacter) {
- // Character length of $chars is too small for $bitsPerCharacter
- // Find greatest acceptable value of $bitsPerCharacter
- $bitsPerCharacter = 1;
- $radix = 2;
-
- while ($charLength >= ($radix <<= 1) && $bitsPerCharacter < 8) {
- $bitsPerCharacter++;
- }
-
- $radix >>= 1;
- throw new InvalidArgumentException(
- '$bitsPerCharacter can not be more than ' . $bitsPerCharacter
- . ' given $chars length of ' . $charLength
- . ' (max radix ' . $radix . ')');
-
- } elseif ($bitsPerCharacter > 8) {
- // $bitsPerCharacter must not be greater than 8
- throw new InvalidArgumentException('$bitsPerCharacter can not be greater than 8');
-
- } else {
- $radix = 1 << $bitsPerCharacter;
- }
-
- $this->_chars = $chars;
- $this->_bitsPerCharacter = $bitsPerCharacter;
- $this->_radix = $radix;
- $this->_rightPadFinalBits = $rightPadFinalBits;
- $this->_padFinalGroup = $padFinalGroup;
- $this->_padCharacter = $padCharacter[0];
- $this->_caseSensitive = $caseSensitive;
- }
-
- /**
- * Encode a string
- *
- * @param string $rawString Binary data to encode
- * @return string
- */
- public function encode($rawString)
- {
- // Unpack string into an array of bytes
- $bytes = unpack('C*', $rawString);
- $byteCount = count($bytes);
-
- $encodedString = '';
- $byte = array_shift($bytes);
- $bitsRead = 0;
- $oldBits = 0;
-
- $chars = $this->_chars;
- $bitsPerCharacter = $this->_bitsPerCharacter;
- $rightPadFinalBits = $this->_rightPadFinalBits;
- $padFinalGroup = $this->_padFinalGroup;
- $padCharacter = $this->_padCharacter;
-
- $charsPerByte = 8 / $bitsPerCharacter;
- $encodedLength = $byteCount * $charsPerByte;
-
- // Generate encoded output; each loop produces one encoded character
- for ($c = 0; $c < $encodedLength; $c++) {
-
- // Get the bits needed for this encoded character
- if ($bitsRead + $bitsPerCharacter > 8) {
- // Not enough bits remain in this byte for the current character
- // Save the remaining bits before getting the next byte
- $oldBitCount = 8 - $bitsRead;
- $oldBits = $byte ^ ($byte >> $oldBitCount << $oldBitCount);
- $newBitCount = $bitsPerCharacter - $oldBitCount;
-
- if (!$bytes) {
- // Last bits; match final character and exit loop
- if ($rightPadFinalBits) $oldBits <<= $newBitCount;
- $encodedString .= $chars[$oldBits];
-
- if ($padFinalGroup) {
- // Array of the lowest common multiples of $bitsPerCharacter and 8, divided by 8
- $lcmMap = array(1 => 1, 2 => 1, 3 => 3, 4 => 1, 5 => 5, 6 => 3, 7 => 7, 8 => 1);
- $bytesPerGroup = $lcmMap[$bitsPerCharacter];
- $pads = $bytesPerGroup * $charsPerByte - ceil((strlen($rawString) % $bytesPerGroup) * $charsPerByte);
- $encodedString .= str_repeat($padCharacter, $pads);
- }
-
- break;
- }
-
- // Get next byte
- $byte = array_shift($bytes);
- $bitsRead = 0;
-
- } else {
- $oldBitCount = 0;
- $newBitCount = $bitsPerCharacter;
- }
-
- // Read only the needed bits from this byte
- $bits = $byte >> 8 - ($bitsRead + ($newBitCount));
- $bits ^= $bits >> $newBitCount << $newBitCount;
- $bitsRead += $newBitCount;
-
- if ($oldBitCount) {
- // Bits come from seperate bytes, add $oldBits to $bits
- $bits = ($oldBits << $newBitCount) | $bits;
- }
-
- $encodedString .= $chars[$bits];
- }
-
- return $encodedString;
- }
-
- /**
- * Decode a string
- *
- * @param string $encodedString Data to decode
- * @param boolean $strict Returns NULL if $encodedString contains an undecodable character
- * @return string
- */
- public function decode($encodedString, $strict = FALSE)
- {
- if (!$encodedString || !is_string($encodedString)) {
- // Empty string, nothing to decode
- return '';
- }
-
- $chars = $this->_chars;
- $bitsPerCharacter = $this->_bitsPerCharacter;
- $radix = $this->_radix;
- $rightPadFinalBits = $this->_rightPadFinalBits;
- $padFinalGroup = $this->_padFinalGroup;
- $padCharacter = $this->_padCharacter;
- $caseSensitive = $this->_caseSensitive;
-
- // Get index of encoded characters
- if ($this->_charmap) {
- $charmap = $this->_charmap;
-
- } else {
- $charmap = array();
-
- for ($i = 0; $i < $radix; $i++) {
- $charmap[$chars[$i]] = $i;
- }
-
- $this->_charmap = $charmap;
- }
-
- // The last encoded character is $encodedString[$lastNotatedIndex]
- $lastNotatedIndex = strlen($encodedString) - 1;
-
- // Remove trailing padding characters
- if ($padFinalGroup) {
- while ($encodedString[$lastNotatedIndex] === $padCharacter) {
- $encodedString = substr($encodedString, 0, $lastNotatedIndex);
- $lastNotatedIndex--;
- }
- }
-
- $rawString = '';
- $byte = 0;
- $bitsWritten = 0;
-
- // Convert each encoded character to a series of unencoded bits
- for ($c = 0; $c <= $lastNotatedIndex; $c++) {
-
- if (!$caseSensitive && !isset($charmap[$encodedString[$c]])) {
- // Encoded character was not found; try other case
- if (isset($charmap[$cUpper = strtoupper($encodedString[$c])])) {
- $charmap[$encodedString[$c]] = $charmap[$cUpper];
-
- } elseif (isset($charmap[$cLower = strtolower($encodedString[$c])])) {
- $charmap[$encodedString[$c]] = $charmap[$cLower];
- }
- }
-
- if (isset($charmap[$encodedString[$c]])) {
- $bitsNeeded = 8 - $bitsWritten;
- $unusedBitCount = $bitsPerCharacter - $bitsNeeded;
-
- // Get the new bits ready
- if ($bitsNeeded > $bitsPerCharacter) {
- // New bits aren't enough to complete a byte; shift them left into position
- $newBits = $charmap[$encodedString[$c]] << $bitsNeeded - $bitsPerCharacter;
- $bitsWritten += $bitsPerCharacter;
-
- } elseif ($c !== $lastNotatedIndex || $rightPadFinalBits) {
- // Zero or more too many bits to complete a byte; shift right
- $newBits = $charmap[$encodedString[$c]] >> $unusedBitCount;
- $bitsWritten = 8; //$bitsWritten += $bitsNeeded;
-
- } else {
- // Final bits don't need to be shifted
- $newBits = $charmap[$encodedString[$c]];
- $bitsWritten = 8;
- }
-
- $byte |= $newBits;
-
- if ($bitsWritten === 8 || $c === $lastNotatedIndex) {
- // Byte is ready to be written
- $rawString .= pack('C', $byte);
-
- if ($c !== $lastNotatedIndex) {
- // Start the next byte
- $bitsWritten = $unusedBitCount;
- $byte = ($charmap[$encodedString[$c]] ^ ($newBits << $unusedBitCount)) << 8 - $bitsWritten;
- }
- }
-
- } elseif ($strict) {
- // Unable to decode character; abort
- return NULL;
- }
- }
-
- return $rawString;
- }
-}
-?>-
\ No newline at end of file
diff --git a/util/Password.class.php b/util/Password.class.php
@@ -1,217 +0,0 @@
-<?php
-
-define('OR_PASSWORD_ALGO_PLAIN',0);
-define('OR_PASSWORD_ALGO_CRYPT',1);
-define('OR_PASSWORD_ALGO_MD5' ,2);
-define('OR_PASSWORD_ALGO_PHP_PASSWORD_HASH',3);
-define('OR_PASSWORD_ALGO_SHA1' ,4);
-
-
-/**
- * Sicherheitsfunktionen für Passwörter.
- *
- * @author dankert
- *
- */
-class Password
-{
- /**
- * Ermittelt den bestverfügbarsten hash-Algorhytmus.
- */
- static public function bestAlgoAvailable()
- {
- if ( function_exists('password_hash') )
- {
- return OR_PASSWORD_ALGO_PHP_PASSWORD_HASH;
- }
- elseif ( function_exists('crypt') && defined('CRYPT_BLOWFISH') && CRYPT_BLOWFISH == 1 )
- {
- return OR_PASSWORD_ALGO_CRYPT;
- }
- elseif ( function_exists('sha1') )
- {
- return OR_PASSWORD_ALGO_SHA1;
- }
- elseif ( function_exists('md5') )
- {
- return OR_PASSWORD_ALGO_MD5;
- }
- else
- {
- return OR_PASSWORD_ALGO_PLAIN;
- }
- }
-
-
-
- /**
- * Hashen eines Kennwortes mit Bcrypt (bzw. MD5).
- * @param $password
- * @param $algo int Algo
- * @param $cost Kostenfaktor: Eine Ganzzahl von 4 bis 31.
- */
- static public function hash( $password,$algo,$cost=10 )
- {
- switch( $algo )
- {
- case OR_PASSWORD_ALGO_PHP_PASSWORD_HASH:
-
- return password_hash( $password, PASSWORD_BCRYPT,array('cost'=>$cost) );
-
- case OR_PASSWORD_ALGO_CRYPT:
-
- $salt = Password::randomHexString(10); // this should be cryptographically safe.
-
- if ( version_compare(PHP_VERSION, '5.3.7') >= 0 )
- $algo = '2y';
- else
- $algo = '2a';
-
- // Kostenfaktor muss zwischen '04' und '31' (jeweils einschließlich) liegen.
- $cost = max(min($cost,31),4);
- $cost = str_pad($cost, 2, '0', STR_PAD_LEFT);
-
- return crypt($password,'$'.$algo.'$'.$cost.'$'.$salt.'$');
-
- case OR_PASSWORD_ALGO_MD5:
- return md5($password); // ooold.
-
- case OR_PASSWORD_ALGO_SHA1:
- return sha1($password); //
-
- case OR_PASSWORD_ALGO_PLAIN:
- return $password; // you want it, you get it.
- }
- }
-
- /**
- * Prüft das Kennwort gegen einen gespeicherten Hashwert.
- *
- * @param String $password Passwort
- * @param String $hash Hash
- * @return boolean true, falls das Passwort dem Hashwert entspricht.
- */
- static public function check( $password,$hash,$algo )
- {
- switch( $algo )
- {
- case OR_PASSWORD_ALGO_PHP_PASSWORD_HASH:
- // This is 'timing attack safe' as the documentation says.
- return password_verify($password,$hash);
-
- case OR_PASSWORD_ALGO_CRYPT:
-
- if ( function_exists('crypt') )
- {
- // Workaround: Die Spalte 'password' war frueher nur 50 Stellen lang, daher
- // wird der mit crypt() erzeugte Hash auf die Länge des gespeicherten Hashes
- // gekürzt. Mit aktuellen Versionen gespeicherte Hashes haben die volle Länge.
- return Password::equals( $hash, substr(crypt($password,$hash),0,strlen($hash)) );
- }
- else
- {
- throw new LogicException("Modular crypt format is not supported by this PHP version (no function 'crypt()')");
- }
-
- case OR_PASSWORD_ALGO_SHA1:
- return Password::equals( $hash, sha1($password) );
-
- case OR_PASSWORD_ALGO_MD5:
- return Password::equals( $hash, md5($password) );
-
- case OR_PASSWORD_ALGO_PLAIN:
- return Password::equals( $hash, $password );
- }
- }
-
-
- /**
- * Creates cryptographic safe random bytes in HEX format.
- * @param int $bytesCount
- * @return string HEX
- */
- static public function randomHexString( $bytesCount )
- {
- return bin2hex( Password::randomBytes($bytesCount) );
- }
-
-
- /**
- * Creates a cryptographic safe number.
- * @param int $bytesCount
- * @return int
- */
- static public function randomNumber( $bytesCount )
- {
- return bindec( Password::randomBytes($bytesCount) );
- }
-
-
- /**
- * Creates cryptographic safe random bytes.
- * @param int $bytesCount
- * @return string Binary bytes
- */
- static public function randomBytes( $bytesCount )
- {
- if ( function_exists('random_bytes') )
- {
- return random_bytes($bytesCount);
- }
- elseif ( function_exists('openssl_random_pseudo_bytes') )
- {
- return openssl_random_pseudo_bytes($bytesCount);
- }
- else
- {
- // This fallback is NOT cryptographic safe!
- $buf = '';
- for ($i = 0; $i < $length; ++$i)
- {
- $buf .= chr(mt_rand(0, 255));
- }
- return $buf;
- }
- }
-
-
- /**
- * Time-safe Compare of 2 strings.
- *
- * @param String $known
- * @param String $user
- * @return boolean true, if equal
- */
- static private function equals( $known, $user )
- {
- if( function_exists('hash_equals'))
- {
- return hash_equals($known, $user);
- }
- else
- {
- if ( strlen($known) != strlen($user) )
- {
- return false;
- }
- else
- {
- $res = $known ^ $user;
- $ret = 0;
- for($i = strlen($res) - 1; $i >= 0; $i--) $ret |= ord($res[$i]);
- return !$ret;
- }
- }
- }
-
-
- /**
- * Cryptographic delay of execution.
- * Delay is from 0 to 168 milliseconds, Steps of 10 nanoseconds(!), which would be very heavy to attack over a network.
- */
- static public function delay()
- {
- time_nanosleep(0, Password::randomNumber(3)*10); // delay: 0-167772150ns (= 0-~168ms)
- }
-}
-?>-
\ No newline at end of file
diff --git a/util/include.inc.php b/util/include.inc.php
@@ -1,7 +1,6 @@
<?php
require_once( OR_SERVICECLASSES_DIR."GlobalFunctions.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."Base2n.class.".PHP_EXT );
require_once( OR_SERVICECLASSES_DIR."Http.class.".PHP_EXT );
require_once( OR_SERVICECLASSES_DIR."Html.class.".PHP_EXT );
require_once( OR_SERVICECLASSES_DIR."Text.class.".PHP_EXT );
@@ -12,7 +11,6 @@ require_once( OR_SERVICECLASSES_DIR."FileUtils.class.".PHP_EXT );
require_once( OR_SERVICECLASSES_DIR."JSON.class.".PHP_EXT );
require_once( OR_SERVICECLASSES_DIR."Less.".PHP_EXT );
require_once( OR_SERVICECLASSES_DIR."JSqueeze.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."Password.class.".PHP_EXT );
require_once( OR_SERVICECLASSES_DIR."Spyc.class.".PHP_EXT );
require_once( OR_SERVICECLASSES_DIR."exception/OpenRatException.class.".PHP_EXT );
require_once( OR_SERVICECLASSES_DIR."exception/SecurityException.class.".PHP_EXT );