summaryrefslogtreecommitdiff
path: root/vendor/aws/aws-sdk-php/src/Crypto/KmsMaterialsProviderV2.php
blob: e7da8b92ef91248c4c37e2397253f9ff52e67d57 (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
<?php
namespace Aws\Crypto;

use Aws\Exception\CryptoException;
use Aws\Kms\KmsClient;

/**
 * Uses KMS to supply materials for encrypting and decrypting data. This
 * V2 implementation should be used with the V2 encryption clients (i.e.
 * S3EncryptionClientV2).
 */
class KmsMaterialsProviderV2 extends MaterialsProviderV2 implements MaterialsProviderInterfaceV2
{
    const WRAP_ALGORITHM_NAME = 'kms+context';

    private $kmsClient;
    private $kmsKeyId;

    /**
     * @param KmsClient $kmsClient A KMS Client for use encrypting and
     *                             decrypting keys.
     * @param string $kmsKeyId The private KMS key id to be used for encrypting
     *                         and decrypting keys.
     */
    public function __construct(
        KmsClient $kmsClient,
        $kmsKeyId = null
    ) {
        $this->kmsClient = $kmsClient;
        $this->kmsKeyId = $kmsKeyId;
    }

    /**
     * @inheritDoc
     */
    public function getWrapAlgorithmName()
    {
        return self::WRAP_ALGORITHM_NAME;
    }

    /**
     * @inheritDoc
     */
    public function decryptCek($encryptedCek, $materialDescription, $options)
    {
        $params = [
            'CiphertextBlob' => $encryptedCek,
            'EncryptionContext' => $materialDescription
        ];
        if (empty($options['@KmsAllowDecryptWithAnyCmk'])) {
            if (empty($this->kmsKeyId)) {
                throw new CryptoException('KMS CMK ID was not specified and the'
                    . ' operation is not opted-in to attempting to use any valid'
                    . ' CMK it discovers. Please specify a CMK ID, or explicitly'
                    . ' enable attempts to use any valid KMS CMK with the'
                    . ' @KmsAllowDecryptWithAnyCmk option.');
            }
            $params['KeyId'] = $this->kmsKeyId;
        }

        $result = $this->kmsClient->decrypt($params);
        return $result['Plaintext'];
    }

    /**
     * @inheritDoc
     */
    public function generateCek($keySize, $context, $options)
    {
        if (empty($this->kmsKeyId)) {
            throw new CryptoException('A KMS key id is required for encryption'
                . ' with KMS keywrap. Use a KmsMaterialsProviderV2 that has been'
                . ' instantiated with a KMS key id.');
        }
        $options = array_change_key_case($options);
        if (!isset($options['@kmsencryptioncontext'])
            || !is_array($options['@kmsencryptioncontext'])
        ) {
            throw new CryptoException("'@KmsEncryptionContext' is a"
                . " required argument when using KmsMaterialsProviderV2, and"
                . " must be an associative array (or empty array).");
        }
        if (isset($options['@kmsencryptioncontext']['aws:x-amz-cek-alg'])) {
            throw new CryptoException("Conflict in reserved @KmsEncryptionContext"
                . " key aws:x-amz-cek-alg. This value is reserved for the S3"
                . " Encryption Client and cannot be set by the user.");
        }
        $context = array_merge($options['@kmsencryptioncontext'], $context);
        $result = $this->kmsClient->generateDataKey([
            'KeyId' => $this->kmsKeyId,
            'KeySpec' => "AES_{$keySize}",
            'EncryptionContext' => $context
        ]);
        return [
            'Plaintext' => $result['Plaintext'],
            'Ciphertext' => base64_encode($result['CiphertextBlob']),
            'UpdatedContext' => $context
        ];
    }
}