立方体哈希。纯 PHP 实现。多字节块问题(CubeHash160+16/32+160-256)

CubeHash. Pure PHP implementation. Multibyte block issue (CubeHash160+16/32+160-256)

我是密码学新手。

我正在尝试在纯 PHP 上实现 CubeHash 散列函数。 我已经将一些 JS 版本(32 位)移植到 PHP(64 位),但是当它大于 1(块大小)时,我无法使用参数 b 实现 CubeHash大于 1 个字节)。

请帮帮我

运行在线:https://repl.it/repls/FreshWickedHashfunction

<?php

// repo: https://github.com/windlace/cubehash
// based on https://github.com/RndPhrase/cubehash.js/blob/master/cubehash.js

namespace Cast\Crypto\CubeHash;

// Cubehash 8/1-256 (CubeHash80+8/1+80-256)
//
// CubeHashi+r/b+f-h
//
// http://cubehash.cr.yp.to
// http://en.wikipedia.org/wiki/CubeHash
//
// example-1: https://github.com/RndPhrase/cubehash.js/blob/master/cubehash.js
// example-2: https://github.com/tearsofphoenix/cubehash/blob/master/index.js

function cubehash256($r, $b, $string)
{
    return CubeHash256::hash($r, $b, $string);
}

// returns 32-bit representation on 64-bit integer
function i32($value)
{
    $value = ($value & 0xFFFFFFFF);
    if ($value & 0x80000000) $value = -((~$value & 0xFFFFFFFF) + 1);
    return $value;
}

class CubeHash256
{   

    /**
     * Init vector computing by 10r rounds as described in the specification.
     *
     * @param   integer $r  The number of rounds per block
     * @param   integer $b  The block size in bytes, defined for {1, 2, 3, ... 128}
     * @param   integer $h  The size of the hash output in bits, defined for {8, 16, 24, 32, ... 512}
     *
     * @return array
     */
    public static function iv($r, $b, $h)
    {
        $initial_state = array_fill(0, 32, 0);
        $initial_state[0] = $h/8;
        $initial_state[1] = $b;
        $initial_state[2] = $r;

        // init state
        $state = new \SplFixedArray(32);

        for ($i = 0; $i < 32; $i += 1) {
            $state[$i] = $initial_state[$i];
        }

        // finalize (10*r)
        for ($i = 0; $i < 10; $i += 1) {
            self::transform($r, $state);
        }

        return $state->toArray();
    }

    /**
     * @param   integer $r  The number of rounds per block
     * @param $b
     * @param $data
     * @return string
     */
    public static function hash($r, $b, $data)
    {
        // init state
        $state = new \SplFixedArray(32);

        $iv = self::iv($r, $b, 256);
        
        for ($i = 0; $i < 32; $i += 1) {
            $state[$i] = $iv[$i];
        }

        // update with data
        $data .= chr(128);

        foreach (str_split($data, $b) as $block) {
            $blockHex = unpack('H*', $block);
            $blockDec = hexdec($blockHex[1]);
            $state[0] ^= $blockDec;
            $state = self::transform($r, $state);
        }

        // finalize
        $state[31] ^= 1;

        for ($i = 0; $i < 10; $i += 1) {
            self::transform($r, $state);
        }

        // Example for '' (empty string hash)
//        $state = [
//              -1561800392 => '38d1e8a2',
//               -961905875 => '2d7baac6',
//               -664644867 => 'fd5262d8',
//              -1399003075 => '3de89cac',
//              -3546249993 => 'f784a02c',
//              -3399252310 => 'aa866335',
//              -2373478103 => '29998772',
//              -2789414358 => '2aeabc59',
//              -1685548184 => '6893889b',
//              -3641377267 => '0dfef426',
//              -1599741972 => 'ecdfa5a0',
//              -3971411240 => 'd8124913',
//              -3644175510 => '6a4bca26',
//              -2826556663 => '092b8657',
//               -854482381 => '33a211cd',
//              -3508776574 => '8251dc2e',
//            -370904486330 => '46ae5ea4',
//            -399992847312 => '308491de',
//            -333955865119 => 'e1e5ad3e',
//            -367735441682 => 'ee764261',
//            -358144429942 => '8ab0ed9c',
//            -372630171200 => 'c0d1823d',
//            -347016896839 => 'b95e2e34',
//            -362137648016 => '7004eaae',
//            -386413349251 => '7d36f807',
//            -350848758866 => 'aecbc84f',
//            -386257376189 => '432c4411',
//            -383406607732 => '8c722fbb',
//            -360378781490 => 'ce30c017',
//            -385614396406 => '0a449737',
//            -401364645913 => 'e787cd8c',
//            -359536410101 => '0bc2f549',
//        ];
        // concat hex produces a hash : 38d1e8a22d7baac6fd5262d83de89cacf784a02caa866335299987722aeabc59

        // convert to hex
        $s = '';
        for ($i = 0; $i < 8; $i += 1) {
            $s .= self::signed2hex($state[$i], false);
        }

        return $s;
    }

    /**
     * @param   integer         $r      The number of rounds per block
     * @param   \SplFixedArray  $state  The main context
     * @return mixed
     */
    protected static function transform($r, $state)
    {
        $y = new \SplFixedArray(16);

        for ($ri = 0;$ri < $r; $ri += 1) {
            for ($i = 0; $i < 16; $i += 1) $state[$i + 16] += $y[$i^8] = $state[$i];
            for ($i = 0; $i < 16; $i += 1) $state[$i]       = i32(self::rotate($y[$i],  7)^$state[$i + 16]);
            for ($i = 0; $i < 16; $i += 1) $y[$i^2]         = $state[$i + 16];
            for ($i = 0; $i < 16; $i += 1) $state[$i + 16]  = $y[$i] + $state[$i];
            for ($i = 0; $i < 16; $i += 1) $y[$i^4]         = $state[$i];
            for ($i = 0; $i < 16; $i += 1) $state[$i]       = i32(self::rotate($y[$i], 11)^$state[$i + 16]);
            for ($i = 0; $i < 16; $i += 2) self::swap($state, $i + 16, $i + 17);
        }

        return $state;
    }

    protected static function rotate($a, $b)
    {
        return (0xffffffff00000000 | ($a << $b)) | (($a & 0x00000000ffffffff) >> (32 - $b));
    }

    protected static function swap($arr, $i, $j)
    {
        $tmp = $arr[$i];
        $arr[$i] = $arr[$j];
        $arr[$j] = $tmp;

        return $arr;
    }

    /**
     * Converts signed decimal to hex (Two's complement)
     *
     * @param $value int, signed
     *
     * @param $reverseEndianness bool, if true reverses the byte order (see machine dependency)
     *
     * @return string, upper case hex value, both bytes padded left with zeros
     */
    protected static function signed2hex($value, $reverseEndianness = true)
    {
        $packed = pack('i', $value);
        $hex='';
        for ($i=0; $i < 4; $i++){
            $hex .= str_pad( dechex(ord($packed[$i])) , 2, '0', STR_PAD_LEFT);
        }
        $tmp = str_split($hex, 2);
        $out = implode('', ($reverseEndianness ? array_reverse($tmp) : $tmp));
        return $out;
    }
}

function padBlock($input, $blockSizeBytes)
{
    $blockSize = $blockSizeBytes * 2;
    $input = bin2hex($input);
    $input = str_repeat('0', $blockSize - ((strlen($input) % $blockSize) ?: $blockSize)) . $input;
    $input = !strlen($input) ? str_repeat('0', $blockSize) : $input;
    return hex2bin($input);
}

function assertEquals($a, $b) {
    ($a === $b) or
      die(
        "Equality assertion failed:\n\n" .
        "Expected:\n" . (is_array($a) ? 'Array' : $a) . "\n".
        "Actual:\n"   . (is_array($b) ? 'Array' : $b) . "\n"
      );
}

// KNOWN EXAPMLES:

// CubeHash10+1/1+10-256:
assertEquals(
    [
          -1364746004,   1413475564,   -896460311,  1495629273,
            831721974,  -1114895892,  -1131802667, -1345622233,
          -966112469,  -1678886309,   -691894012,  1358586066,
          -1854453552,  -1070683759,    748270806,  1529972269,
          2136745624,   4462657195,   9868789035,  6357691622,
          -4981802178,  -6422354290,  -1400617557, -2741214963,
          -272471494,   5329758627,   2868717903,   710720199,
          -709654222,   2807896093,   2249717683,  2710321235,
    ],
    CubeHash256::iv(1, 1, 256)
);

assertEquals('80f72e07d04ddadb44a78823e0af2ea9f72ef3bf366fd773aa1fa33fc030e5cb', cubehash256(1, 1, ''));

assertEquals('f63041a946aa98bd47f3175e6009dcb2ccf597b2718617ba46d56f27ffe35d49', cubehash256(1, 1, 'Hello'));

assertEquals('217a4876f2b24cec489c9171f85d53395cc979156ea0254938c4c2c59dfdf8a4', cubehash256(1, 1, 'The quick brown fox jumps over the lazy dog'));

// CubeHash80+8/1+80-256
assertEquals(
    [
          -2096419883,    658334063,   -679114902,  1246757400,
          -1523021469,   -289996037,   1196718146,  1168084361,
          -2027445816,  -1170162360,   -822837272,   625197683,
          1543712850,  -1365258909,    759513533,  -424228083,
        -13765010209,  -2824905881,  -9887154026, 19352563566,
          5669379852, -31581549269,  21359466527, 10648459874,
          -8561790247,   9779462261, -22434204802, -4124492433,
          19608647852,   9541915967,   5144979599, -4355863926,
    ],
    CubeHash256::iv(8, 1, 256)
);

assertEquals('38d1e8a22d7baac6fd5262d83de89cacf784a02caa866335299987722aeabc59', cubehash256(8, 1, ''));

assertEquals('692638db57760867326f851bd2376533f37b640bd47a0ddc607a9456b692f70f', cubehash256(8, 1, 'Hello'));

assertEquals('94e0c958d85cdfaf554919980f0f50b945b88ad08413e0762d6ff0219aff3e55', cubehash256(8, 1, 'The quick brown fox jumps over the lazy dog'));

// CubeHash160+16/32+160-256
assertEquals(
    [
          -366226252,   -858328417,   1662090865,    893918894,
            575745371,   -438743453,   2120368433,   -187952450,
          -1026509162,   1118773360,   -797832139,    862050956,
            684518564,  -1896305277,   1182837760,   1088813995,
          7928299971,   5922880469, -36834009791, -21731580295,
        -42794932919,  35964212739,  -2587323651, -57649962363,
          5015516590, -27409035680, -45243252771,  58068387621,
        -29703972117,   5548452566, -40318076753, -30769206262,
    ],
    CubeHash256::iv(16, 32, 256)
);

// multybyte-blocks for an empty string works propertly.
assertEquals('44c6de3ac6c73c391bf0906cb7482600ec06b216c7c54a2a8688a6a42676577d', cubehash256(16, 32, ''));

// padding with 0 works propertly
assertEquals('e27007aa498dd2100ffc76ce4eff578e6eb89908967186156f065cf6a61f6855', cubehash256(16, 32, padBlock('', 32)));

// NOT WORKING
assertEquals('e712139e3b892f2f5fe52d0f30d78a0cb16b51b217da0e4acb103dd0856f2db0', cubehash256(16, 32, 'Hello'));

// padding with 0 is NOT WORKING
assertEquals('72070e821ab48ae56cb693c1e59676a9d4c430b0a9321adedea5f00c825c9f69', cubehash256(16, 32, padBlock('Hello', 32)));

// NOT WORKING
assertEquals('3632b64528815b66875e7feb9be68fe3e0e502dd405d7910c23f16e6b6ffeef7', cubehash256(16, 32, hex2bin('8072a313bb0819a9874277b11303e252f3af7a229a407c1a618e8e4f38af6ca3')));

// NOT WORKING
assertEquals('5151e251e348cbbfee46538651c06b138b10eeb71cf6ea6054d7ca5fec82eb79', cubehash256(16, 32, 'The quick brown fox jumps over the lazy dog'));

echo "Done.";

更新:

来自文档:

CubeHash maintains a 128-byte state. It xors the first b-byte input block into the first b bytes of the state, transforms the state invertibly through r identical rounds, xors the next b-byte input block into the first b bytes of the state, transforms the state invertibly through r identical rounds, xors the next b-byte input block into the first b bytes of the state, transforms the state invertibly through r identical rounds, etc.

我已经解决了多字节块的问题。

主要特点是(来自官方文档http://cubehash.cr.yp.to):

  • CubeHash 保持 128 字节的状态。它将第一个 b 字节输入块 异或到状态 的第一个 b 字节,通过 r 相同的轮次可逆地转换状态。每个下一个块 - 相同的过程。
  • 128 字节状态被视为 32 个 4 字节字的序列 x[00000], x[00001], x[00010], x[00011], x[00100], ..., x[11111],每个都以little-endian形式解释为32位整数。

所以我们需要:

  • 将块的每个字节异或到状态的每个字节
  • 在异或之前交换字节字节顺序

修改了下一部分:

发件人:

     ...
     /**
     * @param   integer $r  The number of rounds per block
     * @param $b
     * @param $data
     * @return string
     */
      public static function hash($r, $b, $data)
      {
      ...
        // update with data
        $data .= chr(128);

        foreach (str_split($data, $b) as $block) {
            $blockHex = unpack('H*', $block);
            $blockDec = hexdec($blockHex[1]);
            $state[0] ^= $blockDec;
            $state = self::transform($r, $state);
        }

        // finalize
        $state[31] ^= 1;
      ...
      }

收件人:

    /**
     * @param string $value Hex
     * @return string
     */
    public static function swapEndianness($value)
    {
        return implode('', array_reverse(str_split($value, 2)));
    }

    /**
     * @param string $value Binary array
     * @return false|string
     */
    public static function swapEndiannessBin($value)
    {
        return hex2bin(self::swapEndianness(bin2hex($value)));
    }

    /**
     * @param   integer $r  The number of rounds per block
     * @param $b
     * @param $data
     * @return string
     */
    public static function hash($r, $b, $data)
    {
    ...
       // update with data
        $data .= chr(128);
        $data = self::swapEndiannessBin($data);
        $data = padBlock($data, $b);
        $data = self::swapEndiannessBin($data);

        for ($j = 0; $j < strlen($data); $j += $b) {
            $block = substr($data, $j, $b);
            $block = bin2hex($block);
            $n = 0;
            for ($i = 0; $i < strlen($block); $i += 8) {
                $byte = substr($block, $i, 8);
                $byte = self::swapEndianness($byte);
                $state[$n++] ^= hexdec($byte);
            }
            $state = self::transform($r, $state);
        }

        // finalize
        $state[31] ^= 1;
     ...
     }

完整版为:

<?php

// based on https://github.com/RndPhrase/cubehash.js/blob/master/cubehash.js

namespace Cast\Crypto\CubeHash;

function cubehash256($r, $b, $string)
{
    return CubeHash256::hash($r, $b, $string);
}

// returns 32-bit representation on 64-bit integer
function i32($value)
{
    $value = ($value & 0xFFFFFFFF);
    if ($value & 0x80000000) $value = -((~$value & 0xFFFFFFFF) + 1);
    return $value;
}

// Cubehash 8/1-256 (CubeHash80+8/1+80-256)
//
// CubeHashi+r/b+f-h
//
// http://cubehash.cr.yp.to
// http://en.wikipedia.org/wiki/CubeHash
//
// example-1: https://github.com/RndPhrase/cubehash.js/blob/master/cubehash.js
// example-2: https://github.com/tearsofphoenix/cubehash/blob/master/index.js

class CubeHash256
{   

    /**
     * Init vector computing by 10r rounds as described in the specification.
     *
     * @param   integer $r  The number of rounds per block
     * @param   integer $b  The block size in bytes, defined for {1, 2, 3, ... 128}
     * @param   integer $h  The size of the hash output in bits, defined for {8, 16, 24, 32, ... 512}
     *
     * @return array
     */
    public static function iv($r, $b, $h)
    {
        $initial_state = array_fill(0, 32, 0);
        $initial_state[0] = $h/8;
        $initial_state[1] = $b;
        $initial_state[2] = $r;

        // init state
        $state = new \SplFixedArray(32);

        for ($i = 0; $i < 32; $i += 1) {
            $state[$i] = $initial_state[$i];
        }

        // finalize (10*r)
        for ($i = 0; $i < 10; $i += 1) {
            self::transform($r, $state);
        }

        return $state->toArray();
    }

    /**
     * @param string $value Hex
     * @return string
     */
    public static function swapEndianness($value)
    {
        return implode('', array_reverse(str_split($value, 2)));
    }

    /**
     * @param string $value Binary array
     * @return false|string
     */
    public static function swapEndiannessBin($value)
    {
        return hex2bin(self::swapEndianness(bin2hex($value)));
    }

    /**
     * @param   integer $r  The number of rounds per block
     * @param $b
     * @param $data
     * @return string
     */
    public static function hash($r, $b, $data)
    {
        // init state
        $state = new \SplFixedArray(32);

        $iv = self::iv($r, $b, 256);

        for ($i = 0; $i < 32; $i += 1) {
            $state[$i] = $iv[$i];
        }

        // update with data
        $data .= chr(128);
        $data = self::swapEndiannessBin($data);
        $data = padBlock($data, $b);
        $data = self::swapEndiannessBin($data);

        for ($j = 0; $j < strlen($data); $j += $b) {
            $block = substr($data, $j, $b);
            $block = bin2hex($block);
            $n = 0;
            for ($i = 0; $i < strlen($block); $i += 8) {
                $byte = substr($block, $i, 8);
                $byte = self::swapEndianness($byte);
                $state[$n++] ^= hexdec($byte);
            }
            $state = self::transform($r, $state);
        }

        // finalize
        $state[31] ^= 1;

        for ($i = 0; $i < 10; $i += 1) {
            self::transform($r, $state);
        }

        // Example for '' (empty string hash)
//        $state = [
//              -1561800392 => '38d1e8a2',
//               -961905875 => '2d7baac6',
//               -664644867 => 'fd5262d8',
//              -1399003075 => '3de89cac',
//              -3546249993 => 'f784a02c',
//              -3399252310 => 'aa866335',
//              -2373478103 => '29998772',
//              -2789414358 => '2aeabc59',
//              -1685548184 => '6893889b',
//              -3641377267 => '0dfef426',
//              -1599741972 => 'ecdfa5a0',
//              -3971411240 => 'd8124913',
//              -3644175510 => '6a4bca26',
//              -2826556663 => '092b8657',
//               -854482381 => '33a211cd',
//              -3508776574 => '8251dc2e',
//            -370904486330 => '46ae5ea4',
//            -399992847312 => '308491de',
//            -333955865119 => 'e1e5ad3e',
//            -367735441682 => 'ee764261',
//            -358144429942 => '8ab0ed9c',
//            -372630171200 => 'c0d1823d',
//            -347016896839 => 'b95e2e34',
//            -362137648016 => '7004eaae',
//            -386413349251 => '7d36f807',
//            -350848758866 => 'aecbc84f',
//            -386257376189 => '432c4411',
//            -383406607732 => '8c722fbb',
//            -360378781490 => 'ce30c017',
//            -385614396406 => '0a449737',
//            -401364645913 => 'e787cd8c',
//            -359536410101 => '0bc2f549',
//        ];
        // concat hex produces a hash : 38d1e8a22d7baac6fd5262d83de89cacf784a02caa866335299987722aeabc59

        // convert to hex
        $s = '';
        for ($i = 0; $i < 8; $i += 1) {
            $s .= self::signed2hex($state[$i], false);
        }

        return $s;
    }

    /**
     * @param   integer         $r      The number of rounds per block
     * @param   \SplFixedArray  $state  The main context
     * @return mixed
     */
    protected static function transform($r, $state)
    {
        $y = new \SplFixedArray(16);

        for ($ri = 0;$ri < $r; $ri += 1) {
            for ($i = 0; $i < 16; $i += 1) $state[$i + 16] += $y[$i^8] = $state[$i];
            for ($i = 0; $i < 16; $i += 1) $state[$i]       = i32(self::rotate($y[$i],  7)^$state[$i + 16]);
            for ($i = 0; $i < 16; $i += 1) $y[$i^2]         = $state[$i + 16];
            for ($i = 0; $i < 16; $i += 1) $state[$i + 16]  = $y[$i] + $state[$i];
            for ($i = 0; $i < 16; $i += 1) $y[$i^4]         = $state[$i];
            for ($i = 0; $i < 16; $i += 1) $state[$i]       = i32(self::rotate($y[$i], 11)^$state[$i + 16]);
            for ($i = 0; $i < 16; $i += 2) self::swap($state, $i + 16, $i + 17);
        }

        return $state;
    }

    protected static function rotate($a, $b)
    {
        return (0xffffffff00000000 | ($a << $b)) | (($a & 0x00000000ffffffff) >> (32 - $b));
    }

    protected static function swap($arr, $i, $j)
    {
        $tmp = $arr[$i];
        $arr[$i] = $arr[$j];
        $arr[$j] = $tmp;

        return $arr;
    }

    /**
     * Converts signed decimal to hex (Two's complement)
     *
     * @param $value int, signed
     *
     * @param $reverseEndianness bool, if true reverses the byte order (see machine dependency)
     *
     * @return string, upper case hex value, both bytes padded left with zeros
     */
    protected static function signed2hex($value, $reverseEndianness = true)
    {
        $packed = pack('i', $value);
        $hex='';
        for ($i=0; $i < 4; $i++){
            $hex .= str_pad( dechex(ord($packed[$i])) , 2, '0', STR_PAD_LEFT);
        }

        return $reverseEndianness ? self::swapEndianness($hex) : $hex;
    }
}

function padBlock($input, $blockSizeBytes)
{
    $blockSize = $blockSizeBytes * 2;
    $input = bin2hex($input);
    $input = str_repeat('0', $blockSize - ((strlen($input) % $blockSize) ?: $blockSize)) . $input;
    $input = !strlen($input) ? str_repeat('0', $blockSize) : $input;
    return hex2bin($input);
}

function assertEquals($a, $b) {
    ($a === $b) or
      die(
        "Equality assertion failed:\n\n" .
        "Expected:\n" . (is_array($a) ? 'Array' : $a) . "\n".
        "Actual:\n"   . (is_array($b) ? 'Array' : $b) . "\n"
      );
}

// KNOWN EXAPMLES:

// CubeHash10+1/1+10-256:
assertEquals(
    [
          -1364746004,   1413475564,   -896460311,  1495629273,
            831721974,  -1114895892,  -1131802667, -1345622233,
          -966112469,  -1678886309,   -691894012,  1358586066,
          -1854453552,  -1070683759,    748270806,  1529972269,
          2136745624,   4462657195,   9868789035,  6357691622,
          -4981802178,  -6422354290,  -1400617557, -2741214963,
          -272471494,   5329758627,   2868717903,   710720199,
          -709654222,   2807896093,   2249717683,  2710321235,
    ],
    CubeHash256::iv(1, 1, 256)
);

assertEquals('80f72e07d04ddadb44a78823e0af2ea9f72ef3bf366fd773aa1fa33fc030e5cb', cubehash256(1, 1, ''));

assertEquals('f63041a946aa98bd47f3175e6009dcb2ccf597b2718617ba46d56f27ffe35d49', cubehash256(1, 1, 'Hello'));

assertEquals('217a4876f2b24cec489c9171f85d53395cc979156ea0254938c4c2c59dfdf8a4', cubehash256(1, 1, 'The quick brown fox jumps over the lazy dog'));

// CubeHash80+8/1+80-256
assertEquals(
    [
          -2096419883,    658334063,   -679114902,  1246757400,
          -1523021469,   -289996037,   1196718146,  1168084361,
          -2027445816,  -1170162360,   -822837272,   625197683,
          1543712850,  -1365258909,    759513533,  -424228083,
        -13765010209,  -2824905881,  -9887154026, 19352563566,
          5669379852, -31581549269,  21359466527, 10648459874,
          -8561790247,   9779462261, -22434204802, -4124492433,
          19608647852,   9541915967,   5144979599, -4355863926,
    ],
    CubeHash256::iv(8, 1, 256)
);

assertEquals('38d1e8a22d7baac6fd5262d83de89cacf784a02caa866335299987722aeabc59', cubehash256(8, 1, ''));

assertEquals('692638db57760867326f851bd2376533f37b640bd47a0ddc607a9456b692f70f', cubehash256(8, 1, 'Hello'));

assertEquals('94e0c958d85cdfaf554919980f0f50b945b88ad08413e0762d6ff0219aff3e55', cubehash256(8, 1, 'The quick brown fox jumps over the lazy dog'));

// CubeHash160+16/32+160-256
assertEquals(
    [
          -366226252,   -858328417,   1662090865,    893918894,
            575745371,   -438743453,   2120368433,   -187952450,
          -1026509162,   1118773360,   -797832139,    862050956,
            684518564,  -1896305277,   1182837760,   1088813995,
          7928299971,   5922880469, -36834009791, -21731580295,
        -42794932919,  35964212739,  -2587323651, -57649962363,
          5015516590, -27409035680, -45243252771,  58068387621,
        -29703972117,   5548452566, -40318076753, -30769206262,
    ],
    CubeHash256::iv(16, 32, 256)
);

// multybyte-blocks for an empty string works propertly.
assertEquals('44c6de3ac6c73c391bf0906cb7482600ec06b216c7c54a2a8688a6a42676577d', cubehash256(16, 32, ''));

// padding with 0 works propertly
assertEquals('e27007aa498dd2100ffc76ce4eff578e6eb89908967186156f065cf6a61f6855', cubehash256(16, 32, padBlock('', 32)));

// WORKING
assertEquals('e712139e3b892f2f5fe52d0f30d78a0cb16b51b217da0e4acb103dd0856f2db0', cubehash256(16, 32, 'Hello'));

// padding with 0 is WORKING
assertEquals('72070e821ab48ae56cb693c1e59676a9d4c430b0a9321adedea5f00c825c9f69', cubehash256(16, 32, padBlock('Hello', 32)));

// WORKING
assertEquals('3632b64528815b66875e7feb9be68fe3e0e502dd405d7910c23f16e6b6ffeef7', cubehash256(16, 32, hex2bin('8072a313bb0819a9874277b11303e252f3af7a229a407c1a618e8e4f38af6ca3')));

// WORKING
assertEquals('5151e251e348cbbfee46538651c06b138b10eeb71cf6ea6054d7ca5fec82eb79', cubehash256(16, 32, 'The quick brown fox jumps over the lazy dog'));

echo "Done.";

运行在线:https://repl.it/repls/AwesomeWorldlyWebsite

包装商:https://packagist.org/packages/cast/cubehash