ironcore_alloy/saas_shield/
standard_attached.rs

1use crate::{
2    AlloyMetadata, TenantId,
3    errors::AlloyError,
4    standard::StandardDocumentOps,
5    standard_attached::{
6        EncryptedAttachedDocument, EncryptedAttachedDocuments, PlaintextAttachedDocument,
7        PlaintextAttachedDocuments, RekeyAttachedDocumentsBatchResult,
8        StandardAttachedDecryptBatchResult, StandardAttachedDocumentOps,
9        StandardAttachedEncryptBatchResult, decrypt_batch_core, decrypt_core, encrypt_batch_core,
10        encrypt_core, rekey_core,
11    },
12    tenant_security_client::TenantSecurityClient,
13};
14
15use super::{SaasShieldSecurityEventOps, SecurityEvent, standard::SaasShieldStandardClient};
16use std::sync::Arc;
17
18#[derive(uniffi::Object)]
19pub struct SaasShieldStandardAttachedClient {
20    standard_client: SaasShieldStandardClient,
21}
22
23impl SaasShieldStandardAttachedClient {
24    pub(crate) fn new(tenant_security_client: Arc<TenantSecurityClient>) -> Self {
25        Self {
26            standard_client: SaasShieldStandardClient::new(tenant_security_client),
27        }
28    }
29}
30
31#[uniffi::export]
32#[async_trait::async_trait]
33impl StandardAttachedDocumentOps for SaasShieldStandardAttachedClient {
34    /// Encrypt a document with the provided metadata.
35    /// A DEK (document encryption key) will be generated and encrypted using a derived key.
36    /// The result is a single blob of bytes with the edek put on the front of it.
37    async fn encrypt(
38        &self,
39        plaintext_document: PlaintextAttachedDocument,
40        metadata: &AlloyMetadata,
41    ) -> Result<EncryptedAttachedDocument, AlloyError> {
42        encrypt_core(&self.standard_client, plaintext_document.0, metadata).await
43    }
44
45    /// Encrypt multiple documents with the provided metadata.
46    /// A DEK (document encryption key) will be generated for each document and encrypted using a derived key.
47    async fn encrypt_batch(
48        &self,
49        plaintext_documents: PlaintextAttachedDocuments,
50        metadata: &AlloyMetadata,
51    ) -> Result<StandardAttachedEncryptBatchResult, AlloyError> {
52        encrypt_batch_core(&self.standard_client, plaintext_documents, metadata).await
53    }
54
55    /// Decrypt a document that was encrypted with the provided metadata.
56    /// The document must have been encrypted using attached encryption and not deterministic or standard encryption.
57    async fn decrypt(
58        &self,
59        attached_document: EncryptedAttachedDocument,
60        metadata: &AlloyMetadata,
61    ) -> Result<PlaintextAttachedDocument, AlloyError> {
62        decrypt_core(&self.standard_client, attached_document, metadata)
63            .await
64            .map(PlaintextAttachedDocument)
65    }
66
67    /// Decrypt multiple documents that were encrypted with the provided metadata.
68    /// The documents must have been encrypted using attached encryption and not deterministic or standard encryption.
69    async fn decrypt_batch(
70        &self,
71        encrypted_documents: EncryptedAttachedDocuments,
72        metadata: &AlloyMetadata,
73    ) -> Result<StandardAttachedDecryptBatchResult, AlloyError> {
74        decrypt_batch_core(&self.standard_client, encrypted_documents, metadata).await
75    }
76
77    /// Decrypt the provided documents and re-encrypt them using the tenant's current key. If `new_tenant_id` is `None`,
78    /// the documents will be encrypted to the original tenant.
79    async fn rekey_documents(
80        &self,
81        encrypted_documents: EncryptedAttachedDocuments,
82        metadata: &AlloyMetadata,
83        new_tenant_id: Option<TenantId>,
84    ) -> Result<RekeyAttachedDocumentsBatchResult, AlloyError> {
85        rekey_core(
86            &self.standard_client,
87            encrypted_documents,
88            metadata,
89            new_tenant_id,
90        )
91        .await
92    }
93
94    /// Generate a prefix that could used to search a data store for documents encrypted using an identifier (KMS
95    /// config id for SaaS Shield, secret id for Standalone). These bytes should be encoded into
96    /// a format matching the encoding in the data store. z85/ascii85 users should first pass these bytes through
97    /// `encode_prefix_z85` or `base85_prefix_padding`. Make sure you've read the documentation of those functions to
98    /// avoid pitfalls when encoding across byte boundaries.
99    /// Note that this will not work for matching values that don't use our key_id_header format, such as cloaked search.
100    fn get_searchable_edek_prefix(&self, id: i32) -> Vec<u8> {
101        self.standard_client.get_searchable_edek_prefix(id)
102    }
103}
104
105#[uniffi::export]
106#[async_trait::async_trait]
107impl SaasShieldSecurityEventOps for SaasShieldStandardAttachedClient {
108    /// Log the security event `event` to the tenant's log sink.
109    /// If the event time is unspecified the current time will be used.
110    async fn log_security_event(
111        &self,
112        event: SecurityEvent,
113        metadata: &AlloyMetadata,
114        event_time_millis: Option<i64>,
115    ) -> Result<(), AlloyError> {
116        self.standard_client
117            .log_security_event(event, metadata, event_time_millis)
118            .await
119    }
120}