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