Use PHP polyfills

This commit is contained in:
Lukas Reschke 2015-12-11 06:17:47 +01:00
parent acce1638e5
commit f3360d51c6
14 changed files with 39 additions and 257 deletions

@ -1 +1 @@
Subproject commit a7b34d6f831c8fa363f389d27acd0150128fc0b9
Subproject commit 56934b1fc0f15760690c7a28c6c6429ce6ce6bef

View file

@ -30,7 +30,6 @@ use OC\Encryption\Exceptions\DecryptionFailedException;
use OC\Encryption\Exceptions\EncryptionFailedException;
use OCA\Encryption\Exceptions\MultiKeyDecryptException;
use OCA\Encryption\Exceptions\MultiKeyEncryptException;
use OCA\Encryption\Vendor\PBKDF2Fallback;
use OCP\Encryption\Exceptions\GenericEncryptionException;
use OCP\IConfig;
use OCP\ILogger;
@ -293,28 +292,14 @@ class Crypt {
$salt = hash('sha256', $uid . $instanceId . $instanceSecret, true);
$keySize = $this->getKeySize($cipher);
if (function_exists('hash_pbkdf2')) {
$hash = hash_pbkdf2(
'sha256',
$password,
$salt,
100000,
$keySize,
true
);
} else {
// fallback to 3rdparty lib for PHP <= 5.4.
// FIXME: Can be removed as soon as support for PHP 5.4 was dropped
$fallback = new PBKDF2Fallback();
$hash = $fallback->pbkdf2(
'sha256',
$password,
$salt,
100000,
$keySize,
true
);
}
$hash = hash_pbkdf2(
'sha256',
$password,
$salt,
100000,
$keySize,
true
);
return $hash;
}

View file

@ -1,87 +0,0 @@
<?php
/* Note; This class can be removed as soon as we drop PHP 5.4 support.
*
*
* Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
* Copyright (c) 2013, Taylor Hornby
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OCA\Encryption\Vendor;
class PBKDF2Fallback {
/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
*
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*/
public function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) {
$algorithm = strtolower($algorithm);
if (!in_array($algorithm, hash_algos(), true))
trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
if ($count <= 0 || $key_length <= 0)
trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
if (function_exists("hash_pbkdf2")) {
// The output length is in NIBBLES (4-bits) if $raw_output is false!
if (!$raw_output) {
$key_length = $key_length * 2;
}
return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for ($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if ($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
}
}

View file

@ -139,7 +139,7 @@ class OCSAuthAPI {
protected function isValidToken($url, $token) {
$storedToken = $this->dbHandler->getToken($url);
return StringUtils::equals($storedToken, $token);
return hash_equals($storedToken, $token);
}
}

View file

@ -447,7 +447,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
$deobfuscatedToken = base64_decode($obfuscatedToken) ^ $secret;
// Check if the token is valid
if(\OCP\Security\StringUtils::equals($deobfuscatedToken, $this->items['requesttoken'])) {
if(hash_equals($deobfuscatedToken, $this->items['requesttoken'])) {
return true;
} else {
return false;

View file

@ -227,7 +227,7 @@ class Log implements ILogger {
$request = \OC::$server->getRequest();
// if token is found in the request change set the log condition to satisfied
if($request && StringUtils::equals($request->getParam('log_secret'), $logCondition['shared_secret'])) {
if($request && hash_equals($logCondition['shared_secret'], $request->getParam('log_secret'))) {
$this->logConditionSatisfied = true;
}
}

View file

@ -123,7 +123,7 @@ class Crypto implements ICrypto {
$this->cipher->setIV($iv);
if(!\OCP\Security\StringUtils::equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) {
if(!hash_equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) {
throw new \Exception('HMAC does not match.');
}

View file

@ -109,7 +109,7 @@ class Hasher implements IHasher {
// Verify whether it matches a legacy PHPass or SHA1 string
$hashLength = strlen($hash);
if($hashLength === 60 && password_verify($message.$this->legacySalt, $hash) ||
$hashLength === 40 && StringUtils::equals($hash, sha1($message))) {
$hashLength === 40 && hash_equals($hash, sha1($message))) {
$newHash = $this->hash($message);
return true;
}

View file

@ -27,25 +27,15 @@ use Sabre\DAV\Exception;
use OCP\Security\ISecureRandom;
/**
* Class SecureRandom provides a layer around RandomLib to generate
* secure random strings. For PHP 7 the native CSPRNG is used.
* Class SecureRandom provides a wrapper around the random_int function to generate
* secure random strings. For PHP 7 the native CSPRNG is used, older versions do
* use a fallback.
*
* Usage:
* \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(10);
*
* \OC::$server->getSecureRandom()->generate(10);
* @package OC\Security
*/
class SecureRandom implements ISecureRandom {
/** @var \RandomLib\Factory */
var $factory;
/** @var \RandomLib\Generator */
var $generator;
function __construct() {
$this->factory = new RandomLib\Factory;
}
/**
* Convenience method to get a low strength random number generator.
*
@ -53,10 +43,10 @@ class SecureRandom implements ISecureRandom {
* in a non-cryptographical setting. They are not strong enough to be
* used as keys or salts. They are however useful for one-time use tokens.
*
* @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int()
* @return $this
*/
public function getLowStrengthGenerator() {
$this->generator = $this->factory->getLowStrengthGenerator();
return $this;
}
@ -67,10 +57,10 @@ class SecureRandom implements ISecureRandom {
* They are strong enough to be used as keys and salts. However, they do
* take some time and resources to generate, so they should not be over-used
*
* @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int()
* @return $this
*/
public function getMediumStrengthGenerator() {
$this->generator = $this->factory->getMediumStrengthGenerator();
return $this;
}
@ -80,26 +70,17 @@ class SecureRandom implements ISecureRandom {
* @param string $characters An optional list of characters to use if no character list is
* specified all valid base64 characters are used.
* @return string
* @throws \Exception If the generator is not initialized.
*/
public function generate($length,
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') {
if(is_null($this->generator)) {
throw new \Exception('Generator is not initialized.');
$maxCharIndex = strlen($characters) - 1;
$randomString = '';
while($length > 0) {
$randomNumber = random_int(0, $maxCharIndex);
$randomString .= $characters[$randomNumber];
$length--;
}
if(function_exists('random_int')) {
$maxCharIndex = strlen($characters) - 1;
$randomString = '';
while($length > 0) {
$randomNumber = random_int(0, $maxCharIndex);
$randomString .= $characters[$randomNumber];
$length--;
}
return $randomString;
}
return $this->generator->generateString($length, $characters);
return $randomString;
}
}

View file

@ -1,60 +0,0 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
* @author Morris Jobke <hey@morrisjobke.de>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Security;
class StringUtils {
/**
* Compares whether two strings are equal. To prevent guessing of the string
* length this is done by comparing two hashes against each other and afterwards
* a comparison of the real string to prevent against the unlikely chance of
* collisions.
*
* Be aware that this function may leak whether the string to compare have a different
* length.
*
* @param string $expected The expected value
* @param string $input The input to compare against
* @return bool True if the two strings are equal, otherwise false.
*/
public static function equals($expected, $input) {
if(!is_string($expected) || !is_string($input)) {
return false;
}
if(function_exists('hash_equals')) {
return hash_equals($expected, $input);
}
$randomString = \OC::$server->getSecureRandom()->getLowStrengthGenerator()->generate(10);
if(hash('sha512', $expected.$randomString) === hash('sha512', $input.$randomString)) {
if($expected === $input) {
return true;
}
}
return false;
}
}

View file

@ -23,12 +23,12 @@
namespace OCP\Security;
/**
* Class SecureRandom provides a layer around RandomLib to generate
* secure random strings. For PHP 7 the native CSPRNG is used.
* Class SecureRandom provides a wrapper around the random_int function to generate
* secure random strings. For PHP 7 the native CSPRNG is used, older versions do
* use a fallback.
*
* Usage:
* $rng = new \OC\Security\SecureRandom();
* $randomString = $rng->getMediumStrengthGenerator()->generateString(30);
* \OC::$server->getSecureRandom()->generate(10);
*
* @package OCP\Security
* @since 8.0.0
@ -52,6 +52,7 @@ interface ISecureRandom {
*
* @return $this
* @since 8.0.0
* @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int()
*/
public function getLowStrengthGenerator();
@ -64,6 +65,7 @@ interface ISecureRandom {
*
* @return $this
* @since 8.0.0
* @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int()
*/
public function getMediumStrengthGenerator();
@ -73,7 +75,6 @@ interface ISecureRandom {
* @param string $characters An optional list of characters to use if no character list is
* specified all valid base64 characters are used.
* @return string
* @throws \Exception If the generator is not initialized.
* @since 8.0.0
*/
public function generate($length,

View file

@ -39,8 +39,9 @@ class StringUtils {
* @param string $input The input to compare against
* @return bool True if the two strings are equal, otherwise false.
* @since 8.0.0
* @deprecated 9.0.0 Use hash_equals
*/
public static function equals($expected, $input) {
return \OC\Security\StringUtils::equals($expected, $input);
return hash_equals($expected, $input);
}
}

View file

@ -57,11 +57,10 @@ class SecureRandomTest extends \Test\TestCase {
}
/**
* @expectedException \Exception
* @expectedExceptionMessage Generator is not initialized
* @dataProvider stringGenerationProvider
*/
function testUninitializedGenerate() {
$this->rng->generate(30);
function testUninitializedGenerate($length, $expectedLength) {
$this->assertEquals($expectedLength, strlen($this->rng->generate($length)));
}
/**

View file

@ -1,38 +0,0 @@
<?php
/**
* Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
use \OC\Security\StringUtils;
class StringUtilsTest extends \Test\TestCase {
public function dataProvider()
{
return array(
array('Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.'),
array('', ''),
array('我看这本书。 我看這本書', '我看这本书。 我看這本書'),
array('GpKY9fSnWNJbES99zVGvA', 'GpKY9fSnWNJbES99zVGvA')
);
}
/**
* @dataProvider dataProvider
*/
function testWrongEquals($string) {
$this->assertFalse(StringUtils::equals($string, 'A Completely Wrong String'));
$this->assertFalse(StringUtils::equals($string, null));
}
/**
* @dataProvider dataProvider
*/
function testTrueEquals($string, $expected) {
$this->assertTrue(StringUtils::equals($string, $expected));
}
}