Proyectos de Subversion Moodle

Rev

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