Skip to main content

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