PluginProbe
Media Cloud Sync / 1.4.1
Media Cloud Sync v1.4.1
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 / Crypto / DecryptionTraitV2.php

DecryptionTraitV2.php in Media Cloud Sync 1.4.1, at includes/sdk/s3/Aws/Crypto/DecryptionTraitV2.php

139 lines 7.8 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\Crypto;
4
5 use Dudlewebs\WPMCS\s3\Aws\Exception\CryptoException;
6 use Dudlewebs\WPMCS\s3\GuzzleHttp\Psr7;
7 use Dudlewebs\WPMCS\s3\GuzzleHttp\Psr7\LimitStream;
8 use Dudlewebs\WPMCS\s3\Psr\Http\Message\StreamInterface;
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 protected abstract function getCipherFromAesName($aesName);
22 /**
23 * Dependency to generate a CipherMethod from a set of inputs for loading
24 * in to an AesDecryptingStream.
25 *
26 * @param string $cipherName Name of the cipher to generate for decrypting.
27 * @param string $iv Base Initialization Vector for the cipher.
28 * @param int $keySize Size of the encryption key, in bits, that will be
29 * used.
30 *
31 * @return Cipher\CipherMethod
32 *
33 * @internal
34 */
35 protected abstract function buildCipherMethod($cipherName, $iv, $keySize);
36 /**
37 * Builds an AesStreamInterface using cipher options loaded from the
38 * MetadataEnvelope and MaterialsProvider. Can decrypt data from both the
39 * legacy and V2 encryption client workflows.
40 *
41 * @param string $cipherText Plain-text data to be encrypted using the
42 * materials, algorithm, and data provided.
43 * @param MaterialsProviderInterfaceV2 $provider A provider to supply and encrypt
44 * materials used in encryption.
45 * @param MetadataEnvelope $envelope A storage envelope for encryption
46 * metadata to be read from.
47 * @param array $options Options used for decryption.
48 *
49 * @return AesStreamInterface
50 *
51 * @throws \InvalidArgumentException Thrown when a value in $cipherOptions
52 * is not valid.
53 *
54 * @internal
55 */
56 public function decrypt($cipherText, MaterialsProviderInterfaceV2 $provider, MetadataEnvelope $envelope, array $options = [])
57 {
58 $options['@CipherOptions'] = !empty($options['@CipherOptions']) ? $options['@CipherOptions'] : [];
59 $options['@CipherOptions']['Iv'] = \base64_decode($envelope[MetadataEnvelope::IV_HEADER]);
60 $options['@CipherOptions']['TagLength'] = $envelope[MetadataEnvelope::CRYPTO_TAG_LENGTH_HEADER] / 8;
61 $cek = $provider->decryptCek(\base64_decode($envelope[MetadataEnvelope::CONTENT_KEY_V2_HEADER]), \json_decode($envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER], \true), $options);
62 $options['@CipherOptions']['KeySize'] = \strlen($cek) * 8;
63 $options['@CipherOptions']['Cipher'] = $this->getCipherFromAesName($envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER]);
64 $this->validateOptionsAndEnvelope($options, $envelope);
65 $decryptionStream = $this->getDecryptingStream($cipherText, $cek, $options['@CipherOptions']);
66 unset($cek);
67 return $decryptionStream;
68 }
69 private function getTagFromCiphertextStream(StreamInterface $cipherText, $tagLength)
70 {
71 $cipherTextSize = $cipherText->getSize();
72 if ($cipherTextSize == null || $cipherTextSize <= 0) {
73 throw new \RuntimeException('Cannot decrypt a stream of unknown' . ' size.');
74 }
75 return (string) new LimitStream($cipherText, $tagLength, $cipherTextSize - $tagLength);
76 }
77 private function getStrippedCiphertextStream(StreamInterface $cipherText, $tagLength)
78 {
79 $cipherTextSize = $cipherText->getSize();
80 if ($cipherTextSize == null || $cipherTextSize <= 0) {
81 throw new \RuntimeException('Cannot decrypt a stream of unknown' . ' size.');
82 }
83 return new LimitStream($cipherText, $cipherTextSize - $tagLength, 0);
84 }
85 private function validateOptionsAndEnvelope($options, $envelope)
86 {
87 $allowedCiphers = AbstractCryptoClientV2::$supportedCiphers;
88 $allowedKeywraps = AbstractCryptoClientV2::$supportedKeyWraps;
89 if ($options['@SecurityProfile'] == 'V2_AND_LEGACY') {
90 $allowedCiphers = \array_unique(\array_merge($allowedCiphers, AbstractCryptoClient::$supportedCiphers));
91 $allowedKeywraps = \array_unique(\array_merge($allowedKeywraps, AbstractCryptoClient::$supportedKeyWraps));
92 }
93 $v1SchemaException = new CryptoException("The requested object is encrypted" . " with V1 encryption schemas that have been disabled by" . " client configuration @SecurityProfile=V2. Retry with" . " V2_AND_LEGACY enabled or reencrypt the object.");
94 if (!\in_array($options['@CipherOptions']['Cipher'], $allowedCiphers)) {
95 if (\in_array($options['@CipherOptions']['Cipher'], AbstractCryptoClient::$supportedCiphers)) {
96 throw $v1SchemaException;
97 }
98 throw new CryptoException("The requested object is encrypted with" . " the cipher '{$options['@CipherOptions']['Cipher']}', which is not" . " supported for decryption with the selected security profile." . " This profile allows decryption with: " . \implode(", ", $allowedCiphers));
99 }
100 if (!\in_array($envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER], $allowedKeywraps)) {
101 if (\in_array($envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER], AbstractCryptoClient::$supportedKeyWraps)) {
102 throw $v1SchemaException;
103 }
104 throw new CryptoException("The requested object is encrypted with" . " the keywrap schema '{$envelope[MetadataEnvelope::KEY_WRAP_ALGORITHM_HEADER]}'," . " which is not supported for decryption with the current security" . " profile.");
105 }
106 $matdesc = \json_decode($envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER], \true);
107 if (isset($matdesc['aws:x-amz-cek-alg']) && $envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER] !== $matdesc['aws:x-amz-cek-alg']) {
108 throw new CryptoException("There is a mismatch in specified content" . " encryption algrithm between the materials description value" . " and the metadata envelope value: {$matdesc['aws:x-amz-cek-alg']}" . " vs. {$envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER]}.");
109 }
110 }
111 /**
112 * Generates a stream that wraps the cipher text with the proper cipher and
113 * uses the content encryption key (CEK) to decrypt the data when read.
114 *
115 * @param string $cipherText Plain-text data to be encrypted using the
116 * materials, algorithm, and data provided.
117 * @param string $cek A content encryption key for use by the stream for
118 * encrypting the plaintext data.
119 * @param array $cipherOptions Options for use in determining the cipher to
120 * be used for encrypting data.
121 *
122 * @return AesStreamInterface
123 *
124 * @internal
125 */
126 protected function getDecryptingStream($cipherText, $cek, $cipherOptions)
127 {
128 $cipherTextStream = Psr7\Utils::streamFor($cipherText);
129 switch ($cipherOptions['Cipher']) {
130 case 'gcm':
131 $cipherOptions['Tag'] = $this->getTagFromCiphertextStream($cipherTextStream, $cipherOptions['TagLength']);
132 return new AesGcmDecryptingStream($this->getStrippedCiphertextStream($cipherTextStream, $cipherOptions['TagLength']), $cek, $cipherOptions['Iv'], $cipherOptions['Tag'], $cipherOptions['Aad'] = isset($cipherOptions['Aad']) ? $cipherOptions['Aad'] : '', $cipherOptions['TagLength'] ?: null, $cipherOptions['KeySize']);
133 default:
134 $cipherMethod = $this->buildCipherMethod($cipherOptions['Cipher'], $cipherOptions['Iv'], $cipherOptions['KeySize']);
135 return new AesDecryptingStream($cipherTextStream, $cek, $cipherMethod);
136 }
137 }
138 }
139