phphashcryptographysha-3

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


I'm newbie in cryptography.

I'm trying to implement CubeHash hashing function on pure PHP. I've ported some JS-version (32-bit) to PHP (64-bit), but I'm not able to implement CubeHash with param b when it is grater than 1 (block size is grater than 1 byte).

Please, help me.

run online: 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.";

Update:

From documentation:

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.


Solution

  • I've solve my issue with multi-byte blocks.

    Key features is (from official docs http://cubehash.cr.yp.to):

    So we need to:

    The next part was modified:

    From:

         ...
         /**
         * @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;
          ...
          }
    

    To:

        /**
         * @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;
         ...
         }
    

    The full version is:

    <?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.";
    

    Run online: https://repl.it/repls/AwesomeWorldlyWebsite

    Packagist: https://packagist.org/packages/cast/cubehash