123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- <?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 array returns an array with two elements as follows: [string, AesStreamInterface]
- *
- * @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']
- : '',
- $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()];
- }
- }
- }
|