コード例 #1
0
    def _decrypt_data_key(self,
                          encrypted_data_key,
                          algorithm,
                          encryption_context=None):
        """Decrypts an encrypted data key and returns the plaintext.

        :param data_key: Encrypted data key
        :type data_key: aws_encryption_sdk.structures.EncryptedDataKey
        :type algorithm: `aws_encryption_sdk.identifiers.Algorithm` (not used for KMS)
        :param dict encryption_context: Encryption context to use in decryption
        :returns: Decrypted data key
        :rtype: aws_encryption_sdk.structures.DataKey
        :raises DecryptKeyError: if Master Key is unable to decrypt data key
        """
        edk_key_id = to_str(encrypted_data_key.key_provider.key_info)
        if edk_key_id != self._key_id:
            raise DecryptKeyError(
                "Cannot decrypt EDK wrapped by key_id={}, because it does not match this "
                "provider's key_id={}".format(edk_key_id, self._key_id))

        kms_params = {
            "CiphertextBlob": encrypted_data_key.encrypted_data_key,
            "KeyId": edk_key_id
        }
        if encryption_context:
            kms_params["EncryptionContext"] = encryption_context
        if self.config.grant_tokens:
            kms_params["GrantTokens"] = self.config.grant_tokens
        # Catch any boto3 errors and normalize to expected DecryptKeyError
        try:
            response = self.config.client.decrypt(**kms_params)

            returned_key_id = response["KeyId"]
            if returned_key_id != edk_key_id:
                error_message = "AWS KMS returned unexpected key_id {returned} (expected {key_id})".format(
                    returned=returned_key_id, key_id=edk_key_id)
                _LOGGER.exception(error_message)
                raise DecryptKeyError(error_message)
            plaintext = response["Plaintext"]
        except (ClientError, KeyError):
            error_message = "Master Key {key_id} unable to decrypt data key".format(
                key_id=self._key_id)
            _LOGGER.exception(error_message)
            raise DecryptKeyError(error_message)
        return DataKey(
            key_provider=self.key_provider,
            data_key=plaintext,
            encrypted_data_key=encrypted_data_key.encrypted_data_key)
コード例 #2
0
    def decrypt_data_key_from_list(self, encrypted_data_keys, algorithm, encryption_context):
        """Receives a list of encrypted data keys and returns the first one which this provider is able to decrypt.

        :param encrypted_data_keys: List of encrypted data keys
        :type encrypted_data_keys: list of :class:`aws_encryption_sdk.structures.EncryptedDataKey`
        :param algorithm: Algorithm object which directs how this Master Key will encrypt the data key
        :type algorithm: aws_encryption_sdk.identifiers.Algorithm
        :param dict encryption_context: Encryption context to use in encryption
        :returns: Decrypted data key
        :rtype: aws_encryption_sdk.structures.DataKey
        :raises DecryptKeyError: if unable to decrypt any of the supplied encrypted data keys
        """
        data_key = None
        for encrypted_data_key in encrypted_data_keys:
            try:
                data_key = self.decrypt_data_key(encrypted_data_key, algorithm, encryption_context)
            # MasterKeyProvider.decrypt_data_key throws DecryptKeyError
            # but MasterKey.decrypt_data_key throws IncorrectMasterKeyError
            except (DecryptKeyError, IncorrectMasterKeyError):
                continue
            else:
                break
        if not data_key:
            raise DecryptKeyError("Unable to decrypt any data key")
        return data_key
コード例 #3
0
    def _decrypt_data_key(self,
                          encrypted_data_key,
                          algorithm,
                          encryption_context=None):
        """Decrypts an encrypted data key and returns the plaintext.

        :param data_key: Encrypted data key
        :type data_key: aws_encryption_sdk.structures.EncryptedDataKey
        :type algorithm: `aws_encryption_sdk.identifiers.Algorithm` (not used for KMS)
        :param dict encryption_context: Encryption context to use in decryption
        :returns: Decrypted data key
        :rtype: aws_encryption_sdk.structures.DataKey
        :raises DecryptKeyError: if Master Key is unable to decrypt data key
        """
        kms_params = {"CiphertextBlob": encrypted_data_key.encrypted_data_key}
        if encryption_context:
            kms_params["EncryptionContext"] = encryption_context
        if self.config.grant_tokens:
            kms_params["GrantTokens"] = self.config.grant_tokens
        # Catch any boto3 errors and normalize to expected DecryptKeyError
        try:
            response = self.config.client.decrypt(**kms_params)
            plaintext = response["Plaintext"]
        except (ClientError, KeyError):
            error_message = "Master Key {key_id} unable to decrypt data key".format(
                key_id=self._key_id)
            _LOGGER.exception(error_message)
            raise DecryptKeyError(error_message)
        return DataKey(
            key_provider=self.key_provider,
            data_key=plaintext,
            encrypted_data_key=encrypted_data_key.encrypted_data_key)
コード例 #4
0
def _do_aws_kms_decrypt(client_supplier, key_name, encrypted_data_key, encryption_context, grant_tokens):
    # type: (ClientSupplierType, str, EncryptedDataKey, Dict[str, str], Iterable[str]) -> RawDataKey
    """Attempt to call ``kms:Decrypt`` and return the resulting plaintext data key.

    Any errors encountered are passed up the chain without comment.

    .. versionadded:: 1.5.0

    """
    region = _region_from_key_id(encrypted_data_key.key_provider.key_info.decode("utf-8"))
    client = client_supplier(region)
    response = client.decrypt(
        CiphertextBlob=encrypted_data_key.encrypted_data_key,
        EncryptionContext=encryption_context,
        GrantTokens=grant_tokens,
    )
    response_key_id = response["KeyId"]
    if response_key_id != key_name:
        raise DecryptKeyError(
            "Decryption results from AWS KMS are for an unexpected key ID!"
            " actual '{actual}' != expected '{expected}'".format(actual=response_key_id, expected=key_name)
        )
    return RawDataKey(
        key_provider=MasterKeyInfo(provider_id=KEY_NAMESPACE, key_info=response_key_id), data_key=response["Plaintext"]
    )
コード例 #5
0
    def decrypt_data_key(self, encrypted_data_key, algorithm,
                         encryption_context):
        """Iterates through all currently added Master Keys and Master Key Providers
        to attempt to decrypt data key.

        :param encrypted_data_key: Encrypted data key to decrypt
        :type encrypted_data_key: aws_encryption_sdk.structures.EncryptedDataKey
        :param algorithm: Algorithm object which directs how this Master Key will encrypt the data key
        :type algorithm: aws_encryption_sdk.identifiers.Algorithm
        :param dict encryption_context: Encryption context to use in encryption
        :returns: Decrypted data key
        :rtype: aws_encryption_sdk.structures.DataKey
        :raises DecryptKeyError: if unable to decrypt encrypted data key
        """
        _LOGGER.debug("starting decrypt data key attempt")
        for master_key in self.master_keys_for_data_key(encrypted_data_key):
            try:
                _LOGGER.debug(
                    "attempting to decrypt data key with provider %s",
                    encrypted_data_key.key_provider.key_info)
                return master_key.decrypt_data_key(encrypted_data_key,
                                                   algorithm,
                                                   encryption_context)

            # MasterKeyProvider.decrypt_data_key throws DecryptKeyError
            # but MasterKey.decrypt_data_key throws IncorrectMasterKeyError
            except (IncorrectMasterKeyError, DecryptKeyError) as error:
                _LOGGER.debug(
                    "%s raised when attempting to decrypt data key with master key %s",
                    repr(error),
                    master_key.key_provider,
                )
                continue

        raise DecryptKeyError("Unable to decrypt data key")
コード例 #6
0
    def _validate_allowed_to_decrypt(self, edk_key_id):
        """Checks that this provider is allowed to decrypt with the given key id.

        Compared to the default KMS provider, this checks for MRK equality between the edk and the configured key id
        rather than strict string equality.
        """
        if not _check_mrk_arns_equal(edk_key_id, self._key_id):
            raise DecryptKeyError(
                "Cannot decrypt EDK wrapped by key_id={}, because it does not match this "
                "provider's key_id={}".format(edk_key_id, self._key_id))
コード例 #7
0
    def decrypt_data_key(self, encrypted_data_key, algorithm,
                         encryption_context):
        """Iterates through all currently added Master Keys and Master Key Providers
        to attempt to decrypt data key.

        :param encrypted_data_key: Encrypted data key to decrypt
        :type encrypted_data_key: aws_encryption_sdk.structures.EncryptedDataKey
        :param algorithm: Algorithm object which directs how this Master Key will encrypt the data key
        :type algorithm: aws_encryption_sdk.identifiers.Algorithm
        :param dict encryption_context: Encryption context to use in encryption
        :returns: Decrypted data key
        :rtype: aws_encryption_sdk.structures.DataKey
        :raises DecryptKeyError: if unable to decrypt encrypted data key
        """
        data_key = None
        master_key = None
        _LOGGER.debug("starting decrypt data key attempt")
        for member in [self] + self._members:
            if member.provider_id == encrypted_data_key.key_provider.provider_id:
                _LOGGER.debug(
                    "attempting to locate master key from key provider: %s",
                    member.provider_id)
                if isinstance(member, MasterKey):
                    _LOGGER.debug("using existing master key")
                    master_key = member
                elif self.vend_masterkey_on_decrypt:
                    try:
                        _LOGGER.debug("attempting to add master key: %s",
                                      encrypted_data_key.key_provider.key_info)
                        master_key = member.master_key_for_decrypt(
                            encrypted_data_key.key_provider.key_info)
                    except InvalidKeyIdError:
                        _LOGGER.debug(
                            "master key %s not available in provider",
                            encrypted_data_key.key_provider.key_info)
                        continue
                else:
                    continue
                try:
                    _LOGGER.debug(
                        "attempting to decrypt data key with provider %s",
                        encrypted_data_key.key_provider.key_info)
                    data_key = master_key.decrypt_data_key(
                        encrypted_data_key, algorithm, encryption_context)
                except (IncorrectMasterKeyError, DecryptKeyError) as error:
                    _LOGGER.debug(
                        "%s raised when attempting to decrypt data key with master key %s",
                        repr(error),
                        master_key.key_provider,
                    )
                    continue
                break  # If this point is reached without throwing any errors, the data key has been decrypted
        if not data_key:
            raise DecryptKeyError("Unable to decrypt data key")
        return data_key
 def test_decrypt_data_key_unsuccessful_no_matching_members(self):
     mock_member = MagicMock()
     mock_member.provider_id = sentinel.another_provider_id
     mock_encrypted_data_key = MagicMock()
     mock_encrypted_data_key.key_provider.provider_id = sentinel.provider_id
     mock_encrypted_data_key.key_provider.key_info = sentinel.key_info
     mock_master_key = MagicMock()
     mock_master_key.decrypt_data_key.side_effect = DecryptKeyError()
     mock_master_key_provider = MockMasterKeyProvider(
         provider_id=sentinel.provider_id,
         mock_new_master_key=mock_master_key)
     mock_master_key_provider._members = [mock_member]
     with six.assertRaisesRegex(self, DecryptKeyError,
                                'Unable to decrypt data key'):
         mock_master_key_provider.decrypt_data_key(
             encrypted_data_key=mock_encrypted_data_key,
             algorithm=sentinel.algorithm,
             encryption_context=sentinel.encryption_context)
コード例 #9
0
 def test_decrypt_data_key_unsuccessful_no_matching_members(self):
     mock_member = MagicMock()
     mock_member.provider_id = sentinel.another_provider_id
     mock_encrypted_data_key = MagicMock()
     mock_encrypted_data_key.key_provider.provider_id = sentinel.provider_id
     mock_encrypted_data_key.key_provider.key_info = sentinel.key_info
     mock_master_key = MagicMock()
     mock_master_key.decrypt_data_key.side_effect = DecryptKeyError()
     mock_master_key_provider = MockMasterKeyProvider(
         provider_id=sentinel.provider_id, mock_new_master_key=mock_master_key
     )
     mock_master_key_provider._members = [mock_member]
     with pytest.raises(DecryptKeyError) as excinfo:
         mock_master_key_provider.decrypt_data_key(
             encrypted_data_key=mock_encrypted_data_key,
             algorithm=sentinel.algorithm,
             encryption_context=sentinel.encryption_context,
         )
     excinfo.match("Unable to decrypt data key")
コード例 #10
0
    def _decrypt_data_key(self, encrypted_data_key: EncryptedDataKey,
                          algorithm: AlgorithmSuite,
                          encryption_context: Dict[Text, Text]) -> DataKey:
        """Decrypt an encrypted data key and return the plaintext.

        :param data_key: Encrypted data key
        :type data_key: aws_encryption_sdk.structures.EncryptedDataKey
        :param algorithm: Algorithm object which directs how this Master Key will encrypt the data key
        :type algorithm: aws_encryption_sdk.identifiers.Algorithm
        :param dict encryption_context: Encryption context to use in decryption
        :returns: Data key containing decrypted data key
        :rtype: aws_encryption_sdk.structures.DataKey
        :raises DecryptKeyError: if Master Key is unable to decrypt data key
        """
        if encrypted_data_key.encrypted_data_key != self._encrypted_data_key:
            raise DecryptKeyError(
                'Master Key "{provider}" unable to decrypt data key'.format(
                    provider=self.key_provider))

        return self._generate_data_key(algorithm, encryption_context)
コード例 #11
0
 def test_decrypt_data_key_unsuccessful_master_key_decryt_error(self):
     mock_member = MagicMock()
     mock_member.provider_id = sentinel.provider_id
     mock_master_key = MagicMock()
     mock_master_key.decrypt_data_key.side_effect = (DecryptKeyError, )
     mock_member.master_key_for_decrypt.return_value = mock_master_key
     mock_encrypted_data_key = MagicMock()
     mock_encrypted_data_key.key_provider.provider_id = sentinel.provider_id
     mock_encrypted_data_key.key_provider.key_info = sentinel.key_info
     mock_master_key = MagicMock()
     mock_master_key.decrypt_data_key.side_effect = DecryptKeyError()
     mock_master_key_provider = MockMasterKeyProvider(
         provider_id=sentinel.provider_id,
         mock_new_master_key=mock_master_key)
     mock_master_key_provider._members = [mock_member]
     with six.assertRaisesRegex(self, DecryptKeyError,
                                "Unable to decrypt data key"):
         mock_master_key_provider.decrypt_data_key(
             encrypted_data_key=mock_encrypted_data_key,
             algorithm=sentinel.algorithm,
             encryption_context=sentinel.encryption_context,
         )
コード例 #12
0
 def decrypt_data_key(self, encrypted_data_key, algorithm,
                      encryption_context):
     raise DecryptKeyError(
         "FailingDecryptMasterKeyProvider cannot decrypt!")
コード例 #13
0
    def decrypt_data_key(self, encrypted_data_key, algorithm,
                         encryption_context):
        """Iterates through all currently added Master Keys and Master Key Providers
        to attempt to decrypt data key.

        :param encrypted_data_key: Encrypted data key to decrypt
        :type encrypted_data_key: aws_encryption_sdk.structures.EncryptedDataKey
        :param algorithm: Algorithm object which directs how this Master Key will encrypt the data key
        :type algorithm: aws_encryption_sdk.identifiers.Algorithm
        :param dict encryption_context: Encryption context to use in encryption
        :returns: Decrypted data key
        :rtype: aws_encryption_sdk.structures.DataKey
        :raises DecryptKeyError: if unable to decrypt encrypted data key
        """
        data_key = None
        master_key = None
        _LOGGER.debug("starting decrypt data key attempt")
        for member in [self] + self._members:
            # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.9
            # //# To match the encrypted data key's
            # //# provider ID MUST exactly match the value "aws-kms".
            if member.provider_id == encrypted_data_key.key_provider.provider_id:
                _LOGGER.debug(
                    "attempting to locate master key from key provider: %s",
                    member.provider_id)
                if isinstance(member, MasterKey):
                    _LOGGER.debug("using existing master key")
                    master_key = member
                elif self.vend_masterkey_on_decrypt:
                    # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.9
                    # //# For each encrypted data key in the filtered set, one at a time, the
                    # //# master key provider MUST call Get Master Key (aws-kms-mrk-aware-
                    # //# master-key-provider.md#get-master-key) with the encrypted data key's
                    # //# provider info as the AWS KMS key ARN.
                    # We attempt to decrypt with pre-populated self._members for strict MKPs/MKs
                    # and vend new MKs for Discovery MPKs/MKs.
                    try:
                        _LOGGER.debug("attempting to add master key: %s",
                                      encrypted_data_key.key_provider.key_info)
                        master_key = member.master_key_for_decrypt(
                            encrypted_data_key.key_provider.key_info)
                    except InvalidKeyIdError:
                        _LOGGER.debug(
                            "master key %s not available in provider",
                            encrypted_data_key.key_provider.key_info)
                        continue
                else:
                    continue
                try:
                    _LOGGER.debug(
                        "attempting to decrypt data key with provider %s",
                        encrypted_data_key.key_provider.key_info)
                    # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.9
                    # //# It MUST call Decrypt Data Key
                    # //# (aws-kms-mrk-aware-master-key.md#decrypt-data-key) on this master key
                    # //# with the input algorithm, this single encrypted data key, and the
                    # //# input encryption context.

                    data_key = master_key.decrypt_data_key(
                        encrypted_data_key, algorithm, encryption_context)
                except (IncorrectMasterKeyError, DecryptKeyError) as error:
                    _LOGGER.debug(
                        "%s raised when attempting to decrypt data key with master key %s",
                        repr(error),
                        master_key.key_provider,
                    )
                    continue
                # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.9
                # //# If the AWS KMS response satisfies the requirements then it MUST be
                # //# use and this function MUST return and not attempt to decrypt any more
                # //# encrypted data keys.

                # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.9
                # //# If the decrypt data key call is
                # //# successful, then this function MUST return this result and not
                # //# attempt to decrypt any more encrypted data keys.

                break  # If this point is reached without throwing any errors, the data key has been decrypted
        if not data_key:
            # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.9
            # //# If all the input encrypted data keys have been processed then this
            # //# function MUST yield an error that includes all the collected errors.
            # Note the latter half of "includes all collected errors" is not satisfied

            # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.9
            # //# If all the input encrypted data keys have been processed then this
            # //# function MUST yield an error that includes all the collected errors.
            # Note the latter half of "includes all collected errors" is not satisfied
            raise DecryptKeyError("Unable to decrypt data key")
        return data_key
コード例 #14
0
 def _validate_allowed_to_decrypt(self, edk_key_id):
     """Checks that this provider is allowed to decrypt with the given key id."""
     if edk_key_id != self._key_id:
         raise DecryptKeyError(
             "Cannot decrypt EDK wrapped by key_id={}, because it does not match this "
             "provider's key_id={}".format(edk_key_id, self._key_id))
コード例 #15
0
    def _decrypt_data_key(self,
                          encrypted_data_key,
                          algorithm,
                          encryption_context=None):
        """Decrypts an encrypted data key and returns the plaintext.

        :param data_key: Encrypted data key
        :type data_key: aws_encryption_sdk.structures.EncryptedDataKey
        :type algorithm: `aws_encryption_sdk.identifiers.Algorithm` (not used for KMS)
        :param dict encryption_context: Encryption context to use in decryption
        :returns: Decrypted data key
        :rtype: aws_encryption_sdk.structures.DataKey
        :raises DecryptKeyError: if Master Key is unable to decrypt data key
        """
        # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.9
        # //# Additionally each provider info MUST be a valid AWS KMS ARN
        # //# (aws-kms-key-arn.md#a-valid-aws-kms-arn) with a resource type of
        # //# "key".
        edk_key_id = to_str(encrypted_data_key.key_provider.key_info)
        edk_arn = arn_from_str(edk_key_id)
        if not edk_arn.resource_type == "key":
            error_message = "AWS KMS Provider EDK contains unexpected key_id: {key_id}".format(
                key_id=edk_key_id)
            _LOGGER.exception(error_message)
            raise DecryptKeyError(error_message)

        self._validate_allowed_to_decrypt(edk_key_id)
        kms_params = self._build_decrypt_request(encrypted_data_key,
                                                 encryption_context)
        # Catch any boto3 errors and normalize to expected DecryptKeyError
        try:
            response = self.config.client.decrypt(**kms_params)

            # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.9
            # //# If the call succeeds then the response's "KeyId" MUST be equal to the
            # //# configured AWS KMS key identifier otherwise the function MUST collect
            # //# an error.
            # Note that Python logs but does not collect errors
            returned_key_id = response["KeyId"]
            if returned_key_id != self._key_id:
                error_message = "AWS KMS returned unexpected key_id {returned} (expected {key_id})".format(
                    returned=returned_key_id, key_id=self._key_id)
                _LOGGER.exception(error_message)
                raise DecryptKeyError(error_message)

            # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.9
            # //# The response's "Plaintext"'s length MUST equal the length
            # //# required by the requested algorithm suite otherwise the function MUST
            # //# collect an error.
            # Note that Python logs but does not collect errors
            plaintext = response["Plaintext"]
            if len(plaintext) != algorithm.data_key_len:
                error_message = "Plaintext length ({len1}) does not match algorithm's expected length ({len2})".format(
                    len1=len(plaintext), len2=algorithm.data_key_len)
                raise DecryptKeyError(error_message)

        except (ClientError, KeyError):
            error_message = "Master Key {key_id} unable to decrypt data key".format(
                key_id=self._key_id)
            _LOGGER.exception(error_message)
            raise DecryptKeyError(error_message)
        return DataKey(
            key_provider=self.key_provider,
            data_key=plaintext,
            encrypted_data_key=encrypted_data_key.encrypted_data_key)