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