KmsMaterialsProviderV2.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. <?php
  2. namespace Aws\Crypto;
  3. use Aws\Exception\CryptoException;
  4. use Aws\Kms\KmsClient;
  5. /**
  6. * Uses KMS to supply materials for encrypting and decrypting data. This
  7. * V2 implementation should be used with the V2 encryption clients (i.e.
  8. * S3EncryptionClientV2).
  9. */
  10. class KmsMaterialsProviderV2 extends MaterialsProviderV2 implements MaterialsProviderInterfaceV2
  11. {
  12. const WRAP_ALGORITHM_NAME = 'kms+context';
  13. private $kmsClient;
  14. private $kmsKeyId;
  15. /**
  16. * @param KmsClient $kmsClient A KMS Client for use encrypting and
  17. * decrypting keys.
  18. * @param string $kmsKeyId The private KMS key id to be used for encrypting
  19. * and decrypting keys.
  20. */
  21. public function __construct(
  22. KmsClient $kmsClient,
  23. $kmsKeyId = null
  24. ) {
  25. $this->kmsClient = $kmsClient;
  26. $this->kmsKeyId = $kmsKeyId;
  27. }
  28. /**
  29. * @inheritDoc
  30. */
  31. public function getWrapAlgorithmName()
  32. {
  33. return self::WRAP_ALGORITHM_NAME;
  34. }
  35. /**
  36. * @inheritDoc
  37. */
  38. public function decryptCek($encryptedCek, $materialDescription, $options)
  39. {
  40. $params = [
  41. 'CiphertextBlob' => $encryptedCek,
  42. 'EncryptionContext' => $materialDescription
  43. ];
  44. if (empty($options['@KmsAllowDecryptWithAnyCmk'])) {
  45. if (empty($this->kmsKeyId)) {
  46. throw new CryptoException('KMS CMK ID was not specified and the'
  47. . ' operation is not opted-in to attempting to use any valid'
  48. . ' CMK it discovers. Please specify a CMK ID, or explicitly'
  49. . ' enable attempts to use any valid KMS CMK with the'
  50. . ' @KmsAllowDecryptWithAnyCmk option.');
  51. }
  52. $params['KeyId'] = $this->kmsKeyId;
  53. }
  54. $result = $this->kmsClient->decrypt($params);
  55. return $result['Plaintext'];
  56. }
  57. /**
  58. * @inheritDoc
  59. */
  60. public function generateCek($keySize, $context, $options)
  61. {
  62. if (empty($this->kmsKeyId)) {
  63. throw new CryptoException('A KMS key id is required for encryption'
  64. . ' with KMS keywrap. Use a KmsMaterialsProviderV2 that has been'
  65. . ' instantiated with a KMS key id.');
  66. }
  67. $options = array_change_key_case($options);
  68. if (!isset($options['@kmsencryptioncontext'])
  69. || !is_array($options['@kmsencryptioncontext'])
  70. ) {
  71. throw new CryptoException("'@KmsEncryptionContext' is a"
  72. . " required argument when using KmsMaterialsProviderV2, and"
  73. . " must be an associative array (or empty array).");
  74. }
  75. if (isset($options['@kmsencryptioncontext']['aws:x-amz-cek-alg'])) {
  76. throw new CryptoException("Conflict in reserved @KmsEncryptionContext"
  77. . " key aws:x-amz-cek-alg. This value is reserved for the S3"
  78. . " Encryption Client and cannot be set by the user.");
  79. }
  80. $context = array_merge($options['@kmsencryptioncontext'], $context);
  81. $result = $this->kmsClient->generateDataKey([
  82. 'KeyId' => $this->kmsKeyId,
  83. 'KeySpec' => "AES_{$keySize}",
  84. 'EncryptionContext' => $context
  85. ]);
  86. return [
  87. 'Plaintext' => $result['Plaintext'],
  88. 'Ciphertext' => base64_encode($result['CiphertextBlob']),
  89. 'UpdatedContext' => $context
  90. ];
  91. }
  92. }