use super::*;
impl BucketWarden {
pub(crate) fn envelope_metadata(
&self,
encryption: Option<&ServerSideEncryption>,
ciphertext: &Ciphertext,
plaintext: &[u8],
) -> Option<EnvelopeMetadata> {
let encryption = encryption?;
let key = self.kms.key_record(&ciphertext.key_id)?;
Some(EnvelopeMetadata {
algorithm: encryption.algorithm.clone(),
key_id: key.key_id.clone(),
key_version: key.version,
encrypted_data_key: sha256_hex(format!("{}:{}", key.key_id, key.version).as_bytes())
.into_bytes(),
plaintext_sha256: sha256_hex(plaintext),
})
}
pub fn rotate_kms_key(
&mut self,
actor: &str,
new_key_id: impl Into<String>,
new_key_material: impl Into<Vec<u8>>,
) -> Result<KeyRecord, RuntimeError> {
let record = self
.kms
.rotate_key(new_key_id, new_key_material, self.clock_epoch_seconds)?;
self.audit_kms_admin(actor, "kms:RotateKey", &record.key_id);
Ok(record)
}
pub fn disable_kms_key(&mut self, actor: &str, key_id: &str) -> Result<(), RuntimeError> {
self.kms.disable_key(key_id, self.clock_epoch_seconds)?;
self.audit_kms_admin(actor, "kms:DisableKey", key_id);
Ok(())
}
pub fn delete_kms_key(&mut self, actor: &str, key_id: &str) -> Result<(), RuntimeError> {
if self.object_versions_using_key(key_id).next().is_some() {
return Err(RuntimeError::InvalidEncryption(format!(
"KMS key {key_id} cannot be deleted while encrypted object versions depend on it"
)));
}
self.kms.delete_key(key_id, self.clock_epoch_seconds)?;
self.audit_kms_admin(actor, "kms:DeleteKey", key_id);
Ok(())
}
pub fn kms_key_status(&self, key_id: &str) -> Option<KeyStatus> {
self.kms.key_record(key_id).map(|key| key.status)
}
pub fn rewrap_object_encryption(
&mut self,
principal: &str,
bucket: &str,
key: &str,
version_id: Option<&str>,
) -> Result<UpdateObjectEncryptionResult, RuntimeError> {
let resource = object_resource(bucket, key);
self.authorize(principal, S3Action::UpdateObjectEncryption, &resource)?;
let target_version_id = if let Some(version_id) = version_id {
version_id.to_string()
} else {
self.current_version(bucket, key)?.version_id.clone()
};
let current_version = self.version_by_id(bucket, key, &target_version_id)?.clone();
if current_version.delete_marker {
return Err(RuntimeError::NoSuchKey(resource));
}
let body = match self.kms.decrypt(¤t_version.ciphertext) {
Ok(body) => body,
Err(error) => {
self.audit_kms_failure(
principal,
"kms:Decrypt",
&resource,
¤t_version.ciphertext.key_id,
&error.to_string(),
);
return Err(RuntimeError::Kms(error));
}
};
self.verify_integrity_record(¤t_version.integrity, &body)?;
let active_key_id = self.kms.key_id().to_string();
let encryption = ServerSideEncryption {
algorithm: "aws:kms".to_string(),
kms_key_id: Some(active_key_id.clone()),
};
let ciphertext = match self.kms.encrypt(&body) {
Ok(ciphertext) => ciphertext,
Err(error) => {
self.audit_kms_failure(
principal,
"kms:Encrypt",
&resource,
&active_key_id,
&error.to_string(),
);
return Err(RuntimeError::Kms(error));
}
};
let envelope = self.envelope_metadata(Some(&encryption), &ciphertext, &body);
let version = self.version_by_id_mut(bucket, key, &target_version_id)?;
version.ciphertext = ciphertext;
version.envelope = envelope;
version.metadata.encryption = Some(encryption.clone());
self.record_storage_commit(
"RewrapObjectEncryption",
bucket,
key,
&target_version_id,
¤t_version.integrity.checksum_sha256,
);
self.audit_allowed(
principal,
S3Action::UpdateObjectEncryption,
&resource,
Some(target_version_id.clone()),
);
self.audit_kms_unwrap(principal, &resource, &target_version_id, &active_key_id);
Ok(UpdateObjectEncryptionResult {
bucket: bucket.to_string(),
key: key.to_string(),
version_id: target_version_id,
encryption,
bucket_key_enabled: Some(false),
})
}
fn object_versions_using_key<'a>(
&'a self,
key_id: &'a str,
) -> impl Iterator<Item = &'a StoredVersion> {
self.buckets
.values()
.flat_map(|bucket| bucket.objects.values())
.flat_map(|object| object.versions.iter())
.filter(move |version| version.ciphertext.key_id == key_id && !version.delete_marker)
}
}