balmet.com

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

BLAKE2b.php (23941B)


      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             (int) ($x[0] + $y[0] + (
     92                 ($l < $x[1]) ? 1 : 0
     93             )),
     94             (int) $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]) & 0xffffffff),
    136             (int) (($x[1] ^ $y[1]) & 0xffffffff)
    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(6);
    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         $ctx[5] = 0;                      // last_node (uint8_t)
    309 
    310         for ($i = 8; $i--;) {
    311             $ctx[0][$i] = self::$iv[$i];
    312         }
    313         for ($i = 256; $i--;) {
    314             $ctx[3][$i] = 0;
    315         }
    316 
    317         $zero = self::new64(0, 0);
    318         $ctx[1][0] = $zero;
    319         $ctx[1][1] = $zero;
    320         $ctx[2][0] = $zero;
    321         $ctx[2][1] = $zero;
    322 
    323         return $ctx;
    324     }
    325 
    326     /**
    327      * @internal You should not use this directly from another application
    328      *
    329      * @param SplFixedArray $ctx
    330      * @param SplFixedArray $buf
    331      * @return void
    332      * @throws SodiumException
    333      * @throws TypeError
    334      * @psalm-suppress MixedArgument
    335      * @psalm-suppress MixedAssignment
    336      * @psalm-suppress MixedArrayAccess
    337      * @psalm-suppress MixedArrayAssignment
    338      * @psalm-suppress MixedArrayOffset
    339      */
    340     protected static function compress(SplFixedArray $ctx, SplFixedArray $buf)
    341     {
    342         $m = new SplFixedArray(16);
    343         $v = new SplFixedArray(16);
    344 
    345         for ($i = 16; $i--;) {
    346             $m[$i] = self::load64($buf, $i << 3);
    347         }
    348 
    349         for ($i = 8; $i--;) {
    350             $v[$i] = $ctx[0][$i];
    351         }
    352 
    353         $v[ 8] = self::$iv[0];
    354         $v[ 9] = self::$iv[1];
    355         $v[10] = self::$iv[2];
    356         $v[11] = self::$iv[3];
    357 
    358         $v[12] = self::xor64($ctx[1][0], self::$iv[4]);
    359         $v[13] = self::xor64($ctx[1][1], self::$iv[5]);
    360         $v[14] = self::xor64($ctx[2][0], self::$iv[6]);
    361         $v[15] = self::xor64($ctx[2][1], self::$iv[7]);
    362 
    363         for ($r = 0; $r < 12; ++$r) {
    364             $v = self::G($r, 0, 0, 4, 8, 12, $v, $m);
    365             $v = self::G($r, 1, 1, 5, 9, 13, $v, $m);
    366             $v = self::G($r, 2, 2, 6, 10, 14, $v, $m);
    367             $v = self::G($r, 3, 3, 7, 11, 15, $v, $m);
    368             $v = self::G($r, 4, 0, 5, 10, 15, $v, $m);
    369             $v = self::G($r, 5, 1, 6, 11, 12, $v, $m);
    370             $v = self::G($r, 6, 2, 7, 8, 13, $v, $m);
    371             $v = self::G($r, 7, 3, 4, 9, 14, $v, $m);
    372         }
    373 
    374         for ($i = 8; $i--;) {
    375             $ctx[0][$i] = self::xor64(
    376                 $ctx[0][$i], self::xor64($v[$i], $v[$i+8])
    377             );
    378         }
    379     }
    380 
    381     /**
    382      * @internal You should not use this directly from another application
    383      *
    384      * @param int $r
    385      * @param int $i
    386      * @param int $a
    387      * @param int $b
    388      * @param int $c
    389      * @param int $d
    390      * @param SplFixedArray $v
    391      * @param SplFixedArray $m
    392      * @return SplFixedArray
    393      * @throws SodiumException
    394      * @throws TypeError
    395      * @psalm-suppress MixedArgument
    396      * @psalm-suppress MixedArrayOffset
    397      */
    398     public static function G($r, $i, $a, $b, $c, $d, SplFixedArray $v, SplFixedArray $m)
    399     {
    400         $v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][$i << 1]]);
    401         $v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 32);
    402         $v[$c] = self::add64($v[$c], $v[$d]);
    403         $v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 24);
    404         $v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][($i << 1) + 1]]);
    405         $v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 16);
    406         $v[$c] = self::add64($v[$c], $v[$d]);
    407         $v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 63);
    408         return $v;
    409     }
    410 
    411     /**
    412      * @internal You should not use this directly from another application
    413      *
    414      * @param SplFixedArray $ctx
    415      * @param int $inc
    416      * @return void
    417      * @throws SodiumException
    418      * @psalm-suppress MixedArgument
    419      * @psalm-suppress MixedArrayAccess
    420      * @psalm-suppress MixedArrayAssignment
    421      */
    422     public static function increment_counter($ctx, $inc)
    423     {
    424         if ($inc < 0) {
    425             throw new SodiumException('Increasing by a negative number makes no sense.');
    426         }
    427         $t = self::to64($inc);
    428         # S->t is $ctx[1] in our implementation
    429 
    430         # S->t[0] = ( uint64_t )( t >> 0 );
    431         $ctx[1][0] = self::add64($ctx[1][0], $t);
    432 
    433         # S->t[1] += ( S->t[0] < inc );
    434         if (self::flatten64($ctx[1][0]) < $inc) {
    435             $ctx[1][1] = self::add64($ctx[1][1], self::to64(1));
    436         }
    437     }
    438 
    439     /**
    440      * @internal You should not use this directly from another application
    441      *
    442      * @param SplFixedArray $ctx
    443      * @param SplFixedArray $p
    444      * @param int $plen
    445      * @return void
    446      * @throws SodiumException
    447      * @throws TypeError
    448      * @psalm-suppress MixedArgument
    449      * @psalm-suppress MixedAssignment
    450      * @psalm-suppress MixedArrayAccess
    451      * @psalm-suppress MixedArrayAssignment
    452      * @psalm-suppress MixedArrayOffset
    453      * @psalm-suppress MixedOperand
    454      */
    455     public static function update(SplFixedArray $ctx, SplFixedArray $p, $plen)
    456     {
    457         self::pseudoConstructor();
    458 
    459         $offset = 0;
    460         while ($plen > 0) {
    461             $left = $ctx[4];
    462             $fill = 256 - $left;
    463 
    464             if ($plen > $fill) {
    465                 # memcpy( S->buf + left, in, fill ); /* Fill buffer */
    466                 for ($i = $fill; $i--;) {
    467                     $ctx[3][$i + $left] = $p[$i + $offset];
    468                 }
    469 
    470                 # S->buflen += fill;
    471                 $ctx[4] += $fill;
    472 
    473                 # blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
    474                 self::increment_counter($ctx, 128);
    475 
    476                 # blake2b_compress( S, S->buf ); /* Compress */
    477                 self::compress($ctx, $ctx[3]);
    478 
    479                 # memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); /* Shift buffer left */
    480                 for ($i = 128; $i--;) {
    481                     $ctx[3][$i] = $ctx[3][$i + 128];
    482                 }
    483 
    484                 # S->buflen -= BLAKE2B_BLOCKBYTES;
    485                 $ctx[4] -= 128;
    486 
    487                 # in += fill;
    488                 $offset += $fill;
    489 
    490                 # inlen -= fill;
    491                 $plen -= $fill;
    492             } else {
    493                 for ($i = $plen; $i--;) {
    494                     $ctx[3][$i + $left] = $p[$i + $offset];
    495                 }
    496                 $ctx[4] += $plen;
    497                 $offset += $plen;
    498                 $plen -= $plen;
    499             }
    500         }
    501     }
    502 
    503     /**
    504      * @internal You should not use this directly from another application
    505      *
    506      * @param SplFixedArray $ctx
    507      * @param SplFixedArray $out
    508      * @return SplFixedArray
    509      * @throws SodiumException
    510      * @throws TypeError
    511      * @psalm-suppress MixedArgument
    512      * @psalm-suppress MixedAssignment
    513      * @psalm-suppress MixedArrayAccess
    514      * @psalm-suppress MixedArrayAssignment
    515      * @psalm-suppress MixedArrayOffset
    516      * @psalm-suppress MixedOperand
    517      */
    518     public static function finish(SplFixedArray $ctx, SplFixedArray $out)
    519     {
    520         self::pseudoConstructor();
    521         if ($ctx[4] > 128) {
    522             self::increment_counter($ctx, 128);
    523             self::compress($ctx, $ctx[3]);
    524             $ctx[4] -= 128;
    525             if ($ctx[4] > 128) {
    526                 throw new SodiumException('Failed to assert that buflen <= 128 bytes');
    527             }
    528             for ($i = $ctx[4]; $i--;) {
    529                 $ctx[3][$i] = $ctx[3][$i + 128];
    530             }
    531         }
    532 
    533         self::increment_counter($ctx, $ctx[4]);
    534         $ctx[2][0] = self::new64(0xffffffff, 0xffffffff);
    535 
    536         for ($i = 256 - $ctx[4]; $i--;) {
    537             $ctx[3][$i+$ctx[4]] = 0;
    538         }
    539 
    540         self::compress($ctx, $ctx[3]);
    541 
    542         $i = (int) (($out->getSize() - 1) / 8);
    543         for (; $i >= 0; --$i) {
    544             self::store64($out, $i << 3, $ctx[0][$i]);
    545         }
    546         return $out;
    547     }
    548 
    549     /**
    550      * @internal You should not use this directly from another application
    551      *
    552      * @param SplFixedArray|null $key
    553      * @param int $outlen
    554      * @param SplFixedArray|null $salt
    555      * @param SplFixedArray|null $personal
    556      * @return SplFixedArray
    557      * @throws SodiumException
    558      * @throws TypeError
    559      * @psalm-suppress MixedArgument
    560      * @psalm-suppress MixedAssignment
    561      * @psalm-suppress MixedArrayAccess
    562      * @psalm-suppress MixedArrayAssignment
    563      * @psalm-suppress MixedArrayOffset
    564      */
    565     public static function init(
    566         $key = null,
    567         $outlen = 64,
    568         $salt = null,
    569         $personal = null
    570     ) {
    571         self::pseudoConstructor();
    572         $klen = 0;
    573 
    574         if ($key !== null) {
    575             if (count($key) > 64) {
    576                 throw new SodiumException('Invalid key size');
    577             }
    578             $klen = count($key);
    579         }
    580 
    581         if ($outlen > 64) {
    582             throw new SodiumException('Invalid output size');
    583         }
    584 
    585         $ctx = self::context();
    586 
    587         $p = new SplFixedArray(64);
    588         // Zero our param buffer...
    589         for ($i = 64; --$i;) {
    590             $p[$i] = 0;
    591         }
    592 
    593         $p[0] = $outlen; // digest_length
    594         $p[1] = $klen;   // key_length
    595         $p[2] = 1;       // fanout
    596         $p[3] = 1;       // depth
    597 
    598         if ($salt instanceof SplFixedArray) {
    599             // salt: [32] through [47]
    600             for ($i = 0; $i < 16; ++$i) {
    601                 $p[32 + $i] = (int) $salt[$i];
    602             }
    603         }
    604         if ($personal instanceof SplFixedArray) {
    605             // personal: [48] through [63]
    606             for ($i = 0; $i < 16; ++$i) {
    607                 $p[48 + $i] = (int) $personal[$i];
    608             }
    609         }
    610 
    611         $ctx[0][0] = self::xor64(
    612             $ctx[0][0],
    613             self::load64($p, 0)
    614         );
    615         if ($salt instanceof SplFixedArray || $personal instanceof SplFixedArray) {
    616             // We need to do what blake2b_init_param() does:
    617             for ($i = 1; $i < 8; ++$i) {
    618                 $ctx[0][$i] = self::xor64(
    619                     $ctx[0][$i],
    620                     self::load64($p, $i << 3)
    621                 );
    622             }
    623         }
    624 
    625         if ($klen > 0 && $key instanceof SplFixedArray) {
    626             $block = new SplFixedArray(128);
    627             for ($i = 128; $i--;) {
    628                 $block[$i] = 0;
    629             }
    630             for ($i = $klen; $i--;) {
    631                 $block[$i] = $key[$i];
    632             }
    633             self::update($ctx, $block, 128);
    634             $ctx[4] = 128;
    635         }
    636 
    637         return $ctx;
    638     }
    639 
    640     /**
    641      * Convert a string into an SplFixedArray of integers
    642      *
    643      * @internal You should not use this directly from another application
    644      *
    645      * @param string $str
    646      * @return SplFixedArray
    647      * @psalm-suppress MixedArgumentTypeCoercion
    648      */
    649     public static function stringToSplFixedArray($str = '')
    650     {
    651         $values = unpack('C*', $str);
    652         return SplFixedArray::fromArray(array_values($values));
    653     }
    654 
    655     /**
    656      * Convert an SplFixedArray of integers into a string
    657      *
    658      * @internal You should not use this directly from another application
    659      *
    660      * @param SplFixedArray $a
    661      * @return string
    662      * @throws TypeError
    663      */
    664     public static function SplFixedArrayToString(SplFixedArray $a)
    665     {
    666         /**
    667          * @var array<int, int|string> $arr
    668          */
    669         $arr = $a->toArray();
    670         $c = $a->count();
    671         array_unshift($arr, str_repeat('C', $c));
    672         return (string) (call_user_func_array('pack', $arr));
    673     }
    674 
    675     /**
    676      * @internal You should not use this directly from another application
    677      *
    678      * @param SplFixedArray $ctx
    679      * @return string
    680      * @throws TypeError
    681      * @psalm-suppress MixedArgument
    682      * @psalm-suppress MixedAssignment
    683      * @psalm-suppress MixedArrayAccess
    684      * @psalm-suppress MixedArrayAssignment
    685      * @psalm-suppress MixedArrayOffset
    686      * @psalm-suppress MixedMethodCall
    687      */
    688     public static function contextToString(SplFixedArray $ctx)
    689     {
    690         $str = '';
    691         /** @var array<int, array<int, int>> $ctxA */
    692         $ctxA = $ctx[0]->toArray();
    693 
    694         # uint64_t h[8];
    695         for ($i = 0; $i < 8; ++$i) {
    696             $str .= self::store32_le($ctxA[$i][1]);
    697             $str .= self::store32_le($ctxA[$i][0]);
    698         }
    699 
    700         # uint64_t t[2];
    701         # uint64_t f[2];
    702         for ($i = 1; $i < 3; ++$i) {
    703             $ctxA = $ctx[$i]->toArray();
    704             $str .= self::store32_le($ctxA[0][1]);
    705             $str .= self::store32_le($ctxA[0][0]);
    706             $str .= self::store32_le($ctxA[1][1]);
    707             $str .= self::store32_le($ctxA[1][0]);
    708         }
    709 
    710         # uint8_t buf[2 * 128];
    711         $str .= self::SplFixedArrayToString($ctx[3]);
    712 
    713         /** @var int $ctx4 */
    714         $ctx4 = (int) $ctx[4];
    715 
    716         # size_t buflen;
    717         $str .= implode('', array(
    718             self::intToChr($ctx4 & 0xff),
    719             self::intToChr(($ctx4 >> 8) & 0xff),
    720             self::intToChr(($ctx4 >> 16) & 0xff),
    721             self::intToChr(($ctx4 >> 24) & 0xff),
    722             self::intToChr(($ctx4 >> 32) & 0xff),
    723             self::intToChr(($ctx4 >> 40) & 0xff),
    724             self::intToChr(($ctx4 >> 48) & 0xff),
    725             self::intToChr(($ctx4 >> 56) & 0xff)
    726         ));
    727         # uint8_t last_node;
    728         return $str . self::intToChr($ctx[5]) . str_repeat("\x00", 23);
    729     }
    730 
    731     /**
    732      * Creates an SplFixedArray containing other SplFixedArray elements, from
    733      * a string (compatible with \Sodium\crypto_generichash_{init, update, final})
    734      *
    735      * @internal You should not use this directly from another application
    736      *
    737      * @param string $string
    738      * @return SplFixedArray
    739      * @throws SodiumException
    740      * @throws TypeError
    741      * @psalm-suppress MixedArrayAssignment
    742      */
    743     public static function stringToContext($string)
    744     {
    745         $ctx = self::context();
    746 
    747         # uint64_t h[8];
    748         for ($i = 0; $i < 8; ++$i) {
    749             $ctx[0][$i] = SplFixedArray::fromArray(
    750                 array(
    751                     self::load_4(
    752                         self::substr($string, (($i << 3) + 4), 4)
    753                     ),
    754                     self::load_4(
    755                         self::substr($string, (($i << 3) + 0), 4)
    756                     )
    757                 )
    758             );
    759         }
    760 
    761         # uint64_t t[2];
    762         # uint64_t f[2];
    763         for ($i = 1; $i < 3; ++$i) {
    764             $ctx[$i][1] = SplFixedArray::fromArray(
    765                 array(
    766                     self::load_4(self::substr($string, 76 + (($i - 1) << 4), 4)),
    767                     self::load_4(self::substr($string, 72 + (($i - 1) << 4), 4))
    768                 )
    769             );
    770             $ctx[$i][0] = SplFixedArray::fromArray(
    771                 array(
    772                     self::load_4(self::substr($string, 68 + (($i - 1) << 4), 4)),
    773                     self::load_4(self::substr($string, 64 + (($i - 1) << 4), 4))
    774                 )
    775             );
    776         }
    777 
    778         # uint8_t buf[2 * 128];
    779         $ctx[3] = self::stringToSplFixedArray(self::substr($string, 96, 256));
    780 
    781         # uint8_t buf[2 * 128];
    782         $int = 0;
    783         for ($i = 0; $i < 8; ++$i) {
    784             $int |= self::chrToInt($string[352 + $i]) << ($i << 3);
    785         }
    786         $ctx[4] = $int;
    787 
    788         return $ctx;
    789     }
    790 }