bucketwarden-server 0.1.0

BucketWarden storage server runtime.
Documentation
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(&current_version.ciphertext) {
            Ok(body) => body,
            Err(error) => {
                self.audit_kms_failure(
                    principal,
                    "kms:Decrypt",
                    &resource,
                    &current_version.ciphertext.key_id,
                    &error.to_string(),
                );
                return Err(RuntimeError::Kms(error));
            }
        };
        self.verify_integrity_record(&current_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,
            &current_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)
    }
}