Welcome to mirror list, hosted at ThFree Co, Russian Federation.

S3EncryptionClient.php « Crypto « S3 « src « aws-sdk-php « aws - github.com/nextcloud/3rdparty.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 70c169e2292fa76f2c879e6a490cf8b85bbaff6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
<?php
namespace Aws\S3\Crypto;

use Aws\Crypto\DecryptionTrait;
use Aws\HashingStream;
use Aws\PhpHash;
use Aws\Crypto\AbstractCryptoClient;
use Aws\Crypto\EncryptionTrait;
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.
 *
 * Legacy implementation using older encryption workflow.
 *
 * 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.
 *
 * @deprecated
 */
class S3EncryptionClient extends AbstractCryptoClient
{
    use CipherBuilderTrait;
    use CryptoParamsTrait;
    use DecryptionTrait;
    use EncryptionTrait;
    use UserAgentTrait;

    const CRYPTO_VERSION = '1n';

    private $client;
    private $instructionFileSuffix;

    /**
     * @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, 'S3CryptoV' . self::CRYPTO_VERSION);
        $this->client = $client;
        $this->instructionFileSuffix = $instructionFileSuffix;
    }

    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.
     *
     * @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. Only the
     *   Cipher option is required. Accepts the following:
     *       - Cipher: (string) cbc|gcm
     *            See also: AbstractCryptoClient::$supportedCiphers. Note that
     *            cbc is deprecated and gcm should be used when possible.
     *       - KeySize: (int) 128|192|256
     *            See also: MaterialsProvider::$supportedKeySizes
     *       - Aad: (string) Additional authentication data. This option is
     *            passed directly to OpenSSL when using gcm. It is ignored when
     *            using cbc. Note if you pass in Aad for gcm encryption, the
     *            PHP SDK will be able to decrypt the resulting object, but other
     *            AWS SDKs may not be able to do so.
     *
     * 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\promise_for($this->encrypt(
            Psr7\Utils::streamFor($args['Body']),
            $args['@CipherOptions'] ?: [],
            $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.
     *
     * @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) cbc|gcm
     *            See also: AbstractCryptoClient::$supportedCiphers. Note that
     *            cbc is deprecated and gcm should be used when possible.
     *       - KeySize: (int) 128|192|256
     *            See also: MaterialsProvider::$supportedKeySizes
     *       - Aad: (string) Additional authentication data. This option is
     *            passed directly to OpenSSL when using gcm. It is ignored when
     *            using cbc. Note if you pass in Aad for gcm encryption, the
     *            PHP SDK will be able to decrypt the resulting object, but other
     *            AWS SDKs may not be able to do so.
     *
     * 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: (MaterialsProvider) Provides Cek, Iv, and Cek
     *   encrypting/decrypting for decryption metadata. May have data loaded
     *   from the MetadataEnvelope upon decryption.
     *
     * 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.
     *
     * @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']);

        $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']
                    ]);

                    $provider = $provider->fromDecryptionEnvelope($envelope);

                    $result['Body'] = $this->decrypt(
                        $result['Body'],
                        $provider,
                        $envelope,
                        isset($args['@CipherOptions'])
                            ? $args['@CipherOptions']
                            : []
                    );
                    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: (MaterialsProvider) Provides Cek, Iv, and Cek
     *   encrypting/decrypting for decryption metadata. May have data loaded
     *   from the MetadataEnvelope upon decryption.
     *
     * 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.
     *
     * @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();
    }
}