446 lines
13 KiB
PHP
446 lines
13 KiB
PHP
<?php
|
|
|
|
if (class_exists('ParagonIE_Sodium_Core_Poly1305_State', false)) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Class ParagonIE_Sodium_Core_Poly1305_State
|
|
*/
|
|
class ParagonIE_Sodium_Core_Poly1305_State extends ParagonIE_Sodium_Core_Util
|
|
{
|
|
/**
|
|
* @var array<int, int>
|
|
*/
|
|
protected $buffer = array();
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
protected $final = false;
|
|
|
|
/**
|
|
* @var array<int, int>
|
|
*/
|
|
public $h;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
protected $leftover = 0;
|
|
|
|
/**
|
|
* @var int[]
|
|
*/
|
|
public $r;
|
|
|
|
/**
|
|
* @var int[]
|
|
*/
|
|
public $pad;
|
|
|
|
/**
|
|
* ParagonIE_Sodium_Core_Poly1305_State constructor.
|
|
*
|
|
* @internal You should not use this directly from another application
|
|
*
|
|
* @param string $key
|
|
* @throws InvalidArgumentException
|
|
* @throws TypeError
|
|
*/
|
|
public function __construct($key = '')
|
|
{
|
|
if (self::strlen($key) < 32) {
|
|
throw new InvalidArgumentException(
|
|
'Poly1305 requires a 32-byte key'
|
|
);
|
|
}
|
|
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
|
$this->r = array(
|
|
(int) ((self::load_4(self::substr($key, 0, 4))) & 0x3ffffff),
|
|
(int) ((self::load_4(self::substr($key, 3, 4)) >> 2) & 0x3ffff03),
|
|
(int) ((self::load_4(self::substr($key, 6, 4)) >> 4) & 0x3ffc0ff),
|
|
(int) ((self::load_4(self::substr($key, 9, 4)) >> 6) & 0x3f03fff),
|
|
(int) ((self::load_4(self::substr($key, 12, 4)) >> 8) & 0x00fffff)
|
|
);
|
|
|
|
/* h = 0 */
|
|
$this->h = array(0, 0, 0, 0, 0);
|
|
|
|
/* save pad for later */
|
|
$this->pad = array(
|
|
self::load_4(self::substr($key, 16, 4)),
|
|
self::load_4(self::substr($key, 20, 4)),
|
|
self::load_4(self::substr($key, 24, 4)),
|
|
self::load_4(self::substr($key, 28, 4)),
|
|
);
|
|
|
|
$this->leftover = 0;
|
|
$this->final = false;
|
|
}
|
|
|
|
/**
|
|
* Zero internal buffer upon destruction
|
|
*/
|
|
public function __destruct()
|
|
{
|
|
$this->r[0] ^= $this->r[0];
|
|
$this->r[1] ^= $this->r[1];
|
|
$this->r[2] ^= $this->r[2];
|
|
$this->r[3] ^= $this->r[3];
|
|
$this->r[4] ^= $this->r[4];
|
|
$this->h[0] ^= $this->h[0];
|
|
$this->h[1] ^= $this->h[1];
|
|
$this->h[2] ^= $this->h[2];
|
|
$this->h[3] ^= $this->h[3];
|
|
$this->h[4] ^= $this->h[4];
|
|
$this->pad[0] ^= $this->pad[0];
|
|
$this->pad[1] ^= $this->pad[1];
|
|
$this->pad[2] ^= $this->pad[2];
|
|
$this->pad[3] ^= $this->pad[3];
|
|
$this->leftover = 0;
|
|
$this->final = true;
|
|
}
|
|
|
|
/**
|
|
* @internal You should not use this directly from another application
|
|
*
|
|
* @param string $message
|
|
* @return self
|
|
* @throws SodiumException
|
|
* @throws TypeError
|
|
*/
|
|
public function update($message = '')
|
|
{
|
|
$bytes = self::strlen($message);
|
|
if ($bytes < 1) {
|
|
return $this;
|
|
}
|
|
|
|
/* handle leftover */
|
|
if ($this->leftover) {
|
|
$want = ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE - $this->leftover;
|
|
if ($want > $bytes) {
|
|
$want = $bytes;
|
|
}
|
|
for ($i = 0; $i < $want; ++$i) {
|
|
$mi = self::chrToInt($message[$i]);
|
|
$this->buffer[$this->leftover + $i] = $mi;
|
|
}
|
|
// We snip off the leftmost bytes.
|
|
$message = self::substr($message, $want);
|
|
$bytes = self::strlen($message);
|
|
$this->leftover += $want;
|
|
if ($this->leftover < ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE) {
|
|
// We still don't have enough to run $this->blocks()
|
|
return $this;
|
|
}
|
|
|
|
$this->blocks(
|
|
self::intArrayToString($this->buffer),
|
|
ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE
|
|
);
|
|
$this->leftover = 0;
|
|
}
|
|
|
|
/* process full blocks */
|
|
if ($bytes >= ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE) {
|
|
/** @var int $want */
|
|
$want = $bytes & ~(ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE - 1);
|
|
if ($want >= ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE) {
|
|
$block = self::substr($message, 0, $want);
|
|
if (self::strlen($block) >= ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE) {
|
|
$this->blocks($block, $want);
|
|
$message = self::substr($message, $want);
|
|
$bytes = self::strlen($message);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* store leftover */
|
|
if ($bytes) {
|
|
for ($i = 0; $i < $bytes; ++$i) {
|
|
$mi = self::chrToInt($message[$i]);
|
|
$this->buffer[$this->leftover + $i] = $mi;
|
|
}
|
|
$this->leftover = (int) $this->leftover + $bytes;
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @internal You should not use this directly from another application
|
|
*
|
|
* @param string $message
|
|
* @param int $bytes
|
|
* @return self
|
|
* @throws TypeError
|
|
*/
|
|
public function blocks($message, $bytes)
|
|
{
|
|
if (self::strlen($message) < 16) {
|
|
$message = str_pad($message, 16, "\x00", STR_PAD_RIGHT);
|
|
}
|
|
/** @var int $hibit */
|
|
$hibit = $this->final ? 0 : 1 << 24; /* 1 << 128 */
|
|
$r0 = (int) $this->r[0];
|
|
$r1 = (int) $this->r[1];
|
|
$r2 = (int) $this->r[2];
|
|
$r3 = (int) $this->r[3];
|
|
$r4 = (int) $this->r[4];
|
|
|
|
$s1 = self::mul($r1, 5, 3);
|
|
$s2 = self::mul($r2, 5, 3);
|
|
$s3 = self::mul($r3, 5, 3);
|
|
$s4 = self::mul($r4, 5, 3);
|
|
|
|
$h0 = $this->h[0];
|
|
$h1 = $this->h[1];
|
|
$h2 = $this->h[2];
|
|
$h3 = $this->h[3];
|
|
$h4 = $this->h[4];
|
|
|
|
while ($bytes >= ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE) {
|
|
/* h += m[i] */
|
|
$h0 += self::load_4(self::substr($message, 0, 4)) & 0x3ffffff;
|
|
$h1 += (self::load_4(self::substr($message, 3, 4)) >> 2) & 0x3ffffff;
|
|
$h2 += (self::load_4(self::substr($message, 6, 4)) >> 4) & 0x3ffffff;
|
|
$h3 += (self::load_4(self::substr($message, 9, 4)) >> 6) & 0x3ffffff;
|
|
$h4 += (self::load_4(self::substr($message, 12, 4)) >> 8) | $hibit;
|
|
|
|
/* h *= r */
|
|
$d0 = (
|
|
self::mul($h0, $r0, 27) +
|
|
self::mul($s4, $h1, 27) +
|
|
self::mul($s3, $h2, 27) +
|
|
self::mul($s2, $h3, 27) +
|
|
self::mul($s1, $h4, 27)
|
|
);
|
|
|
|
$d1 = (
|
|
self::mul($h0, $r1, 27) +
|
|
self::mul($h1, $r0, 27) +
|
|
self::mul($s4, $h2, 27) +
|
|
self::mul($s3, $h3, 27) +
|
|
self::mul($s2, $h4, 27)
|
|
);
|
|
|
|
$d2 = (
|
|
self::mul($h0, $r2, 27) +
|
|
self::mul($h1, $r1, 27) +
|
|
self::mul($h2, $r0, 27) +
|
|
self::mul($s4, $h3, 27) +
|
|
self::mul($s3, $h4, 27)
|
|
);
|
|
|
|
$d3 = (
|
|
self::mul($h0, $r3, 27) +
|
|
self::mul($h1, $r2, 27) +
|
|
self::mul($h2, $r1, 27) +
|
|
self::mul($h3, $r0, 27) +
|
|
self::mul($s4, $h4, 27)
|
|
);
|
|
|
|
$d4 = (
|
|
self::mul($h0, $r4, 27) +
|
|
self::mul($h1, $r3, 27) +
|
|
self::mul($h2, $r2, 27) +
|
|
self::mul($h3, $r1, 27) +
|
|
self::mul($h4, $r0, 27)
|
|
);
|
|
|
|
/* (partial) h %= p */
|
|
/** @var int $c */
|
|
$c = $d0 >> 26;
|
|
/** @var int $h0 */
|
|
$h0 = $d0 & 0x3ffffff;
|
|
$d1 += $c;
|
|
|
|
/** @var int $c */
|
|
$c = $d1 >> 26;
|
|
/** @var int $h1 */
|
|
$h1 = $d1 & 0x3ffffff;
|
|
$d2 += $c;
|
|
|
|
/** @var int $c */
|
|
$c = $d2 >> 26;
|
|
/** @var int $h2 */
|
|
$h2 = $d2 & 0x3ffffff;
|
|
$d3 += $c;
|
|
|
|
/** @var int $c */
|
|
$c = $d3 >> 26;
|
|
/** @var int $h3 */
|
|
$h3 = $d3 & 0x3ffffff;
|
|
$d4 += $c;
|
|
|
|
/** @var int $c */
|
|
$c = $d4 >> 26;
|
|
/** @var int $h4 */
|
|
$h4 = $d4 & 0x3ffffff;
|
|
$h0 += (int) self::mul($c, 5, 3);
|
|
|
|
/** @var int $c */
|
|
$c = $h0 >> 26;
|
|
/** @var int $h0 */
|
|
$h0 &= 0x3ffffff;
|
|
$h1 += $c;
|
|
|
|
// Chop off the left 32 bytes.
|
|
$message = self::substr(
|
|
$message,
|
|
ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE
|
|
);
|
|
$bytes -= ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE;
|
|
}
|
|
|
|
$this->h = array(
|
|
(int) ($h0 & 0xffffffff),
|
|
(int) ($h1 & 0xffffffff),
|
|
(int) ($h2 & 0xffffffff),
|
|
(int) ($h3 & 0xffffffff),
|
|
(int) ($h4 & 0xffffffff)
|
|
);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @internal You should not use this directly from another application
|
|
*
|
|
* @return string
|
|
* @throws TypeError
|
|
*/
|
|
public function finish()
|
|
{
|
|
/* process the remaining block */
|
|
if ($this->leftover) {
|
|
$i = $this->leftover;
|
|
$this->buffer[$i++] = 1;
|
|
for (; $i < ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE; ++$i) {
|
|
$this->buffer[$i] = 0;
|
|
}
|
|
$this->final = true;
|
|
$this->blocks(
|
|
self::substr(
|
|
self::intArrayToString($this->buffer),
|
|
0,
|
|
ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE
|
|
),
|
|
ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE
|
|
);
|
|
}
|
|
|
|
$h0 = (int) $this->h[0];
|
|
$h1 = (int) $this->h[1];
|
|
$h2 = (int) $this->h[2];
|
|
$h3 = (int) $this->h[3];
|
|
$h4 = (int) $this->h[4];
|
|
|
|
/** @var int $c */
|
|
$c = $h1 >> 26;
|
|
/** @var int $h1 */
|
|
$h1 &= 0x3ffffff;
|
|
/** @var int $h2 */
|
|
$h2 += $c;
|
|
/** @var int $c */
|
|
$c = $h2 >> 26;
|
|
/** @var int $h2 */
|
|
$h2 &= 0x3ffffff;
|
|
$h3 += $c;
|
|
/** @var int $c */
|
|
$c = $h3 >> 26;
|
|
$h3 &= 0x3ffffff;
|
|
$h4 += $c;
|
|
/** @var int $c */
|
|
$c = $h4 >> 26;
|
|
$h4 &= 0x3ffffff;
|
|
/** @var int $h0 */
|
|
$h0 += self::mul($c, 5, 3);
|
|
/** @var int $c */
|
|
$c = $h0 >> 26;
|
|
/** @var int $h0 */
|
|
$h0 &= 0x3ffffff;
|
|
/** @var int $h1 */
|
|
$h1 += $c;
|
|
|
|
/* compute h + -p */
|
|
/** @var int $g0 */
|
|
$g0 = $h0 + 5;
|
|
/** @var int $c */
|
|
$c = $g0 >> 26;
|
|
/** @var int $g0 */
|
|
$g0 &= 0x3ffffff;
|
|
|
|
/** @var int $g1 */
|
|
$g1 = $h1 + $c;
|
|
/** @var int $c */
|
|
$c = $g1 >> 26;
|
|
$g1 &= 0x3ffffff;
|
|
|
|
/** @var int $g2 */
|
|
$g2 = $h2 + $c;
|
|
/** @var int $c */
|
|
$c = $g2 >> 26;
|
|
/** @var int $g2 */
|
|
$g2 &= 0x3ffffff;
|
|
|
|
/** @var int $g3 */
|
|
$g3 = $h3 + $c;
|
|
/** @var int $c */
|
|
$c = $g3 >> 26;
|
|
/** @var int $g3 */
|
|
$g3 &= 0x3ffffff;
|
|
|
|
/** @var int $g4 */
|
|
$g4 = ($h4 + $c - (1 << 26)) & 0xffffffff;
|
|
|
|
/* select h if h < p, or h + -p if h >= p */
|
|
/** @var int $mask */
|
|
$mask = ($g4 >> 31) - 1;
|
|
|
|
$g0 &= $mask;
|
|
$g1 &= $mask;
|
|
$g2 &= $mask;
|
|
$g3 &= $mask;
|
|
$g4 &= $mask;
|
|
|
|
/** @var int $mask */
|
|
$mask = ~$mask & 0xffffffff;
|
|
/** @var int $h0 */
|
|
$h0 = ($h0 & $mask) | $g0;
|
|
/** @var int $h1 */
|
|
$h1 = ($h1 & $mask) | $g1;
|
|
/** @var int $h2 */
|
|
$h2 = ($h2 & $mask) | $g2;
|
|
/** @var int $h3 */
|
|
$h3 = ($h3 & $mask) | $g3;
|
|
/** @var int $h4 */
|
|
$h4 = ($h4 & $mask) | $g4;
|
|
|
|
/* h = h % (2^128) */
|
|
/** @var int $h0 */
|
|
$h0 = (($h0) | ($h1 << 26)) & 0xffffffff;
|
|
/** @var int $h1 */
|
|
$h1 = (($h1 >> 6) | ($h2 << 20)) & 0xffffffff;
|
|
/** @var int $h2 */
|
|
$h2 = (($h2 >> 12) | ($h3 << 14)) & 0xffffffff;
|
|
/** @var int $h3 */
|
|
$h3 = (($h3 >> 18) | ($h4 << 8)) & 0xffffffff;
|
|
|
|
/* mac = (h + pad) % (2^128) */
|
|
$f = (int) ($h0 + $this->pad[0]);
|
|
$h0 = (int) $f;
|
|
$f = (int) ($h1 + $this->pad[1] + ($f >> 32));
|
|
$h1 = (int) $f;
|
|
$f = (int) ($h2 + $this->pad[2] + ($f >> 32));
|
|
$h2 = (int) $f;
|
|
$f = (int) ($h3 + $this->pad[3] + ($f >> 32));
|
|
$h3 = (int) $f;
|
|
|
|
return self::store32_le($h0 & 0xffffffff) .
|
|
self::store32_le($h1 & 0xffffffff) .
|
|
self::store32_le($h2 & 0xffffffff) .
|
|
self::store32_le($h3 & 0xffffffff);
|
|
}
|
|
}
|