PluginProbe
Media Cloud Sync / 1.3.11
Media Cloud Sync v1.3.11
1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 All 35 releases
media-cloud-sync / includes / sdk / s3 / Aws / S3 / Crypto / S3EncryptionClientV2.php

S3EncryptionClientV2.php in Media Cloud Sync 1.3.11, at includes/sdk/s3/Aws/S3/Crypto/S3EncryptionClientV2.php

373 lines 17.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Dudlewebs\WPMCS\s3\Aws\S3\Crypto;
4
5 use Dudlewebs\WPMCS\s3\Aws\Crypto\DecryptionTraitV2;
6 use Dudlewebs\WPMCS\s3\Aws\Exception\CryptoException;
7 use Dudlewebs\WPMCS\s3\Aws\HashingStream;
8 use Dudlewebs\WPMCS\s3\Aws\MetricsBuilder;
9 use Dudlewebs\WPMCS\s3\Aws\PhpHash;
10 use Dudlewebs\WPMCS\s3\Aws\Crypto\AbstractCryptoClientV2;
11 use Dudlewebs\WPMCS\s3\Aws\Crypto\EncryptionTraitV2;
12 use Dudlewebs\WPMCS\s3\Aws\Crypto\MetadataEnvelope;
13 use Dudlewebs\WPMCS\s3\Aws\Crypto\MaterialsProvider;
14 use Dudlewebs\WPMCS\s3\Aws\Crypto\Cipher\CipherBuilderTrait;
15 use Dudlewebs\WPMCS\s3\Aws\S3\S3Client;
16 use Dudlewebs\WPMCS\s3\GuzzleHttp\Promise;
17 use Dudlewebs\WPMCS\s3\GuzzleHttp\Promise\PromiseInterface;
18 use Dudlewebs\WPMCS\s3\GuzzleHttp\Psr7;
19 /**
20 * Provides a wrapper for an S3Client that supplies functionality to encrypt
21 * data on putObject[Async] calls and decrypt data on getObject[Async] calls.
22 *
23 * AWS strongly recommends the upgrade to the S3EncryptionClientV2 (over the
24 * S3EncryptionClient), as it offers updated data security best practices to our
25 * customers who upgrade. S3EncryptionClientV2 contains breaking changes, so this
26 * will require planning by engineering teams to migrate. New workflows should
27 * just start with S3EncryptionClientV2.
28 *
29 * Note that for PHP versions of < 7.1, this class uses an AES-GCM polyfill
30 * for encryption since there is no native PHP support. The performance for large
31 * inputs will be a lot slower than for PHP 7.1+, so upgrading older PHP version
32 * environments may be necessary to use this effectively.
33 *
34 * Example write path:
35 *
36 * <code>
37 * use Aws\Crypto\KmsMaterialsProviderV2;
38 * use Aws\S3\Crypto\S3EncryptionClientV2;
39 * use Aws\S3\S3Client;
40 *
41 * $encryptionClient = new S3EncryptionClientV2(
42 * new S3Client([
43 * 'region' => 'us-west-2',
44 * 'version' => 'latest'
45 * ])
46 * );
47 * $materialsProvider = new KmsMaterialsProviderV2(
48 * new KmsClient([
49 * 'profile' => 'default',
50 * 'region' => 'us-east-1',
51 * 'version' => 'latest',
52 * ],
53 * 'your-kms-key-id'
54 * );
55 *
56 * $encryptionClient->putObject([
57 * '@MaterialsProvider' => $materialsProvider,
58 * '@CipherOptions' => [
59 * 'Cipher' => 'gcm',
60 * 'KeySize' => 256,
61 * ],
62 * '@KmsEncryptionContext' => ['foo' => 'bar'],
63 * 'Bucket' => 'your-bucket',
64 * 'Key' => 'your-key',
65 * 'Body' => 'your-encrypted-data',
66 * ]);
67 * </code>
68 *
69 * Example read call (using objects from previous example):
70 *
71 * <code>
72 * $encryptionClient->getObject([
73 * '@MaterialsProvider' => $materialsProvider,
74 * '@CipherOptions' => [
75 * 'Cipher' => 'gcm',
76 * 'KeySize' => 256,
77 * ],
78 * 'Bucket' => 'your-bucket',
79 * 'Key' => 'your-key',
80 * ]);
81 * </code>
82 */
83 class S3EncryptionClientV2 extends AbstractCryptoClientV2
84 {
85 use CipherBuilderTrait;
86 use CryptoParamsTraitV2;
87 use DecryptionTraitV2;
88 use EncryptionTraitV2;
89 use UserAgentTrait;
90 const CRYPTO_VERSION = '2.1';
91 private $client;
92 private $instructionFileSuffix;
93 private $legacyWarningCount;
94 /**
95 * @param S3Client $client The S3Client to be used for true uploading and
96 * retrieving objects from S3 when using the
97 * encryption client.
98 * @param string|null $instructionFileSuffix Suffix for a client wide
99 * default when using instruction
100 * files for metadata storage.
101 */
102 public function __construct(S3Client $client, $instructionFileSuffix = null)
103 {
104 $this->client = $client;
105 $this->instructionFileSuffix = $instructionFileSuffix;
106 $this->legacyWarningCount = 0;
107 MetricsBuilder::appendMetricsCaptureMiddleware($this->client->getHandlerList(), MetricsBuilder::S3_CRYPTO_V2);
108 }
109 private static function getDefaultStrategy()
110 {
111 return new HeadersMetadataStrategy();
112 }
113 /**
114 * Encrypts the data in the 'Body' field of $args and promises to upload it
115 * to the specified location on S3.
116 *
117 * Note that for PHP versions of < 7.1, this operation uses an AES-GCM
118 * polyfill for encryption since there is no native PHP support. The
119 * performance for large inputs will be a lot slower than for PHP 7.1+, so
120 * upgrading older PHP version environments may be necessary to use this
121 * effectively.
122 *
123 * @param array $args Arguments for encrypting an object and uploading it
124 * to S3 via PutObject.
125 *
126 * The required configuration arguments are as follows:
127 *
128 * - @MaterialsProvider: (MaterialsProviderV2) Provides Cek, Iv, and Cek
129 * encrypting/decrypting for encryption metadata.
130 * - @CipherOptions: (array) Cipher options for encrypting data. Only the
131 * Cipher option is required. Accepts the following:
132 * - Cipher: (string) gcm
133 * See also: AbstractCryptoClientV2::$supportedCiphers
134 * - KeySize: (int) 128|256
135 * See also: MaterialsProvider::$supportedKeySizes
136 * - Aad: (string) Additional authentication data. This option is
137 * passed directly to OpenSSL when using gcm. Note if you pass in
138 * Aad, the PHP SDK will be able to decrypt the resulting object,
139 * but other AWS SDKs may not be able to do so.
140 * - @KmsEncryptionContext: (array) Only required if using
141 * KmsMaterialsProviderV2. An associative array of key-value
142 * pairs to be added to the encryption context for KMS key encryption. An
143 * empty array may be passed if no additional context is desired.
144 *
145 * The optional configuration arguments are as follows:
146 *
147 * - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
148 * MetadataEnvelope information. Defaults to using a
149 * HeadersMetadataStrategy. Can either be a class implementing
150 * MetadataStrategy, a class name of a predefined strategy, or empty/null
151 * to default.
152 * - @InstructionFileSuffix: (string|null) Suffix used when writing to an
153 * instruction file if using an InstructionFileMetadataHandler.
154 *
155 * @return PromiseInterface
156 *
157 * @throws \InvalidArgumentException Thrown when arguments above are not
158 * passed or are passed incorrectly.
159 */
160 public function putObjectAsync(array $args)
161 {
162 $provider = $this->getMaterialsProvider($args);
163 unset($args['@MaterialsProvider']);
164 $instructionFileSuffix = $this->getInstructionFileSuffix($args);
165 unset($args['@InstructionFileSuffix']);
166 $strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);
167 unset($args['@MetadataStrategy']);
168 $envelope = new MetadataEnvelope();
169 return Promise\Create::promiseFor($this->encrypt(Psr7\Utils::streamFor($args['Body']), $args, $provider, $envelope))->then(function ($encryptedBodyStream) use($args) {
170 $hash = new PhpHash('sha256');
171 $hashingEncryptedBodyStream = new HashingStream($encryptedBodyStream, $hash, self::getContentShaDecorator($args));
172 return [$hashingEncryptedBodyStream, $args];
173 })->then(function ($putObjectContents) use($strategy, $envelope) {
174 list($bodyStream, $args) = $putObjectContents;
175 if ($strategy === null) {
176 $strategy = self::getDefaultStrategy();
177 }
178 $updatedArgs = $strategy->save($envelope, $args);
179 $updatedArgs['Body'] = $bodyStream;
180 return $updatedArgs;
181 })->then(function ($args) {
182 unset($args['@CipherOptions']);
183 return $this->client->putObjectAsync($args);
184 });
185 }
186 private static function getContentShaDecorator(&$args)
187 {
188 return function ($hash) use(&$args) {
189 $args['ContentSHA256'] = \bin2hex($hash);
190 };
191 }
192 /**
193 * Encrypts the data in the 'Body' field of $args and uploads it to the
194 * specified location on S3.
195 *
196 * Note that for PHP versions of < 7.1, this operation uses an AES-GCM
197 * polyfill for encryption since there is no native PHP support. The
198 * performance for large inputs will be a lot slower than for PHP 7.1+, so
199 * upgrading older PHP version environments may be necessary to use this
200 * effectively.
201 *
202 * @param array $args Arguments for encrypting an object and uploading it
203 * to S3 via PutObject.
204 *
205 * The required configuration arguments are as follows:
206 *
207 * - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
208 * encrypting/decrypting for encryption metadata.
209 * - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
210 * is required. Accepts the following options:
211 * - Cipher: (string) gcm
212 * See also: AbstractCryptoClientV2::$supportedCiphers
213 * - KeySize: (int) 128|256
214 * See also: MaterialsProvider::$supportedKeySizes
215 * - Aad: (string) Additional authentication data. This option is
216 * passed directly to OpenSSL when using gcm. Note if you pass in
217 * Aad, the PHP SDK will be able to decrypt the resulting object,
218 * but other AWS SDKs may not be able to do so.
219 * - @KmsEncryptionContext: (array) Only required if using
220 * KmsMaterialsProviderV2. An associative array of key-value
221 * pairs to be added to the encryption context for KMS key encryption. An
222 * empty array may be passed if no additional context is desired.
223 *
224 * The optional configuration arguments are as follows:
225 *
226 * - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
227 * MetadataEnvelope information. Defaults to using a
228 * HeadersMetadataStrategy. Can either be a class implementing
229 * MetadataStrategy, a class name of a predefined strategy, or empty/null
230 * to default.
231 * - @InstructionFileSuffix: (string|null) Suffix used when writing to an
232 * instruction file if an using an InstructionFileMetadataHandler was
233 * determined.
234 *
235 * @return \Aws\Result PutObject call result with the details of uploading
236 * the encrypted file.
237 *
238 * @throws \InvalidArgumentException Thrown when arguments above are not
239 * passed or are passed incorrectly.
240 */
241 public function putObject(array $args)
242 {
243 return $this->putObjectAsync($args)->wait();
244 }
245 /**
246 * Promises to retrieve an object from S3 and decrypt the data in the
247 * 'Body' field.
248 *
249 * @param array $args Arguments for retrieving an object from S3 via
250 * GetObject and decrypting it.
251 *
252 * The required configuration argument is as follows:
253 *
254 * - @MaterialsProvider: (MaterialsProviderInterface) Provides Cek, Iv, and Cek
255 * encrypting/decrypting for decryption metadata. May have data loaded
256 * from the MetadataEnvelope upon decryption.
257 * - @SecurityProfile: (string) Must be set to 'V2' or 'V2_AND_LEGACY'.
258 * - 'V2' indicates that only objects encrypted with S3EncryptionClientV2
259 * content encryption and key wrap schemas are able to be decrypted.
260 * - 'V2_AND_LEGACY' indicates that objects encrypted with both
261 * S3EncryptionClientV2 and older legacy encryption clients are able
262 * to be decrypted.
263 *
264 * The optional configuration arguments are as follows:
265 *
266 * - SaveAs: (string) The path to a file on disk to save the decrypted
267 * object data. This will be handled by file_put_contents instead of the
268 * Guzzle sink.
269 *
270 * - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for reading
271 * MetadataEnvelope information. Defaults to determining based on object
272 * response headers. Can either be a class implementing MetadataStrategy,
273 * a class name of a predefined strategy, or empty/null to default.
274 * - @InstructionFileSuffix: (string) Suffix used when looking for an
275 * instruction file if an InstructionFileMetadataHandler is being used.
276 * - @CipherOptions: (array) Cipher options for decrypting data. A Cipher
277 * is required. Accepts the following options:
278 * - Aad: (string) Additional authentication data. This option is
279 * passed directly to OpenSSL when using gcm. It is ignored when
280 * using cbc.
281 * - @KmsAllowDecryptWithAnyCmk: (bool) This allows decryption with
282 * KMS materials for any KMS key ID, instead of needing the KMS key ID to
283 * be specified and provided to the decrypt operation. Ignored for non-KMS
284 * materials providers. Defaults to false.
285 *
286 * @return PromiseInterface
287 *
288 * @throws \InvalidArgumentException Thrown when required arguments are not
289 * passed or are passed incorrectly.
290 */
291 public function getObjectAsync(array $args)
292 {
293 $provider = $this->getMaterialsProvider($args);
294 unset($args['@MaterialsProvider']);
295 $instructionFileSuffix = $this->getInstructionFileSuffix($args);
296 unset($args['@InstructionFileSuffix']);
297 $strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);
298 unset($args['@MetadataStrategy']);
299 if (!isset($args['@SecurityProfile']) || !\in_array($args['@SecurityProfile'], self::$supportedSecurityProfiles)) {
300 throw new CryptoException("@SecurityProfile is required and must be" . " set to 'V2' or 'V2_AND_LEGACY'");
301 }
302 // Only throw this legacy warning once per client
303 if (\in_array($args['@SecurityProfile'], self::$legacySecurityProfiles) && $this->legacyWarningCount < 1) {
304 $this->legacyWarningCount++;
305 \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);
306 }
307 $saveAs = null;
308 if (!empty($args['SaveAs'])) {
309 $saveAs = $args['SaveAs'];
310 }
311 $promise = $this->client->getObjectAsync($args)->then(function ($result) use($provider, $instructionFileSuffix, $strategy, $args) {
312 if ($strategy === null) {
313 $strategy = $this->determineGetObjectStrategy($result, $instructionFileSuffix);
314 }
315 $envelope = $strategy->load($args + ['Metadata' => $result['Metadata']]);
316 $result['Body'] = $this->decrypt($result['Body'], $provider, $envelope, $args);
317 return $result;
318 })->then(function ($result) use($saveAs) {
319 if (!empty($saveAs)) {
320 \file_put_contents($saveAs, (string) $result['Body'], \LOCK_EX);
321 }
322 return $result;
323 });
324 return $promise;
325 }
326 /**
327 * Retrieves an object from S3 and decrypts the data in the 'Body' field.
328 *
329 * @param array $args Arguments for retrieving an object from S3 via
330 * GetObject and decrypting it.
331 *
332 * The required configuration argument is as follows:
333 *
334 * - @MaterialsProvider: (MaterialsProviderInterface) Provides Cek, Iv, and Cek
335 * encrypting/decrypting for decryption metadata. May have data loaded
336 * from the MetadataEnvelope upon decryption.
337 * - @SecurityProfile: (string) Must be set to 'V2' or 'V2_AND_LEGACY'.
338 * - 'V2' indicates that only objects encrypted with S3EncryptionClientV2
339 * content encryption and key wrap schemas are able to be decrypted.
340 * - 'V2_AND_LEGACY' indicates that objects encrypted with both
341 * S3EncryptionClientV2 and older legacy encryption clients are able
342 * to be decrypted.
343 *
344 * The optional configuration arguments are as follows:
345 *
346 * - SaveAs: (string) The path to a file on disk to save the decrypted
347 * object data. This will be handled by file_put_contents instead of the
348 * Guzzle sink.
349 * - @InstructionFileSuffix: (string|null) Suffix used when looking for an
350 * instruction file if an InstructionFileMetadataHandler was detected.
351 * - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
352 * is required. Accepts the following options:
353 * - Aad: (string) Additional authentication data. This option is
354 * passed directly to OpenSSL when using gcm. It is ignored when
355 * using cbc.
356 * - @KmsAllowDecryptWithAnyCmk: (bool) This allows decryption with
357 * KMS materials for any KMS key ID, instead of needing the KMS key ID to
358 * be specified and provided to the decrypt operation. Ignored for non-KMS
359 * materials providers. Defaults to false.
360 *
361 * @return \Aws\Result GetObject call result with the 'Body' field
362 * wrapped in a decryption stream with its metadata
363 * information.
364 *
365 * @throws \InvalidArgumentException Thrown when arguments above are not
366 * passed or are passed incorrectly.
367 */
368 public function getObject(array $args)
369 {
370 return $this->getObjectAsync($args)->wait();
371 }
372 }
373