summaryrefslogtreecommitdiff
path: root/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Gmac.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Gmac.php')
-rw-r--r--vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Gmac.php223
1 files changed, 223 insertions, 0 deletions
diff --git a/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Gmac.php b/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Gmac.php
new file mode 100644
index 0000000..535cfca
--- /dev/null
+++ b/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Gmac.php
@@ -0,0 +1,223 @@
+<?php
+namespace Aws\Crypto\Polyfill;
+
+/**
+ * Class Gmac
+ *
+ * @package Aws\Crypto\Polyfill
+ */
+class Gmac
+{
+ use NeedsTrait;
+
+ const BLOCK_SIZE = 16;
+
+ /** @var ByteArray $buf */
+ protected $buf;
+
+ /** @var int $bufLength */
+ protected $bufLength = 0;
+
+ /** @var ByteArray $h */
+ protected $h;
+
+ /** @var ByteArray $hf */
+ protected $hf;
+
+ /** @var Key $key */
+ protected $key;
+
+ /** @var ByteArray $x */
+ protected $x;
+
+ /**
+ * Gmac constructor.
+ *
+ * @param Key $aesKey
+ * @param string $nonce
+ * @param int $keySize
+ */
+ public function __construct(Key $aesKey, $nonce, $keySize = 256)
+ {
+ $this->buf = new ByteArray(16);
+ $this->h = new ByteArray(
+ \openssl_encrypt(
+ \str_repeat("\0", 16),
+ "aes-{$keySize}-ecb",
+ $aesKey->get(),
+ OPENSSL_RAW_DATA | OPENSSL_NO_PADDING
+ )
+ );
+ $this->key = $aesKey;
+ $this->x = new ByteArray(16);
+ $this->hf = new ByteArray(
+ \openssl_encrypt(
+ $nonce,
+ "aes-{$keySize}-ecb",
+ $aesKey->get(),
+ OPENSSL_RAW_DATA | OPENSSL_NO_PADDING
+ )
+ );
+ }
+
+ /**
+ * Update the object with some data.
+ *
+ * This method mutates this Gmac object.
+ *
+ * @param ByteArray $blocks
+ * @return self
+ */
+ public function update(ByteArray $blocks)
+ {
+ if (($blocks->count() + $this->bufLength) < self::BLOCK_SIZE) {
+ // Write to internal buffer until we reach enough to write.
+ $this->buf->set($blocks, $this->bufLength);
+ $this->bufLength += $blocks->count();
+ return $this;
+ }
+
+ // Process internal buffer first.
+ if ($this->bufLength > 0) {
+ // 0 <= state.buf_len < BLOCK_SIZE is an invariant
+ $tmp = new ByteArray(self::BLOCK_SIZE);
+ $tmp->set($this->buf->slice(0, $this->bufLength));
+ $remainingBlockLength = self::BLOCK_SIZE - $this->bufLength;
+ $tmp->set($blocks->slice(0, $remainingBlockLength), $this->bufLength);
+ $blocks = $blocks->slice($remainingBlockLength);
+ $this->bufLength = 0;
+ $this->x = $this->blockMultiply($this->x->exclusiveOr($tmp), $this->h);
+ }
+
+ // Process full blocks.
+ $numBlocks = $blocks->count() >> 4;
+ for ($i = 0; $i < $numBlocks; ++$i) {
+ $tmp = $blocks->slice($i << 4, self::BLOCK_SIZE);
+ $this->x = $this->blockMultiply($this->x->exclusiveOr($tmp), $this->h);
+ }
+ $last = $numBlocks << 4;
+
+ // Zero-fill buffer
+ for ($i = 0; $i < 16; ++$i) {
+ $this->buf[$i] = 0;
+ }
+ // Feed leftover into buffer.
+ if ($last < $blocks->count()) {
+ $tmp = $blocks->slice($last);
+ $this->buf->set($tmp);
+ $this->bufLength += ($blocks->count() - $last);
+ }
+ return $this;
+ }
+
+ /**
+ * Finish processing the authentication tag.
+ *
+ * This method mutates this Gmac object (effectively resetting it).
+ *
+ * @param int $aadLength
+ * @param int $ciphertextLength
+ * @return ByteArray
+ */
+ public function finish($aadLength, $ciphertextLength)
+ {
+ $lengthBlock = new ByteArray(16);
+ $state = $this->flush();
+
+ // AES-GCM expects bit lengths, not byte lengths.
+ $lengthBlock->set(ByteArray::enc32be($aadLength >> 29), 0);
+ $lengthBlock->set(ByteArray::enc32be($aadLength << 3), 4);
+ $lengthBlock->set(ByteArray::enc32be($ciphertextLength >> 29), 8);
+ $lengthBlock->set(ByteArray::enc32be($ciphertextLength << 3), 12);
+
+ $state->update($lengthBlock);
+ $output = $state->x->exclusiveOr($state->hf);
+
+ // Zeroize the internal values as a best-effort.
+ $state->buf->zeroize();
+ $state->x->zeroize();
+ $state->h->zeroize();
+ $state->hf->zeroize();
+ return $output;
+ }
+
+ /**
+ * Get a specific bit from the provided array, at the given index.
+ *
+ * [01234567], 8+[01234567], 16+[01234567], ...
+ *
+ * @param ByteArray $x
+ * @param int $i
+ * @return int
+ */
+ protected function bit(ByteArray $x, $i)
+ {
+ $byte = $i >> 3;
+ return ($x[$byte] >> ((7 - $i) & 7)) & 1;
+ }
+
+ /**
+ * Galois Field Multiplication
+ *
+ * This function is the critical path that must be constant-time in order to
+ * avoid timing side-channels against AES-GCM.
+ *
+ * The contents of each are always calculated, regardless of the branching
+ * condition, to prevent another kind of timing leak.
+ *
+ * @param ByteArray $x
+ * @param ByteArray $y
+ * @return ByteArray
+ */
+ protected function blockMultiply(ByteArray $x, ByteArray $y)
+ {
+ static $fieldPolynomial = null;
+ if (!$fieldPolynomial) {
+ $fieldPolynomial = new ByteArray([
+ 0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ ]);
+ }
+ self::needs($x->count() === 16, 'Argument 1 must be a ByteArray of exactly 16 bytes');
+ self::needs($y->count() === 16, 'Argument 2 must be a ByteArray of exactly 16 bytes');
+
+ $v = clone $y;
+ $z = new ByteArray(16);
+
+ for ($i = 0; $i < 128; ++$i) {
+ // if ($b) $z = $z->exclusiveOr($v);
+ $b = $this->bit($x, $i);
+ $z = ByteArray::select(
+ $b,
+ $z->exclusiveOr($v),
+ $z
+ );
+
+ // if ($b) $v = $v->exclusiveOr($fieldPolynomial);
+ $b = $v[15] & 1;
+ $v = $v->rshift();
+ $v = ByteArray::select(
+ $b,
+ $v->exclusiveOr($fieldPolynomial),
+ $v
+ );
+ }
+ return $z;
+ }
+
+ /**
+ * Finish processing any leftover bytes in the internal buffer.
+ *
+ * @return self
+ */
+ public function flush()
+ {
+ if ($this->bufLength !== 0) {
+ $this->x = $this->blockMultiply(
+ $this->x->exclusiveOr($this->buf),
+ $this->h
+ );
+ $this->bufLength = 0;
+ }
+ return $this;
+ }
+}