mongodb/client/csfle/
client_encryption.rs

1//! Support for explicit encryption.
2
3mod create_data_key;
4mod encrypt;
5
6use std::time::Duration;
7
8use mongocrypt::{ctx::KmsProvider, Crypt};
9use serde::{Deserialize, Serialize};
10use typed_builder::TypedBuilder;
11
12#[cfg(feature = "bson-3")]
13use crate::bson_compat::RawBsonRefExt as _;
14use crate::{
15    bson::{doc, spec::BinarySubtype, Binary, RawBinaryRef, RawDocumentBuf},
16    client::options::TlsOptions,
17    coll::options::CollectionOptions,
18    error::{Error, Result},
19    options::{ReadConcern, WriteConcern},
20    results::DeleteResult,
21    Client,
22    Collection,
23    Cursor,
24    Namespace,
25};
26
27use super::{options::KmsProviders, state_machine::CryptExecutor};
28
29pub use super::client_builder::EncryptedClientBuilder;
30pub use crate::action::csfle::encrypt::{EncryptKey, RangeOptions};
31
32/// A handle to the key vault.  Used to create data encryption keys, and to explicitly encrypt and
33/// decrypt values when auto-encryption is not an option.
34pub struct ClientEncryption {
35    crypt: Crypt,
36    exec: CryptExecutor,
37    key_vault: Collection<RawDocumentBuf>,
38}
39
40impl ClientEncryption {
41    /// Initialize a new `ClientEncryption`.
42    ///
43    /// ```no_run
44    /// # use mongocrypt::ctx::KmsProvider;
45    /// # use mongodb::{bson::doc, client_encryption::ClientEncryption, error::Result};
46    /// # fn func() -> Result<()> {
47    /// # let kv_client = todo!();
48    /// # let kv_namespace = todo!();
49    /// # let local_key = doc! { };
50    /// let enc = ClientEncryption::new(
51    ///     kv_client,
52    ///     kv_namespace,
53    ///     [
54    ///         (KmsProvider::local(), doc! { "key": local_key }, None),
55    ///         (KmsProvider::kmip(), doc! { "endpoint": "localhost:5698" }, None),
56    ///     ]
57    /// )?;
58    /// # Ok(())
59    /// # }
60    /// ```
61    pub fn new(
62        key_vault_client: Client,
63        key_vault_namespace: Namespace,
64        kms_providers: impl IntoIterator<
65            Item = (KmsProvider, crate::bson::Document, Option<TlsOptions>),
66        >,
67    ) -> Result<Self> {
68        Self::builder(key_vault_client, key_vault_namespace, kms_providers).build()
69    }
70
71    /// Initialize a builder to construct a [`ClientEncryption`]. Methods on
72    /// [`ClientEncryptionBuilder`] can be chained to set options.
73    ///
74    /// ```no_run
75    /// # use mongocrypt::ctx::KmsProvider;
76    /// # use mongodb::{bson::doc, client_encryption::ClientEncryption, error::Result};
77    /// # fn func() -> Result<()> {
78    /// # let kv_client = todo!();
79    /// # let kv_namespace = todo!();
80    /// # let local_key = doc! { };
81    /// let enc = ClientEncryption::builder(
82    ///     kv_client,
83    ///     kv_namespace,
84    ///     [
85    ///         (KmsProvider::local(), doc! { "key": local_key }, None),
86    ///         (KmsProvider::kmip(), doc! { "endpoint": "localhost:5698" }, None),
87    ///     ]
88    /// )
89    /// .build()?;
90    /// # Ok(())
91    /// # }
92    /// ```
93    pub fn builder(
94        key_vault_client: Client,
95        key_vault_namespace: Namespace,
96        kms_providers: impl IntoIterator<
97            Item = (KmsProvider, crate::bson::Document, Option<TlsOptions>),
98        >,
99    ) -> ClientEncryptionBuilder {
100        ClientEncryptionBuilder {
101            key_vault_client,
102            key_vault_namespace,
103            kms_providers: kms_providers.into_iter().collect(),
104            key_cache_expiration: None,
105        }
106    }
107
108    // pub async fn rewrap_many_data_key(&self, _filter: Document, _opts: impl
109    // Into<Option<RewrapManyDataKeyOptions>>) -> Result<RewrapManyDataKeyResult> {
110    // todo!("RUST-1441") }
111
112    /// Removes the key document with the given UUID (BSON binary subtype 0x04) from the key vault
113    /// collection. Returns the result of the internal deleteOne() operation on the key vault
114    /// collection.
115    pub async fn delete_key(&self, id: &Binary) -> Result<DeleteResult> {
116        self.key_vault.delete_one(doc! { "_id": id }).await
117    }
118
119    /// Finds a single key document with the given UUID (BSON binary subtype 0x04).
120    /// Returns the result of the internal find() operation on the key vault collection.
121    pub async fn get_key(&self, id: &Binary) -> Result<Option<RawDocumentBuf>> {
122        self.key_vault.find_one(doc! { "_id": id }).await
123    }
124
125    /// Finds all documents in the key vault collection.
126    /// Returns the result of the internal find() operation on the key vault collection.
127    pub async fn get_keys(&self) -> Result<Cursor<RawDocumentBuf>> {
128        self.key_vault.find(doc! {}).await
129    }
130
131    /// Adds a keyAltName to the keyAltNames array of the key document in the key vault collection
132    /// with the given UUID (BSON binary subtype 0x04). Returns the previous version of the key
133    /// document.
134    pub async fn add_key_alt_name(
135        &self,
136        id: &Binary,
137        key_alt_name: &str,
138    ) -> Result<Option<RawDocumentBuf>> {
139        self.key_vault
140            .find_one_and_update(
141                doc! { "_id": id },
142                doc! { "$addToSet": { "keyAltNames": key_alt_name } },
143            )
144            .await
145    }
146
147    /// Removes a keyAltName from the keyAltNames array of the key document in the key vault
148    /// collection with the given UUID (BSON binary subtype 0x04). Returns the previous version
149    /// of the key document.
150    pub async fn remove_key_alt_name(
151        &self,
152        id: &Binary,
153        key_alt_name: &str,
154    ) -> Result<Option<RawDocumentBuf>> {
155        let update = doc! {
156            "$set": {
157                "keyAltNames": {
158                    "$cond": [
159                        { "$eq": ["$keyAltNames", [key_alt_name]] },
160                        "$$REMOVE",
161                        {
162                            "$filter": {
163                                "input": "$keyAltNames",
164                                "cond": { "$ne": ["$$this", key_alt_name] },
165                            }
166                        }
167                    ]
168                }
169            }
170        };
171        self.key_vault
172            .find_one_and_update(doc! { "_id": id }, vec![update])
173            .await
174    }
175
176    /// Returns a key document in the key vault collection with the given keyAltName.
177    pub async fn get_key_by_alt_name(
178        &self,
179        key_alt_name: impl AsRef<str>,
180    ) -> Result<Option<RawDocumentBuf>> {
181        self.key_vault
182            .find_one(doc! { "keyAltNames": key_alt_name.as_ref() })
183            .await
184    }
185
186    /// Decrypts an encrypted value (BSON binary of subtype 6).
187    /// Returns the original BSON value.
188    pub async fn decrypt(&self, value: RawBinaryRef<'_>) -> Result<crate::bson::RawBson> {
189        if value.subtype != BinarySubtype::Encrypted {
190            return Err(Error::invalid_argument(format!(
191                "Invalid binary subtype for decrypt: expected {:?}, got {:?}",
192                BinarySubtype::Encrypted,
193                value.subtype
194            )));
195        }
196        let ctx = self
197            .crypt
198            .ctx_builder()
199            .build_explicit_decrypt(value.bytes)?;
200        let result = self.exec.run_ctx(ctx, None).await?;
201        Ok(result
202            .get("v")?
203            .ok_or_else(|| Error::internal("invalid decryption result"))?
204            .to_raw_bson())
205    }
206}
207
208/// Builder for constructing a [`ClientEncryption`]. Construct by calling
209/// [`ClientEncryption::builder`].
210pub struct ClientEncryptionBuilder {
211    key_vault_client: Client,
212    key_vault_namespace: Namespace,
213    kms_providers: Vec<(KmsProvider, crate::bson::Document, Option<TlsOptions>)>,
214    key_cache_expiration: Option<Duration>,
215}
216
217impl ClientEncryptionBuilder {
218    /// Set the duration of time after which the data encryption key cache should expire. Defaults
219    /// to 60 seconds if unset.
220    pub fn key_cache_expiration(mut self, expiration: impl Into<Option<Duration>>) -> Self {
221        self.key_cache_expiration = expiration.into();
222        self
223    }
224
225    /// Build the [`ClientEncryption`].
226    pub fn build(self) -> Result<ClientEncryption> {
227        let kms_providers = KmsProviders::new(self.kms_providers)?;
228
229        let mut crypt_builder = Crypt::builder()
230            .kms_providers(&kms_providers.credentials_doc()?)?
231            .use_need_kms_credentials_state()
232            .use_range_v2()?
233            .retry_kms(true)?;
234        if let Some(key_cache_expiration) = self.key_cache_expiration {
235            let expiration_ms: u64 = key_cache_expiration.as_millis().try_into().map_err(|_| {
236                Error::invalid_argument(format!(
237                    "key_cache_expiration must not exceed {} milliseconds, got {:?}",
238                    u64::MAX,
239                    key_cache_expiration
240                ))
241            })?;
242            crypt_builder = crypt_builder.key_cache_expiration(expiration_ms)?;
243        }
244        let crypt = crypt_builder.build()?;
245
246        let exec = CryptExecutor::new_explicit(
247            self.key_vault_client.weak(),
248            self.key_vault_namespace.clone(),
249            kms_providers,
250        )?;
251        let key_vault = self
252            .key_vault_client
253            .database(&self.key_vault_namespace.db)
254            .collection_with_options(
255                &self.key_vault_namespace.coll,
256                CollectionOptions::builder()
257                    .write_concern(WriteConcern::majority())
258                    .read_concern(ReadConcern::majority())
259                    .build(),
260            );
261
262        Ok(ClientEncryption {
263            crypt,
264            exec,
265            key_vault,
266        })
267    }
268}
269
270/// A KMS-specific key used to encrypt data keys.
271#[derive(Debug, Clone, Serialize, Deserialize)]
272#[serde(untagged)]
273#[non_exhaustive]
274#[allow(missing_docs)]
275pub enum MasterKey {
276    Aws(AwsMasterKey),
277    Azure(AzureMasterKey),
278    Gcp(GcpMasterKey),
279    Kmip(KmipMasterKey),
280    Local(LocalMasterKey),
281}
282
283/// An AWS master key.
284#[serde_with::skip_serializing_none]
285#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
286#[builder(field_defaults(default, setter(into)))]
287#[serde(rename_all = "camelCase")]
288#[non_exhaustive]
289pub struct AwsMasterKey {
290    /// The name for the key. The value for this field must be the same as the corresponding
291    /// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name.
292    #[serde(skip)]
293    pub name: Option<String>,
294
295    /// The region.
296    pub region: String,
297
298    /// The Amazon Resource Name (ARN) to the AWS customer master key (CMK).
299    pub key: String,
300
301    /// An alternate host identifier to send KMS requests to. May include port number. Defaults to
302    /// "kms.\<region\>.amazonaws.com".
303    pub endpoint: Option<String>,
304}
305
306impl From<AwsMasterKey> for MasterKey {
307    fn from(aws_master_key: AwsMasterKey) -> Self {
308        Self::Aws(aws_master_key)
309    }
310}
311
312/// An Azure master key.
313#[serde_with::skip_serializing_none]
314#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
315#[builder(field_defaults(default, setter(into)))]
316#[serde(rename_all = "camelCase")]
317#[non_exhaustive]
318pub struct AzureMasterKey {
319    /// The name for the key. The value for this field must be the same as the corresponding
320    /// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name.
321    #[serde(skip)]
322    pub name: Option<String>,
323
324    /// Host with optional port. Example: "example.vault.azure.net".
325    pub key_vault_endpoint: String,
326
327    /// The key name.
328    pub key_name: String,
329
330    /// A specific version of the named key, defaults to using the key's primary version.
331    pub key_version: Option<String>,
332}
333
334impl From<AzureMasterKey> for MasterKey {
335    fn from(azure_master_key: AzureMasterKey) -> Self {
336        Self::Azure(azure_master_key)
337    }
338}
339
340/// A GCP master key.
341#[serde_with::skip_serializing_none]
342#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
343#[builder(field_defaults(default, setter(into)))]
344#[serde(rename_all = "camelCase")]
345#[non_exhaustive]
346pub struct GcpMasterKey {
347    /// The name for the key. The value for this field must be the same as the corresponding
348    /// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name.
349    #[serde(skip)]
350    pub name: Option<String>,
351
352    /// The project ID.
353    pub project_id: String,
354
355    /// The location.
356    pub location: String,
357
358    /// The key ring.
359    pub key_ring: String,
360
361    /// The key name.
362    pub key_name: String,
363
364    /// A specific version of the named key. Defaults to using the key's primary version.
365    pub key_version: Option<String>,
366
367    /// Host with optional port. Defaults to "cloudkms.googleapis.com".
368    pub endpoint: Option<String>,
369}
370
371impl From<GcpMasterKey> for MasterKey {
372    fn from(gcp_master_key: GcpMasterKey) -> Self {
373        Self::Gcp(gcp_master_key)
374    }
375}
376
377/// A local master key.
378#[serde_with::skip_serializing_none]
379#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
380#[builder(field_defaults(default, setter(into)))]
381#[serde(rename_all = "camelCase")]
382#[non_exhaustive]
383pub struct LocalMasterKey {
384    /// The name for the key. The value for this field must be the same as the corresponding
385    /// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name.
386    #[serde(skip)]
387    pub name: Option<String>,
388}
389
390impl From<LocalMasterKey> for MasterKey {
391    fn from(local_master_key: LocalMasterKey) -> Self {
392        Self::Local(local_master_key)
393    }
394}
395
396/// A KMIP master key.
397#[serde_with::skip_serializing_none]
398#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
399#[builder(field_defaults(default, setter(into)))]
400#[serde(rename_all = "camelCase")]
401#[non_exhaustive]
402pub struct KmipMasterKey {
403    /// The name for the key. The value for this field must be the same as the corresponding
404    /// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name.
405    #[serde(skip)]
406    pub name: Option<String>,
407
408    /// The KMIP Unique Identifier to a 96 byte KMIP Secret Data managed object. If this field is
409    /// not specified, the driver creates a random 96 byte KMIP Secret Data managed object.
410    pub key_id: Option<String>,
411
412    /// If true (recommended), the KMIP server must decrypt this key. Defaults to false.
413    pub delegated: Option<bool>,
414
415    /// Host with optional port.
416    pub endpoint: Option<String>,
417}
418
419impl From<KmipMasterKey> for MasterKey {
420    fn from(kmip_master_key: KmipMasterKey) -> Self {
421        Self::Kmip(kmip_master_key)
422    }
423}
424
425impl MasterKey {
426    /// Returns the `KmsProvider` associated with this key.
427    pub fn provider(&self) -> KmsProvider {
428        let (provider, name) = match self {
429            MasterKey::Aws(AwsMasterKey { name, .. }) => (KmsProvider::aws(), name.clone()),
430            MasterKey::Azure(AzureMasterKey { name, .. }) => (KmsProvider::azure(), name.clone()),
431            MasterKey::Gcp(GcpMasterKey { name, .. }) => (KmsProvider::gcp(), name.clone()),
432            MasterKey::Kmip(KmipMasterKey { name, .. }) => (KmsProvider::kmip(), name.clone()),
433            MasterKey::Local(LocalMasterKey { name, .. }) => (KmsProvider::local(), name.clone()),
434        };
435        if let Some(name) = name {
436            provider.with_name(name)
437        } else {
438            provider
439        }
440    }
441}
442
443// #[non_exhaustive]
444// pub struct RewrapManyDataKeyOptions {
445// pub provider: KmsProvider,
446// pub master_key: Option<Document>,
447// }
448//
449//
450// #[non_exhaustive]
451// pub struct RewrapManyDataKeyResult {
452// pub bulk_write_result: Option<BulkWriteResult>,
453// }