Permalink
Browse files

Merge branch 'feature/scrypt' into develop

Close #2430
  • Loading branch information...
weierophinney committed Oct 4, 2012
2 parents 5961247 + 57f0d4f commit 0b328762e63710db7756ac6c4a34afa34dcc4cdf
@@ -42,10 +42,6 @@ class Hmac
*/
public static function compute($key, $hash, $data, $output = self::OUTPUT_STRING)
{
- if (!isset($key) || empty($key)) {
- throw new Exception\InvalidArgumentException('Provided key is null or empty');
- }
-
$hash = strtolower($hash);
if (!self::isSupported($hash)) {
throw new Exception\InvalidArgumentException(
@@ -0,0 +1,345 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Crypt
+ */
+
+namespace Zend\Crypt\Key\Derivation;
+
+use Zend\Crypt\Key\Derivation\Pbkdf2;
+
+/**
+ * Scrypt key derivation function
+ *
+ * @category Zend
+ * @package Zend_Crypt
+ * @see http://www.tarsnap.com/scrypt.html
+ * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01
+ */
+abstract class Scrypt
+{
+ /**
+ * Execute the scrypt algorithm
+ *
+ * @param string $password
+ * @param string $salt
+ * @param integer $n CPU cost
+ * @param integer $r Memory cost
+ * @param integer $p parallelization cost
+ * @param integer $length size of the output key
+ * @return string
+ */
+ public static function calc($password, $salt, $n, $r, $p, $length)
+ {
+ if ($n == 0 || ($n & ($n - 1)) != 0) {
+ throw new Exception\InvalidArgumentException("N must be > 0 and a power of 2");
+ }
+ if ($n > PHP_INT_MAX / 128 / $r) {
+ throw new Exception\InvalidArgumentException("Parameter n is too large");
+ }
+ if ($r > PHP_INT_MAX / 128 / $p) {
+ throw new Exception\InvalidArgumentException("Parameter r is too large");
+ }
+
+ if (extension_loaded('Scrypt')) {
+ if ($length < 16) {
+ throw new Exception\InvalidArgumentException("Key length is too low, must be greater or equal to 16");
+ }
+ return self::hex2bin(scrypt($password, $salt, $n, $r, $p, $length));
+ }
+
+ $b = Pbkdf2::calc('sha256', $password, $salt, 1, $p * 128 * $r);
+
+ $s = '';
+ for ($i = 0; $i < $p; $i++) {
+ $s .= self::scryptROMix(substr($b, $i * 128 * $r, 128 * $r), $n, $r);
+ }
+
+ return Pbkdf2::calc('sha256', $password, $s, 1, $length);
+ }
+
+ /**
+ * scryptROMix
+ *
+ * @param string $b
+ * @param integer $n
+ * @param integer $r
+ * @return string
+ * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-4
+ */
+ protected static function scryptROMix($b, $n, $r)
+ {
+ $x = $b;
+ $v = array();
+ for ($i = 0; $i < $n; $i++) {
+ $v[$i] = $x;
+ $x = self::scryptBlockMix($x, $r);
+ }
+ for ($i = 0; $i < $n; $i++) {
+ $j = self::integerify($x) % $n;
+ $t = $x ^ $v[$j];
+ $x = self::scryptBlockMix($t, $r);
+ }
+ return $x;
+ }
+
+ /**
+ * scryptBlockMix
+ *
+ * @param string $b
+ * @param integer $r
+ * @return string
+ * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-3
+ */
+ protected static function scryptBlockMix($b, $r)
+ {
+ $x = substr($b, -64);
+ $even = '';
+ $odd = '';
+ $len = 2 * $r;
+
+ for ($i = 0; $i < $len; $i++) {
+ if (PHP_INT_SIZE === 4) {
+ $x = self::salsa208Core32($x ^ substr($b, 64 * $i, 64));
+ } else {
+ $x = self::salsa208Core64($x ^ substr($b, 64 * $i, 64));
+ }
+ if ($i % 2 == 0) {
+ $even .= $x;
+ } else {
+ $odd .= $x;
+ }
+ }
+ return $even . $odd;
+ }
+
+ /**
+ * Salsa 20/8 core (32 bit version)
+ *
+ * @param string $b
+ * @return string
+ * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-2
+ * @see http://cr.yp.to/salsa20.html
+ */
+ protected static function salsa208Core32($b)
+ {
+ $b32 = array();
+ for ($i = 0; $i < 16; $i++) {
+ list(, $b32[$i]) = unpack("V", substr($b, $i * 4, 4));
+ }
+
+ $x = $b32;
+ for ($i = 0; $i < 8; $i += 2) {
+ $a = ($x[ 0] + $x[12]);
+ $x[ 4] ^= ($a << 7) | ($a >> 25) & 0x7f;
+ $a = ($x[ 4] + $x[ 0]);
+ $x[ 8] ^= ($a << 9) | ($a >> 23) & 0x1ff;
+ $a = ($x[ 8] + $x[ 4]);
+ $x[12] ^= ($a << 13) | ($a >> 19) & 0x1fff;
+ $a = ($x[12] + $x[ 8]);
+ $x[ 0] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
+ $a = ($x[ 5] + $x[ 1]);
+ $x[ 9] ^= ($a << 7) | ($a >> 25) & 0x7f;
+ $a = ($x[ 9] + $x[ 5]);
+ $x[13] ^= ($a << 9) | ($a >> 23) & 0x1ff;
+ $a = ($x[13] + $x[ 9]);
+ $x[ 1] ^= ($a << 13) | ($a >> 19) & 0x1fff;
+ $a = ($x[ 1] + $x[13]);
+ $x[ 5] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
+ $a = ($x[10] + $x[ 6]);
+ $x[14] ^= ($a << 7) | ($a >> 25) & 0x7f;
+ $a = ($x[14] + $x[10]);
+ $x[ 2] ^= ($a << 9) | ($a >> 23) & 0x1ff;
+ $a = ($x[ 2] + $x[14]);
+ $x[ 6] ^= ($a << 13) | ($a >> 19) & 0x1fff;
+ $a = ($x[ 6] + $x[ 2]);
+ $x[10] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
+ $a = ($x[15] + $x[11]);
+ $x[ 3] ^= ($a << 7) | ($a >> 25) & 0x7f;
+ $a = ($x[ 3] + $x[15]);
+ $x[ 7] ^= ($a << 9) | ($a >> 23) & 0x1ff;
+ $a = ($x[ 7] + $x[ 3]);
+ $x[11] ^= ($a << 13) | ($a >> 19) & 0x1fff;
+ $a = ($x[11] + $x[ 7]);
+ $x[15] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
+ $a = ($x[ 0] + $x[ 3]);
+ $x[ 1] ^= ($a << 7) | ($a >> 25) & 0x7f;
+ $a = ($x[ 1] + $x[ 0]);
+ $x[ 2] ^= ($a << 9) | ($a >> 23) & 0x1ff;
+ $a = ($x[ 2] + $x[ 1]);
+ $x[ 3] ^= ($a << 13) | ($a >> 19) & 0x1fff;
+ $a = ($x[ 3] + $x[ 2]);
+ $x[ 0] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
+ $a = ($x[ 5] + $x[ 4]);
+ $x[ 6] ^= ($a << 7) | ($a >> 25) & 0x7f;
+ $a = ($x[ 6] + $x[ 5]);
+ $x[ 7] ^= ($a << 9) | ($a >> 23) & 0x1ff;
+ $a = ($x[ 7] + $x[ 6]);
+ $x[ 4] ^= ($a << 13) | ($a >> 19) & 0x1fff;
+ $a = ($x[ 4] + $x[ 7]);
+ $x[ 5] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
+ $a = ($x[10] + $x[ 9]);
+ $x[11] ^= ($a << 7) | ($a >> 25) & 0x7f;
+ $a = ($x[11] + $x[10]);
+ $x[ 8] ^= ($a << 9) | ($a >> 23) & 0x1ff;
+ $a = ($x[ 8] + $x[11]);
+ $x[ 9] ^= ($a << 13) | ($a >> 19) & 0x1fff;
+ $a = ($x[ 9] + $x[ 8]);
+ $x[10] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
+ $a = ($x[15] + $x[14]);
+ $x[12] ^= ($a << 7) | ($a >> 25) & 0x7f;
+ $a = ($x[12] + $x[15]);
+ $x[13] ^= ($a << 9) | ($a >> 23) & 0x1ff;
+ $a = ($x[13] + $x[12]);
+ $x[14] ^= ($a << 13) | ($a >> 19) & 0x1fff;
+ $a = ($x[14] + $x[13]);
+ $x[15] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
+ }
+ for ($i = 0; $i < 16; $i++) {
+ $b32[$i] = $b32[$i] + $x[$i];
+ }
+ $result = '';
+ for ($i = 0; $i < 16; $i++) {
+ $result .= pack("V", $b32[$i]);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Salsa 20/8 core (64 bit version)
+ *
+ * @param string $b
+ * @return string
+ * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-2
+ * @see http://cr.yp.to/salsa20.html
+ */
+ protected static function salsa208Core64($b)
+ {
+ $b32 = array();
+ for ($i = 0; $i < 16; $i++) {
+ list(, $b32[$i]) = unpack("V", substr($b, $i * 4, 4));
+ }
+
+ $x = $b32;
+ for ($i = 0; $i < 8; $i += 2) {
+ $a = ($x[ 0] + $x[12]) & 0xffffffff;
+ $x[ 4] ^= ($a << 7) | ($a >> 25);
+ $a = ($x[ 4] + $x[ 0]) & 0xffffffff;
+ $x[ 8] ^= ($a << 9) | ($a >> 23);
+ $a = ($x[ 8] + $x[ 4]) & 0xffffffff;
+ $x[12] ^= ($a << 13) | ($a >> 19);
+ $a = ($x[12] + $x[ 8]) & 0xffffffff;
+ $x[ 0] ^= ($a << 18) | ($a >> 14);
+ $a = ($x[ 5] + $x[ 1]) & 0xffffffff;
+ $x[ 9] ^= ($a << 7) | ($a >> 25);
+ $a = ($x[ 9] + $x[ 5]) & 0xffffffff;
+ $x[13] ^= ($a << 9) | ($a >> 23);
+ $a = ($x[13] + $x[ 9]) & 0xffffffff;
+ $x[ 1] ^= ($a << 13) | ($a >> 19);
+ $a = ($x[ 1] + $x[13]) & 0xffffffff;
+ $x[ 5] ^= ($a << 18) | ($a >> 14);
+ $a = ($x[10] + $x[ 6]) & 0xffffffff;
+ $x[14] ^= ($a << 7) | ($a >> 25);
+ $a = ($x[14] + $x[10]) & 0xffffffff;
+ $x[ 2] ^= ($a << 9) | ($a >> 23);
+ $a = ($x[ 2] + $x[14]) & 0xffffffff;
+ $x[ 6] ^= ($a << 13) | ($a >> 19);
+ $a = ($x[ 6] + $x[ 2]) & 0xffffffff;
+ $x[10] ^= ($a << 18) | ($a >> 14);
+ $a = ($x[15] + $x[11]) & 0xffffffff;
+ $x[ 3] ^= ($a << 7) | ($a >> 25);
+ $a = ($x[ 3] + $x[15]) & 0xffffffff;
+ $x[ 7] ^= ($a << 9) | ($a >> 23);
+ $a = ($x[ 7] + $x[ 3]) & 0xffffffff;
+ $x[11] ^= ($a << 13) | ($a >> 19);
+ $a = ($x[11] + $x[ 7]) & 0xffffffff;
+ $x[15] ^= ($a << 18) | ($a >> 14);
+ $a = ($x[ 0] + $x[ 3]) & 0xffffffff;
+ $x[ 1] ^= ($a << 7) | ($a >> 25);
+ $a = ($x[ 1] + $x[ 0]) & 0xffffffff;
+ $x[ 2] ^= ($a << 9) | ($a >> 23);
+ $a = ($x[ 2] + $x[ 1]) & 0xffffffff;
+ $x[ 3] ^= ($a << 13) | ($a >> 19);
+ $a = ($x[ 3] + $x[ 2]) & 0xffffffff;
+ $x[ 0] ^= ($a << 18) | ($a >> 14);
+ $a = ($x[ 5] + $x[ 4]) & 0xffffffff;
+ $x[ 6] ^= ($a << 7) | ($a >> 25);
+ $a = ($x[ 6] + $x[ 5]) & 0xffffffff;
+ $x[ 7] ^= ($a << 9) | ($a >> 23);
+ $a = ($x[ 7] + $x[ 6]) & 0xffffffff;
+ $x[ 4] ^= ($a << 13) | ($a >> 19);
+ $a = ($x[ 4] + $x[ 7]) & 0xffffffff;
+ $x[ 5] ^= ($a << 18) | ($a >> 14);
+ $a = ($x[10] + $x[ 9]) & 0xffffffff;
+ $x[11] ^= ($a << 7) | ($a >> 25);
+ $a = ($x[11] + $x[10]) & 0xffffffff;
+ $x[ 8] ^= ($a << 9) | ($a >> 23);
+ $a = ($x[ 8] + $x[11]) & 0xffffffff;
+ $x[ 9] ^= ($a << 13) | ($a >> 19);
+ $a = ($x[ 9] + $x[ 8]) & 0xffffffff;
+ $x[10] ^= ($a << 18) | ($a >> 14);
+ $a = ($x[15] + $x[14]) & 0xffffffff;
+ $x[12] ^= ($a << 7) | ($a >> 25);
+ $a = ($x[12] + $x[15]) & 0xffffffff;
+ $x[13] ^= ($a << 9) | ($a >> 23);
+ $a = ($x[13] + $x[12]) & 0xffffffff;
+ $x[14] ^= ($a << 13) | ($a >> 19);
+ $a = ($x[14] + $x[13]) & 0xffffffff;
+ $x[15] ^= ($a << 18) | ($a >> 14);
+ }
+ for ($i = 0; $i < 16; $i++) {
+ $b32[$i] = ($b32[$i] + $x[$i]) & 0xffffffff;
+ }
+ $result = '';
+ for ($i = 0; $i < 16; $i++) {
+ $result .= pack("V", $b32[$i]);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Integerify
+ *
+ * Integerify (B[0] ... B[2 * r - 1]) is defined as the result
+ * of interpreting B[2 * r - 1] as a little-endian integer.
+ * Each block B is a string of 64 bytes.
+ *
+ * @param string $b
+ * @return integer
+ * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-4
+ */
+ protected static function integerify($b)
+ {
+ $v = 'v';
+ if (PHP_INT_SIZE === 8) {
+ $v = 'V';
+ }
+ list(,$n) = unpack($v, substr($b, -64));
+ return $n;
+ }
+
+ /**
+ * Convert hex string in a binary string
+ *
+ * @param string $hex
+ * @return string
+ */
+ protected static function hex2bin($hex)
+ {
+ if (version_compare(PHP_VERSION, '5.4') >= 0) {
+ return hex2bin($hex);
+ }
+ $len = strlen($hex);
+ $result = '';
+ for ($i = 0; $i < $len; $i+=2) {
+ $result .= chr(hexdec($hex[$i] . $hex[$i+1]));
+ }
+ return $result;
+ }
+}
@@ -200,13 +200,6 @@ public function testHmacRIPEMD160_7()
$this->assertEquals('69ea60798d71616cce5fd0871e23754cd75d5a0a', $hmac);
}
- public function testEmptyKey()
- {
- $this->setExpectedException('Zend\Crypt\Exception\InvalidArgumentException',
- 'Provided key is null or empty');
- $hash = HMAC::compute(null, 'md5', 'test');
- }
-
public function testWrongHashAlgorithm()
{
$this->setExpectedException('Zend\Crypt\Exception\InvalidArgumentException',
Oops, something went wrong.

0 comments on commit 0b32876

Please sign in to comment.