Merge pull request #813 from bantu/hash-rewrite

[master] More work on Hash.php

* bantu/hash-rewrite:
  Hash: Cover remaining uncovered line.
  Hash: Update incorrect documentation, use max line length 80.
  Hash: Remove constructor return value documentation.
  Hash: Do not assign false to length property. It's documented as an int.
  Hash: Rename properly l to length.
  Slightly rework of HashTest.php.
This commit is contained in:
Andreas Fischer 2015-09-14 22:47:37 +02:00
commit 3b33941d3d
2 changed files with 103 additions and 51 deletions

View File

@ -1,23 +1,20 @@
<?php <?php
/** /**
* Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. * Wrapper around hash() and hash_hmac() functions supporting truncated hashes
* such as sha256-96. Any hash algorithm returned by hash_algos() (and
* truncated versions thereof) are supported.
* *
* Basically a wrapper for hash(). Currently supports the following: * If {@link \phpseclib\Crypt\Hash::setKey() setKey()} is called,
* * {@link \phpseclib\Crypt\Hash::hash() hash()} will return the HMAC as opposed
* md2, md5, md5-96, sha1, sha1-96, sha256, sha256-96, sha384, and sha512, sha512-96 * to the hash.
*
* If {@link \phpseclib\Crypt\Hash::setKey() setKey()} is called, {@link \phpseclib\Crypt\Hash::hash() hash()} will return the HMAC as opposed to
* the hash. If no valid algorithm is provided, sha1 will be used.
*
* PHP version 5
* *
* Here's a short example of how to use this library: * Here's a short example of how to use this library:
* <code> * <code>
* <?php * <?php
* include 'vendor/autoload.php'; * include 'vendor/autoload.php';
* *
* $hash = new \phpseclib\Crypt\Hash('sha1'); * $hash = new \phpseclib\Crypt\Hash('sha512');
* *
* $hash->setKey('abcdefg'); * $hash->setKey('abcdefg');
* *
@ -28,7 +25,9 @@
* @category Crypt * @category Crypt
* @package Hash * @package Hash
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton * @copyright 2015 Jim Wigginton
* @author Andreas Fischer <bantu@phpbb.com>
* @copyright 2015 Andreas Fischer
* @license http://www.opensource.org/licenses/mit-license.html MIT License * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -38,10 +37,9 @@ namespace phpseclib\Crypt;
use phpseclib\Exception\UnsupportedAlgorithmException; use phpseclib\Exception\UnsupportedAlgorithmException;
/** /**
* Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
*
* @package Hash * @package Hash
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @author Andreas Fischer <bantu@phpbb.com>
* @access public * @access public
*/ */
class Hash class Hash
@ -62,7 +60,7 @@ class Hash
* @var int * @var int
* @access private * @access private
*/ */
var $l = false; var $length;
/** /**
* Hash Algorithm * Hash Algorithm
@ -86,7 +84,6 @@ class Hash
* Default Constructor. * Default Constructor.
* *
* @param string $hash * @param string $hash
* @return \phpseclib\Crypt\Hash
* @access public * @access public
*/ */
function __construct($hash = 'sha256') function __construct($hash = 'sha256')
@ -135,36 +132,44 @@ class Hash
case 'sha256-96': case 'sha256-96':
case 'sha512-96': case 'sha512-96':
$hash = substr($hash, 0, -3); $hash = substr($hash, 0, -3);
$this->l = 12; // 96 / 8 = 12 $this->length = 12; // 96 / 8 = 12
break; break;
case 'md2': case 'md2':
case 'md5': case 'md5':
$this->l = 16; $this->length = 16;
break; break;
case 'sha1': case 'sha1':
$this->l = 20; $this->length = 20;
break; break;
case 'sha256': case 'sha256':
$this->l = 32; $this->length = 32;
break; break;
case 'sha384': case 'sha384':
$this->l = 48; $this->length = 48;
break; break;
case 'sha512': case 'sha512':
$this->l = 64; $this->length = 64;
break; break;
default: default:
// see if the hash isn't "officially" supported see if it can be "unofficially" supported and calculate the length accordingly // see if the hash isn't "officially" supported see if it can
// be "unofficially" supported and calculate the length
// accordingly.
if (in_array($hash, hash_algos())) { if (in_array($hash, hash_algos())) {
$this->l = strlen(hash($hash, '', true)); $this->length = strlen(hash($hash, '', true));
break; break;
} }
// if the hash algorithm doens't exist maybe it's a truncated hash. eg. md5-96 or some such // if the hash algorithm doens't exist maybe it's a truncated
if (preg_match('#(-\d+)$#', $hash, $matches) && in_array($hash = substr($hash, 0, -strlen($matches[1])), hash_algos())) { // hash, e.g. whirlpool-12 or some such.
$this->l = abs($matches[1]) >> 3; if (preg_match('#(-\d+)$#', $hash, $matches)) {
break; $hash = substr($hash, 0, -strlen($matches[1]));
if (in_array($hash, hash_algos())) {
$this->length = abs($matches[1]) >> 3;
break;
}
} }
throw new UnsupportedAlgorithmException("$hash is not a supported algorithm"); throw new UnsupportedAlgorithmException(
"$hash is not a supported algorithm"
);
} }
$this->hash = $hash; $this->hash = $hash;
@ -183,7 +188,9 @@ class Hash
hash_hmac($this->hash, $text, $this->key, true) : hash_hmac($this->hash, $text, $this->key, true) :
hash($this->hash, $text, true); hash($this->hash, $text, true);
return strlen($output) > $this->l ? substr($output, 0, $this->l) : $output; return strlen($output) > $this->length
? substr($output, 0, $this->length)
: $output;
} }
/** /**
@ -194,6 +201,6 @@ class Hash
*/ */
function getLength() function getLength()
{ {
return $this->l; return $this->length;
} }
} }

View File

@ -13,7 +13,7 @@ class Unit_Crypt_HashTest extends PhpseclibTestCase
{ {
$hash = new Hash($hash); $hash = new Hash($hash);
$this->assertEquals( $this->assertSame(
strtolower($expected), strtolower($expected),
bin2hex($hash->hash($message)), bin2hex($hash->hash($message)),
sprintf("Failed asserting that '%s' hashes to '%s'.", $message, $expected) sprintf("Failed asserting that '%s' hashes to '%s'.", $message, $expected)
@ -25,7 +25,7 @@ class Unit_Crypt_HashTest extends PhpseclibTestCase
$hash = new Hash($hash); $hash = new Hash($hash);
$hash->setKey($key); $hash->setKey($key);
$this->assertEquals( $this->assertSame(
strtolower($expected), strtolower($expected),
bin2hex($hash->hash($message)), bin2hex($hash->hash($message)),
sprintf( sprintf(
@ -43,6 +43,8 @@ class Unit_Crypt_HashTest extends PhpseclibTestCase
array('md5', '', 'd41d8cd98f00b204e9800998ecf8427e'), array('md5', '', 'd41d8cd98f00b204e9800998ecf8427e'),
array('md5', 'The quick brown fox jumps over the lazy dog', '9e107d9d372bb6826bd81d3542a419d6'), array('md5', 'The quick brown fox jumps over the lazy dog', '9e107d9d372bb6826bd81d3542a419d6'),
array('md5', 'The quick brown fox jumps over the lazy dog.', 'e4d909c290d0fb1ca068ffaddf22cbd0'), array('md5', 'The quick brown fox jumps over the lazy dog.', 'e4d909c290d0fb1ca068ffaddf22cbd0'),
array('sha1', 'The quick brown fox jumps over the lazy dog', '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12'),
array('sha1', 'The quick brown fox jumps over the lazy dog.', '408d94384216f890ff7a0c3528e8bed1e0b01621'),
array( array(
'sha256', 'sha256',
'', '',
@ -58,6 +60,16 @@ class Unit_Crypt_HashTest extends PhpseclibTestCase
'The quick brown fox jumps over the lazy dog.', 'The quick brown fox jumps over the lazy dog.',
'ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c', 'ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c',
), ),
array(
'sha384',
'',
'38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b'
),
array(
'sha384',
'The quick brown fox jumps over the lazy dog',
'ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1',
),
array( array(
'sha512', 'sha512',
'', '',
@ -190,31 +202,64 @@ class Unit_Crypt_HashTest extends PhpseclibTestCase
$this->assertHashesTo($hash . '-96', $message, substr($result, 0, 24)); $this->assertHashesTo($hash . '-96', $message, substr($result, 0, 24));
} }
public function testGetHash() public function testConstructorDefault()
{ {
$hash = new Hash(); $hash = new Hash();
$this->assertEquals($hash->getHash(), 'sha256'); $this->assertSame($hash->getHash(), 'sha256');
$hash = new Hash('whirlpool');
$this->assertEquals($hash->getHash(), 'whirlpool');
$hash = new Hash('md5');
$this->assertEquals($hash->getHash(), 'md5');
$hash->setHash('whirlpool');
$this->assertEquals($hash->getHash(), 'whirlpool');
$hash->setHash('md5');
$this->assertEquals($hash->getHash(), 'md5');
} }
public function testGetLength() /**
* @expectedException \phpseclib\Exception\UnsupportedAlgorithmException
*/
public function testConstructorArgumentInvalid()
{
new Hash('abcdefghijklmnopqrst');
}
public function testConstructorArgumentValid()
{ {
$hash = new Hash();
$this->assertEquals($hash->getLength(), 32);
$hash = new Hash('whirlpool'); $hash = new Hash('whirlpool');
$this->assertEquals($hash->getLength(), 64); $this->assertSame($hash->getHash(), 'whirlpool');
}
/**
* @expectedException \phpseclib\Exception\UnsupportedAlgorithmException
*/
public function testSetHashInvalid()
{
$hash = new Hash('md5'); $hash = new Hash('md5');
$this->assertEquals($hash->getLength(), 16); $hash->setHash('abcdefghijklmnopqrst-96');
$hash->setHash('whirlpool'); }
$this->assertEquals($hash->getLength(), 64);
$hash->setHash('md5'); public function testSetHashValid()
$this->assertEquals($hash->getLength(), 16); {
$hash = new Hash('md5');
$this->assertSame($hash->getHash(), 'md5');
$hash->setHash('sha1');
$this->assertSame($hash->getHash(), 'sha1');
}
/**
* @dataProvider lengths
*/
public function testGetLengthKnown($algorithm, $length)
{
$hash = new Hash($algorithm);
$this->assertSame($hash->getLength(), $length);
}
public function lengths()
{
return array(
// known
array('md5-96', 12),
array('md5', 16),
array('sha1', 20),
array('sha256', 32),
array('sha384', 48),
array('sha512', 64),
// unknown
array('whirlpool', 64),
);
} }
} }