Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
namespace Aws\S3\Crypto;
3
 
4
use Aws\Crypto\DecryptionTrait;
5
use Aws\HashingStream;
1441 ariadna 6
use Aws\MetricsBuilder;
1 efrain 7
use Aws\PhpHash;
8
use Aws\Crypto\AbstractCryptoClient;
9
use Aws\Crypto\EncryptionTrait;
10
use Aws\Crypto\MetadataEnvelope;
11
use Aws\Crypto\MaterialsProvider;
12
use Aws\Crypto\Cipher\CipherBuilderTrait;
13
use Aws\S3\S3Client;
14
use GuzzleHttp\Promise;
15
use GuzzleHttp\Promise\PromiseInterface;
16
use GuzzleHttp\Psr7;
17
 
18
/**
19
 * Provides a wrapper for an S3Client that supplies functionality to encrypt
20
 * data on putObject[Async] calls and decrypt data on getObject[Async] calls.
21
 *
22
 * Legacy implementation using older encryption workflow.
23
 *
24
 * AWS strongly recommends the upgrade to the S3EncryptionClientV2 (over the
25
 * S3EncryptionClient), as it offers updated data security best practices to our
26
 * customers who upgrade. S3EncryptionClientV2 contains breaking changes, so this
27
 * will require planning by engineering teams to migrate. New workflows should
28
 * just start with S3EncryptionClientV2.
29
 *
30
 * @deprecated
31
 */
32
class S3EncryptionClient extends AbstractCryptoClient
33
{
34
    use CipherBuilderTrait;
35
    use CryptoParamsTrait;
36
    use DecryptionTrait;
37
    use EncryptionTrait;
38
    use UserAgentTrait;
39
 
40
    const CRYPTO_VERSION = '1n';
41
 
42
    private $client;
43
    private $instructionFileSuffix;
44
 
45
    /**
46
     * @param S3Client $client The S3Client to be used for true uploading and
47
     *                         retrieving objects from S3 when using the
48
     *                         encryption client.
49
     * @param string|null $instructionFileSuffix Suffix for a client wide
50
     *                                           default when using instruction
51
     *                                           files for metadata storage.
52
     */
53
    public function __construct(
54
        S3Client $client,
55
        $instructionFileSuffix = null
56
    ) {
57
        $this->client = $client;
58
        $this->instructionFileSuffix = $instructionFileSuffix;
1441 ariadna 59
        MetricsBuilder::appendMetricsCaptureMiddleware(
60
            $this->client->getHandlerList(),
61
            MetricsBuilder::S3_CRYPTO_V1N
62
        );
1 efrain 63
    }
64
 
65
    private static function getDefaultStrategy()
66
    {
67
        return new HeadersMetadataStrategy();
68
    }
69
 
70
    /**
71
     * Encrypts the data in the 'Body' field of $args and promises to upload it
72
     * to the specified location on S3.
73
     *
74
     * @param array $args Arguments for encrypting an object and uploading it
75
     *                    to S3 via PutObject.
76
     *
77
     * The required configuration arguments are as follows:
78
     *
79
     * - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
80
     *   encrypting/decrypting for encryption metadata.
81
     * - @CipherOptions: (array) Cipher options for encrypting data. Only the
82
     *   Cipher option is required. Accepts the following:
83
     *       - Cipher: (string) cbc|gcm
84
     *            See also: AbstractCryptoClient::$supportedCiphers. Note that
85
     *            cbc is deprecated and gcm should be used when possible.
86
     *       - KeySize: (int) 128|192|256
87
     *            See also: MaterialsProvider::$supportedKeySizes
88
     *       - Aad: (string) Additional authentication data. This option is
89
     *            passed directly to OpenSSL when using gcm. It is ignored when
90
     *            using cbc. Note if you pass in Aad for gcm encryption, the
91
     *            PHP SDK will be able to decrypt the resulting object, but other
92
     *            AWS SDKs may not be able to do so.
93
     *
94
     * The optional configuration arguments are as follows:
95
     *
96
     * - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
97
     *   MetadataEnvelope information. Defaults to using a
98
     *   HeadersMetadataStrategy. Can either be a class implementing
99
     *   MetadataStrategy, a class name of a predefined strategy, or empty/null
100
     *   to default.
101
     * - @InstructionFileSuffix: (string|null) Suffix used when writing to an
102
     *   instruction file if using an InstructionFileMetadataHandler.
103
     *
104
     * @return PromiseInterface
105
     *
106
     * @throws \InvalidArgumentException Thrown when arguments above are not
107
     *                                   passed or are passed incorrectly.
108
     */
109
    public function putObjectAsync(array $args)
110
    {
111
        $provider = $this->getMaterialsProvider($args);
112
        unset($args['@MaterialsProvider']);
113
 
114
        $instructionFileSuffix = $this->getInstructionFileSuffix($args);
115
        unset($args['@InstructionFileSuffix']);
116
 
117
        $strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);
118
        unset($args['@MetadataStrategy']);
119
 
120
        $envelope = new MetadataEnvelope();
121
 
122
        return Promise\Create::promiseFor($this->encrypt(
123
            Psr7\Utils::streamFor($args['Body']),
124
            $args['@CipherOptions'] ?: [],
125
            $provider,
126
            $envelope
127
        ))->then(
128
            function ($encryptedBodyStream) use ($args) {
129
                $hash = new PhpHash('sha256');
130
                $hashingEncryptedBodyStream = new HashingStream(
131
                    $encryptedBodyStream,
132
                    $hash,
133
                    self::getContentShaDecorator($args)
134
                );
135
                return [$hashingEncryptedBodyStream, $args];
136
            }
137
        )->then(
138
            function ($putObjectContents) use ($strategy, $envelope) {
139
                list($bodyStream, $args) = $putObjectContents;
140
                if ($strategy === null) {
141
                    $strategy = self::getDefaultStrategy();
142
                }
143
 
144
                $updatedArgs = $strategy->save($envelope, $args);
145
                $updatedArgs['Body'] = $bodyStream;
146
                return $updatedArgs;
147
            }
148
        )->then(
149
            function ($args) {
150
                unset($args['@CipherOptions']);
151
                return $this->client->putObjectAsync($args);
152
            }
153
        );
154
    }
155
 
156
    private static function getContentShaDecorator(&$args)
157
    {
158
        return function ($hash) use (&$args) {
159
            $args['ContentSHA256'] = bin2hex($hash);
160
        };
161
    }
162
 
163
    /**
164
     * Encrypts the data in the 'Body' field of $args and uploads it to the
165
     * specified location on S3.
166
     *
167
     * @param array $args Arguments for encrypting an object and uploading it
168
     *                    to S3 via PutObject.
169
     *
170
     * The required configuration arguments are as follows:
171
     *
172
     * - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
173
     *   encrypting/decrypting for encryption metadata.
174
     * - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
175
     *   is required. Accepts the following options:
176
     *       - Cipher: (string) cbc|gcm
177
     *            See also: AbstractCryptoClient::$supportedCiphers. Note that
178
     *            cbc is deprecated and gcm should be used when possible.
179
     *       - KeySize: (int) 128|192|256
180
     *            See also: MaterialsProvider::$supportedKeySizes
181
     *       - Aad: (string) Additional authentication data. This option is
182
     *            passed directly to OpenSSL when using gcm. It is ignored when
183
     *            using cbc. Note if you pass in Aad for gcm encryption, the
184
     *            PHP SDK will be able to decrypt the resulting object, but other
185
     *            AWS SDKs may not be able to do so.
186
     *
187
     * The optional configuration arguments are as follows:
188
     *
189
     * - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
190
     *   MetadataEnvelope information. Defaults to using a
191
     *   HeadersMetadataStrategy. Can either be a class implementing
192
     *   MetadataStrategy, a class name of a predefined strategy, or empty/null
193
     *   to default.
194
     * - @InstructionFileSuffix: (string|null) Suffix used when writing to an
195
     *   instruction file if an using an InstructionFileMetadataHandler was
196
     *   determined.
197
     *
198
     * @return \Aws\Result PutObject call result with the details of uploading
199
     *                     the encrypted file.
200
     *
201
     * @throws \InvalidArgumentException Thrown when arguments above are not
202
     *                                   passed or are passed incorrectly.
203
     */
204
    public function putObject(array $args)
205
    {
206
        return $this->putObjectAsync($args)->wait();
207
    }
208
 
209
    /**
210
     * Promises to retrieve an object from S3 and decrypt the data in the
211
     * 'Body' field.
212
     *
213
     * @param array $args Arguments for retrieving an object from S3 via
214
     *                    GetObject and decrypting it.
215
     *
216
     * The required configuration argument is as follows:
217
     *
218
     * - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
219
     *   encrypting/decrypting for decryption metadata. May have data loaded
220
     *   from the MetadataEnvelope upon decryption.
221
     *
222
     * The optional configuration arguments are as follows:
223
     *
224
     * - SaveAs: (string) The path to a file on disk to save the decrypted
225
     *   object data. This will be handled by file_put_contents instead of the
226
     *   Guzzle sink.
227
     *
228
     * - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for reading
229
     *   MetadataEnvelope information. Defaults to determining based on object
230
     *   response headers. Can either be a class implementing MetadataStrategy,
231
     *   a class name of a predefined strategy, or empty/null to default.
232
     * - @InstructionFileSuffix: (string) Suffix used when looking for an
233
     *   instruction file if an InstructionFileMetadataHandler is being used.
234
     * - @CipherOptions: (array) Cipher options for decrypting data. A Cipher
235
     *   is required. Accepts the following options:
236
     *       - Aad: (string) Additional authentication data. This option is
237
     *            passed directly to OpenSSL when using gcm. It is ignored when
238
     *            using cbc.
239
     *
240
     * @return PromiseInterface
241
     *
242
     * @throws \InvalidArgumentException Thrown when required arguments are not
243
     *                                   passed or are passed incorrectly.
244
     */
245
    public function getObjectAsync(array $args)
246
    {
247
        $provider = $this->getMaterialsProvider($args);
248
        unset($args['@MaterialsProvider']);
249
 
250
        $instructionFileSuffix = $this->getInstructionFileSuffix($args);
251
        unset($args['@InstructionFileSuffix']);
252
 
253
        $strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);
254
        unset($args['@MetadataStrategy']);
255
 
256
        $saveAs = null;
257
        if (!empty($args['SaveAs'])) {
258
            $saveAs = $args['SaveAs'];
259
        }
260
 
261
        $promise = $this->client->getObjectAsync($args)
262
            ->then(
263
                function ($result) use (
264
                    $provider,
265
                    $instructionFileSuffix,
266
                    $strategy,
267
                    $args
268
                ) {
269
                    if ($strategy === null) {
270
                        $strategy = $this->determineGetObjectStrategy(
271
                            $result,
272
                            $instructionFileSuffix
273
                        );
274
                    }
275
 
276
                    $envelope = $strategy->load($args + [
277
                        'Metadata' => $result['Metadata']
278
                    ]);
279
 
280
                    $provider = $provider->fromDecryptionEnvelope($envelope);
281
 
282
                    $result['Body'] = $this->decrypt(
283
                        $result['Body'],
284
                        $provider,
285
                        $envelope,
286
                        isset($args['@CipherOptions'])
287
                            ? $args['@CipherOptions']
288
                            : []
289
                    );
290
                    return $result;
291
                }
292
            )->then(
293
                function ($result) use ($saveAs) {
294
                    if (!empty($saveAs)) {
295
                        file_put_contents(
296
                            $saveAs,
297
                            (string)$result['Body'],
298
                            LOCK_EX
299
                        );
300
                    }
301
                    return $result;
302
                }
303
            );
304
 
305
        return $promise;
306
    }
307
 
308
    /**
309
     * Retrieves an object from S3 and decrypts the data in the 'Body' field.
310
     *
311
     * @param array $args Arguments for retrieving an object from S3 via
312
     *                    GetObject and decrypting it.
313
     *
314
     * The required configuration argument is as follows:
315
     *
316
     * - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
317
     *   encrypting/decrypting for decryption metadata. May have data loaded
318
     *   from the MetadataEnvelope upon decryption.
319
     *
320
     * The optional configuration arguments are as follows:
321
     *
322
     * - SaveAs: (string) The path to a file on disk to save the decrypted
323
     *   object data. This will be handled by file_put_contents instead of the
324
     *   Guzzle sink.
325
     * - @InstructionFileSuffix: (string|null) Suffix used when looking for an
326
     *   instruction file if an InstructionFileMetadataHandler was detected.
327
     * - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
328
     *   is required. Accepts the following options:
329
     *       - Aad: (string) Additional authentication data. This option is
330
     *            passed directly to OpenSSL when using gcm. It is ignored when
331
     *            using cbc.
332
     *
333
     * @return \Aws\Result GetObject call result with the 'Body' field
334
     *                     wrapped in a decryption stream with its metadata
335
     *                     information.
336
     *
337
     * @throws \InvalidArgumentException Thrown when arguments above are not
338
     *                                   passed or are passed incorrectly.
339
     */
340
    public function getObject(array $args)
341
    {
342
        return $this->getObjectAsync($args)->wait();
343
    }
344
}