def on_encrypt(self, encryption_materials): # type: (EncryptionMaterials) -> EncryptionMaterials """Generate a data key using generator keyring and encrypt it using any available wrapping key in any child keyring. :param EncryptionMaterials encryption_materials: Encryption materials for keyring to modify. :returns: Encryption materials containing data key and encrypted data key :rtype: EncryptionMaterials """ new_materials = encryption_materials if new_materials.data_encryption_key is None: new_materials = _generate_data_key( encryption_materials=new_materials, key_provider=self._key_provider) if self._public_wrapping_key is None: # This should be impossible, but just in case, give a useful error message. raise EncryptKeyError( "Raw RSA keyring unable to encrypt data key: no public key available" ) try: # Encrypt data key encrypted_wrapped_key = EncryptedData( iv=None, ciphertext=self._public_wrapping_key.encrypt( plaintext=new_materials.data_encryption_key.data_key, padding=self._wrapping_algorithm.padding, ), tag=None, ) # EncryptedData to EncryptedDataKey encrypted_data_key = serialize_wrapped_key( key_provider=self._key_provider, wrapping_algorithm=self._wrapping_algorithm, wrapping_key_id=self.key_name, encrypted_wrapped_key=encrypted_wrapped_key, ) except Exception: # pylint: disable=broad-except error_message = "Raw RSA keyring unable to encrypt data key" _LOGGER.exception(error_message) raise EncryptKeyError(error_message) # Update Keyring Trace keyring_trace = KeyringTrace( wrapping_key=self._key_provider, flags={KeyringTraceFlag.ENCRYPTED_DATA_KEY}) # Add encrypted data key to encryption_materials return new_materials.with_encrypted_data_key( encrypted_data_key=encrypted_data_key, keyring_trace=keyring_trace)
def _encrypt_data_key(self, data_key, algorithm, encryption_context=None): """Encrypts a data key and returns the ciphertext. :param data_key: Unencrypted data key :type data_key: :class:`aws_encryption_sdk.structures.RawDataKey` or :class:`aws_encryption_sdk.structures.DataKey` :param algorithm: Placeholder to maintain API compatibility with parent :param dict encryption_context: Encryption context to pass to KMS :returns: Data key containing encrypted data key :rtype: aws_encryption_sdk.structures.EncryptedDataKey :raises EncryptKeyError: if Master Key is unable to encrypt data key """ kms_params = {"KeyId": self._key_id, "Plaintext": data_key.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 EncryptKeyError try: response = self.config.client.encrypt(**kms_params) ciphertext = response["CiphertextBlob"] key_id = response["KeyId"] except (ClientError, KeyError): error_message = "Master Key {key_id} unable to encrypt data key".format( key_id=self._key_id) _LOGGER.exception(error_message) raise EncryptKeyError(error_message) return EncryptedDataKey(key_provider=MasterKeyInfo( provider_id=self.provider_id, key_info=key_id), encrypted_data_key=ciphertext)
def on_encrypt(self, encryption_materials): # type: (EncryptionMaterials) -> EncryptionMaterials """Generate a data key using generator keyring and encrypt it using any available wrapping key in any child keyring. :param EncryptionMaterials encryption_materials: Encryption materials for keyring to modify. :returns: Optionally modified encryption materials. :rtype: EncryptionMaterials :raises EncryptKeyError: if unable to encrypt data key. """ # Check if generator keyring is not provided and data key is not generated if self.generator is None and encryption_materials.data_encryption_key is None: raise EncryptKeyError( "Generator keyring not provided " "and encryption materials do not already contain a plaintext data key." ) new_materials = encryption_materials # Call on_encrypt on the generator keyring if it is provided if self.generator is not None: new_materials = self.generator.on_encrypt( encryption_materials=new_materials) # Check if data key is generated if new_materials.data_encryption_key is None: raise GenerateKeyError("Unable to generate data encryption key.") # Call on_encrypt on all other keyrings for keyring in self.children: new_materials = keyring.on_encrypt( encryption_materials=new_materials) return new_materials
def on_encrypt(self, encryption_materials): # type: (EncryptionMaterials) -> EncryptionMaterials trace_info = MasterKeyInfo(provider_id=KEY_NAMESPACE, key_info=self._key_id) new_materials = encryption_materials try: if new_materials.data_encryption_key is None: plaintext_key, encrypted_key = _do_aws_kms_generate_data_key( client_supplier=self._client_supplier, key_name=self._key_id, encryption_context=new_materials.encryption_context, algorithm=new_materials.algorithm, grant_tokens=self._grant_tokens, ) new_materials = new_materials.with_data_encryption_key( data_encryption_key=plaintext_key, keyring_trace=KeyringTrace(wrapping_key=trace_info, flags=_GENERATE_FLAGS), ) else: encrypted_key = _do_aws_kms_encrypt( client_supplier=self._client_supplier, key_name=self._key_id, plaintext_data_key=new_materials.data_encryption_key, encryption_context=new_materials.encryption_context, grant_tokens=self._grant_tokens, ) except Exception: # pylint: disable=broad-except # We intentionally WANT to catch all exceptions here message = "Unable to generate or encrypt data key using {}".format(trace_info) _LOGGER.exception(message) raise EncryptKeyError(message) return new_materials.with_encrypted_data_key( encrypted_data_key=encrypted_key, keyring_trace=KeyringTrace(wrapping_key=trace_info, flags=_ENCRYPT_FLAGS) )
def _encrypt_data_key(self, data_key, algorithm, encryption_context=None): """Encrypts a data key and returns the ciphertext. :param data_key: Unencrypted data key :type data_key: :class:`aws_encryption_sdk.structures.RawDataKey` or :class:`aws_encryption_sdk.structures.DataKey` :param algorithm: Placeholder to maintain API compatibility with parent :param dict encryption_context: Encryption context to pass to KMS :returns: Data key containing encrypted data key :rtype: aws_encryption_sdk.structures.EncryptedDataKey :raises EncryptKeyError: if Master Key is unable to encrypt data key """ kms_params = self._build_encrypt_request(data_key, encryption_context) # Catch any boto3 errors and normalize to expected EncryptKeyError try: response = self.config.client.encrypt(**kms_params) # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.11 # //# The response's cipher text blob MUST be used as the "ciphertext" for the # //# encrypted data key. ciphertext = response["CiphertextBlob"] key_id = response["KeyId"] except (ClientError, KeyError): error_message = "Master Key {key_id} unable to encrypt data key".format( key_id=self._key_id) _LOGGER.exception(error_message) raise EncryptKeyError(error_message) # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.11 # //# The AWS KMS Encrypt response MUST contain a valid "KeyId". # arn_from_str will error if given an invalid key ARN try: key_id_str = to_str(key_id) arn_from_str(key_id_str) except MalformedArnError: error_message = "Retrieved an unexpected KeyID in response from KMS: {key_id}".format( key_id=key_id) _LOGGER.exception(error_message) raise EncryptKeyError(error_message) return EncryptedDataKey(key_provider=MasterKeyInfo( provider_id=self.provider_id, key_info=key_id), encrypted_data_key=ciphertext)
def on_encrypt(self, encryption_materials): # type: (EncryptionMaterials) -> EncryptionMaterials """Generate a data key if not present and encrypt it using any available wrapping key :param EncryptionMaterials encryption_materials: Encryption materials for the keyring to modify :returns: Encryption materials containing data key and encrypted data key :rtype: EncryptionMaterials """ new_materials = encryption_materials if new_materials.data_encryption_key is None: # Get encryption materials with a new data key. new_materials = _generate_data_key( encryption_materials=new_materials, key_provider=self._key_provider) try: # Encrypt data key encrypted_wrapped_key = self._wrapping_key_structure.encrypt( plaintext_data_key=new_materials.data_encryption_key.data_key, encryption_context=new_materials.encryption_context, ) # EncryptedData to EncryptedDataKey encrypted_data_key = serialize_wrapped_key( key_provider=self._key_provider, wrapping_algorithm=self._wrapping_algorithm, wrapping_key_id=self.key_name, encrypted_wrapped_key=encrypted_wrapped_key, ) except Exception: # pylint: disable=broad-except error_message = "Raw AES keyring unable to encrypt data key" _LOGGER.exception(error_message) raise EncryptKeyError(error_message) # Update Keyring Trace keyring_trace = KeyringTrace( wrapping_key=self._key_provider, flags={ KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT }, ) return new_materials.with_encrypted_data_key( encrypted_data_key=encrypted_data_key, keyring_trace=keyring_trace)