wp/wp-includes/sodium_compat/src/Core/BLAKE2b.php
changeset 9 177826044cd9
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
       
     1 <?php
       
     2 
       
     3 if (class_exists('ParagonIE_Sodium_Core_BLAKE2b', false)) {
       
     4     return;
       
     5 }
       
     6 
       
     7 /**
       
     8  * Class ParagonIE_Sodium_Core_BLAKE2b
       
     9  *
       
    10  * Based on the work of Devi Mandiri in devi/salt.
       
    11  */
       
    12 abstract class ParagonIE_Sodium_Core_BLAKE2b extends ParagonIE_Sodium_Core_Util
       
    13 {
       
    14     /**
       
    15      * @var SplFixedArray
       
    16      */
       
    17     protected static $iv;
       
    18 
       
    19     /**
       
    20      * @var array<int, array<int, int>>
       
    21      */
       
    22     protected static $sigma = array(
       
    23         array(  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15),
       
    24         array( 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3),
       
    25         array( 11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4),
       
    26         array(  7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8),
       
    27         array(  9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13),
       
    28         array(  2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9),
       
    29         array( 12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11),
       
    30         array( 13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10),
       
    31         array(  6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5),
       
    32         array( 10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13 , 0),
       
    33         array(  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15),
       
    34         array( 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3)
       
    35     );
       
    36 
       
    37     const BLOCKBYTES = 128;
       
    38     const OUTBYTES   = 64;
       
    39     const KEYBYTES   = 64;
       
    40 
       
    41     /**
       
    42      * Turn two 32-bit integers into a fixed array representing a 64-bit integer.
       
    43      *
       
    44      * @internal You should not use this directly from another application
       
    45      *
       
    46      * @param int $high
       
    47      * @param int $low
       
    48      * @return SplFixedArray
       
    49      * @psalm-suppress MixedAssignment
       
    50      */
       
    51     public static function new64($high, $low)
       
    52     {
       
    53         $i64 = new SplFixedArray(2);
       
    54         $i64[0] = $high & 0xffffffff;
       
    55         $i64[1] = $low & 0xffffffff;
       
    56         return $i64;
       
    57     }
       
    58 
       
    59     /**
       
    60      * Convert an arbitrary number into an SplFixedArray of two 32-bit integers
       
    61      * that represents a 64-bit integer.
       
    62      *
       
    63      * @internal You should not use this directly from another application
       
    64      *
       
    65      * @param int $num
       
    66      * @return SplFixedArray
       
    67      */
       
    68     protected static function to64($num)
       
    69     {
       
    70         list($hi, $lo) = self::numericTo64BitInteger($num);
       
    71         return self::new64($hi, $lo);
       
    72     }
       
    73 
       
    74     /**
       
    75      * Adds two 64-bit integers together, returning their sum as a SplFixedArray
       
    76      * containing two 32-bit integers (representing a 64-bit integer).
       
    77      *
       
    78      * @internal You should not use this directly from another application
       
    79      *
       
    80      * @param SplFixedArray $x
       
    81      * @param SplFixedArray $y
       
    82      * @return SplFixedArray
       
    83      * @psalm-suppress MixedArgument
       
    84      * @psalm-suppress MixedAssignment
       
    85      * @psalm-suppress MixedOperand
       
    86      */
       
    87     protected static function add64($x, $y)
       
    88     {
       
    89         $l = ($x[1] + $y[1]) & 0xffffffff;
       
    90         return self::new64(
       
    91             $x[0] + $y[0] + (
       
    92                 ($l < $x[1]) ? 1 : 0
       
    93             ),
       
    94             $l
       
    95         );
       
    96     }
       
    97 
       
    98     /**
       
    99      * @internal You should not use this directly from another application
       
   100      *
       
   101      * @param SplFixedArray $x
       
   102      * @param SplFixedArray $y
       
   103      * @param SplFixedArray $z
       
   104      * @return SplFixedArray
       
   105      */
       
   106     protected static function add364($x, $y, $z)
       
   107     {
       
   108         return self::add64($x, self::add64($y, $z));
       
   109     }
       
   110 
       
   111     /**
       
   112      * @internal You should not use this directly from another application
       
   113      *
       
   114      * @param SplFixedArray $x
       
   115      * @param SplFixedArray $y
       
   116      * @return SplFixedArray
       
   117      * @throws SodiumException
       
   118      * @throws TypeError
       
   119      */
       
   120     protected static function xor64(SplFixedArray $x, SplFixedArray $y)
       
   121     {
       
   122         if (!is_numeric($x[0])) {
       
   123             throw new SodiumException('x[0] is not an integer');
       
   124         }
       
   125         if (!is_numeric($x[1])) {
       
   126             throw new SodiumException('x[1] is not an integer');
       
   127         }
       
   128         if (!is_numeric($y[0])) {
       
   129             throw new SodiumException('y[0] is not an integer');
       
   130         }
       
   131         if (!is_numeric($y[1])) {
       
   132             throw new SodiumException('y[1] is not an integer');
       
   133         }
       
   134         return self::new64(
       
   135             (int) ($x[0] ^ $y[0]),
       
   136             (int) ($x[1] ^ $y[1])
       
   137         );
       
   138     }
       
   139 
       
   140     /**
       
   141      * @internal You should not use this directly from another application
       
   142      *
       
   143      * @param SplFixedArray $x
       
   144      * @param int $c
       
   145      * @return SplFixedArray
       
   146      * @psalm-suppress MixedAssignment
       
   147      */
       
   148     public static function rotr64($x, $c)
       
   149     {
       
   150         if ($c >= 64) {
       
   151             $c %= 64;
       
   152         }
       
   153         if ($c >= 32) {
       
   154             /** @var int $tmp */
       
   155             $tmp = $x[0];
       
   156             $x[0] = $x[1];
       
   157             $x[1] = $tmp;
       
   158             $c -= 32;
       
   159         }
       
   160         if ($c === 0) {
       
   161             return $x;
       
   162         }
       
   163 
       
   164         $l0 = 0;
       
   165         $c = 64 - $c;
       
   166 
       
   167         if ($c < 32) {
       
   168             /** @var int $h0 */
       
   169             $h0 = ((int) ($x[0]) << $c) | (
       
   170                 (
       
   171                     (int) ($x[1]) & ((1 << $c) - 1)
       
   172                         <<
       
   173                     (32 - $c)
       
   174                 ) >> (32 - $c)
       
   175             );
       
   176             /** @var int $l0 */
       
   177             $l0 = (int) ($x[1]) << $c;
       
   178         } else {
       
   179             /** @var int $h0 */
       
   180             $h0 = (int) ($x[1]) << ($c - 32);
       
   181         }
       
   182 
       
   183         $h1 = 0;
       
   184         $c1 = 64 - $c;
       
   185 
       
   186         if ($c1 < 32) {
       
   187             /** @var int $h1 */
       
   188             $h1 = (int) ($x[0]) >> $c1;
       
   189             /** @var int $l1 */
       
   190             $l1 = ((int) ($x[1]) >> $c1) | ((int) ($x[0]) & ((1 << $c1) - 1)) << (32 - $c1);
       
   191         } else {
       
   192             /** @var int $l1 */
       
   193             $l1 = (int) ($x[0]) >> ($c1 - 32);
       
   194         }
       
   195 
       
   196         return self::new64($h0 | $h1, $l0 | $l1);
       
   197     }
       
   198 
       
   199     /**
       
   200      * @internal You should not use this directly from another application
       
   201      *
       
   202      * @param SplFixedArray $x
       
   203      * @return int
       
   204      * @psalm-suppress MixedOperand
       
   205      */
       
   206     protected static function flatten64($x)
       
   207     {
       
   208         return (int) ($x[0] * 4294967296 + $x[1]);
       
   209     }
       
   210 
       
   211     /**
       
   212      * @internal You should not use this directly from another application
       
   213      *
       
   214      * @param SplFixedArray $x
       
   215      * @param int $i
       
   216      * @return SplFixedArray
       
   217      * @psalm-suppress MixedArgument
       
   218      * @psalm-suppress MixedArrayOffset
       
   219      */
       
   220     protected static function load64(SplFixedArray $x, $i)
       
   221     {
       
   222         /** @var int $l */
       
   223         $l = (int) ($x[$i])
       
   224              | ((int) ($x[$i+1]) << 8)
       
   225              | ((int) ($x[$i+2]) << 16)
       
   226              | ((int) ($x[$i+3]) << 24);
       
   227         /** @var int $h */
       
   228         $h = (int) ($x[$i+4])
       
   229              | ((int) ($x[$i+5]) << 8)
       
   230              | ((int) ($x[$i+6]) << 16)
       
   231              | ((int) ($x[$i+7]) << 24);
       
   232         return self::new64($h, $l);
       
   233     }
       
   234 
       
   235     /**
       
   236      * @internal You should not use this directly from another application
       
   237      *
       
   238      * @param SplFixedArray $x
       
   239      * @param int $i
       
   240      * @param SplFixedArray $u
       
   241      * @return void
       
   242      * @psalm-suppress MixedAssignment
       
   243      */
       
   244     protected static function store64(SplFixedArray $x, $i, SplFixedArray $u)
       
   245     {
       
   246         $maxLength = $x->getSize() - 1;
       
   247         for ($j = 0; $j < 8; ++$j) {
       
   248             /*
       
   249                [0, 1, 2, 3, 4, 5, 6, 7]
       
   250                     ... becomes ...
       
   251                [0, 0, 0, 0, 1, 1, 1, 1]
       
   252             */
       
   253             /** @var int $uIdx */
       
   254             $uIdx = ((7 - $j) & 4) >> 2;
       
   255             $x[$i]   = ((int) ($u[$uIdx]) & 0xff);
       
   256             if (++$i > $maxLength) {
       
   257                 return;
       
   258             }
       
   259             /** @psalm-suppress MixedOperand */
       
   260             $u[$uIdx] >>= 8;
       
   261         }
       
   262     }
       
   263 
       
   264     /**
       
   265      * This just sets the $iv static variable.
       
   266      *
       
   267      * @internal You should not use this directly from another application
       
   268      *
       
   269      * @return void
       
   270      */
       
   271     public static function pseudoConstructor()
       
   272     {
       
   273         static $called = false;
       
   274         if ($called) {
       
   275             return;
       
   276         }
       
   277         self::$iv = new SplFixedArray(8);
       
   278         self::$iv[0] = self::new64(0x6a09e667, 0xf3bcc908);
       
   279         self::$iv[1] = self::new64(0xbb67ae85, 0x84caa73b);
       
   280         self::$iv[2] = self::new64(0x3c6ef372, 0xfe94f82b);
       
   281         self::$iv[3] = self::new64(0xa54ff53a, 0x5f1d36f1);
       
   282         self::$iv[4] = self::new64(0x510e527f, 0xade682d1);
       
   283         self::$iv[5] = self::new64(0x9b05688c, 0x2b3e6c1f);
       
   284         self::$iv[6] = self::new64(0x1f83d9ab, 0xfb41bd6b);
       
   285         self::$iv[7] = self::new64(0x5be0cd19, 0x137e2179);
       
   286 
       
   287         $called = true;
       
   288     }
       
   289 
       
   290     /**
       
   291      * Returns a fresh BLAKE2 context.
       
   292      *
       
   293      * @internal You should not use this directly from another application
       
   294      *
       
   295      * @return SplFixedArray
       
   296      * @psalm-suppress MixedAssignment
       
   297      * @psalm-suppress MixedArrayAccess
       
   298      * @psalm-suppress MixedArrayAssignment
       
   299      */
       
   300     protected static function context()
       
   301     {
       
   302         $ctx    = new SplFixedArray(5);
       
   303         $ctx[0] = new SplFixedArray(8);   // h
       
   304         $ctx[1] = new SplFixedArray(2);   // t
       
   305         $ctx[2] = new SplFixedArray(2);   // f
       
   306         $ctx[3] = new SplFixedArray(256); // buf
       
   307         $ctx[4] = 0;                      // buflen
       
   308 
       
   309         for ($i = 8; $i--;) {
       
   310             $ctx[0][$i] = self::$iv[$i];
       
   311         }
       
   312         for ($i = 256; $i--;) {
       
   313             $ctx[3][$i] = 0;
       
   314         }
       
   315 
       
   316         $zero = self::new64(0, 0);
       
   317         $ctx[1][0] = $zero;
       
   318         $ctx[1][1] = $zero;
       
   319         $ctx[2][0] = $zero;
       
   320         $ctx[2][1] = $zero;
       
   321 
       
   322         return $ctx;
       
   323     }
       
   324 
       
   325     /**
       
   326      * @internal You should not use this directly from another application
       
   327      *
       
   328      * @param SplFixedArray $ctx
       
   329      * @param SplFixedArray $buf
       
   330      * @return void
       
   331      * @throws SodiumException
       
   332      * @throws TypeError
       
   333      * @psalm-suppress MixedArgument
       
   334      * @psalm-suppress MixedAssignment
       
   335      * @psalm-suppress MixedArrayAccess
       
   336      * @psalm-suppress MixedArrayAssignment
       
   337      * @psalm-suppress MixedArrayOffset
       
   338      */
       
   339     protected static function compress(SplFixedArray $ctx, SplFixedArray $buf)
       
   340     {
       
   341         $m = new SplFixedArray(16);
       
   342         $v = new SplFixedArray(16);
       
   343 
       
   344         for ($i = 16; $i--;) {
       
   345             $m[$i] = self::load64($buf, $i << 3);
       
   346         }
       
   347 
       
   348         for ($i = 8; $i--;) {
       
   349             $v[$i] = $ctx[0][$i];
       
   350         }
       
   351 
       
   352         $v[ 8] = self::$iv[0];
       
   353         $v[ 9] = self::$iv[1];
       
   354         $v[10] = self::$iv[2];
       
   355         $v[11] = self::$iv[3];
       
   356 
       
   357         $v[12] = self::xor64($ctx[1][0], self::$iv[4]);
       
   358         $v[13] = self::xor64($ctx[1][1], self::$iv[5]);
       
   359         $v[14] = self::xor64($ctx[2][0], self::$iv[6]);
       
   360         $v[15] = self::xor64($ctx[2][1], self::$iv[7]);
       
   361 
       
   362         for ($r = 0; $r < 12; ++$r) {
       
   363             $v = self::G($r, 0, 0, 4, 8, 12, $v, $m);
       
   364             $v = self::G($r, 1, 1, 5, 9, 13, $v, $m);
       
   365             $v = self::G($r, 2, 2, 6, 10, 14, $v, $m);
       
   366             $v = self::G($r, 3, 3, 7, 11, 15, $v, $m);
       
   367             $v = self::G($r, 4, 0, 5, 10, 15, $v, $m);
       
   368             $v = self::G($r, 5, 1, 6, 11, 12, $v, $m);
       
   369             $v = self::G($r, 6, 2, 7, 8, 13, $v, $m);
       
   370             $v = self::G($r, 7, 3, 4, 9, 14, $v, $m);
       
   371         }
       
   372 
       
   373         for ($i = 8; $i--;) {
       
   374             $ctx[0][$i] = self::xor64(
       
   375                 $ctx[0][$i], self::xor64($v[$i], $v[$i+8])
       
   376             );
       
   377         }
       
   378     }
       
   379 
       
   380     /**
       
   381      * @internal You should not use this directly from another application
       
   382      *
       
   383      * @param int $r
       
   384      * @param int $i
       
   385      * @param int $a
       
   386      * @param int $b
       
   387      * @param int $c
       
   388      * @param int $d
       
   389      * @param SplFixedArray $v
       
   390      * @param SplFixedArray $m
       
   391      * @return SplFixedArray
       
   392      * @throws SodiumException
       
   393      * @throws TypeError
       
   394      * @psalm-suppress MixedArgument
       
   395      * @psalm-suppress MixedArrayOffset
       
   396      */
       
   397     public static function G($r, $i, $a, $b, $c, $d, SplFixedArray $v, SplFixedArray $m)
       
   398     {
       
   399         $v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][$i << 1]]);
       
   400         $v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 32);
       
   401         $v[$c] = self::add64($v[$c], $v[$d]);
       
   402         $v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 24);
       
   403         $v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][($i << 1) + 1]]);
       
   404         $v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 16);
       
   405         $v[$c] = self::add64($v[$c], $v[$d]);
       
   406         $v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 63);
       
   407         return $v;
       
   408     }
       
   409 
       
   410     /**
       
   411      * @internal You should not use this directly from another application
       
   412      *
       
   413      * @param SplFixedArray $ctx
       
   414      * @param int $inc
       
   415      * @return void
       
   416      * @throws SodiumException
       
   417      * @psalm-suppress MixedArgument
       
   418      * @psalm-suppress MixedArrayAccess
       
   419      * @psalm-suppress MixedArrayAssignment
       
   420      */
       
   421     public static function increment_counter($ctx, $inc)
       
   422     {
       
   423         if ($inc < 0) {
       
   424             throw new SodiumException('Increasing by a negative number makes no sense.');
       
   425         }
       
   426         $t = self::to64($inc);
       
   427         # S->t is $ctx[1] in our implementation
       
   428 
       
   429         # S->t[0] = ( uint64_t )( t >> 0 );
       
   430         $ctx[1][0] = self::add64($ctx[1][0], $t);
       
   431 
       
   432         # S->t[1] += ( S->t[0] < inc );
       
   433         if (self::flatten64($ctx[1][0]) < $inc) {
       
   434             $ctx[1][1] = self::add64($ctx[1][1], self::to64(1));
       
   435         }
       
   436     }
       
   437 
       
   438     /**
       
   439      * @internal You should not use this directly from another application
       
   440      *
       
   441      * @param SplFixedArray $ctx
       
   442      * @param SplFixedArray $p
       
   443      * @param int $plen
       
   444      * @return void
       
   445      * @throws SodiumException
       
   446      * @throws TypeError
       
   447      * @psalm-suppress MixedArgument
       
   448      * @psalm-suppress MixedAssignment
       
   449      * @psalm-suppress MixedArrayAccess
       
   450      * @psalm-suppress MixedArrayAssignment
       
   451      * @psalm-suppress MixedArrayOffset
       
   452      * @psalm-suppress MixedOperand
       
   453      */
       
   454     public static function update(SplFixedArray $ctx, SplFixedArray $p, $plen)
       
   455     {
       
   456         self::pseudoConstructor();
       
   457 
       
   458         $offset = 0;
       
   459         while ($plen > 0) {
       
   460             $left = $ctx[4];
       
   461             $fill = 256 - $left;
       
   462 
       
   463             if ($plen > $fill) {
       
   464                 # memcpy( S->buf + left, in, fill ); /* Fill buffer */
       
   465                 for ($i = $fill; $i--;) {
       
   466                     $ctx[3][$i + $left] = $p[$i + $offset];
       
   467                 }
       
   468 
       
   469                 # S->buflen += fill;
       
   470                 $ctx[4] += $fill;
       
   471 
       
   472                 # blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
       
   473                 self::increment_counter($ctx, 128);
       
   474 
       
   475                 # blake2b_compress( S, S->buf ); /* Compress */
       
   476                 self::compress($ctx, $ctx[3]);
       
   477 
       
   478                 # memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); /* Shift buffer left */
       
   479                 for ($i = 128; $i--;) {
       
   480                     $ctx[3][$i] = $ctx[3][$i + 128];
       
   481                 }
       
   482 
       
   483                 # S->buflen -= BLAKE2B_BLOCKBYTES;
       
   484                 $ctx[4] -= 128;
       
   485 
       
   486                 # in += fill;
       
   487                 $offset += $fill;
       
   488 
       
   489                 # inlen -= fill;
       
   490                 $plen -= $fill;
       
   491             } else {
       
   492                 for ($i = $plen; $i--;) {
       
   493                     $ctx[3][$i + $left] = $p[$i + $offset];
       
   494                 }
       
   495                 $ctx[4] += $plen;
       
   496                 $offset += $plen;
       
   497                 $plen -= $plen;
       
   498             }
       
   499         }
       
   500     }
       
   501 
       
   502     /**
       
   503      * @internal You should not use this directly from another application
       
   504      *
       
   505      * @param SplFixedArray $ctx
       
   506      * @param SplFixedArray $out
       
   507      * @return SplFixedArray
       
   508      * @throws SodiumException
       
   509      * @throws TypeError
       
   510      * @psalm-suppress MixedArgument
       
   511      * @psalm-suppress MixedAssignment
       
   512      * @psalm-suppress MixedArrayAccess
       
   513      * @psalm-suppress MixedArrayAssignment
       
   514      * @psalm-suppress MixedArrayOffset
       
   515      * @psalm-suppress MixedOperand
       
   516      */
       
   517     public static function finish(SplFixedArray $ctx, SplFixedArray $out)
       
   518     {
       
   519         self::pseudoConstructor();
       
   520         if ($ctx[4] > 128) {
       
   521             self::increment_counter($ctx, 128);
       
   522             self::compress($ctx, $ctx[3]);
       
   523             $ctx[4] -= 128;
       
   524             if ($ctx[4] > 128) {
       
   525                 throw new SodiumException('Failed to assert that buflen <= 128 bytes');
       
   526             }
       
   527             for ($i = $ctx[4]; $i--;) {
       
   528                 $ctx[3][$i] = $ctx[3][$i + 128];
       
   529             }
       
   530         }
       
   531 
       
   532         self::increment_counter($ctx, $ctx[4]);
       
   533         $ctx[2][0] = self::new64(0xffffffff, 0xffffffff);
       
   534 
       
   535         for ($i = 256 - $ctx[4]; $i--;) {
       
   536             $ctx[3][$i+$ctx[4]] = 0;
       
   537         }
       
   538 
       
   539         self::compress($ctx, $ctx[3]);
       
   540 
       
   541         $i = (int) (($out->getSize() - 1) / 8);
       
   542         for (; $i >= 0; --$i) {
       
   543             self::store64($out, $i << 3, $ctx[0][$i]);
       
   544         }
       
   545         return $out;
       
   546     }
       
   547 
       
   548     /**
       
   549      * @internal You should not use this directly from another application
       
   550      *
       
   551      * @param SplFixedArray|null $key
       
   552      * @param int $outlen
       
   553      * @return SplFixedArray
       
   554      * @throws SodiumException
       
   555      * @throws TypeError
       
   556      * @psalm-suppress MixedArgument
       
   557      * @psalm-suppress MixedAssignment
       
   558      * @psalm-suppress MixedArrayAccess
       
   559      * @psalm-suppress MixedArrayAssignment
       
   560      * @psalm-suppress MixedArrayOffset
       
   561      */
       
   562     public static function init($key = null, $outlen = 64)
       
   563     {
       
   564         self::pseudoConstructor();
       
   565         $klen = 0;
       
   566 
       
   567         if ($key !== null) {
       
   568             if (count($key) > 64) {
       
   569                 throw new SodiumException('Invalid key size');
       
   570             }
       
   571             $klen = count($key);
       
   572         }
       
   573 
       
   574         if ($outlen > 64) {
       
   575             throw new SodiumException('Invalid output size');
       
   576         }
       
   577 
       
   578         $ctx = self::context();
       
   579 
       
   580         $p = new SplFixedArray(64);
       
   581         for ($i = 64; --$i;) {
       
   582             $p[$i] = 0;
       
   583         }
       
   584 
       
   585         $p[0] = $outlen; // digest_length
       
   586         $p[1] = $klen;   // key_length
       
   587         $p[2] = 1;       // fanout
       
   588         $p[3] = 1;       // depth
       
   589 
       
   590         $ctx[0][0] = self::xor64(
       
   591             $ctx[0][0],
       
   592             self::load64($p, 0)
       
   593         );
       
   594 
       
   595         if ($klen > 0 && $key instanceof SplFixedArray) {
       
   596             $block = new SplFixedArray(128);
       
   597             for ($i = 128; $i--;) {
       
   598                 $block[$i] = 0;
       
   599             }
       
   600             for ($i = $klen; $i--;) {
       
   601                 $block[$i] = $key[$i];
       
   602             }
       
   603             self::update($ctx, $block, 128);
       
   604         }
       
   605 
       
   606         return $ctx;
       
   607     }
       
   608 
       
   609     /**
       
   610      * Convert a string into an SplFixedArray of integers
       
   611      *
       
   612      * @internal You should not use this directly from another application
       
   613      *
       
   614      * @param string $str
       
   615      * @return SplFixedArray
       
   616      */
       
   617     public static function stringToSplFixedArray($str = '')
       
   618     {
       
   619         $values = unpack('C*', $str);
       
   620         return SplFixedArray::fromArray(array_values($values));
       
   621     }
       
   622 
       
   623     /**
       
   624      * Convert an SplFixedArray of integers into a string
       
   625      *
       
   626      * @internal You should not use this directly from another application
       
   627      *
       
   628      * @param SplFixedArray $a
       
   629      * @return string
       
   630      * @throws TypeError
       
   631      */
       
   632     public static function SplFixedArrayToString(SplFixedArray $a)
       
   633     {
       
   634         /**
       
   635          * @var array<int, int|string> $arr
       
   636          */
       
   637         $arr = $a->toArray();
       
   638         $c = $a->count();
       
   639         array_unshift($arr, str_repeat('C', $c));
       
   640         return (string) (call_user_func_array('pack', $arr));
       
   641     }
       
   642 
       
   643     /**
       
   644      * @internal You should not use this directly from another application
       
   645      *
       
   646      * @param SplFixedArray $ctx
       
   647      * @return string
       
   648      * @throws TypeError
       
   649      * @psalm-suppress MixedArgument
       
   650      * @psalm-suppress MixedAssignment
       
   651      * @psalm-suppress MixedArrayAccess
       
   652      * @psalm-suppress MixedArrayAssignment
       
   653      * @psalm-suppress MixedArrayOffset
       
   654      * @psalm-suppress MixedMethodCall
       
   655      */
       
   656     public static function contextToString(SplFixedArray $ctx)
       
   657     {
       
   658         $str = '';
       
   659         /** @var array<int, array<int, int>> $ctxA */
       
   660         $ctxA = $ctx[0]->toArray();
       
   661 
       
   662         # uint64_t h[8];
       
   663         for ($i = 0; $i < 8; ++$i) {
       
   664             $str .= self::store32_le($ctxA[$i][1]);
       
   665             $str .= self::store32_le($ctxA[$i][0]);
       
   666         }
       
   667 
       
   668         # uint64_t t[2];
       
   669         # uint64_t f[2];
       
   670         for ($i = 1; $i < 3; ++$i) {
       
   671             $ctxA = $ctx[$i]->toArray();
       
   672             $str .= self::store32_le($ctxA[0][1]);
       
   673             $str .= self::store32_le($ctxA[0][0]);
       
   674             $str .= self::store32_le($ctxA[1][1]);
       
   675             $str .= self::store32_le($ctxA[1][0]);
       
   676         }
       
   677 
       
   678         # uint8_t buf[2 * 128];
       
   679         $str .= self::SplFixedArrayToString($ctx[3]);
       
   680 
       
   681         /** @var int $ctx4 */
       
   682         $ctx4 = (int) $ctx[4];
       
   683 
       
   684         # size_t buflen;
       
   685         $str .= implode('', array(
       
   686             self::intToChr($ctx4 & 0xff),
       
   687             self::intToChr(($ctx4 >> 8) & 0xff),
       
   688             self::intToChr(($ctx4 >> 16) & 0xff),
       
   689             self::intToChr(($ctx4 >> 24) & 0xff),
       
   690             self::intToChr(($ctx4 >> 32) & 0xff),
       
   691             self::intToChr(($ctx4 >> 40) & 0xff),
       
   692             self::intToChr(($ctx4 >> 48) & 0xff),
       
   693             self::intToChr(($ctx4 >> 56) & 0xff)
       
   694         ));
       
   695         # uint8_t last_node;
       
   696         return $str . "\x00";
       
   697     }
       
   698 
       
   699     /**
       
   700      * Creates an SplFixedArray containing other SplFixedArray elements, from
       
   701      * a string (compatible with \Sodium\crypto_generichash_{init, update, final})
       
   702      *
       
   703      * @internal You should not use this directly from another application
       
   704      *
       
   705      * @param string $string
       
   706      * @return SplFixedArray
       
   707      * @throws SodiumException
       
   708      * @throws TypeError
       
   709      * @psalm-suppress MixedArrayAssignment
       
   710      */
       
   711     public static function stringToContext($string)
       
   712     {
       
   713         $ctx = self::context();
       
   714 
       
   715         # uint64_t h[8];
       
   716         for ($i = 0; $i < 8; ++$i) {
       
   717             $ctx[0][$i] = SplFixedArray::fromArray(
       
   718                 array(
       
   719                     self::load_4(
       
   720                         self::substr($string, (($i << 3) + 4), 4)
       
   721                     ),
       
   722                     self::load_4(
       
   723                         self::substr($string, (($i << 3) + 0), 4)
       
   724                     )
       
   725                 )
       
   726             );
       
   727         }
       
   728 
       
   729         # uint64_t t[2];
       
   730         # uint64_t f[2];
       
   731         for ($i = 1; $i < 3; ++$i) {
       
   732             $ctx[$i][1] = SplFixedArray::fromArray(
       
   733                 array(
       
   734                     self::load_4(self::substr($string, 76 + (($i - 1) << 4), 4)),
       
   735                     self::load_4(self::substr($string, 72 + (($i - 1) << 4), 4))
       
   736                 )
       
   737             );
       
   738             $ctx[$i][0] = SplFixedArray::fromArray(
       
   739                 array(
       
   740                     self::load_4(self::substr($string, 68 + (($i - 1) << 4), 4)),
       
   741                     self::load_4(self::substr($string, 64 + (($i - 1) << 4), 4))
       
   742                 )
       
   743             );
       
   744         }
       
   745 
       
   746         # uint8_t buf[2 * 128];
       
   747         $ctx[3] = self::stringToSplFixedArray(self::substr($string, 96, 256));
       
   748 
       
   749 
       
   750         # uint8_t buf[2 * 128];
       
   751         $int = 0;
       
   752         for ($i = 0; $i < 8; ++$i) {
       
   753             $int |= self::chrToInt($string[352 + $i]) << ($i << 3);
       
   754         }
       
   755         $ctx[4] = $int;
       
   756 
       
   757         return $ctx;
       
   758     }
       
   759 }