diff options
author | Andrew Dolgov <[email protected]> | 2022-11-23 21:14:33 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2022-11-23 21:14:33 +0300 |
commit | 0c8af4992cb0f7589dcafaad65ada12753c64594 (patch) | |
tree | 18e83d068c3e7dd2499331de977782b382279396 /vendor/aws/aws-sdk-php/src/Crypto |
initial
Diffstat (limited to 'vendor/aws/aws-sdk-php/src/Crypto')
28 files changed, 3288 insertions, 0 deletions
diff --git a/vendor/aws/aws-sdk-php/src/Crypto/AbstractCryptoClient.php b/vendor/aws/aws-sdk-php/src/Crypto/AbstractCryptoClient.php new file mode 100644 index 0000000..823467b --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/AbstractCryptoClient.php @@ -0,0 +1,121 @@ +<?php +namespace Aws\Crypto; + +use Aws\Crypto\Cipher\CipherMethod; +use Aws\Crypto\Cipher\Cbc; +use GuzzleHttp\Psr7\Stream; + +/** + * Legacy abstract encryption client. New workflows should use + * AbstractCryptoClientV2. + * + * @deprecated + * @internal + */ +abstract class AbstractCryptoClient +{ + public static $supportedCiphers = ['cbc', 'gcm']; + + public static $supportedKeyWraps = [ + KmsMaterialsProvider::WRAP_ALGORITHM_NAME + ]; + + /** + * Returns if the passed cipher name is supported for encryption by the SDK. + * + * @param string $cipherName The name of a cipher to verify is registered. + * + * @return bool If the cipher passed is in our supported list. + */ + public static function isSupportedCipher($cipherName) + { + return in_array($cipherName, self::$supportedCiphers); + } + + /** + * Returns an identifier recognizable by `openssl_*` functions, such as + * `aes-256-cbc` or `aes-128-ctr`. + * + * @param string $cipherName Name of the cipher being used for encrypting + * or decrypting. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @return string + */ + abstract protected function getCipherOpenSslName($cipherName, $keySize); + + /** + * Constructs a CipherMethod for the given name, initialized with the other + * data passed for use in encrypting or decrypting. + * + * @param string $cipherName Name of the cipher to generate for encrypting. + * @param string $iv Base Initialization Vector for the cipher. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @return CipherMethod + * + * @internal + */ + abstract protected function buildCipherMethod($cipherName, $iv, $keySize); + + /** + * Performs a reverse lookup to get the openssl_* cipher name from the + * AESName passed in from the MetadataEnvelope. + * + * @param $aesName + * + * @return string + * + * @internal + */ + abstract protected function getCipherFromAesName($aesName); + + /** + * Dependency to provide an interface for building an encryption stream for + * data given cipher details, metadata, and materials to do so. + * + * @param Stream $plaintext Plain-text data to be encrypted using the + * materials, algorithm, and data provided. + * @param array $cipherOptions Options for use in determining the cipher to + * be used for encrypting data. + * @param MaterialsProvider $provider A provider to supply and encrypt + * materials used in encryption. + * @param MetadataEnvelope $envelope A storage envelope for encryption + * metadata to be added to. + * + * @return AesStreamInterface + * + * @internal + */ + abstract public function encrypt( + Stream $plaintext, + array $cipherOptions, + MaterialsProvider $provider, + MetadataEnvelope $envelope + ); + + /** + * Dependency to provide an interface for building a decryption stream for + * cipher text given metadata and materials to do so. + * + * @param string $cipherText Plain-text data to be decrypted using the + * materials, algorithm, and data provided. + * @param MaterialsProviderInterface $provider A provider to supply and encrypt + * materials used in encryption. + * @param MetadataEnvelope $envelope A storage envelope for encryption + * metadata to be read from. + * @param array $cipherOptions Additional verification options. + * + * @return AesStreamInterface + * + * @internal + */ + abstract public function decrypt( + $cipherText, + MaterialsProviderInterface $provider, + MetadataEnvelope $envelope, + array $cipherOptions = [] + ); +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/AbstractCryptoClientV2.php b/vendor/aws/aws-sdk-php/src/Crypto/AbstractCryptoClientV2.php new file mode 100644 index 0000000..2c7e7c2 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/AbstractCryptoClientV2.php @@ -0,0 +1,119 @@ +<?php +namespace Aws\Crypto; + +use Aws\Crypto\Cipher\CipherMethod; +use GuzzleHttp\Psr7\Stream; + +/** + * @internal + */ +abstract class AbstractCryptoClientV2 +{ + public static $supportedCiphers = ['gcm']; + + public static $supportedKeyWraps = [ + KmsMaterialsProviderV2::WRAP_ALGORITHM_NAME + ]; + + public static $supportedSecurityProfiles = ['V2', 'V2_AND_LEGACY']; + + public static $legacySecurityProfiles = ['V2_AND_LEGACY']; + + /** + * Returns if the passed cipher name is supported for encryption by the SDK. + * + * @param string $cipherName The name of a cipher to verify is registered. + * + * @return bool If the cipher passed is in our supported list. + */ + public static function isSupportedCipher($cipherName) + { + return in_array($cipherName, self::$supportedCiphers, true); + } + + /** + * Returns an identifier recognizable by `openssl_*` functions, such as + * `aes-256-gcm` + * + * @param string $cipherName Name of the cipher being used for encrypting + * or decrypting. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @return string + */ + abstract protected function getCipherOpenSslName($cipherName, $keySize); + + /** + * Constructs a CipherMethod for the given name, initialized with the other + * data passed for use in encrypting or decrypting. + * + * @param string $cipherName Name of the cipher to generate for encrypting. + * @param string $iv Base Initialization Vector for the cipher. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @return CipherMethod + * + * @internal + */ + abstract protected function buildCipherMethod($cipherName, $iv, $keySize); + + /** + * Performs a reverse lookup to get the openssl_* cipher name from the + * AESName passed in from the MetadataEnvelope. + * + * @param $aesName + * + * @return string + * + * @internal + */ + abstract protected function getCipherFromAesName($aesName); + + /** + * Dependency to provide an interface for building an encryption stream for + * data given cipher details, metadata, and materials to do so. + * + * @param Stream $plaintext Plain-text data to be encrypted using the + * materials, algorithm, and data provided. + * @param array $options Options for use in encryption. + * @param MaterialsProviderV2 $provider A provider to supply and encrypt + * materials used in encryption. + * @param MetadataEnvelope $envelope A storage envelope for encryption + * metadata to be added to. + * + * @return AesStreamInterface + * + * @internal + */ + abstract public function encrypt( + Stream $plaintext, + array $options, + MaterialsProviderV2 $provider, + MetadataEnvelope $envelope + ); + + /** + * Dependency to provide an interface for building a decryption stream for + * cipher text given metadata and materials to do so. + * + * @param string $cipherText Plain-text data to be decrypted using the + * materials, algorithm, and data provided. + * @param MaterialsProviderInterface $provider A provider to supply and encrypt + * materials used in encryption. + * @param MetadataEnvelope $envelope A storage envelope for encryption + * metadata to be read from. + * @param array $options Options used for decryption. + * + * @return AesStreamInterface + * + * @internal + */ + abstract public function decrypt( + $cipherText, + MaterialsProviderInterfaceV2 $provider, + MetadataEnvelope $envelope, + array $options = [] + ); +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/AesDecryptingStream.php b/vendor/aws/aws-sdk-php/src/Crypto/AesDecryptingStream.php new file mode 100644 index 0000000..c1524cc --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/AesDecryptingStream.php @@ -0,0 +1,146 @@ +<?php +namespace Aws\Crypto; + +use GuzzleHttp\Psr7\StreamDecoratorTrait; +use \LogicException; +use Psr\Http\Message\StreamInterface; +use Aws\Crypto\Cipher\CipherMethod; + +/** + * @internal Represents a stream of data to be decrypted with passed cipher. + */ +class AesDecryptingStream implements AesStreamInterface +{ + const BLOCK_SIZE = 16; // 128 bits + + use StreamDecoratorTrait; + + /** + * @var string + */ + private $buffer = ''; + + /** + * @var CipherMethod + */ + private $cipherMethod; + + /** + * @var string + */ + private $key; + + /** + * @var StreamInterface + */ + private $stream; + + /** + * @param StreamInterface $cipherText + * @param string $key + * @param CipherMethod $cipherMethod + */ + public function __construct( + StreamInterface $cipherText, + $key, + CipherMethod $cipherMethod + ) { + $this->stream = $cipherText; + $this->key = $key; + $this->cipherMethod = clone $cipherMethod; + } + + public function getOpenSslName() + { + return $this->cipherMethod->getOpenSslName(); + } + + public function getAesName() + { + return $this->cipherMethod->getAesName(); + } + + public function getCurrentIv() + { + return $this->cipherMethod->getCurrentIv(); + } + + public function getSize() + { + $plainTextSize = $this->stream->getSize(); + + if ($this->cipherMethod->requiresPadding()) { + // PKCS7 padding requires that between 1 and self::BLOCK_SIZE be + // added to the plaintext to make it an even number of blocks. The + // plaintext is between strlen($cipherText) - self::BLOCK_SIZE and + // strlen($cipherText) - 1 + return null; + } + + return $plainTextSize; + } + + public function isWritable() + { + return false; + } + + public function read($length) + { + if ($length > strlen($this->buffer)) { + $this->buffer .= $this->decryptBlock( + (int) ( + self::BLOCK_SIZE * ceil(($length - strlen($this->buffer)) / self::BLOCK_SIZE) + ) + ); + } + + $data = substr($this->buffer, 0, $length); + $this->buffer = substr($this->buffer, $length); + + return $data ? $data : ''; + } + + public function seek($offset, $whence = SEEK_SET) + { + if ($offset === 0 && $whence === SEEK_SET) { + $this->buffer = ''; + $this->cipherMethod->seek(0, SEEK_SET); + $this->stream->seek(0, SEEK_SET); + } else { + throw new LogicException('AES encryption streams only support being' + . ' rewound, not arbitrary seeking.'); + } + } + + private function decryptBlock($length) + { + if ($this->stream->eof()) { + return ''; + } + + $cipherText = ''; + do { + $cipherText .= $this->stream->read((int) ($length - strlen($cipherText))); + } while (strlen($cipherText) < $length && !$this->stream->eof()); + + $options = OPENSSL_RAW_DATA; + if (!$this->stream->eof() + && $this->stream->getSize() !== $this->stream->tell() + ) { + $options |= OPENSSL_ZERO_PADDING; + } + + $plaintext = openssl_decrypt( + $cipherText, + $this->cipherMethod->getOpenSslName(), + $this->key, + $options, + $this->cipherMethod->getCurrentIv() + ); + + $this->cipherMethod->update($cipherText); + + return $plaintext; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/AesEncryptingStream.php b/vendor/aws/aws-sdk-php/src/Crypto/AesEncryptingStream.php new file mode 100644 index 0000000..3b7c446 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/AesEncryptingStream.php @@ -0,0 +1,151 @@ +<?php +namespace Aws\Crypto; + +use GuzzleHttp\Psr7\StreamDecoratorTrait; +use \LogicException; +use Psr\Http\Message\StreamInterface; +use Aws\Crypto\Cipher\CipherMethod; + +/** + * @internal Represents a stream of data to be encrypted with a passed cipher. + */ +class AesEncryptingStream implements AesStreamInterface +{ + const BLOCK_SIZE = 16; // 128 bits + + use StreamDecoratorTrait; + + /** + * @var string + */ + private $buffer = ''; + + /** + * @var CipherMethod + */ + private $cipherMethod; + + /** + * @var string + */ + private $key; + + /** + * @var StreamInterface + */ + private $stream; + + /** + * @param StreamInterface $plainText + * @param string $key + * @param CipherMethod $cipherMethod + */ + public function __construct( + StreamInterface $plainText, + $key, + CipherMethod $cipherMethod + ) { + $this->stream = $plainText; + $this->key = $key; + $this->cipherMethod = clone $cipherMethod; + } + + public function getOpenSslName() + { + return $this->cipherMethod->getOpenSslName(); + } + + public function getAesName() + { + return $this->cipherMethod->getAesName(); + } + + public function getCurrentIv() + { + return $this->cipherMethod->getCurrentIv(); + } + + public function getSize() + { + $plainTextSize = $this->stream->getSize(); + + if ($this->cipherMethod->requiresPadding() && $plainTextSize !== null) { + // PKCS7 padding requires that between 1 and self::BLOCK_SIZE be + // added to the plaintext to make it an even number of blocks. + $padding = self::BLOCK_SIZE - $plainTextSize % self::BLOCK_SIZE; + return $plainTextSize + $padding; + } + + return $plainTextSize; + } + + public function isWritable() + { + return false; + } + + public function read($length) + { + if ($length > strlen($this->buffer)) { + $this->buffer .= $this->encryptBlock( + (int) + self::BLOCK_SIZE * ceil(($length - strlen($this->buffer)) / self::BLOCK_SIZE) + ); + } + + $data = substr($this->buffer, 0, $length); + $this->buffer = substr($this->buffer, $length); + + return $data ? $data : ''; + } + + public function seek($offset, $whence = SEEK_SET) + { + if ($whence === SEEK_CUR) { + $offset = $this->tell() + $offset; + $whence = SEEK_SET; + } + + if ($whence === SEEK_SET) { + $this->buffer = ''; + $wholeBlockOffset + = (int) ($offset / self::BLOCK_SIZE) * self::BLOCK_SIZE; + $this->stream->seek($wholeBlockOffset); + $this->cipherMethod->seek($wholeBlockOffset); + $this->read($offset - $wholeBlockOffset); + } else { + throw new LogicException('Unrecognized whence.'); + } + } + + private function encryptBlock($length) + { + if ($this->stream->eof()) { + return ''; + } + + $plainText = ''; + do { + $plainText .= $this->stream->read((int) ($length - strlen($plainText))); + } while (strlen($plainText) < $length && !$this->stream->eof()); + + $options = OPENSSL_RAW_DATA; + if (!$this->stream->eof() + || $this->stream->getSize() !== $this->stream->tell() + ) { + $options |= OPENSSL_ZERO_PADDING; + } + + $cipherText = openssl_encrypt( + $plainText, + $this->cipherMethod->getOpenSslName(), + $this->key, + $options, + $this->cipherMethod->getCurrentIv() + ); + + $this->cipherMethod->update($cipherText); + + return $cipherText; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/AesGcmDecryptingStream.php b/vendor/aws/aws-sdk-php/src/Crypto/AesGcmDecryptingStream.php new file mode 100644 index 0000000..76feaa1 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/AesGcmDecryptingStream.php @@ -0,0 +1,107 @@ +<?php +namespace Aws\Crypto; + +use Aws\Exception\CryptoException; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\StreamDecoratorTrait; +use Psr\Http\Message\StreamInterface; +use Aws\Crypto\Polyfill\AesGcm; +use Aws\Crypto\Polyfill\Key; + +/** + * @internal Represents a stream of data to be gcm decrypted. + */ +class AesGcmDecryptingStream implements AesStreamInterface +{ + use StreamDecoratorTrait; + + private $aad; + + private $initializationVector; + + private $key; + + private $keySize; + + private $cipherText; + + private $tag; + + private $tagLength; + + /** + * @param StreamInterface $cipherText + * @param string $key + * @param string $initializationVector + * @param string $tag + * @param string $aad + * @param int $tagLength + * @param int $keySize + */ + public function __construct( + StreamInterface $cipherText, + $key, + $initializationVector, + $tag, + $aad = '', + $tagLength = 128, + $keySize = 256 + ) { + $this->cipherText = $cipherText; + $this->key = $key; + $this->initializationVector = $initializationVector; + $this->tag = $tag; + $this->aad = $aad; + $this->tagLength = $tagLength; + $this->keySize = $keySize; + } + + public function getOpenSslName() + { + return "aes-{$this->keySize}-gcm"; + } + + public function getAesName() + { + return 'AES/GCM/NoPadding'; + } + + public function getCurrentIv() + { + return $this->initializationVector; + } + + public function createStream() + { + if (version_compare(PHP_VERSION, '7.1', '<')) { + return Psr7\Utils::streamFor(AesGcm::decrypt( + (string) $this->cipherText, + $this->initializationVector, + new Key($this->key), + $this->aad, + $this->tag, + $this->keySize + )); + } else { + $result = \openssl_decrypt( + (string)$this->cipherText, + $this->getOpenSslName(), + $this->key, + OPENSSL_RAW_DATA, + $this->initializationVector, + $this->tag, + $this->aad + ); + if ($result === false) { + throw new CryptoException('The requested object could not be' + . ' decrypted due to an invalid authentication tag.'); + } + return Psr7\Utils::streamFor($result); + } + } + + public function isWritable() + { + return false; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/AesGcmEncryptingStream.php b/vendor/aws/aws-sdk-php/src/Crypto/AesGcmEncryptingStream.php new file mode 100644 index 0000000..13357f5 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/AesGcmEncryptingStream.php @@ -0,0 +1,125 @@ +<?php +namespace Aws\Crypto; + +use Aws\Crypto\Polyfill\AesGcm; +use Aws\Crypto\Polyfill\Key; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\StreamDecoratorTrait; +use Psr\Http\Message\StreamInterface; +use \RuntimeException; + +/** + * @internal Represents a stream of data to be gcm encrypted. + */ +class AesGcmEncryptingStream implements AesStreamInterface, AesStreamInterfaceV2 +{ + use StreamDecoratorTrait; + + private $aad; + + private $initializationVector; + + private $key; + + private $keySize; + + private $plaintext; + + private $tag = ''; + + private $tagLength; + + /** + * Same as non-static 'getAesName' method, allowing calls in a static + * context. + * + * @return string + */ + public static function getStaticAesName() + { + return 'AES/GCM/NoPadding'; + } + + /** + * @param StreamInterface $plaintext + * @param string $key + * @param string $initializationVector + * @param string $aad + * @param int $tagLength + * @param int $keySize + */ + public function __construct( + StreamInterface $plaintext, + $key, + $initializationVector, + $aad = '', + $tagLength = 16, + $keySize = 256 + ) { + + $this->plaintext = $plaintext; + $this->key = $key; + $this->initializationVector = $initializationVector; + $this->aad = $aad; + $this->tagLength = $tagLength; + $this->keySize = $keySize; + } + + public function getOpenSslName() + { + return "aes-{$this->keySize}-gcm"; + } + + /** + * Same as static method and retained for backwards compatibility + * + * @return string + */ + public function getAesName() + { + return self::getStaticAesName(); + } + + public function getCurrentIv() + { + return $this->initializationVector; + } + + public function createStream() + { + if (version_compare(PHP_VERSION, '7.1', '<')) { + return Psr7\Utils::streamFor(AesGcm::encrypt( + (string) $this->plaintext, + $this->initializationVector, + new Key($this->key), + $this->aad, + $this->tag, + $this->keySize + )); + } else { + return Psr7\Utils::streamFor(\openssl_encrypt( + (string)$this->plaintext, + $this->getOpenSslName(), + $this->key, + OPENSSL_RAW_DATA, + $this->initializationVector, + $this->tag, + $this->aad, + $this->tagLength + )); + } + } + + /** + * @return string + */ + public function getTag() + { + return $this->tag; + } + + public function isWritable() + { + return false; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/AesStreamInterface.php b/vendor/aws/aws-sdk-php/src/Crypto/AesStreamInterface.php new file mode 100644 index 0000000..ce7b85d --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/AesStreamInterface.php @@ -0,0 +1,30 @@ +<?php +namespace Aws\Crypto; + +use Psr\Http\Message\StreamInterface; + +interface AesStreamInterface extends StreamInterface +{ + /** + * Returns an identifier recognizable by `openssl_*` functions, such as + * `aes-256-cbc` or `aes-128-ctr`. + * + * @return string + */ + public function getOpenSslName(); + + /** + * Returns an AES recognizable name, such as 'AES/GCM/NoPadding'. + * + * @return string + */ + public function getAesName(); + + /** + * Returns the IV that should be used to initialize the next block in + * encrypt or decrypt. + * + * @return string + */ + public function getCurrentIv(); +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/AesStreamInterfaceV2.php b/vendor/aws/aws-sdk-php/src/Crypto/AesStreamInterfaceV2.php new file mode 100644 index 0000000..4bd2d46 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/AesStreamInterfaceV2.php @@ -0,0 +1,31 @@ +<?php +namespace Aws\Crypto; + +use Psr\Http\Message\StreamInterface; + +interface AesStreamInterfaceV2 extends StreamInterface +{ + /** + * Returns an AES recognizable name, such as 'AES/GCM/NoPadding'. V2 + * interface is accessible from a static context. + * + * @return string + */ + public static function getStaticAesName(); + + /** + * Returns an identifier recognizable by `openssl_*` functions, such as + * `aes-256-cbc` or `aes-128-ctr`. + * + * @return string + */ + public function getOpenSslName(); + + /** + * Returns the IV that should be used to initialize the next block in + * encrypt or decrypt. + * + * @return string + */ + public function getCurrentIv(); +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/Cipher/Cbc.php b/vendor/aws/aws-sdk-php/src/Crypto/Cipher/Cbc.php new file mode 100644 index 0000000..926f87c --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/Cipher/Cbc.php @@ -0,0 +1,88 @@ +<?php +namespace Aws\Crypto\Cipher; + +use \InvalidArgumentException; +use \LogicException; + +/** + * An implementation of the CBC cipher for use with an AesEncryptingStream or + * AesDecrypting stream. + * + * This cipher method is deprecated and in maintenance mode - no new updates will be + * released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html + * for more information. + * + * @deprecated + */ +class Cbc implements CipherMethod +{ + const BLOCK_SIZE = 16; + + /** + * @var string + */ + private $baseIv; + + /** + * @var string + */ + private $iv; + + /** + * @var int + */ + private $keySize; + + /** + * @param string $iv Base Initialization Vector for the cipher. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @throws InvalidArgumentException Thrown if the passed iv does not match + * the iv length required by the cipher. + */ + public function __construct($iv, $keySize = 256) + { + $this->baseIv = $this->iv = $iv; + $this->keySize = $keySize; + + if (strlen($iv) !== openssl_cipher_iv_length($this->getOpenSslName())) { + throw new InvalidArgumentException('Invalid initialization vector'); + } + } + + public function getOpenSslName() + { + return "aes-{$this->keySize}-cbc"; + } + + public function getAesName() + { + return 'AES/CBC/PKCS5Padding'; + } + + public function getCurrentIv() + { + return $this->iv; + } + + public function requiresPadding() + { + return true; + } + + public function seek($offset, $whence = SEEK_SET) + { + if ($offset === 0 && $whence === SEEK_SET) { + $this->iv = $this->baseIv; + } else { + throw new LogicException('CBC initialization only support being' + . ' rewound, not arbitrary seeking.'); + } + } + + public function update($cipherTextBlock) + { + $this->iv = substr($cipherTextBlock, self::BLOCK_SIZE * -1); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/Cipher/CipherBuilderTrait.php b/vendor/aws/aws-sdk-php/src/Crypto/Cipher/CipherBuilderTrait.php new file mode 100644 index 0000000..ed9feb9 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/Cipher/CipherBuilderTrait.php @@ -0,0 +1,72 @@ +<?php +namespace Aws\Crypto\Cipher; + +use Aws\Exception\CryptoException; + +trait CipherBuilderTrait +{ + /** + * Returns an identifier recognizable by `openssl_*` functions, such as + * `aes-256-cbc` or `aes-128-ctr`. + * + * @param string $cipherName Name of the cipher being used for encrypting + * or decrypting. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @return string + */ + protected function getCipherOpenSslName($cipherName, $keySize) + { + return "aes-{$keySize}-{$cipherName}"; + } + + /** + * Constructs a CipherMethod for the given name, initialized with the other + * data passed for use in encrypting or decrypting. + * + * @param string $cipherName Name of the cipher to generate for encrypting. + * @param string $iv Base Initialization Vector for the cipher. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @return CipherMethod + * + * @internal + */ + protected function buildCipherMethod($cipherName, $iv, $keySize) + { + switch ($cipherName) { + case 'cbc': + return new Cbc( + $iv, + $keySize + ); + default: + return null; + } + } + + /** + * Performs a reverse lookup to get the openssl_* cipher name from the + * AESName passed in from the MetadataEnvelope. + * + * @param $aesName + * + * @return string + * + * @internal + */ + protected function getCipherFromAesName($aesName) + { + switch ($aesName) { + case 'AES/GCM/NoPadding': + return 'gcm'; + case 'AES/CBC/PKCS5Padding': + return 'cbc'; + default: + throw new CryptoException('Unrecognized or unsupported' + . ' AESName for reverse lookup.'); + } + } +}
\ No newline at end of file diff --git a/vendor/aws/aws-sdk-php/src/Crypto/Cipher/CipherMethod.php b/vendor/aws/aws-sdk-php/src/Crypto/Cipher/CipherMethod.php new file mode 100644 index 0000000..a99aaa7 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/Cipher/CipherMethod.php @@ -0,0 +1,59 @@ +<?php +namespace Aws\Crypto\Cipher; + +interface CipherMethod +{ + /** + * Returns an identifier recognizable by `openssl_*` functions, such as + * `aes-256-cbc` or `aes-128-ctr`. + * + * @return string + */ + public function getOpenSslName(); + + /** + * Returns an AES recognizable name, such as 'AES/GCM/NoPadding'. + * + * @return string + */ + public function getAesName(); + + /** + * Returns the IV that should be used to initialize the next block in + * encrypt or decrypt. + * + * @return string + */ + public function getCurrentIv(); + + /** + * Indicates whether the cipher method used with this IV requires padding + * the final block to make sure the plaintext is evenly divisible by the + * block size. + * + * @return boolean + */ + public function requiresPadding(); + + /** + * Adjust the return of this::getCurrentIv to reflect a seek performed on + * the encryption stream using this IV object. + * + * @param int $offset + * @param int $whence + * + * @throws LogicException Thrown if the requested seek is not supported by + * this IV implementation. For example, a CBC IV + * only supports a full rewind ($offset === 0 && + * $whence === SEEK_SET) + */ + public function seek($offset, $whence = SEEK_SET); + + /** + * Take account of the last cipher text block to adjust the return of + * this::getCurrentIv + * + * @param string $cipherTextBlock + */ + public function update($cipherTextBlock); +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/DecryptionTrait.php b/vendor/aws/aws-sdk-php/src/Crypto/DecryptionTrait.php new file mode 100644 index 0000000..7aa1a9a --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/DecryptionTrait.php @@ -0,0 +1,181 @@ +<?php +namespace Aws\Crypto; + +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\LimitStream; +use Psr\Http\Message\StreamInterface; + +trait DecryptionTrait +{ + /** + * Dependency to reverse lookup the openssl_* cipher name from the AESName + * in the MetadataEnvelope. + * + * @param $aesName + * + * @return string + * + * @internal + */ + abstract protected function getCipherFromAesName($aesName); + + /** + * Dependency to generate a CipherMethod from a set of inputs for loading + * in to an AesDecryptingStream. + * + * @param string $cipherName Name of the cipher to generate for decrypting. + * @param string $iv Base Initialization Vector for the cipher. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @return Cipher\CipherMethod + * + * @internal + */ + abstract protected function buildCipherMethod($cipherName, $iv, $keySize); + + /** + * Builds an AesStreamInterface using cipher options loaded from the + * MetadataEnvelope and MaterialsProvider. Can decrypt data from both the + * legacy and V2 encryption client workflows. + * + * @param string $cipherText Plain-text data to be encrypted using the + * materials, algorithm, and data provided. + * @param MaterialsProviderInterface $provider A provider to supply and encrypt + * materials used in encryption. + * @param MetadataEnvelope $envelope A storage envelope for encryption + * metadata to be read from. + * @param array $cipherOptions Additional verification options. + * + * @return AesStreamInterface + * + * @throws \InvalidArgumentException Thrown when a value in $cipherOptions + * is not valid. + * + * @internal + */ + public function decrypt( + $cipherText, + MaterialsProviderInterface $provider, + MetadataEnvelope $envelope, + array $cipherOptions = [] + ) { + $cipherOptions['Iv'] = base64_decode( + $envelope[MetadataEnvelope::IV_HEADER] + ); + + $cipherOptions['TagLength'] = + $envelope[MetadataEnvelope::CRYPTO_TAG_LENGTH_HEADER] / 8; + + $cek = $provider->decryptCek( + base64_decode( + $envelope[MetadataEnvelope::CONTENT_KEY_V2_HEADER] + ), + json_decode( + $envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER], + true + ) + ); + $cipherOptions['KeySize'] = strlen($cek) * 8; + $cipherOptions['Cipher'] = $this->getCipherFromAesName( + $envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER] + ); + + $decryptionStream = $this->getDecryptingStream( + $cipherText, + $cek, + $cipherOptions + ); + unset($cek); + + return $decryptionStream; + } + + private function getTagFromCiphertextStream( + StreamInterface $cipherText, + $tagLength + ) { + $cipherTextSize = $cipherText->getSize(); + if ($cipherTextSize == null || $cipherTextSize <= 0) { + throw new \RuntimeException('Cannot decrypt a stream of unknown' + . ' size.'); + } + return (string) new LimitStream( + $cipherText, + $tagLength, + $cipherTextSize - $tagLength + ); + } + + private function getStrippedCiphertextStream( + StreamInterface $cipherText, + $tagLength + ) { + $cipherTextSize = $cipherText->getSize(); + if ($cipherTextSize == null || $cipherTextSize <= 0) { + throw new \RuntimeException('Cannot decrypt a stream of unknown' + . ' size.'); + } + return new LimitStream( + $cipherText, + $cipherTextSize - $tagLength, + 0 + ); + } + + /** + * Generates a stream that wraps the cipher text with the proper cipher and + * uses the content encryption key (CEK) to decrypt the data when read. + * + * @param string $cipherText Plain-text data to be encrypted using the + * materials, algorithm, and data provided. + * @param string $cek A content encryption key for use by the stream for + * encrypting the plaintext data. + * @param array $cipherOptions Options for use in determining the cipher to + * be used for encrypting data. + * + * @return AesStreamInterface + * + * @internal + */ + protected function getDecryptingStream( + $cipherText, + $cek, + $cipherOptions + ) { + $cipherTextStream = Psr7\Utils::streamFor($cipherText); + switch ($cipherOptions['Cipher']) { + case 'gcm': + $cipherOptions['Tag'] = $this->getTagFromCiphertextStream( + $cipherTextStream, + $cipherOptions['TagLength'] + ); + + return new AesGcmDecryptingStream( + $this->getStrippedCiphertextStream( + $cipherTextStream, + $cipherOptions['TagLength'] + ), + $cek, + $cipherOptions['Iv'], + $cipherOptions['Tag'], + $cipherOptions['Aad'] = isset($cipherOptions['Aad']) + ? $cipherOptions['Aad'] + : null, + $cipherOptions['TagLength'] ?: null, + $cipherOptions['KeySize'] + ); + default: + $cipherMethod = $this->buildCipherMethod( + $cipherOptions['Cipher'], + $cipherOptions['Iv'], + $cipherOptions['KeySize'] + ); + return new AesDecryptingStream( + $cipherTextStream, + $cek, + $cipherMethod + ); + } + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/DecryptionTraitV2.php b/vendor/aws/aws-sdk-php/src/Crypto/DecryptionTraitV2.php new file mode 100644 index 0000000..800319d --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/DecryptionTraitV2.php @@ -0,0 +1,249 @@ +<?php +namespace Aws\Crypto; + +use Aws\Exception\CryptoException; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\LimitStream; +use Psr\Http\Message\StreamInterface; + +trait DecryptionTraitV2 +{ + /** + * Dependency to reverse lookup the openssl_* cipher name from the AESName + * in the MetadataEnvelope. + * + * @param $aesName + * + * @return string + * + * @internal + */ + abstract protected function getCipherFromAesName($aesName); + + /** + * Dependency to generate a CipherMethod from a set of inputs for loading + * in to an AesDecryptingStream. + * + * @param string $cipherName Name of the cipher to generate for decrypting. + * @param string $iv Base Initialization Vector for the cipher. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @return Cipher\CipherMethod + * + * @internal + */ + abstract protected function buildCipherMethod($cipherName, $iv, $keySize); + + /** + * Builds an AesStreamInterface using cipher options loaded from the + * MetadataEnvelope and MaterialsProvider. Can decrypt data from both the + * legacy and V2 encryption client workflows. + * + * @param string $cipherText Plain-text data to be encrypted using the + * materials, algorithm, and data provided. + * @param MaterialsProviderInterfaceV2 $provider A provider to supply and encrypt + * materials used in encryption. + * @param MetadataEnvelope $envelope A storage envelope for encryption + * metadata to be read from. + * @param array $options Options used for decryption. + * + * @return AesStreamInterface + * + * @throws \InvalidArgumentException Thrown when a value in $cipherOptions + * is not valid. + * + * @internal + */ + public function decrypt( + $cipherText, + MaterialsProviderInterfaceV2 $provider, + MetadataEnvelope $envelope, + array $options = [] + ) { + $options['@CipherOptions'] = !empty($options['@CipherOptions']) + ? $options['@CipherOptions'] + : []; + $options['@CipherOptions']['Iv'] = base64_decode( + $envelope[MetadataEnvelope::IV_HEADER] + ); + + $options['@CipherOptions']['TagLength'] = + $envelope[MetadataEnvelope::CRYPTO_TAG_LENGTH_HEADER] / 8; + + $cek = $provider->decryptCek( + base64_decode( + $envelope[MetadataEnvelope::CONTENT_KEY_V2_HEADER] + ), + json_decode( + $envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER], + true + ), + $options + ); + $options['@CipherOptions']['KeySize'] = strlen($cek) * 8; + $options['@CipherOptions']['Cipher'] = $this->getCipherFromAesName( + $envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER] + ); + + $this->validateOptionsAndEnvelope($options, $envelope); + + $decryptionStream = $this->getDecryptingStream( + $cipherText, + $cek, + $options['@CipherOptions'] + ); + unset($cek); + + return $decryptionStream; + } + + private function getTagFromCiphertextStream( + StreamInterface $cipherText, + $tagLength + ) { + $cipherTextSize = $cipherText->getSize(); + if ($cipherTextSize == null || $cipherTextSize <= 0) { + throw new \RuntimeException('Cannot decrypt a stream of unknown' + . ' size.'); + } + return (string) new LimitStream( + $cipherText, + $tagLength, + $cipherTextSize - $tagLength + ); + } + + private function getStrippedCiphertextStream( + StreamInterface $cipherText, + $tagLength + ) { + $cipherTextSize = $cipherText->getSize(); + if ($cipherTextSize == null || $cipherTextSize <= 0) { + throw new \RuntimeException('Cannot decrypt a stream of unknown' + . ' size.'); + } + return new LimitStream( + $cipherText, + $cipherTextSize - $tagLength, + 0 + ); + } + + private function validateOptionsAndEnvelope($options, $envelope) + { + $allowedCiphers = AbstractCryptoClientV2::$supportedCiphers; + $allowedKeywraps = AbstractCryptoClientV2::$supportedKeyWraps; + if ($options['@SecurityProfile'] == 'V2_AND_LEGACY') { + $allowedCiphers = array_unique(array_merge( + $allowedCiphers, + AbstractCryptoClient::$supportedCiphers + )); + $allowedKeywraps = array_unique(array_merge( + $allowedKeywraps, + AbstractCryptoClient::$supportedKeyWraps + )); + } + + $v1SchemaException = new CryptoException("The requested object is encrypted" + . " with V1 encryption schemas that have been disabled by" + . " client configuration @SecurityProfile=V2. Retry with" + . " V2_AND_LEGACY enabled or reencrypt the object."); + + if (!in_array($options['@CipherOptions']['Cipher'], $allowedCiphers)) { + if (in_array($options['@CipherOptions']['Cipher'], AbstractCryptoClient::$supportedCiphers)) { + throw $v1SchemaException; + } + throw new CryptoException("The requested object is encrypted with" + . " the cipher '{$options['@CipherOptions']['Cipher']}', which is not" + . " supported for decryption with the selected security profile." + . " This profile allows decryption with: " + . implode(", ", $allowedCiphers)); + } + if (!in_array( + $envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER], + $allowedKeywraps + )) { + if (in_array( + $envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER], + AbstractCryptoClient::$supportedKeyWraps) + ) { + throw $v1SchemaException; + } + throw new CryptoException("The requested object is encrypted with" + . " the keywrap schema '{$envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER]}'," + . " which is not supported for decryption with the current security" + . " profile."); + } + + $matdesc = json_decode( + $envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER], + true + ); + if (isset($matdesc['aws:x-amz-cek-alg']) + && $envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER] !== + $matdesc['aws:x-amz-cek-alg'] + ) { + throw new CryptoException("There is a mismatch in specified content" + . " encryption algrithm between the materials description value" + . " and the metadata envelope value: {$matdesc['aws:x-amz-cek-alg']}" + . " vs. {$envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER]}."); + } + } + + /** + * Generates a stream that wraps the cipher text with the proper cipher and + * uses the content encryption key (CEK) to decrypt the data when read. + * + * @param string $cipherText Plain-text data to be encrypted using the + * materials, algorithm, and data provided. + * @param string $cek A content encryption key for use by the stream for + * encrypting the plaintext data. + * @param array $cipherOptions Options for use in determining the cipher to + * be used for encrypting data. + * + * @return AesStreamInterface + * + * @internal + */ + protected function getDecryptingStream( + $cipherText, + $cek, + $cipherOptions + ) { + $cipherTextStream = Psr7\Utils::streamFor($cipherText); + switch ($cipherOptions['Cipher']) { + case 'gcm': + $cipherOptions['Tag'] = $this->getTagFromCiphertextStream( + $cipherTextStream, + $cipherOptions['TagLength'] + ); + + return new AesGcmDecryptingStream( + $this->getStrippedCiphertextStream( + $cipherTextStream, + $cipherOptions['TagLength'] + ), + $cek, + $cipherOptions['Iv'], + $cipherOptions['Tag'], + $cipherOptions['Aad'] = isset($cipherOptions['Aad']) + ? $cipherOptions['Aad'] + : null, + $cipherOptions['TagLength'] ?: null, + $cipherOptions['KeySize'] + ); + default: + $cipherMethod = $this->buildCipherMethod( + $cipherOptions['Cipher'], + $cipherOptions['Iv'], + $cipherOptions['KeySize'] + ); + return new AesDecryptingStream( + $cipherTextStream, + $cek, + $cipherMethod + ); + } + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/EncryptionTrait.php b/vendor/aws/aws-sdk-php/src/Crypto/EncryptionTrait.php new file mode 100644 index 0000000..f6d1b7d --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/EncryptionTrait.php @@ -0,0 +1,192 @@ +<?php +namespace Aws\Crypto; + +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\AppendStream; +use GuzzleHttp\Psr7\Stream; + +trait EncryptionTrait +{ + private static $allowedOptions = [ + 'Cipher' => true, + 'KeySize' => true, + 'Aad' => true, + ]; + + /** + * Dependency to generate a CipherMethod from a set of inputs for loading + * in to an AesEncryptingStream. + * + * @param string $cipherName Name of the cipher to generate for encrypting. + * @param string $iv Base Initialization Vector for the cipher. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @return Cipher\CipherMethod + * + * @internal + */ + abstract protected function buildCipherMethod($cipherName, $iv, $keySize); + + /** + * Builds an AesStreamInterface and populates encryption metadata into the + * supplied envelope. + * + * @param Stream $plaintext Plain-text data to be encrypted using the + * materials, algorithm, and data provided. + * @param array $cipherOptions Options for use in determining the cipher to + * be used for encrypting data. + * @param MaterialsProvider $provider A provider to supply and encrypt + * materials used in encryption. + * @param MetadataEnvelope $envelope A storage envelope for encryption + * metadata to be added to. + * + * @return AesStreamInterface + * + * @throws \InvalidArgumentException Thrown when a value in $cipherOptions + * is not valid. + * + * @internal + */ + public function encrypt( + Stream $plaintext, + array $cipherOptions, + MaterialsProvider $provider, + MetadataEnvelope $envelope + ) { + $materialsDescription = $provider->getMaterialsDescription(); + + $cipherOptions = array_intersect_key( + $cipherOptions, + self::$allowedOptions + ); + + if (empty($cipherOptions['Cipher'])) { + throw new \InvalidArgumentException('An encryption cipher must be' + . ' specified in the "cipher_options".'); + } + + if (!self::isSupportedCipher($cipherOptions['Cipher'])) { + throw new \InvalidArgumentException('The cipher requested is not' + . ' supported by the SDK.'); + } + + if (empty($cipherOptions['KeySize'])) { + $cipherOptions['KeySize'] = 256; + } + if (!is_int($cipherOptions['KeySize'])) { + throw new \InvalidArgumentException('The cipher "KeySize" must be' + . ' an integer.'); + } + + if (!MaterialsProvider::isSupportedKeySize( + $cipherOptions['KeySize'] + )) { + throw new \InvalidArgumentException('The cipher "KeySize" requested' + . ' is not supported by AES (128, 192, or 256).'); + } + + $cipherOptions['Iv'] = $provider->generateIv( + $this->getCipherOpenSslName( + $cipherOptions['Cipher'], + $cipherOptions['KeySize'] + ) + ); + + $cek = $provider->generateCek($cipherOptions['KeySize']); + + list($encryptingStream, $aesName) = $this->getEncryptingStream( + $plaintext, + $cek, + $cipherOptions + ); + + // Populate envelope data + $envelope[MetadataEnvelope::CONTENT_KEY_V2_HEADER] = + $provider->encryptCek( + $cek, + $materialsDescription + ); + unset($cek); + + $envelope[MetadataEnvelope::IV_HEADER] = + base64_encode($cipherOptions['Iv']); + $envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER] = + $provider->getWrapAlgorithmName(); + $envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER] = $aesName; + $envelope[MetadataEnvelope::UNENCRYPTED_CONTENT_LENGTH_HEADER] = + strlen($plaintext); + $envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER] = + json_encode($materialsDescription); + if (!empty($cipherOptions['Tag'])) { + $envelope[MetadataEnvelope::CRYPTO_TAG_LENGTH_HEADER] = + strlen($cipherOptions['Tag']) * 8; + } + + return $encryptingStream; + } + + /** + * Generates a stream that wraps the plaintext with the proper cipher and + * uses the content encryption key (CEK) to encrypt the data when read. + * + * @param Stream $plaintext Plain-text data to be encrypted using the + * materials, algorithm, and data provided. + * @param string $cek A content encryption key for use by the stream for + * encrypting the plaintext data. + * @param array $cipherOptions Options for use in determining the cipher to + * be used for encrypting data. + * + * @return [AesStreamInterface, string] + * + * @internal + */ + protected function getEncryptingStream( + Stream $plaintext, + $cek, + &$cipherOptions + ) { + switch ($cipherOptions['Cipher']) { + case 'gcm': + $cipherOptions['TagLength'] = 16; + + $cipherTextStream = new AesGcmEncryptingStream( + $plaintext, + $cek, + $cipherOptions['Iv'], + $cipherOptions['Aad'] = isset($cipherOptions['Aad']) + ? $cipherOptions['Aad'] + : null, + $cipherOptions['TagLength'], + $cipherOptions['KeySize'] + ); + + if (!empty($cipherOptions['Aad'])) { + trigger_error("'Aad' has been supplied for content encryption" + . " with " . $cipherTextStream->getAesName() . ". The" + . " PHP SDK encryption client can decrypt an object" + . " encrypted in this way, but other AWS SDKs may not be" + . " able to.", E_USER_WARNING); + } + + $appendStream = new AppendStream([ + $cipherTextStream->createStream() + ]); + $cipherOptions['Tag'] = $cipherTextStream->getTag(); + $appendStream->addStream(Psr7\Utils::streamFor($cipherOptions['Tag'])); + return [$appendStream, $cipherTextStream->getAesName()]; + default: + $cipherMethod = $this->buildCipherMethod( + $cipherOptions['Cipher'], + $cipherOptions['Iv'], + $cipherOptions['KeySize'] + ); + $cipherTextStream = new AesEncryptingStream( + $plaintext, + $cek, + $cipherMethod + ); + return [$cipherTextStream, $cipherTextStream->getAesName()]; + } + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/EncryptionTraitV2.php b/vendor/aws/aws-sdk-php/src/Crypto/EncryptionTraitV2.php new file mode 100644 index 0000000..8db0717 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/EncryptionTraitV2.php @@ -0,0 +1,196 @@ +<?php +namespace Aws\Crypto; + +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\AppendStream; +use GuzzleHttp\Psr7\Stream; +use Psr\Http\Message\StreamInterface; + +trait EncryptionTraitV2 +{ + private static $allowedOptions = [ + 'Cipher' => true, + 'KeySize' => true, + 'Aad' => true, + ]; + + private static $encryptClasses = [ + 'gcm' => AesGcmEncryptingStream::class + ]; + + /** + * Dependency to generate a CipherMethod from a set of inputs for loading + * in to an AesEncryptingStream. + * + * @param string $cipherName Name of the cipher to generate for encrypting. + * @param string $iv Base Initialization Vector for the cipher. + * @param int $keySize Size of the encryption key, in bits, that will be + * used. + * + * @return Cipher\CipherMethod + * + * @internal + */ + abstract protected function buildCipherMethod($cipherName, $iv, $keySize); + + /** + * Builds an AesStreamInterface and populates encryption metadata into the + * supplied envelope. + * + * @param Stream $plaintext Plain-text data to be encrypted using the + * materials, algorithm, and data provided. + * @param array $options Options for use in encryption, including cipher + * options, and encryption context. + * @param MaterialsProviderV2 $provider A provider to supply and encrypt + * materials used in encryption. + * @param MetadataEnvelope $envelope A storage envelope for encryption + * metadata to be added to. + * + * @return StreamInterface + * + * @throws \InvalidArgumentException Thrown when a value in $options['@CipherOptions'] + * is not valid. + *s + * @internal + */ + public function encrypt( + Stream $plaintext, + array $options, + MaterialsProviderV2 $provider, + MetadataEnvelope $envelope + ) { + $options = array_change_key_case($options); + $cipherOptions = array_intersect_key( + $options['@cipheroptions'], + self::$allowedOptions + ); + + if (empty($cipherOptions['Cipher'])) { + throw new \InvalidArgumentException('An encryption cipher must be' + . ' specified in @CipherOptions["Cipher"].'); + } + + $cipherOptions['Cipher'] = strtolower($cipherOptions['Cipher']); + + if (!self::isSupportedCipher($cipherOptions['Cipher'])) { + throw new \InvalidArgumentException('The cipher requested is not' + . ' supported by the SDK.'); + } + + if (empty($cipherOptions['KeySize'])) { + $cipherOptions['KeySize'] = 256; + } + if (!is_int($cipherOptions['KeySize'])) { + throw new \InvalidArgumentException('The cipher "KeySize" must be' + . ' an integer.'); + } + + if (!MaterialsProviderV2::isSupportedKeySize( + $cipherOptions['KeySize'] + )) { + throw new \InvalidArgumentException('The cipher "KeySize" requested' + . ' is not supported by AES (128 or 256).'); + } + + $cipherOptions['Iv'] = $provider->generateIv( + $this->getCipherOpenSslName( + $cipherOptions['Cipher'], + $cipherOptions['KeySize'] + ) + ); + + $encryptClass = self::$encryptClasses[$cipherOptions['Cipher']]; + $aesName = $encryptClass::getStaticAesName(); + $materialsDescription = ['aws:x-amz-cek-alg' => $aesName]; + + $keys = $provider->generateCek( + $cipherOptions['KeySize'], + $materialsDescription, + $options + ); + + // Some providers modify materials description based on options + if (isset($keys['UpdatedContext'])) { + $materialsDescription = $keys['UpdatedContext']; + } + + $encryptingStream = $this->getEncryptingStream( + $plaintext, + $keys['Plaintext'], + $cipherOptions + ); + + // Populate envelope data + $envelope[MetadataEnvelope::CONTENT_KEY_V2_HEADER] = $keys['Ciphertext']; + unset($keys); + + $envelope[MetadataEnvelope::IV_HEADER] = + base64_encode($cipherOptions['Iv']); + $envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER] = + $provider->getWrapAlgorithmName(); + $envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER] = $aesName; + $envelope[MetadataEnvelope::UNENCRYPTED_CONTENT_LENGTH_HEADER] = + strlen($plaintext); + $envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER] = + json_encode($materialsDescription); + if (!empty($cipherOptions['Tag'])) { + $envelope[MetadataEnvelope::CRYPTO_TAG_LENGTH_HEADER] = + strlen($cipherOptions['Tag']) * 8; + } + + return $encryptingStream; + } + + /** + * Generates a stream that wraps the plaintext with the proper cipher and + * uses the content encryption key (CEK) to encrypt the data when read. + * + * @param Stream $plaintext Plain-text data to be encrypted using the + * materials, algorithm, and data provided. + * @param string $cek A content encryption key for use by the stream for + * encrypting the plaintext data. + * @param array $cipherOptions Options for use in determining the cipher to + * be used for encrypting data. + * + * @return [AesStreamInterface, string] + * + * @internal + */ + protected function getEncryptingStream( + Stream $plaintext, + $cek, + &$cipherOptions + ) { + switch ($cipherOptions['Cipher']) { + // Only 'gcm' is supported for encryption currently + case 'gcm': + $cipherOptions['TagLength'] = 16; + $encryptClass = self::$encryptClasses['gcm']; + $cipherTextStream = new $encryptClass( + $plaintext, + $cek, + $cipherOptions['Iv'], + $cipherOptions['Aad'] = isset($cipherOptions['Aad']) + ? $cipherOptions['Aad'] + : '', + $cipherOptions['TagLength'], + $cipherOptions['KeySize'] + ); + + if (!empty($cipherOptions['Aad'])) { + trigger_error("'Aad' has been supplied for content encryption" + . " with " . $cipherTextStream->getAesName() . ". The" + . " PHP SDK encryption client can decrypt an object" + . " encrypted in this way, but other AWS SDKs may not be" + . " able to.", E_USER_WARNING); + } + + $appendStream = new AppendStream([ + $cipherTextStream->createStream() + ]); + $cipherOptions['Tag'] = $cipherTextStream->getTag(); + $appendStream->addStream(Psr7\Utils::streamFor($cipherOptions['Tag'])); + return $appendStream; + } + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/KmsMaterialsProvider.php b/vendor/aws/aws-sdk-php/src/Crypto/KmsMaterialsProvider.php new file mode 100644 index 0000000..fc75138 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/KmsMaterialsProvider.php @@ -0,0 +1,121 @@ +<?php +namespace Aws\Crypto; + +use Aws\Kms\KmsClient; + +/** + * Uses KMS to supply materials for encrypting and decrypting data. + * + * Legacy implementation that supports legacy S3EncryptionClient and + * S3EncryptionMultipartUploader, which use an older encryption workflow. Use + * KmsMaterialsProviderV2 with S3EncryptionClientV2 or + * S3EncryptionMultipartUploaderV2 if possible. + * + * @deprecated + */ +class KmsMaterialsProvider extends MaterialsProvider implements MaterialsProviderInterface +{ + const WRAP_ALGORITHM_NAME = 'kms'; + + private $kmsClient; + private $kmsKeyId; + + /** + * @param KmsClient $kmsClient A KMS Client for use encrypting and + * decrypting keys. + * @param string $kmsKeyId The private KMS key id to be used for encrypting + * and decrypting keys. + */ + public function __construct( + KmsClient $kmsClient, + $kmsKeyId = null + ) { + $this->kmsClient = $kmsClient; + $this->kmsKeyId = $kmsKeyId; + } + + public function fromDecryptionEnvelope(MetadataEnvelope $envelope) + { + if (empty($envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER])) { + throw new \RuntimeException('Not able to detect the materials description.'); + } + + $materialsDescription = json_decode( + $envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER], + true + ); + + if (empty($materialsDescription['kms_cmk_id']) + && empty($materialsDescription['aws:x-amz-cek-alg'])) { + throw new \RuntimeException('Not able to detect kms_cmk_id (legacy' + . ' implementation) or aws:x-amz-cek-alg (current implementation)' + . ' from kms materials description.'); + } + + return new self( + $this->kmsClient, + isset($materialsDescription['kms_cmk_id']) + ? $materialsDescription['kms_cmk_id'] + : null + ); + } + + /** + * The KMS key id for use in matching this Provider to its keys, + * consistently with other SDKs as 'kms_cmk_id'. + * + * @return array + */ + public function getMaterialsDescription() + { + return ['kms_cmk_id' => $this->kmsKeyId]; + } + + public function getWrapAlgorithmName() + { + return self::WRAP_ALGORITHM_NAME; + } + + /** + * Takes a content encryption key (CEK) and description to return an encrypted + * key by using KMS' Encrypt API. + * + * @param string $unencryptedCek Key for use in encrypting other data + * that itself needs to be encrypted by the + * Provider. + * @param string $materialDescription Material Description for use in + * encrypting the $cek. + * + * @return string + */ + public function encryptCek($unencryptedCek, $materialDescription) + { + $encryptedDataKey = $this->kmsClient->encrypt([ + 'Plaintext' => $unencryptedCek, + 'KeyId' => $this->kmsKeyId, + 'EncryptionContext' => $materialDescription + ]); + return base64_encode($encryptedDataKey['CiphertextBlob']); + } + + /** + * Takes an encrypted content encryption key (CEK) and material description + * for use decrypting the key by using KMS' Decrypt API. + * + * @param string $encryptedCek Encrypted key to be decrypted by the Provider + * for use decrypting other data. + * @param string $materialDescription Material Description for use in + * encrypting the $cek. + * + * @return string + */ + public function decryptCek($encryptedCek, $materialDescription) + { + $result = $this->kmsClient->decrypt([ + 'CiphertextBlob' => $encryptedCek, + 'EncryptionContext' => $materialDescription + ]); + + return $result['Plaintext']; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/KmsMaterialsProviderV2.php b/vendor/aws/aws-sdk-php/src/Crypto/KmsMaterialsProviderV2.php new file mode 100644 index 0000000..e7da8b9 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/KmsMaterialsProviderV2.php @@ -0,0 +1,100 @@ +<?php +namespace Aws\Crypto; + +use Aws\Exception\CryptoException; +use Aws\Kms\KmsClient; + +/** + * Uses KMS to supply materials for encrypting and decrypting data. This + * V2 implementation should be used with the V2 encryption clients (i.e. + * S3EncryptionClientV2). + */ +class KmsMaterialsProviderV2 extends MaterialsProviderV2 implements MaterialsProviderInterfaceV2 +{ + const WRAP_ALGORITHM_NAME = 'kms+context'; + + private $kmsClient; + private $kmsKeyId; + + /** + * @param KmsClient $kmsClient A KMS Client for use encrypting and + * decrypting keys. + * @param string $kmsKeyId The private KMS key id to be used for encrypting + * and decrypting keys. + */ + public function __construct( + KmsClient $kmsClient, + $kmsKeyId = null + ) { + $this->kmsClient = $kmsClient; + $this->kmsKeyId = $kmsKeyId; + } + + /** + * @inheritDoc + */ + public function getWrapAlgorithmName() + { + return self::WRAP_ALGORITHM_NAME; + } + + /** + * @inheritDoc + */ + public function decryptCek($encryptedCek, $materialDescription, $options) + { + $params = [ + 'CiphertextBlob' => $encryptedCek, + 'EncryptionContext' => $materialDescription + ]; + if (empty($options['@KmsAllowDecryptWithAnyCmk'])) { + if (empty($this->kmsKeyId)) { + throw new CryptoException('KMS CMK ID was not specified and the' + . ' operation is not opted-in to attempting to use any valid' + . ' CMK it discovers. Please specify a CMK ID, or explicitly' + . ' enable attempts to use any valid KMS CMK with the' + . ' @KmsAllowDecryptWithAnyCmk option.'); + } + $params['KeyId'] = $this->kmsKeyId; + } + + $result = $this->kmsClient->decrypt($params); + return $result['Plaintext']; + } + + /** + * @inheritDoc + */ + public function generateCek($keySize, $context, $options) + { + if (empty($this->kmsKeyId)) { + throw new CryptoException('A KMS key id is required for encryption' + . ' with KMS keywrap. Use a KmsMaterialsProviderV2 that has been' + . ' instantiated with a KMS key id.'); + } + $options = array_change_key_case($options); + if (!isset($options['@kmsencryptioncontext']) + || !is_array($options['@kmsencryptioncontext']) + ) { + throw new CryptoException("'@KmsEncryptionContext' is a" + . " required argument when using KmsMaterialsProviderV2, and" + . " must be an associative array (or empty array)."); + } + if (isset($options['@kmsencryptioncontext']['aws:x-amz-cek-alg'])) { + throw new CryptoException("Conflict in reserved @KmsEncryptionContext" + . " key aws:x-amz-cek-alg. This value is reserved for the S3" + . " Encryption Client and cannot be set by the user."); + } + $context = array_merge($options['@kmsencryptioncontext'], $context); + $result = $this->kmsClient->generateDataKey([ + 'KeyId' => $this->kmsKeyId, + 'KeySpec' => "AES_{$keySize}", + 'EncryptionContext' => $context + ]); + return [ + 'Plaintext' => $result['Plaintext'], + 'Ciphertext' => base64_encode($result['CiphertextBlob']), + 'UpdatedContext' => $context + ]; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProvider.php b/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProvider.php new file mode 100644 index 0000000..1c6941c --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProvider.php @@ -0,0 +1,105 @@ +<?php +namespace Aws\Crypto; + +abstract class MaterialsProvider implements MaterialsProviderInterface +{ + private static $supportedKeySizes = [ + 128 => true, + 192 => true, + 256 => true, + ]; + + /** + * Returns if the requested size is supported by AES. + * + * @param int $keySize Size of the requested key in bits. + * + * @return bool + */ + public static function isSupportedKeySize($keySize) + { + return isset(self::$supportedKeySizes[$keySize]); + } + + /** + * Performs further initialization of the MaterialsProvider based on the + * data inside the MetadataEnvelope. + * + * @param MetadataEnvelope $envelope A storage envelope for encryption + * metadata to be read from. + * + * @return MaterialsProvider + * + * @throws \RuntimeException Thrown when there is an empty or improperly + * formed materials description in the envelope. + * + * @internal + */ + abstract public function fromDecryptionEnvelope(MetadataEnvelope $envelope); + + /** + * Returns the material description for this Provider so it can be verified + * by encryption mechanisms. + * + * @return string + */ + abstract public function getMaterialsDescription(); + + /** + * Returns the wrap algorithm name for this Provider. + * + * @return string + */ + abstract public function getWrapAlgorithmName(); + + /** + * Takes a content encryption key (CEK) and description to return an + * encrypted key according to the Provider's specifications. + * + * @param string $unencryptedCek Key for use in encrypting other data + * that itself needs to be encrypted by the + * Provider. + * @param string $materialDescription Material Description for use in + * encrypting the $cek. + * + * @return string + */ + abstract public function encryptCek($unencryptedCek, $materialDescription); + + /** + * Takes an encrypted content encryption key (CEK) and material description + * for use decrypting the key according to the Provider's specifications. + * + * @param string $encryptedCek Encrypted key to be decrypted by the Provider + * for use decrypting other data. + * @param string $materialDescription Material Description for use in + * encrypting the $cek. + * + * @return string + */ + abstract public function decryptCek($encryptedCek, $materialDescription); + + /** + * @param string $keySize Length of a cipher key in bits for generating a + * random content encryption key (CEK). + * + * @return string + */ + public function generateCek($keySize) + { + return openssl_random_pseudo_bytes($keySize / 8); + } + + /** + * @param string $openSslName Cipher OpenSSL name to use for generating + * an initialization vector. + * + * @return string + */ + public function generateIv($openSslName) + { + return openssl_random_pseudo_bytes( + openssl_cipher_iv_length($openSslName) + ); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterface.php b/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterface.php new file mode 100644 index 0000000..a22016d --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterface.php @@ -0,0 +1,61 @@ +<?php +namespace Aws\Crypto; + +interface MaterialsProviderInterface +{ + /** + * Returns if the requested size is supported by AES. + * + * @param int $keySize Size of the requested key in bits. + * + * @return bool + */ + public static function isSupportedKeySize($keySize); + + /** + * Performs further initialization of the MaterialsProvider based on the + * data inside the MetadataEnvelope. + * + * @param MetadataEnvelope $envelope A storage envelope for encryption + * metadata to be read from. + * + * @internal + */ + public function fromDecryptionEnvelope(MetadataEnvelope $envelope); + + /** + * Returns the wrap algorithm name for this Provider. + * + * @return string + */ + public function getWrapAlgorithmName(); + + /** + * Takes an encrypted content encryption key (CEK) and material description + * for use decrypting the key according to the Provider's specifications. + * + * @param string $encryptedCek Encrypted key to be decrypted by the Provider + * for use decrypting other data. + * @param string $materialDescription Material Description for use in + * encrypting the $cek. + * + * @return string + */ + public function decryptCek($encryptedCek, $materialDescription); + + /** + * @param string $keySize Length of a cipher key in bits for generating a + * random content encryption key (CEK). + * + * @return string + */ + public function generateCek($keySize); + + /** + * @param string $openSslName Cipher OpenSSL name to use for generating + * an initialization vector. + * + * @return string + */ + public function generateIv($openSslName); +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterfaceV2.php b/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterfaceV2.php new file mode 100644 index 0000000..265af1a --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterfaceV2.php @@ -0,0 +1,53 @@ +<?php +namespace Aws\Crypto; + +interface MaterialsProviderInterfaceV2 +{ + /** + * Returns if the requested size is supported by AES. + * + * @param int $keySize Size of the requested key in bits. + * + * @return bool + */ + public static function isSupportedKeySize($keySize); + + /** + * Returns the wrap algorithm name for this Provider. + * + * @return string + */ + public function getWrapAlgorithmName(); + + /** + * Takes an encrypted content encryption key (CEK) and material description + * for use decrypting the key according to the Provider's specifications. + * + * @param string $encryptedCek Encrypted key to be decrypted by the Provider + * for use decrypting other data. + * @param string $materialDescription Material Description for use in + * decrypting the CEK. + * @param array $options Options for use in decrypting the CEK. + * + * @return string + */ + public function decryptCek($encryptedCek, $materialDescription, $options); + + /** + * @param string $keySize Length of a cipher key in bits for generating a + * random content encryption key (CEK). + * @param array $context Context map needed for key encryption + * @param array $options Additional options to be used in CEK generation + * + * @return array + */ + public function generateCek($keySize, $context, $options); + + /** + * @param string $openSslName Cipher OpenSSL name to use for generating + * an initialization vector. + * + * @return string + */ + public function generateIv($openSslName); +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProviderV2.php b/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProviderV2.php new file mode 100644 index 0000000..685cbf5 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/MaterialsProviderV2.php @@ -0,0 +1,66 @@ +<?php +namespace Aws\Crypto; + +abstract class MaterialsProviderV2 implements MaterialsProviderInterfaceV2 +{ + private static $supportedKeySizes = [ + 128 => true, + 256 => true, + ]; + + /** + * Returns if the requested size is supported by AES. + * + * @param int $keySize Size of the requested key in bits. + * + * @return bool + */ + public static function isSupportedKeySize($keySize) + { + return isset(self::$supportedKeySizes[$keySize]); + } + + /** + * Returns the wrap algorithm name for this Provider. + * + * @return string + */ + abstract public function getWrapAlgorithmName(); + + /** + * Takes an encrypted content encryption key (CEK) and material description + * for use decrypting the key according to the Provider's specifications. + * + * @param string $encryptedCek Encrypted key to be decrypted by the Provider + * for use decrypting other data. + * @param string $materialDescription Material Description for use in + * decrypting the CEK. + * @param string $options Options for use in decrypting the CEK. + * + * @return string + */ + abstract public function decryptCek($encryptedCek, $materialDescription, $options); + + /** + * @param string $keySize Length of a cipher key in bits for generating a + * random content encryption key (CEK). + * @param array $context Context map needed for key encryption + * @param array $options Additional options to be used in CEK generation + * + * @return array + */ + abstract public function generateCek($keySize, $context, $options); + + /** + * @param string $openSslName Cipher OpenSSL name to use for generating + * an initialization vector. + * + * @return string + */ + public function generateIv($openSslName) + { + return openssl_random_pseudo_bytes( + openssl_cipher_iv_length($openSslName) + ); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/MetadataEnvelope.php b/vendor/aws/aws-sdk-php/src/Crypto/MetadataEnvelope.php new file mode 100644 index 0000000..5a7c692 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/MetadataEnvelope.php @@ -0,0 +1,61 @@ +<?php +namespace Aws\Crypto; + +use Aws\HasDataTrait; +use \ArrayAccess; +use \IteratorAggregate; +use \InvalidArgumentException; +use \JsonSerializable; + +/** + * Stores encryption metadata for reading and writing. + * + * @internal + */ +class MetadataEnvelope implements ArrayAccess, IteratorAggregate, JsonSerializable +{ + use HasDataTrait; + + const CONTENT_KEY_V2_HEADER = 'x-amz-key-v2'; + const IV_HEADER = 'x-amz-iv'; + const MATERIALS_DESCRIPTION_HEADER = 'x-amz-matdesc'; + const KEY_WRAP_ALGORITHM_HEADER = 'x-amz-wrap-alg'; + const CONTENT_CRYPTO_SCHEME_HEADER = 'x-amz-cek-alg'; + const CRYPTO_TAG_LENGTH_HEADER = 'x-amz-tag-len'; + const UNENCRYPTED_CONTENT_LENGTH_HEADER = 'x-amz-unencrypted-content-length'; + + private static $constants = []; + + public static function getConstantValues() + { + if (empty(self::$constants)) { + $reflection = new \ReflectionClass(static::class); + foreach (array_values($reflection->getConstants()) as $constant) { + self::$constants[$constant] = true; + } + } + + return array_keys(self::$constants); + } + + /** + * @return void + */ + #[\ReturnTypeWillChange] + public function offsetSet($name, $value) + { + $constants = self::getConstantValues(); + if (is_null($name) || !in_array($name, $constants)) { + throw new InvalidArgumentException('MetadataEnvelope fields must' + . ' must match a predefined offset; use the header constants.'); + } + + $this->data[$name] = $value; + } + + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->data; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Crypto/MetadataStrategyInterface.php b/vendor/aws/aws-sdk-php/src/Crypto/MetadataStrategyInterface.php new file mode 100644 index 0000000..5270c7e --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Crypto/MetadataStrategyInterface.php @@ -0,0 +1,30 @@ +<?php +namespace Aws\Crypto; + +interface MetadataStrategyInterface +{ + /** + * Places the information in the MetadataEnvelope to the strategy specific + * location. Populates the PutObject arguments with any information + * necessary for loading. + * + * @param MetadataEnvelope $envelope Encryption data to save according to + * the strategy. + * @param array $args Starting arguments for PutObject. + * + * @return array Updated arguments for PutObject. + */ + public function save(MetadataEnvelope $envelope, array $args); + + /** + * Generates a MetadataEnvelope according to the specific strategy using the + * passed arguments. + * + * @param array $args Arguments from Command and Result that contains + * S3 Object information, relevant headers, and command + * configuration. + * + * @return MetadataEnvelope + */ + public function load(array $args); +} 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); + } + } +} |