Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?phpnamespace Aws\S3\Crypto;use Aws\Crypto\DecryptionTraitV2;use Aws\Exception\CryptoException;use Aws\HashingStream;use Aws\PhpHash;use Aws\Crypto\AbstractCryptoClientV2;use Aws\Crypto\EncryptionTraitV2;use Aws\Crypto\MetadataEnvelope;use Aws\Crypto\MaterialsProvider;use Aws\Crypto\Cipher\CipherBuilderTrait;use Aws\S3\S3Client;use GuzzleHttp\Promise;use GuzzleHttp\Promise\PromiseInterface;use GuzzleHttp\Psr7;/*** Provides a wrapper for an S3Client that supplies functionality to encrypt* data on putObject[Async] calls and decrypt data on getObject[Async] calls.** AWS strongly recommends the upgrade to the S3EncryptionClientV2 (over the* S3EncryptionClient), as it offers updated data security best practices to our* customers who upgrade. S3EncryptionClientV2 contains breaking changes, so this* will require planning by engineering teams to migrate. New workflows should* just start with S3EncryptionClientV2.** Note that for PHP versions of < 7.1, this class uses an AES-GCM polyfill* for encryption since there is no native PHP support. The performance for large* inputs will be a lot slower than for PHP 7.1+, so upgrading older PHP version* environments may be necessary to use this effectively.** Example write path:** <code>* use Aws\Crypto\KmsMaterialsProviderV2;* use Aws\S3\Crypto\S3EncryptionClientV2;* use Aws\S3\S3Client;** $encryptionClient = new S3EncryptionClientV2(* new S3Client([* 'region' => 'us-west-2',* 'version' => 'latest'* ])* );* $materialsProvider = new KmsMaterialsProviderV2(* new KmsClient([* 'profile' => 'default',* 'region' => 'us-east-1',* 'version' => 'latest',* ],* 'your-kms-key-id'* );** $encryptionClient->putObject([* '@MaterialsProvider' => $materialsProvider,* '@CipherOptions' => [* 'Cipher' => 'gcm',* 'KeySize' => 256,* ],* '@KmsEncryptionContext' => ['foo' => 'bar'],* 'Bucket' => 'your-bucket',* 'Key' => 'your-key',* 'Body' => 'your-encrypted-data',* ]);* </code>** Example read call (using objects from previous example):** <code>* $encryptionClient->getObject([* '@MaterialsProvider' => $materialsProvider,* '@CipherOptions' => [* 'Cipher' => 'gcm',* 'KeySize' => 256,* ],* 'Bucket' => 'your-bucket',* 'Key' => 'your-key',* ]);* </code>*/class S3EncryptionClientV2 extends AbstractCryptoClientV2{use CipherBuilderTrait;use CryptoParamsTraitV2;use DecryptionTraitV2;use EncryptionTraitV2;use UserAgentTrait;const CRYPTO_VERSION = '2.1';private $client;private $instructionFileSuffix;private $legacyWarningCount;/*** @param S3Client $client The S3Client to be used for true uploading and* retrieving objects from S3 when using the* encryption client.* @param string|null $instructionFileSuffix Suffix for a client wide* default when using instruction* files for metadata storage.*/public function __construct(S3Client $client,$instructionFileSuffix = null) {$this->appendUserAgent($client, 'feat/s3-encrypt/' . self::CRYPTO_VERSION);$this->client = $client;$this->instructionFileSuffix = $instructionFileSuffix;$this->legacyWarningCount = 0;}private static function getDefaultStrategy(){return new HeadersMetadataStrategy();}/*** Encrypts the data in the 'Body' field of $args and promises to upload it* to the specified location on S3.** Note that for PHP versions of < 7.1, this operation uses an AES-GCM* polyfill for encryption since there is no native PHP support. The* performance for large inputs will be a lot slower than for PHP 7.1+, so* upgrading older PHP version environments may be necessary to use this* effectively.** @param array $args Arguments for encrypting an object and uploading it* to S3 via PutObject.** The required configuration arguments are as follows:** - @MaterialsProvider: (MaterialsProviderV2) Provides Cek, Iv, and Cek* encrypting/decrypting for encryption metadata.* - @CipherOptions: (array) Cipher options for encrypting data. Only the* Cipher option is required. Accepts the following:* - Cipher: (string) gcm* See also: AbstractCryptoClientV2::$supportedCiphers* - KeySize: (int) 128|256* See also: MaterialsProvider::$supportedKeySizes* - Aad: (string) Additional authentication data. This option is* passed directly to OpenSSL when using gcm. Note if you pass in* Aad, the PHP SDK will be able to decrypt the resulting object,* but other AWS SDKs may not be able to do so.* - @KmsEncryptionContext: (array) Only required if using* KmsMaterialsProviderV2. An associative array of key-value* pairs to be added to the encryption context for KMS key encryption. An* empty array may be passed if no additional context is desired.** The optional configuration arguments are as follows:** - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing* MetadataEnvelope information. Defaults to using a* HeadersMetadataStrategy. Can either be a class implementing* MetadataStrategy, a class name of a predefined strategy, or empty/null* to default.* - @InstructionFileSuffix: (string|null) Suffix used when writing to an* instruction file if using an InstructionFileMetadataHandler.** @return PromiseInterface** @throws \InvalidArgumentException Thrown when arguments above are not* passed or are passed incorrectly.*/public function putObjectAsync(array $args){$provider = $this->getMaterialsProvider($args);unset($args['@MaterialsProvider']);$instructionFileSuffix = $this->getInstructionFileSuffix($args);unset($args['@InstructionFileSuffix']);$strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);unset($args['@MetadataStrategy']);$envelope = new MetadataEnvelope();return Promise\Create::promiseFor($this->encrypt(Psr7\Utils::streamFor($args['Body']),$args,$provider,$envelope))->then(function ($encryptedBodyStream) use ($args) {$hash = new PhpHash('sha256');$hashingEncryptedBodyStream = new HashingStream($encryptedBodyStream,$hash,self::getContentShaDecorator($args));return [$hashingEncryptedBodyStream, $args];})->then(function ($putObjectContents) use ($strategy, $envelope) {list($bodyStream, $args) = $putObjectContents;if ($strategy === null) {$strategy = self::getDefaultStrategy();}$updatedArgs = $strategy->save($envelope, $args);$updatedArgs['Body'] = $bodyStream;return $updatedArgs;})->then(function ($args) {unset($args['@CipherOptions']);return $this->client->putObjectAsync($args);});}private static function getContentShaDecorator(&$args){return function ($hash) use (&$args) {$args['ContentSHA256'] = bin2hex($hash);};}/*** Encrypts the data in the 'Body' field of $args and uploads it to the* specified location on S3.** Note that for PHP versions of < 7.1, this operation uses an AES-GCM* polyfill for encryption since there is no native PHP support. The* performance for large inputs will be a lot slower than for PHP 7.1+, so* upgrading older PHP version environments may be necessary to use this* effectively.** @param array $args Arguments for encrypting an object and uploading it* to S3 via PutObject.** The required configuration arguments are as follows:** - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek* encrypting/decrypting for encryption metadata.* - @CipherOptions: (array) Cipher options for encrypting data. A Cipher* is required. Accepts the following options:* - Cipher: (string) gcm* See also: AbstractCryptoClientV2::$supportedCiphers* - KeySize: (int) 128|256* See also: MaterialsProvider::$supportedKeySizes* - Aad: (string) Additional authentication data. This option is* passed directly to OpenSSL when using gcm. Note if you pass in* Aad, the PHP SDK will be able to decrypt the resulting object,* but other AWS SDKs may not be able to do so.* - @KmsEncryptionContext: (array) Only required if using* KmsMaterialsProviderV2. An associative array of key-value* pairs to be added to the encryption context for KMS key encryption. An* empty array may be passed if no additional context is desired.** The optional configuration arguments are as follows:** - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing* MetadataEnvelope information. Defaults to using a* HeadersMetadataStrategy. Can either be a class implementing* MetadataStrategy, a class name of a predefined strategy, or empty/null* to default.* - @InstructionFileSuffix: (string|null) Suffix used when writing to an* instruction file if an using an InstructionFileMetadataHandler was* determined.** @return \Aws\Result PutObject call result with the details of uploading* the encrypted file.** @throws \InvalidArgumentException Thrown when arguments above are not* passed or are passed incorrectly.*/public function putObject(array $args){return $this->putObjectAsync($args)->wait();}/*** Promises to retrieve an object from S3 and decrypt the data in the* 'Body' field.** @param array $args Arguments for retrieving an object from S3 via* GetObject and decrypting it.** The required configuration argument is as follows:** - @MaterialsProvider: (MaterialsProviderInterface) Provides Cek, Iv, and Cek* encrypting/decrypting for decryption metadata. May have data loaded* from the MetadataEnvelope upon decryption.* - @SecurityProfile: (string) Must be set to 'V2' or 'V2_AND_LEGACY'.* - 'V2' indicates that only objects encrypted with S3EncryptionClientV2* content encryption and key wrap schemas are able to be decrypted.* - 'V2_AND_LEGACY' indicates that objects encrypted with both* S3EncryptionClientV2 and older legacy encryption clients are able* to be decrypted.** The optional configuration arguments are as follows:** - SaveAs: (string) The path to a file on disk to save the decrypted* object data. This will be handled by file_put_contents instead of the* Guzzle sink.** - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for reading* MetadataEnvelope information. Defaults to determining based on object* response headers. Can either be a class implementing MetadataStrategy,* a class name of a predefined strategy, or empty/null to default.* - @InstructionFileSuffix: (string) Suffix used when looking for an* instruction file if an InstructionFileMetadataHandler is being used.* - @CipherOptions: (array) Cipher options for decrypting data. A Cipher* is required. Accepts the following options:* - Aad: (string) Additional authentication data. This option is* passed directly to OpenSSL when using gcm. It is ignored when* using cbc.* - @KmsAllowDecryptWithAnyCmk: (bool) This allows decryption with* KMS materials for any KMS key ID, instead of needing the KMS key ID to* be specified and provided to the decrypt operation. Ignored for non-KMS* materials providers. Defaults to false.** @return PromiseInterface** @throws \InvalidArgumentException Thrown when required arguments are not* passed or are passed incorrectly.*/public function getObjectAsync(array $args){$provider = $this->getMaterialsProvider($args);unset($args['@MaterialsProvider']);$instructionFileSuffix = $this->getInstructionFileSuffix($args);unset($args['@InstructionFileSuffix']);$strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);unset($args['@MetadataStrategy']);if (!isset($args['@SecurityProfile'])|| !in_array($args['@SecurityProfile'], self::$supportedSecurityProfiles)) {throw new CryptoException("@SecurityProfile is required and must be". " set to 'V2' or 'V2_AND_LEGACY'");}// Only throw this legacy warning once per clientif (in_array($args['@SecurityProfile'], self::$legacySecurityProfiles)&& $this->legacyWarningCount < 1) {$this->legacyWarningCount++;trigger_error("This S3 Encryption Client operation is configured to". " read encrypted data with legacy encryption modes. If you". " don't have objects encrypted with these legacy modes,". " you should disable support for them to enhance security. ",E_USER_WARNING);}$saveAs = null;if (!empty($args['SaveAs'])) {$saveAs = $args['SaveAs'];}$promise = $this->client->getObjectAsync($args)->then(function ($result) use ($provider,$instructionFileSuffix,$strategy,$args) {if ($strategy === null) {$strategy = $this->determineGetObjectStrategy($result,$instructionFileSuffix);}$envelope = $strategy->load($args + ['Metadata' => $result['Metadata']]);$result['Body'] = $this->decrypt($result['Body'],$provider,$envelope,$args);return $result;})->then(function ($result) use ($saveAs) {if (!empty($saveAs)) {file_put_contents($saveAs,(string)$result['Body'],LOCK_EX);}return $result;});return $promise;}/*** Retrieves an object from S3 and decrypts the data in the 'Body' field.** @param array $args Arguments for retrieving an object from S3 via* GetObject and decrypting it.** The required configuration argument is as follows:** - @MaterialsProvider: (MaterialsProviderInterface) Provides Cek, Iv, and Cek* encrypting/decrypting for decryption metadata. May have data loaded* from the MetadataEnvelope upon decryption.* - @SecurityProfile: (string) Must be set to 'V2' or 'V2_AND_LEGACY'.* - 'V2' indicates that only objects encrypted with S3EncryptionClientV2* content encryption and key wrap schemas are able to be decrypted.* - 'V2_AND_LEGACY' indicates that objects encrypted with both* S3EncryptionClientV2 and older legacy encryption clients are able* to be decrypted.** The optional configuration arguments are as follows:** - SaveAs: (string) The path to a file on disk to save the decrypted* object data. This will be handled by file_put_contents instead of the* Guzzle sink.* - @InstructionFileSuffix: (string|null) Suffix used when looking for an* instruction file if an InstructionFileMetadataHandler was detected.* - @CipherOptions: (array) Cipher options for encrypting data. A Cipher* is required. Accepts the following options:* - Aad: (string) Additional authentication data. This option is* passed directly to OpenSSL when using gcm. It is ignored when* using cbc.* - @KmsAllowDecryptWithAnyCmk: (bool) This allows decryption with* KMS materials for any KMS key ID, instead of needing the KMS key ID to* be specified and provided to the decrypt operation. Ignored for non-KMS* materials providers. Defaults to false.** @return \Aws\Result GetObject call result with the 'Body' field* wrapped in a decryption stream with its metadata* information.** @throws \InvalidArgumentException Thrown when arguments above are not* passed or are passed incorrectly.*/public function getObject(array $args){return $this->getObjectAsync($args)->wait();}}