Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
namespace Aws\Crypto;
3
 
4
use Aws\Exception\CryptoException;
5
use GuzzleHttp\Psr7;
6
use GuzzleHttp\Psr7\LimitStream;
7
use Psr\Http\Message\StreamInterface;
8
 
9
trait DecryptionTraitV2
10
{
11
    /**
12
     * Dependency to reverse lookup the openssl_* cipher name from the AESName
13
     * in the MetadataEnvelope.
14
     *
15
     * @param $aesName
16
     *
17
     * @return string
18
     *
19
     * @internal
20
     */
21
    abstract protected function getCipherFromAesName($aesName);
22
 
23
    /**
24
     * Dependency to generate a CipherMethod from a set of inputs for loading
25
     * in to an AesDecryptingStream.
26
     *
27
     * @param string $cipherName Name of the cipher to generate for decrypting.
28
     * @param string $iv Base Initialization Vector for the cipher.
29
     * @param int $keySize Size of the encryption key, in bits, that will be
30
     *                     used.
31
     *
32
     * @return Cipher\CipherMethod
33
     *
34
     * @internal
35
     */
36
    abstract protected function buildCipherMethod($cipherName, $iv, $keySize);
37
 
38
    /**
39
     * Builds an AesStreamInterface using cipher options loaded from the
40
     * MetadataEnvelope and MaterialsProvider. Can decrypt data from both the
41
     * legacy and V2 encryption client workflows.
42
     *
43
     * @param string $cipherText Plain-text data to be encrypted using the
44
     *                           materials, algorithm, and data provided.
45
     * @param MaterialsProviderInterfaceV2 $provider A provider to supply and encrypt
46
     *                                             materials used in encryption.
47
     * @param MetadataEnvelope $envelope A storage envelope for encryption
48
     *                                   metadata to be read from.
49
     * @param array $options Options used for decryption.
50
     *
51
     * @return AesStreamInterface
52
     *
53
     * @throws \InvalidArgumentException Thrown when a value in $cipherOptions
54
     *                                   is not valid.
55
     *
56
     * @internal
57
     */
58
    public function decrypt(
59
        $cipherText,
60
        MaterialsProviderInterfaceV2 $provider,
61
        MetadataEnvelope $envelope,
62
        array $options = []
63
    ) {
64
        $options['@CipherOptions'] = !empty($options['@CipherOptions'])
65
            ? $options['@CipherOptions']
66
            : [];
67
        $options['@CipherOptions']['Iv'] = base64_decode(
68
            $envelope[MetadataEnvelope::IV_HEADER]
69
        );
70
 
71
        $options['@CipherOptions']['TagLength'] =
72
            $envelope[MetadataEnvelope::CRYPTO_TAG_LENGTH_HEADER] / 8;
73
 
74
        $cek = $provider->decryptCek(
75
            base64_decode(
76
                $envelope[MetadataEnvelope::CONTENT_KEY_V2_HEADER]
77
            ),
78
            json_decode(
79
                $envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER],
80
                true
81
            ),
82
            $options
83
        );
84
        $options['@CipherOptions']['KeySize'] = strlen($cek) * 8;
85
        $options['@CipherOptions']['Cipher'] = $this->getCipherFromAesName(
86
            $envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER]
87
        );
88
 
89
        $this->validateOptionsAndEnvelope($options, $envelope);
90
 
91
        $decryptionStream = $this->getDecryptingStream(
92
            $cipherText,
93
            $cek,
94
            $options['@CipherOptions']
95
        );
96
        unset($cek);
97
 
98
        return $decryptionStream;
99
    }
100
 
101
    private function getTagFromCiphertextStream(
102
        StreamInterface $cipherText,
103
        $tagLength
104
    ) {
105
        $cipherTextSize = $cipherText->getSize();
106
        if ($cipherTextSize == null || $cipherTextSize <= 0) {
107
            throw new \RuntimeException('Cannot decrypt a stream of unknown'
108
                . ' size.');
109
        }
110
        return (string) new LimitStream(
111
            $cipherText,
112
            $tagLength,
113
            $cipherTextSize - $tagLength
114
        );
115
    }
116
 
117
    private function getStrippedCiphertextStream(
118
        StreamInterface $cipherText,
119
        $tagLength
120
    ) {
121
        $cipherTextSize = $cipherText->getSize();
122
        if ($cipherTextSize == null || $cipherTextSize <= 0) {
123
            throw new \RuntimeException('Cannot decrypt a stream of unknown'
124
                . ' size.');
125
        }
126
        return new LimitStream(
127
            $cipherText,
128
            $cipherTextSize - $tagLength,
129
 
130
        );
131
    }
132
 
133
    private function validateOptionsAndEnvelope($options, $envelope)
134
    {
135
        $allowedCiphers = AbstractCryptoClientV2::$supportedCiphers;
136
        $allowedKeywraps = AbstractCryptoClientV2::$supportedKeyWraps;
137
        if ($options['@SecurityProfile'] == 'V2_AND_LEGACY') {
138
            $allowedCiphers = array_unique(array_merge(
139
                $allowedCiphers,
140
                AbstractCryptoClient::$supportedCiphers
141
            ));
142
            $allowedKeywraps = array_unique(array_merge(
143
                $allowedKeywraps,
144
                AbstractCryptoClient::$supportedKeyWraps
145
            ));
146
        }
147
 
148
        $v1SchemaException = new CryptoException("The requested object is encrypted"
149
            . " with V1 encryption schemas that have been disabled by"
150
            . " client configuration @SecurityProfile=V2. Retry with"
151
            . " V2_AND_LEGACY enabled or reencrypt the object.");
152
 
153
        if (!in_array($options['@CipherOptions']['Cipher'], $allowedCiphers)) {
154
            if (in_array($options['@CipherOptions']['Cipher'], AbstractCryptoClient::$supportedCiphers)) {
155
                throw $v1SchemaException;
156
            }
157
            throw new CryptoException("The requested object is encrypted with"
158
                . " the cipher '{$options['@CipherOptions']['Cipher']}', which is not"
159
                . " supported for decryption with the selected security profile."
160
                . " This profile allows decryption with: "
161
                . implode(", ", $allowedCiphers));
162
        }
163
        if (!in_array(
164
            $envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER],
165
            $allowedKeywraps
166
        )) {
167
            if (in_array(
168
                $envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER],
169
                AbstractCryptoClient::$supportedKeyWraps)
170
            ) {
171
                throw $v1SchemaException;
172
            }
173
            throw new CryptoException("The requested object is encrypted with"
174
                . " the keywrap schema '{$envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER]}',"
175
                . " which is not supported for decryption with the current security"
176
                . " profile.");
177
        }
178
 
179
        $matdesc = json_decode(
180
            $envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER],
181
            true
182
        );
183
        if (isset($matdesc['aws:x-amz-cek-alg'])
184
            && $envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER] !==
185
                $matdesc['aws:x-amz-cek-alg']
186
        ) {
187
            throw new CryptoException("There is a mismatch in specified content"
188
                . " encryption algrithm between the materials description value"
189
                . " and the metadata envelope value: {$matdesc['aws:x-amz-cek-alg']}"
190
                . " vs. {$envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER]}.");
191
        }
192
    }
193
 
194
    /**
195
     * Generates a stream that wraps the cipher text with the proper cipher and
196
     * uses the content encryption key (CEK) to decrypt the data when read.
197
     *
198
     * @param string $cipherText Plain-text data to be encrypted using the
199
     *                           materials, algorithm, and data provided.
200
     * @param string $cek A content encryption key for use by the stream for
201
     *                    encrypting the plaintext data.
202
     * @param array $cipherOptions Options for use in determining the cipher to
203
     *                             be used for encrypting data.
204
     *
205
     * @return AesStreamInterface
206
     *
207
     * @internal
208
     */
209
    protected function getDecryptingStream(
210
        $cipherText,
211
        $cek,
212
        $cipherOptions
213
    ) {
214
        $cipherTextStream = Psr7\Utils::streamFor($cipherText);
215
        switch ($cipherOptions['Cipher']) {
216
            case 'gcm':
217
                $cipherOptions['Tag'] = $this->getTagFromCiphertextStream(
218
                        $cipherTextStream,
219
                        $cipherOptions['TagLength']
220
                    );
221
 
222
                return new AesGcmDecryptingStream(
223
                    $this->getStrippedCiphertextStream(
224
                        $cipherTextStream,
225
                        $cipherOptions['TagLength']
226
                    ),
227
                    $cek,
228
                    $cipherOptions['Iv'],
229
                    $cipherOptions['Tag'],
230
                    $cipherOptions['Aad'] = isset($cipherOptions['Aad'])
231
                        ? $cipherOptions['Aad']
232
                        : '',
233
                    $cipherOptions['TagLength'] ?: null,
234
                    $cipherOptions['KeySize']
235
                );
236
            default:
237
                $cipherMethod = $this->buildCipherMethod(
238
                    $cipherOptions['Cipher'],
239
                    $cipherOptions['Iv'],
240
                    $cipherOptions['KeySize']
241
                );
242
                return new AesDecryptingStream(
243
                    $cipherTextStream,
244
                    $cek,
245
                    $cipherMethod
246
                );
247
        }
248
    }
249
}