diff options
Diffstat (limited to 'vendor/aws/aws-sdk-php/src/Crypto/Polyfill')
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Crypto/Polyfill/AesGcm.php | 228 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Crypto/Polyfill/ByteArray.php | 258 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Gmac.php | 223 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Key.php | 77 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Crypto/Polyfill/NeedsTrait.php | 38 |
5 files changed, 824 insertions, 0 deletions
diff --git a/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/AesGcm.php b/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/AesGcm.php new file mode 100644 index 0000000..baf8ae2 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/AesGcm.php @@ -0,0 +1,228 @@ +<?php +namespace Aws\Crypto\Polyfill; + +use Aws\Exception\CryptoPolyfillException; +use InvalidArgumentException; +use RangeException; + +/** + * Class AesGcm + * + * This provides a polyfill for AES-GCM encryption/decryption, with caveats: + * + * 1. Only 96-bit nonces are supported. + * 2. Only 128-bit authentication tags are supported. (i.e. non-truncated) + * + * Supports AES key sizes of 128-bit, 192-bit, and 256-bit. + * + * @package Aws\Crypto\Polyfill + */ +class AesGcm +{ + use NeedsTrait; + + /** @var Key $aesKey */ + private $aesKey; + + /** @var int $keySize */ + private $keySize; + + /** @var int $blockSize */ + protected $blockSize = 8192; + + /** + * AesGcm constructor. + * + * @param Key $aesKey + * @param int $keySize + * @param int $blockSize + * + * @throws CryptoPolyfillException + * @throws InvalidArgumentException + * @throws RangeException + */ + public function __construct(Key $aesKey, $keySize = 256, $blockSize = 8192) + { + /* Preconditions: */ + self::needs( + \in_array($keySize, [128, 192, 256], true), + "Key size must be 128, 192, or 256 bits; {$keySize} given", + InvalidArgumentException::class + ); + self::needs( + \is_int($blockSize) && $blockSize > 0 && $blockSize <= PHP_INT_MAX, + 'Block size must be a positive integer.', + RangeException::class + ); + self::needs( + $aesKey->length() << 3 === $keySize, + 'Incorrect key size; expected ' . $keySize . ' bits, got ' . ($aesKey->length() << 3) . ' bits.' + ); + $this->aesKey = $aesKey; + $this->keySize = $keySize; + } + + /** + * Encryption interface for AES-GCM + * + * @param string $plaintext Message to be encrypted + * @param string $nonce Number to be used ONCE + * @param Key $key AES Key + * @param string $aad Additional authenticated data + * @param string &$tag Reference to variable to hold tag + * @param int $keySize Key size (bits) + * @param int $blockSize Block size (bytes) -- How much memory to buffer + * @return string + * @throws InvalidArgumentException + */ + public static function encrypt( + $plaintext, + $nonce, + Key $key, + $aad, + &$tag, + $keySize = 256, + $blockSize = 8192 + ) { + self::needs( + self::strlen($nonce) === 12, + 'Nonce must be exactly 12 bytes', + InvalidArgumentException::class + ); + + $encryptor = new AesGcm($key, $keySize, $blockSize); + list($aadLength, $gmac) = $encryptor->gmacInit($nonce, $aad); + + $ciphertext = \openssl_encrypt( + $plaintext, + "aes-{$encryptor->keySize}-ctr", + $key->get(), + OPENSSL_NO_PADDING | OPENSSL_RAW_DATA, + $nonce . "\x00\x00\x00\x02" + ); + + /* Calculate auth tag in a streaming fashion to minimize memory usage: */ + $ciphertextLength = self::strlen($ciphertext); + for ($i = 0; $i < $ciphertextLength; $i += $encryptor->blockSize) { + $cBlock = new ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize)); + $gmac->update($cBlock); + } + $tag = $gmac->finish($aadLength, $ciphertextLength)->toString(); + return $ciphertext; + } + + /** + * Decryption interface for AES-GCM + * + * @param string $ciphertext Ciphertext to decrypt + * @param string $nonce Number to be used ONCE + * @param Key $key AES key + * @param string $aad Additional authenticated data + * @param string $tag Authentication tag + * @param int $keySize Key size (bits) + * @param int $blockSize Block size (bytes) -- How much memory to buffer + * @return string Plaintext + * + * @throws CryptoPolyfillException + * @throws InvalidArgumentException + */ + public static function decrypt( + $ciphertext, + $nonce, + Key $key, + $aad, + &$tag, + $keySize = 256, + $blockSize = 8192 + ) { + /* Precondition: */ + self::needs( + self::strlen($nonce) === 12, + 'Nonce must be exactly 12 bytes', + InvalidArgumentException::class + ); + + $encryptor = new AesGcm($key, $keySize, $blockSize); + list($aadLength, $gmac) = $encryptor->gmacInit($nonce, $aad); + + /* Calculate auth tag in a streaming fashion to minimize memory usage: */ + $ciphertextLength = self::strlen($ciphertext); + for ($i = 0; $i < $ciphertextLength; $i += $encryptor->blockSize) { + $cBlock = new ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize)); + $gmac->update($cBlock); + } + + /* Validate auth tag in constant-time: */ + $calc = $gmac->finish($aadLength, $ciphertextLength); + $expected = new ByteArray($tag); + self::needs($calc->equals($expected), 'Invalid authentication tag'); + + /* Return plaintext if auth tag check succeeded: */ + return \openssl_decrypt( + $ciphertext, + "aes-{$encryptor->keySize}-ctr", + $key->get(), + OPENSSL_NO_PADDING | OPENSSL_RAW_DATA, + $nonce . "\x00\x00\x00\x02" + ); + } + + /** + * Initialize a Gmac object with the nonce and this object's key. + * + * @param string $nonce Must be exactly 12 bytes long. + * @param string|null $aad + * @return array + */ + protected function gmacInit($nonce, $aad = null) + { + $gmac = new Gmac( + $this->aesKey, + $nonce . "\x00\x00\x00\x01", + $this->keySize + ); + $aadBlock = new ByteArray($aad); + $aadLength = $aadBlock->count(); + $gmac->update($aadBlock); + $gmac->flush(); + return [$aadLength, $gmac]; + } + + /** + * Calculate the length of a string. + * + * Uses the appropriate PHP function without being brittle to + * mbstring.func_overload. + * + * @param string $string + * @return int + */ + protected static function strlen($string) + { + if (\is_callable('\\mb_strlen')) { + return (int) \mb_strlen($string, '8bit'); + } + return (int) \strlen($string); + } + + /** + * Return a substring of the provided string. + * + * Uses the appropriate PHP function without being brittle to + * mbstring.func_overload. + * + * @param string $string + * @param int $offset + * @param int|null $length + * @return string + */ + protected static function substr($string, $offset = 0, $length = null) + { + if (\is_callable('\\mb_substr')) { + return \mb_substr($string, $offset, $length, '8bit'); + } elseif (!\is_null($length)) { + return \substr($string, $offset, $length); + } + return \substr($string, $offset); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/ByteArray.php b/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/ByteArray.php new file mode 100644 index 0000000..c3472b0 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/ByteArray.php @@ -0,0 +1,258 @@ +<?php +namespace Aws\Crypto\Polyfill; + +/** + * Class ByteArray + * @package Aws\Crypto\Polyfill + */ +class ByteArray extends \SplFixedArray +{ + use NeedsTrait; + + /** + * ByteArray constructor. + * + * @param int|string|int[] $size + * If you pass in an integer, it creates a ByteArray of that size. + * If you pass in a string or array, it converts it to an array of + * integers between 0 and 255. + * @throws \InvalidArgumentException + */ + public function __construct($size = 0) + { + $arr = null; + // Integer? This behaves just like SplFixedArray. + if (\is_array($size)) { + // Array? We need to pass the count to parent::__construct() then populate + $arr = $size; + $size = \count($arr); + } elseif (\is_string($size)) { + // We need to avoid mbstring.func_overload + if (\is_callable('\\mb_str_split')) { + $tmp = \mb_str_split($size, 1, '8bit'); + } else { + $tmp = \str_split($size, 1); + } + // Let's convert each character to an 8-bit integer and store in $arr + $arr = []; + if (!empty($tmp)) { + foreach ($tmp as $t) { + if (strlen($t) < 1) { + continue; + } + $arr []= \unpack('C', $t)[1] & 0xff; + } + } + $size = \count($arr); + } elseif ($size instanceof ByteArray) { + $arr = $size->toArray(); + $size = $size->count(); + } elseif (!\is_int($size)) { + throw new \InvalidArgumentException( + 'Argument must be an integer, string, or array of integers.' + ); + } + + parent::__construct($size); + + if (!empty($arr)) { + // Populate this object with values from constructor argument + foreach ($arr as $i => $v) { + $this->offsetSet($i, $v); + } + } else { + // Initialize to zero. + for ($i = 0; $i < $size; ++$i) { + $this->offsetSet($i, 0); + } + } + } + + /** + * Encode an integer into a byte array. 32-bit (unsigned), big endian byte order. + * + * @param int $num + * @return self + */ + public static function enc32be($num) + { + return new ByteArray(\pack('N', $num)); + } + + /** + * @param ByteArray $other + * @return bool + */ + public function equals(ByteArray $other) + { + if ($this->count() !== $other->count()) { + return false; + } + $d = 0; + for ($i = $this->count() - 1; $i >= 0; --$i) { + $d |= $this[$i] ^ $other[$i]; + } + return $d === 0; + } + + /** + * @param ByteArray $array + * @return ByteArray + */ + public function exclusiveOr(ByteArray $array) + { + self::needs( + $this->count() === $array->count(), + 'Both ByteArrays must be equal size for exclusiveOr()' + ); + $out = clone $this; + for ($i = 0; $i < $this->count(); ++$i) { + $out[$i] = $array[$i] ^ $out[$i]; + } + return $out; + } + + /** + * Returns a new ByteArray incremented by 1 (big endian byte order). + * + * @param int $increase + * @return self + */ + public function getIncremented($increase = 1) + { + $clone = clone $this; + $index = $clone->count(); + while ($index > 0) { + --$index; + $tmp = ($clone[$index] + $increase) & PHP_INT_MAX; + $clone[$index] = $tmp & 0xff; + $increase = $tmp >> 8; + } + return $clone; + } + + /** + * Sets a value. See SplFixedArray for more. + * + * @param int $index + * @param int $newval + * @return void + */ + public function offsetSet($index, $newval) + { + parent::offsetSet($index, $newval & 0xff); + } + + /** + * Return a copy of this ByteArray, bitshifted to the right by 1. + * Used in Gmac. + * + * @return self + */ + public function rshift() + { + $out = clone $this; + for ($j = $this->count() - 1; $j > 0; --$j) { + $out[$j] = (($out[$j - 1] & 1) << 7) | ($out[$j] >> 1); + } + $out[0] >>= 1; + return $out; + } + + /** + * Constant-time conditional select. This is meant to read like a ternary operator. + * + * $z = ByteArray::select(1, $x, $y); // $z is equal to $x + * $z = ByteArray::select(0, $x, $y); // $z is equal to $y + * + * @param int $select + * @param ByteArray $left + * @param ByteArray $right + * @return ByteArray + */ + public static function select($select, ByteArray $left, ByteArray $right) + { + self::needs( + $left->count() === $right->count(), + 'Both ByteArrays must be equal size for select()' + ); + $rightLength = $right->count(); + $out = clone $right; + $mask = (-($select & 1)) & 0xff; + for ($i = 0; $i < $rightLength; $i++) { + $out[$i] = $out[$i] ^ (($left[$i] ^ $right[$i]) & $mask); + } + return $out; + } + + /** + * Overwrite values of this ByteArray based on a separate ByteArray, with + * a given starting offset and length. + * + * See JavaScript's Uint8Array.set() for more information. + * + * @param ByteArray $input + * @param int $offset + * @param int|null $length + * @return self + */ + public function set(ByteArray $input, $offset = 0, $length = null) + { + self::needs( + is_int($offset) && $offset >= 0, + 'Offset must be a positive integer or zero' + ); + if (is_null($length)) { + $length = $input->count(); + } + + $i = 0; $j = $offset; + while ($i < $length && $j < $this->count()) { + $this[$j] = $input[$i]; + ++$i; + ++$j; + } + return $this; + } + + /** + * Returns a slice of this ByteArray. + * + * @param int $start + * @param null $length + * @return self + */ + public function slice($start = 0, $length = null) + { + return new ByteArray(\array_slice($this->toArray(), $start, $length)); + } + + /** + * Mutates the current state and sets all values to zero. + * + * @return void + */ + public function zeroize() + { + for ($i = $this->count() - 1; $i >= 0; --$i) { + $this->offsetSet($i, 0); + } + } + + /** + * Converts the ByteArray to a raw binary string. + * + * @return string + */ + public function toString() + { + $count = $this->count(); + if ($count === 0) { + return ''; + } + $args = $this->toArray(); + \array_unshift($args, \str_repeat('C', $count)); + // constant-time, PHP <5.6 equivalent to pack('C*', ...$args); + return \call_user_func_array('\\pack', $args); + } +} 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; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Key.php b/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Key.php new file mode 100644 index 0000000..49d0c69 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/Key.php @@ -0,0 +1,77 @@ +<?php +namespace Aws\Crypto\Polyfill; + +/** + * Class Key + * + * Wraps a string to keep it hidden from stack traces. + * + * @package Aws\Crypto\Polyfill + */ +class Key +{ + /** + * @var string $internalString + */ + private $internalString; + + /** + * Hide contents of + * + * @return array + */ + public function __debugInfo() + { + return []; + } + + /** + * Key constructor. + * @param string $str + */ + public function __construct($str) + { + $this->internalString = $str; + } + + /** + * Defense in depth: + * + * PHP 7.2 includes the Sodium cryptography library, which (among other things) + * exposes a function called sodium_memzero() that we can use to zero-fill strings + * to minimize the risk of sensitive cryptographic materials persisting in memory. + * + * If this function is not available, we XOR the string in-place with itself as a + * best-effort attempt. + */ + public function __destruct() + { + if (extension_loaded('sodium') && function_exists('sodium_memzero')) { + try { + \sodium_memzero($this->internalString); + } catch (\SodiumException $ex) { + // This is a best effort, but does not provide the same guarantees as sodium_memzero(): + $this->internalString ^= $this->internalString; + } + } + } + + /** + * @return string + */ + public function get() + { + return $this->internalString; + } + + /** + * @return int + */ + public function length() + { + if (\is_callable('\\mb_strlen')) { + return (int) \mb_strlen($this->internalString, '8bit'); + } + return (int) \strlen($this->internalString); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/NeedsTrait.php b/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/NeedsTrait.php new file mode 100644 index 0000000..5ba4d64 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/Polyfill/NeedsTrait.php @@ -0,0 +1,38 @@ +<?php +namespace Aws\Crypto\Polyfill; + +use Aws\Exception\CryptoPolyfillException; + +/** + * Trait NeedsTrait + * @package Aws\Crypto\Polyfill + */ +trait NeedsTrait +{ + /** + * Preconditions, postconditions, and loop invariants are very + * useful for safe programing. They also document the specifications. + * This function is to help simplify the semantic burden of parsing + * these constructions. + * + * Instead of constructions like + * if (!(GOOD CONDITION)) { + * throw new \Exception('condition not true'); + * } + * + * you can write: + * needs(GOOD CONDITION, 'condition not true'); + * @param $condition + * @param $errorMessage + * @param null $exceptionClass + */ + public static function needs($condition, $errorMessage, $exceptionClass = null) + { + if (!$condition) { + if (!$exceptionClass) { + $exceptionClass = CryptoPolyfillException::class; + } + throw new $exceptionClass($errorMessage); + } + } +} |