ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

BLAKE2b.php (22324B)


      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(6);
    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         $ctx[5] = 0;                      // last_node (uint8_t)
    233 
    234         for ($i = 8; $i--;) {
    235             $ctx[0][$i] = self::$iv[$i];
    236         }
    237         for ($i = 256; $i--;) {
    238             $ctx[3][$i] = 0;
    239         }
    240 
    241         $zero = self::new64(0, 0);
    242         $ctx[1][0] = $zero;
    243         $ctx[1][1] = $zero;
    244         $ctx[2][0] = $zero;
    245         $ctx[2][1] = $zero;
    246 
    247         return $ctx;
    248     }
    249 
    250     /**
    251      * @internal You should not use this directly from another application
    252      *
    253      * @param SplFixedArray $ctx
    254      * @param SplFixedArray $buf
    255      * @return void
    256      * @throws SodiumException
    257      * @throws TypeError
    258      * @psalm-suppress MixedArgument
    259      * @psalm-suppress MixedArrayAccess
    260      * @psalm-suppress MixedArrayAssignment
    261      * @psalm-suppress MixedAssignment
    262      */
    263     protected static function compress(SplFixedArray $ctx, SplFixedArray $buf)
    264     {
    265         $m = new SplFixedArray(16);
    266         $v = new SplFixedArray(16);
    267 
    268         for ($i = 16; $i--;) {
    269             $m[$i] = self::load64($buf, $i << 3);
    270         }
    271 
    272         for ($i = 8; $i--;) {
    273             $v[$i] = $ctx[0][$i];
    274         }
    275 
    276         $v[ 8] = self::$iv[0];
    277         $v[ 9] = self::$iv[1];
    278         $v[10] = self::$iv[2];
    279         $v[11] = self::$iv[3];
    280 
    281         $v[12] = self::xor64($ctx[1][0], self::$iv[4]);
    282         $v[13] = self::xor64($ctx[1][1], self::$iv[5]);
    283         $v[14] = self::xor64($ctx[2][0], self::$iv[6]);
    284         $v[15] = self::xor64($ctx[2][1], self::$iv[7]);
    285 
    286         for ($r = 0; $r < 12; ++$r) {
    287             $v = self::G($r, 0, 0, 4, 8, 12, $v, $m);
    288             $v = self::G($r, 1, 1, 5, 9, 13, $v, $m);
    289             $v = self::G($r, 2, 2, 6, 10, 14, $v, $m);
    290             $v = self::G($r, 3, 3, 7, 11, 15, $v, $m);
    291             $v = self::G($r, 4, 0, 5, 10, 15, $v, $m);
    292             $v = self::G($r, 5, 1, 6, 11, 12, $v, $m);
    293             $v = self::G($r, 6, 2, 7, 8, 13, $v, $m);
    294             $v = self::G($r, 7, 3, 4, 9, 14, $v, $m);
    295         }
    296 
    297         for ($i = 8; $i--;) {
    298             $ctx[0][$i] = self::xor64(
    299                 $ctx[0][$i], self::xor64($v[$i], $v[$i+8])
    300             );
    301         }
    302     }
    303 
    304     /**
    305      * @internal You should not use this directly from another application
    306      *
    307      * @param int $r
    308      * @param int $i
    309      * @param int $a
    310      * @param int $b
    311      * @param int $c
    312      * @param int $d
    313      * @param SplFixedArray $v
    314      * @param SplFixedArray $m
    315      * @return SplFixedArray
    316      * @throws SodiumException
    317      * @throws TypeError
    318      * @psalm-suppress MixedArgument
    319      * @psalm-suppress MixedArrayOffset
    320      */
    321     public static function G($r, $i, $a, $b, $c, $d, SplFixedArray $v, SplFixedArray $m)
    322     {
    323         $v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][$i << 1]]);
    324         $v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 32);
    325         $v[$c] = self::add64($v[$c], $v[$d]);
    326         $v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 24);
    327         $v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][($i << 1) + 1]]);
    328         $v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 16);
    329         $v[$c] = self::add64($v[$c], $v[$d]);
    330         $v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 63);
    331         return $v;
    332     }
    333 
    334     /**
    335      * @internal You should not use this directly from another application
    336      *
    337      * @param SplFixedArray $ctx
    338      * @param int $inc
    339      * @return void
    340      * @throws SodiumException
    341      * @throws TypeError
    342      * @psalm-suppress MixedArgument
    343      * @psalm-suppress MixedArrayAccess
    344      * @psalm-suppress MixedArrayAssignment
    345      */
    346     public static function increment_counter($ctx, $inc)
    347     {
    348         if ($inc < 0) {
    349             throw new SodiumException('Increasing by a negative number makes no sense.');
    350         }
    351         $t = self::to64($inc);
    352         # S->t is $ctx[1] in our implementation
    353 
    354         # S->t[0] = ( uint64_t )( t >> 0 );
    355         $ctx[1][0] = self::add64($ctx[1][0], $t);
    356 
    357         # S->t[1] += ( S->t[0] < inc );
    358         if (!($ctx[1][0] instanceof ParagonIE_Sodium_Core32_Int64)) {
    359             throw new TypeError('Not an int64');
    360         }
    361         /** @var ParagonIE_Sodium_Core32_Int64 $c*/
    362         $c = $ctx[1][0];
    363         if ($c->isLessThanInt($inc)) {
    364             $ctx[1][1] = self::add64($ctx[1][1], self::to64(1));
    365         }
    366     }
    367 
    368     /**
    369      * @internal You should not use this directly from another application
    370      *
    371      * @param SplFixedArray $ctx
    372      * @param SplFixedArray $p
    373      * @param int $plen
    374      * @return void
    375      * @throws SodiumException
    376      * @throws TypeError
    377      * @psalm-suppress MixedArgument
    378      * @psalm-suppress MixedAssignment
    379      * @psalm-suppress MixedArrayAccess
    380      * @psalm-suppress MixedArrayAssignment
    381      * @psalm-suppress MixedArrayOffset
    382      * @psalm-suppress MixedMethodCall
    383      * @psalm-suppress MixedOperand
    384      */
    385     public static function update(SplFixedArray $ctx, SplFixedArray $p, $plen)
    386     {
    387         self::pseudoConstructor();
    388 
    389         $offset = 0;
    390         while ($plen > 0) {
    391             $left = $ctx[4];
    392             $fill = 256 - $left;
    393 
    394             if ($plen > $fill) {
    395                 # memcpy( S->buf + left, in, fill ); /* Fill buffer */
    396                 for ($i = $fill; $i--;) {
    397                     $ctx[3][$i + $left] = $p[$i + $offset];
    398                 }
    399 
    400                 # S->buflen += fill;
    401                 $ctx[4] += $fill;
    402 
    403                 # blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
    404                 self::increment_counter($ctx, 128);
    405 
    406                 # blake2b_compress( S, S->buf ); /* Compress */
    407                 self::compress($ctx, $ctx[3]);
    408 
    409                 # memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); /* Shift buffer left */
    410                 for ($i = 128; $i--;) {
    411                     $ctx[3][$i] = $ctx[3][$i + 128];
    412                 }
    413 
    414                 # S->buflen -= BLAKE2B_BLOCKBYTES;
    415                 $ctx[4] -= 128;
    416 
    417                 # in += fill;
    418                 $offset += $fill;
    419 
    420                 # inlen -= fill;
    421                 $plen -= $fill;
    422             } else {
    423                 for ($i = $plen; $i--;) {
    424                     $ctx[3][$i + $left] = $p[$i + $offset];
    425                 }
    426                 $ctx[4] += $plen;
    427                 $offset += $plen;
    428                 $plen -= $plen;
    429             }
    430         }
    431     }
    432 
    433     /**
    434      * @internal You should not use this directly from another application
    435      *
    436      * @param SplFixedArray $ctx
    437      * @param SplFixedArray $out
    438      * @return SplFixedArray
    439      * @throws SodiumException
    440      * @throws TypeError
    441      * @psalm-suppress MixedArgument
    442      * @psalm-suppress MixedAssignment
    443      * @psalm-suppress MixedArrayAccess
    444      * @psalm-suppress MixedArrayAssignment
    445      * @psalm-suppress MixedArrayOffset
    446      * @psalm-suppress MixedMethodCall
    447      * @psalm-suppress MixedOperand
    448      */
    449     public static function finish(SplFixedArray $ctx, SplFixedArray $out)
    450     {
    451         self::pseudoConstructor();
    452         if ($ctx[4] > 128) {
    453             self::increment_counter($ctx, 128);
    454             self::compress($ctx, $ctx[3]);
    455             $ctx[4] -= 128;
    456             if ($ctx[4] > 128) {
    457                 throw new SodiumException('Failed to assert that buflen <= 128 bytes');
    458             }
    459             for ($i = $ctx[4]; $i--;) {
    460                 $ctx[3][$i] = $ctx[3][$i + 128];
    461             }
    462         }
    463 
    464         self::increment_counter($ctx, $ctx[4]);
    465         $ctx[2][0] = self::new64(0xffffffff, 0xffffffff);
    466 
    467         for ($i = 256 - $ctx[4]; $i--;) {
    468             /** @var int $i */
    469             $ctx[3][$i + $ctx[4]] = 0;
    470         }
    471 
    472         self::compress($ctx, $ctx[3]);
    473 
    474         $i = (int) (($out->getSize() - 1) / 8);
    475         for (; $i >= 0; --$i) {
    476             self::store64($out, $i << 3, $ctx[0][$i]);
    477         }
    478         return $out;
    479     }
    480 
    481     /**
    482      * @internal You should not use this directly from another application
    483      *
    484      * @param SplFixedArray|null $key
    485      * @param int $outlen
    486      * @param SplFixedArray|null $salt
    487      * @param SplFixedArray|null $personal
    488      * @return SplFixedArray
    489      * @throws SodiumException
    490      * @throws TypeError
    491      * @psalm-suppress MixedArgument
    492      * @psalm-suppress MixedAssignment
    493      * @psalm-suppress MixedArrayAccess
    494      * @psalm-suppress MixedArrayAssignment
    495      * @psalm-suppress MixedMethodCall
    496      */
    497     public static function init(
    498         $key = null,
    499         $outlen = 64,
    500         $salt = null,
    501         $personal = null
    502     ) {
    503         self::pseudoConstructor();
    504         $klen = 0;
    505 
    506         if ($key !== null) {
    507             if (count($key) > 64) {
    508                 throw new SodiumException('Invalid key size');
    509             }
    510             $klen = count($key);
    511         }
    512 
    513         if ($outlen > 64) {
    514             throw new SodiumException('Invalid output size');
    515         }
    516 
    517         $ctx = self::context();
    518 
    519         $p = new SplFixedArray(64);
    520         // Zero our param buffer...
    521         for ($i = 64; --$i;) {
    522             $p[$i] = 0;
    523         }
    524 
    525         $p[0] = $outlen; // digest_length
    526         $p[1] = $klen;   // key_length
    527         $p[2] = 1;       // fanout
    528         $p[3] = 1;       // depth
    529 
    530         if ($salt instanceof SplFixedArray) {
    531             // salt: [32] through [47]
    532             for ($i = 0; $i < 16; ++$i) {
    533                 $p[32 + $i] = (int) $salt[$i];
    534             }
    535         }
    536         if ($personal instanceof SplFixedArray) {
    537             // personal: [48] through [63]
    538             for ($i = 0; $i < 16; ++$i) {
    539                 $p[48 + $i] = (int) $personal[$i];
    540             }
    541         }
    542 
    543         $ctx[0][0] = self::xor64(
    544             $ctx[0][0],
    545             self::load64($p, 0)
    546         );
    547 
    548         if ($salt instanceof SplFixedArray || $personal instanceof SplFixedArray) {
    549             // We need to do what blake2b_init_param() does:
    550             for ($i = 1; $i < 8; ++$i) {
    551                 $ctx[0][$i] = self::xor64(
    552                     $ctx[0][$i],
    553                     self::load64($p, $i << 3)
    554                 );
    555             }
    556         }
    557 
    558         if ($klen > 0 && $key instanceof SplFixedArray) {
    559             $block = new SplFixedArray(128);
    560             for ($i = 128; $i--;) {
    561                 $block[$i] = 0;
    562             }
    563             for ($i = $klen; $i--;) {
    564                 $block[$i] = $key[$i];
    565             }
    566             self::update($ctx, $block, 128);
    567             $ctx[4] = 128;
    568         }
    569 
    570         return $ctx;
    571     }
    572 
    573     /**
    574      * Convert a string into an SplFixedArray of integers
    575      *
    576      * @internal You should not use this directly from another application
    577      *
    578      * @param string $str
    579      * @return SplFixedArray
    580      * @psalm-suppress MixedArgumentTypeCoercion
    581      */
    582     public static function stringToSplFixedArray($str = '')
    583     {
    584         $values = unpack('C*', $str);
    585         return SplFixedArray::fromArray(array_values($values));
    586     }
    587 
    588     /**
    589      * Convert an SplFixedArray of integers into a string
    590      *
    591      * @internal You should not use this directly from another application
    592      *
    593      * @param SplFixedArray $a
    594      * @return string
    595      */
    596     public static function SplFixedArrayToString(SplFixedArray $a)
    597     {
    598         /**
    599          * @var array<int, string|int>
    600          */
    601         $arr = $a->toArray();
    602         $c = $a->count();
    603         array_unshift($arr, str_repeat('C', $c));
    604         return (string) (call_user_func_array('pack', $arr));
    605     }
    606 
    607     /**
    608      * @internal You should not use this directly from another application
    609      *
    610      * @param SplFixedArray $ctx
    611      * @return string
    612      * @throws TypeError
    613      * @psalm-suppress MixedArgument
    614      * @psalm-suppress MixedArrayAccess
    615      * @psalm-suppress MixedArrayAssignment
    616      * @psalm-suppress MixedMethodCall
    617      */
    618     public static function contextToString(SplFixedArray $ctx)
    619     {
    620         $str = '';
    621         /** @var array<int, ParagonIE_Sodium_Core32_Int64> $ctxA */
    622         $ctxA = $ctx[0]->toArray();
    623 
    624         # uint64_t h[8];
    625         for ($i = 0; $i < 8; ++$i) {
    626             if (!($ctxA[$i] instanceof ParagonIE_Sodium_Core32_Int64)) {
    627                 throw new TypeError('Not an instance of Int64');
    628             }
    629             /** @var ParagonIE_Sodium_Core32_Int64 $ctxAi */
    630             $ctxAi = $ctxA[$i];
    631             $str .= $ctxAi->toReverseString();
    632         }
    633 
    634         # uint64_t t[2];
    635         # uint64_t f[2];
    636         for ($i = 1; $i < 3; ++$i) {
    637             /** @var array<int, ParagonIE_Sodium_Core32_Int64> $ctxA */
    638             $ctxA = $ctx[$i]->toArray();
    639             /** @var ParagonIE_Sodium_Core32_Int64 $ctxA1 */
    640             $ctxA1 = $ctxA[0];
    641             /** @var ParagonIE_Sodium_Core32_Int64 $ctxA2 */
    642             $ctxA2 = $ctxA[1];
    643 
    644             $str .= $ctxA1->toReverseString();
    645             $str .= $ctxA2->toReverseString();
    646         }
    647 
    648         # uint8_t buf[2 * 128];
    649         $str .= self::SplFixedArrayToString($ctx[3]);
    650 
    651         /** @var int $ctx4 */
    652         $ctx4 = $ctx[4];
    653 
    654         # size_t buflen;
    655         $str .= implode('', array(
    656             self::intToChr($ctx4 & 0xff),
    657             self::intToChr(($ctx4 >> 8) & 0xff),
    658             self::intToChr(($ctx4 >> 16) & 0xff),
    659             self::intToChr(($ctx4 >> 24) & 0xff),
    660             "\x00\x00\x00\x00"
    661             /*
    662             self::intToChr(($ctx4 >> 32) & 0xff),
    663             self::intToChr(($ctx4 >> 40) & 0xff),
    664             self::intToChr(($ctx4 >> 48) & 0xff),
    665             self::intToChr(($ctx4 >> 56) & 0xff)
    666             */
    667         ));
    668         # uint8_t last_node;
    669         return $str . self::intToChr($ctx[5]) . str_repeat("\x00", 23);
    670     }
    671 
    672     /**
    673      * Creates an SplFixedArray containing other SplFixedArray elements, from
    674      * a string (compatible with \Sodium\crypto_generichash_{init, update, final})
    675      *
    676      * @internal You should not use this directly from another application
    677      *
    678      * @param string $string
    679      * @return SplFixedArray
    680      * @throws SodiumException
    681      * @throws TypeError
    682      * @psalm-suppress MixedArrayAccess
    683      * @psalm-suppress MixedArrayAssignment
    684      */
    685     public static function stringToContext($string)
    686     {
    687         $ctx = self::context();
    688 
    689         # uint64_t h[8];
    690         for ($i = 0; $i < 8; ++$i) {
    691             $ctx[0][$i] = ParagonIE_Sodium_Core32_Int64::fromReverseString(
    692                 self::substr($string, (($i << 3) + 0), 8)
    693             );
    694         }
    695 
    696         # uint64_t t[2];
    697         # uint64_t f[2];
    698         for ($i = 1; $i < 3; ++$i) {
    699             $ctx[$i][1] = ParagonIE_Sodium_Core32_Int64::fromReverseString(
    700                 self::substr($string, 72 + (($i - 1) << 4), 8)
    701             );
    702             $ctx[$i][0] = ParagonIE_Sodium_Core32_Int64::fromReverseString(
    703                 self::substr($string, 64 + (($i - 1) << 4), 8)
    704             );
    705         }
    706 
    707         # uint8_t buf[2 * 128];
    708         $ctx[3] = self::stringToSplFixedArray(self::substr($string, 96, 256));
    709 
    710         # uint8_t buf[2 * 128];
    711         $int = 0;
    712         for ($i = 0; $i < 8; ++$i) {
    713             $int |= self::chrToInt($string[352 + $i]) << ($i << 3);
    714         }
    715         $ctx[4] = $int;
    716 
    717         return $ctx;
    718     }
    719 }