diff --git a/src/action/csfle/create_data_key.rs b/src/action/csfle/create_data_key.rs index 912ade95f..bc5d21bd1 100644 --- a/src/action/csfle/create_data_key.rs +++ b/src/action/csfle/create_data_key.rs @@ -8,10 +8,10 @@ impl ClientEncryption { /// `await` will return d[`Result`] (subtype 0x04) with the _id of the created /// document as a UUID. #[deeplink] - pub fn create_data_key(&self, master_key: MasterKey) -> CreateDataKey { + pub fn create_data_key(&self, master_key: impl Into) -> CreateDataKey { CreateDataKey { client_enc: self, - master_key, + master_key: master_key.into(), options: None, #[cfg(test)] test_kms_provider: None, diff --git a/src/client/csfle/client_encryption.rs b/src/client/csfle/client_encryption.rs index 9b5999faa..483177cd6 100644 --- a/src/client/csfle/client_encryption.rs +++ b/src/client/csfle/client_encryption.rs @@ -5,6 +5,7 @@ mod encrypt; use mongocrypt::{ctx::KmsProvider, Crypt}; use serde::{Deserialize, Serialize}; +use typed_builder::TypedBuilder; use crate::{ bson::{doc, spec::BinarySubtype, Binary, RawBinaryRef, RawDocumentBuf}, @@ -187,63 +188,174 @@ impl ClientEncryption { } /// A KMS-specific key used to encrypt data keys. -#[serde_with::skip_serializing_none] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] #[non_exhaustive] #[allow(missing_docs)] pub enum MasterKey { - #[serde(rename_all = "camelCase")] - Aws { - region: String, - /// The Amazon Resource Name (ARN) to the AWS customer master key (CMK). - key: String, - /// An alternate host identifier to send KMS requests to. May include port number. Defaults - /// to "kms.REGION.amazonaws.com" - endpoint: Option, - }, - #[serde(rename_all = "camelCase")] - Azure { - /// Host with optional port. Example: "example.vault.azure.net". - key_vault_endpoint: String, - key_name: String, - /// A specific version of the named key, defaults to using the key's primary version. - key_version: Option, - }, - #[serde(rename_all = "camelCase")] - Gcp { - project_id: String, - location: String, - key_ring: String, - key_name: String, - /// A specific version of the named key, defaults to using the key's primary version. - key_version: Option, - /// Host with optional port. Defaults to "cloudkms.googleapis.com". - endpoint: Option, - }, - /// Master keys are not applicable to `KmsProvider::Local`. - Local, - #[serde(rename_all = "camelCase")] - Kmip { - /// keyId is the KMIP Unique Identifier to a 96 byte KMIP Secret Data managed object. If - /// keyId is omitted, the driver creates a random 96 byte KMIP Secret Data managed object. - key_id: Option, - /// If true (recommended), the KMIP server must decrypt this key. Defaults to false. - delegated: Option, - /// Host with optional port. - endpoint: Option, - }, + Aws(AwsMasterKey), + Azure(AzureMasterKey), + Gcp(GcpMasterKey), + Kmip(KmipMasterKey), + Local(LocalMasterKey), +} + +/// An AWS master key. +#[serde_with::skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)] +#[builder(field_defaults(default, setter(into)))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct AwsMasterKey { + /// The name for the key. The value for this field must be the same as the corresponding + /// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name. + #[serde(skip)] + pub name: Option, + + /// The region. + pub region: String, + + /// The Amazon Resource Name (ARN) to the AWS customer master key (CMK). + pub key: String, + + /// An alternate host identifier to send KMS requests to. May include port number. Defaults to + /// "kms.\.amazonaws.com". + pub endpoint: Option, +} + +impl From for MasterKey { + fn from(aws_master_key: AwsMasterKey) -> Self { + Self::Aws(aws_master_key) + } +} + +/// An Azure master key. +#[serde_with::skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)] +#[builder(field_defaults(default, setter(into)))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct AzureMasterKey { + /// The name for the key. The value for this field must be the same as the corresponding + /// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name. + #[serde(skip)] + pub name: Option, + + /// Host with optional port. Example: "example.vault.azure.net". + pub key_vault_endpoint: String, + + /// The key name. + pub key_name: String, + + /// A specific version of the named key, defaults to using the key's primary version. + pub key_version: Option, +} + +impl From for MasterKey { + fn from(azure_master_key: AzureMasterKey) -> Self { + Self::Azure(azure_master_key) + } +} + +/// A GCP master key. +#[serde_with::skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)] +#[builder(field_defaults(default, setter(into)))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct GcpMasterKey { + /// The name for the key. The value for this field must be the same as the corresponding + /// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name. + #[serde(skip)] + pub name: Option, + + /// The project ID. + pub project_id: String, + + /// The location. + pub location: String, + + /// The key ring. + pub key_ring: String, + + /// The key name. + pub key_name: String, + + /// A specific version of the named key. Defaults to using the key's primary version. + pub key_version: Option, + + /// Host with optional port. Defaults to "cloudkms.googleapis.com". + pub endpoint: Option, +} + +impl From for MasterKey { + fn from(gcp_master_key: GcpMasterKey) -> Self { + Self::Gcp(gcp_master_key) + } +} + +/// A local master key. +#[serde_with::skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)] +#[builder(field_defaults(default, setter(into)))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct LocalMasterKey { + /// The name for the key. The value for this field must be the same as the corresponding + /// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name. + #[serde(skip)] + pub name: Option, +} + +impl From for MasterKey { + fn from(local_master_key: LocalMasterKey) -> Self { + Self::Local(local_master_key) + } +} + +/// A KMIP master key. +#[serde_with::skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)] +#[builder(field_defaults(default, setter(into)))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct KmipMasterKey { + /// The name for the key. The value for this field must be the same as the corresponding + /// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name. + #[serde(skip)] + pub name: Option, + + /// The KMIP Unique Identifier to a 96 byte KMIP Secret Data managed object. If this field is + /// not specified, the driver creates a random 96 byte KMIP Secret Data managed object. + pub key_id: Option, + + /// If true (recommended), the KMIP server must decrypt this key. Defaults to false. + pub delegated: Option, + + /// Host with optional port. + pub endpoint: Option, +} + +impl From for MasterKey { + fn from(kmip_master_key: KmipMasterKey) -> Self { + Self::Kmip(kmip_master_key) + } } impl MasterKey { /// Returns the `KmsProvider` associated with this key. pub fn provider(&self) -> KmsProvider { - match self { - MasterKey::Aws { .. } => KmsProvider::Aws, - MasterKey::Azure { .. } => KmsProvider::Azure, - MasterKey::Gcp { .. } => KmsProvider::Gcp, - MasterKey::Kmip { .. } => KmsProvider::Kmip, - MasterKey::Local => KmsProvider::Local, + let (provider, name) = match self { + MasterKey::Aws(AwsMasterKey { name, .. }) => (KmsProvider::aws(), name.clone()), + MasterKey::Azure(AzureMasterKey { name, .. }) => (KmsProvider::azure(), name.clone()), + MasterKey::Gcp(GcpMasterKey { name, .. }) => (KmsProvider::gcp(), name.clone()), + MasterKey::Kmip(KmipMasterKey { name, .. }) => (KmsProvider::kmip(), name.clone()), + MasterKey::Local(LocalMasterKey { name, .. }) => (KmsProvider::local(), name.clone()), + }; + if let Some(name) = name { + provider.with_name(name) + } else { + provider } } } diff --git a/src/client/csfle/client_encryption/create_data_key.rs b/src/client/csfle/client_encryption/create_data_key.rs index 04d6fdfeb..5ecb3da3f 100644 --- a/src/client/csfle/client_encryption/create_data_key.rs +++ b/src/client/csfle/client_encryption/create_data_key.rs @@ -42,8 +42,8 @@ impl ClientEncryption { opts: Option, ) -> Result { let mut builder = self.crypt.ctx_builder(); - let mut key_doc = doc! { "provider": kms_provider.name() }; - if !matches!(master_key, MasterKey::Local) { + let mut key_doc = doc! { "provider": kms_provider.as_string() }; + if !matches!(master_key, MasterKey::Local(_)) { let master_doc = bson::to_document(&master_key)?; key_doc.extend(master_doc); } diff --git a/src/client/csfle/options.rs b/src/client/csfle/options.rs index 711cb5924..b63762b66 100644 --- a/src/client/csfle/options.rs +++ b/src/client/csfle/options.rs @@ -130,6 +130,49 @@ impl KmsProviders { &self.credentials } + #[cfg(test)] + pub(crate) fn set_test_options(&mut self) { + use mongocrypt::ctx::KmsProviderType; + + use crate::{ + bson::doc, + test::csfle::{ALL_KMS_PROVIDERS, AWS_KMS}, + }; + + let all_kms_providers = ALL_KMS_PROVIDERS.clone(); + for (provider, test_credentials, tls_options) in all_kms_providers { + if self.credentials.contains_key(&provider) + && !matches!(provider.provider_type(), KmsProviderType::Local) + { + self.set(provider, test_credentials, tls_options); + } + } + + let aws_temp_provider = KmsProvider::other("awsTemporary".to_string()); + if self.credentials.contains_key(&aws_temp_provider) { + let aws_credentials = doc! { + "accessKeyId": std::env::var("CSFLE_AWS_TEMP_ACCESS_KEY_ID").unwrap(), + "secretAccessKey": std::env::var("CSFLE_AWS_TEMP_SECRET_ACCESS_KEY").unwrap(), + "sessionToken": std::env::var("CSFLE_AWS_TEMP_SESSION_TOKEN").unwrap() + }; + self.set(KmsProvider::aws(), aws_credentials, AWS_KMS.clone().2); + self.clear(&aws_temp_provider); + } + + let aws_temp_no_session_token_provider = KmsProvider::other("awsTemporaryNoSessionToken"); + if self + .credentials + .contains_key(&aws_temp_no_session_token_provider) + { + let aws_credentials = doc! { + "accessKeyId": std::env::var("CSFLE_AWS_TEMP_ACCESS_KEY_ID").unwrap(), + "secretAccessKey": std::env::var("CSFLE_AWS_TEMP_SECRET_ACCESS_KEY").unwrap(), + }; + self.set(KmsProvider::aws(), aws_credentials, AWS_KMS.clone().2); + self.clear(&aws_temp_no_session_token_provider); + } + } + #[cfg(test)] pub(crate) fn set(&mut self, provider: KmsProvider, creds: Document, tls: Option) { self.credentials.insert(provider.clone(), creds); diff --git a/src/client/csfle/state_machine.rs b/src/client/csfle/state_machine.rs index bc7aa5f05..67cb296ec 100644 --- a/src/client/csfle/state_machine.rs +++ b/src/client/csfle/state_machine.rs @@ -6,7 +6,7 @@ use std::{ use bson::{rawdoc, Document, RawDocument, RawDocumentBuf}; use futures_util::{stream, TryStreamExt}; -use mongocrypt::ctx::{Ctx, KmsProvider, State}; +use mongocrypt::ctx::{Ctx, KmsProviderType, State}; use rayon::ThreadPool; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, @@ -211,97 +211,110 @@ impl CryptExecutor { #[allow(unused_mut)] let mut kms_providers = rawdoc! {}; let credentials = self.kms_providers.credentials(); - if credentials - .get(&KmsProvider::Aws) - .map_or(false, Document::is_empty) - { - #[cfg(feature = "aws-auth")] - { - let aws_creds = crate::client::auth::aws::AwsCredential::get( - &crate::client::auth::Credential::default(), - &crate::runtime::HttpClient::default(), - ) - .await?; - let mut creds = rawdoc! { - "accessKeyId": aws_creds.access_key(), - "secretAccessKey": aws_creds.secret_key(), - }; - if let Some(token) = aws_creds.session_token() { - creds.append("sessionToken", token); - } - kms_providers.append("aws", creds); - } - #[cfg(not(feature = "aws-auth"))] - { - return Err(Error::invalid_argument( - "On-demand AWS KMS credentials require the `aws-auth` feature.", - )); - } - } - if credentials - .get(&KmsProvider::Azure) - .map_or(false, Document::is_empty) - { - #[cfg(feature = "azure-kms")] - { - kms_providers.append("azure", self.azure.get_token().await?); - } - #[cfg(not(feature = "azure-kms"))] - { - return Err(Error::invalid_argument( - "On-demand Azure KMS credentials require the `azure-kms` feature.", - )); + for (provider, options) in credentials { + if !options.is_empty() { + continue; } - } - if credentials - .get(&KmsProvider::Gcp) - .map_or(false, Document::is_empty) - { - #[cfg(feature = "gcp-kms")] - { - use crate::runtime::HttpClient; - use serde::Deserialize; - - #[derive(Deserialize)] - struct ResponseBody { - access_token: String, - } - fn kms_error(error: String) -> Error { - let message = format!( - "An error occurred when obtaining GCP credentials: {}", - error - ); - let error = mongocrypt::error::Error { - kind: mongocrypt::error::ErrorKind::Kms, - message: Some(message), - code: None, - }; - error.into() + match provider.provider_type() { + KmsProviderType::Aws => { + #[cfg(feature = "aws-auth")] + { + use crate::{ + client::auth::{aws::AwsCredential, Credential}, + runtime::HttpClient, + }; + + let aws_creds = AwsCredential::get( + &Credential::default(), + &HttpClient::default(), + ) + .await?; + let mut creds = rawdoc! { + "accessKeyId": aws_creds.access_key(), + "secretAccessKey": aws_creds.secret_key(), + }; + if let Some(token) = aws_creds.session_token() { + creds.append("sessionToken", token); + } + kms_providers.append(provider.as_string(), creds); + } + #[cfg(not(feature = "aws-auth"))] + { + return Err(Error::invalid_argument( + "On-demand AWS KMS credentials require the `aws-auth` \ + feature.", + )); + } } + KmsProviderType::Azure => { + #[cfg(feature = "azure-kms")] + { + kms_providers.append( + provider.as_string(), + self.azure.get_token().await?, + ); + } + #[cfg(not(feature = "azure-kms"))] + { + return Err(Error::invalid_argument( + "On-demand Azure KMS credentials require the `azure-kms` \ + feature.", + )); + } + } + KmsProviderType::Gcp => { + #[cfg(feature = "gcp-kms")] + { + use crate::runtime::HttpClient; + use serde::Deserialize; + + #[derive(Deserialize)] + struct ResponseBody { + access_token: String, + } - let http_client = HttpClient::default(); - let host = std::env::var("GCE_METADATA_HOST") - .unwrap_or_else(|_| "metadata.google.internal".into()); - let uri = format!( - "http://{}/computeMetadata/v1/instance/service-accounts/default/token", - host - ); - - let response: ResponseBody = http_client - .get(&uri) - .headers(&[("Metadata-Flavor", "Google")]) - .send() - .await - .map_err(|e| kms_error(e.to_string()))?; - kms_providers - .append("gcp", rawdoc! { "accessToken": response.access_token }); - } - #[cfg(not(feature = "gcp-kms"))] - { - return Err(Error::invalid_argument( - "On-demand GCP KMS credentials require the `gcp-kms` feature.", - )); + fn kms_error(error: String) -> Error { + let message = format!( + "An error occurred when obtaining GCP credentials: {}", + error + ); + let error = mongocrypt::error::Error { + kind: mongocrypt::error::ErrorKind::Kms, + message: Some(message), + code: None, + }; + error.into() + } + + let http_client = HttpClient::default(); + let host = std::env::var("GCE_METADATA_HOST") + .unwrap_or_else(|_| "metadata.google.internal".into()); + let uri = format!( + "http://{}/computeMetadata/v1/instance/service-accounts/default/token", + host + ); + + let response: ResponseBody = http_client + .get(&uri) + .headers(&[("Metadata-Flavor", "Google")]) + .send() + .await + .map_err(|e| kms_error(e.to_string()))?; + kms_providers.append( + "gcp", + rawdoc! { "accessToken": response.access_token }, + ); + } + #[cfg(not(feature = "gcp-kms"))] + { + return Err(Error::invalid_argument( + "On-demand GCP KMS credentials require the `gcp-kms` \ + feature.", + )); + } + } + _ => {} } } ctx.provide_kms_providers(&kms_providers)?; diff --git a/src/test.rs b/src/test.rs index de1887bc7..e46c9209f 100644 --- a/src/test.rs +++ b/src/test.rs @@ -17,7 +17,7 @@ mod coll; ))] mod compression; #[cfg(feature = "in-use-encryption-unstable")] -mod csfle; +pub(crate) mod csfle; mod cursor; mod db; mod documentation_examples; @@ -27,9 +27,6 @@ pub(crate) mod spec; mod timeseries; pub(crate) mod util; -#[cfg(feature = "in-use-encryption-unstable")] -pub(crate) use self::csfle::{KmsProviderList, KMS_PROVIDERS_MAP}; - pub(crate) use self::{ spec::{run_spec_test, RunOn, Serverless, Topology}, util::{ diff --git a/src/test/bulk_write.rs b/src/test/bulk_write.rs index 3c29bc8c3..def92f421 100644 --- a/src/test/bulk_write.rs +++ b/src/test/bulk_write.rs @@ -538,7 +538,7 @@ async fn encryption_error() { }; let kms_providers = KmsProviders::new(vec![( - KmsProvider::Aws, + KmsProvider::aws(), doc! { "accessKeyId": "foo", "secretAccessKey": "bar" }, None, )]) diff --git a/src/test/csfle.rs b/src/test/csfle.rs index 651efa2a2..42804d955 100644 --- a/src/test/csfle.rs +++ b/src/test/csfle.rs @@ -1,5 +1,6 @@ use std::{ - collections::{BTreeMap, HashMap}, + collections::BTreeMap, + env::var, path::PathBuf, sync::{ atomic::{AtomicBool, Ordering}, @@ -22,13 +23,23 @@ use bson::{ RawDocumentBuf, }; use futures_util::TryStreamExt; -use mongocrypt::ctx::{Algorithm, KmsProvider}; +use mongocrypt::ctx::{Algorithm, KmsProvider, KmsProviderType}; use once_cell::sync::Lazy; use tokio::net::TcpListener; use crate::{ action::Action, - client_encryption::{ClientEncryption, EncryptKey, MasterKey, RangeOptions}, + client_encryption::{ + AwsMasterKey, + AzureMasterKey, + ClientEncryption, + EncryptKey, + GcpMasterKey, + KmipMasterKey, + LocalMasterKey, + MasterKey, + RangeOptions, + }, error::{ErrorKind, WriteError, WriteFailure}, event::{ command::{CommandFailedEvent, CommandStartedEvent, CommandSucceededEvent}, @@ -81,84 +92,125 @@ async fn init_client() -> Result<(EventClient, Collection)> { Ok((client, datakeys)) } -pub(crate) type KmsProviderList = Vec<(KmsProvider, bson::Document, Option)>; +pub(crate) type KmsInfo = (KmsProvider, Document, Option); +pub(crate) type KmsProviderList = Vec; -static KMS_PROVIDERS: Lazy = Lazy::new(|| { - fn env(name: &str) -> String { - std::env::var(name).unwrap() - } - vec![ - ( - KmsProvider::Aws, - doc! { - "accessKeyId": env("FLE_AWS_KEY"), - "secretAccessKey": env("FLE_AWS_SECRET"), - }, - None, - ), - ( - KmsProvider::Azure, - doc! { - "tenantId": env("FLE_AZURE_TENANTID"), - "clientId": env("FLE_AZURE_CLIENTID"), - "clientSecret": env("FLE_AZURE_CLIENTSECRET"), - }, - None, - ), - ( - KmsProvider::Gcp, - doc! { - "email": env("FLE_GCP_EMAIL"), - "privateKey": env("FLE_GCP_PRIVATEKEY"), - }, - None, - ), - ( - KmsProvider::Local, - doc! { - "key": bson::Binary { - subtype: bson::spec::BinarySubtype::Generic, - bytes: base64::decode(env("CSFLE_LOCAL_KEY")).unwrap(), - }, - }, - None, - ), - ( - KmsProvider::Kmip, - doc! { - "endpoint": "localhost:5698", - }, - { - let cert_dir = PathBuf::from(env("CSFLE_TLS_CERT_DIR")); - Some( - TlsOptions::builder() - .ca_file_path(cert_dir.join("ca.pem")) - .cert_key_file_path(cert_dir.join("client.pem")) - .build(), - ) +fn add_name_to_info(kms_info: KmsInfo, name: &str) -> KmsInfo { + (kms_info.0.with_name(name), kms_info.1, kms_info.2) +} + +pub(crate) static AWS_KMS: Lazy = Lazy::new(|| { + ( + KmsProvider::aws(), + doc! { + "accessKeyId": var("FLE_AWS_KEY").unwrap(), + "secretAccessKey": var("FLE_AWS_SECRET").unwrap()}, + None, + ) +}); +pub(crate) static AWS_KMS_NAME1: Lazy = Lazy::new(|| { + let aws_info = AWS_KMS.clone(); + (aws_info.0.with_name("name1"), aws_info.1, aws_info.2) +}); +pub(crate) static AWS_KMS_NAME2: Lazy = Lazy::new(|| { + ( + KmsProvider::aws().with_name("name2"), + doc! { + "accessKeyId": var("FLE_AWS_KEY").unwrap(), + "secretAccessKey": var("FLE_AWS_SECRET").unwrap() + }, + None, + ) +}); +pub(crate) static AZURE_KMS: Lazy = Lazy::new(|| { + ( + KmsProvider::azure(), + doc! { + "tenantId": var("FLE_AZURE_TENANTID").unwrap(), + "clientId": var("FLE_AZURE_CLIENTID").unwrap(), + "clientSecret": var("FLE_AZURE_CLIENTSECRET").unwrap(), + }, + None, + ) +}); +pub(crate) static AZURE_KMS_NAME1: Lazy = Lazy::new(|| { + let azure_info = AZURE_KMS.clone(); + (azure_info.0.with_name("name1"), azure_info.1, azure_info.2) +}); +pub(crate) static GCP_KMS: Lazy = Lazy::new(|| { + ( + KmsProvider::gcp(), + doc! { + "email": var("FLE_GCP_EMAIL").unwrap(), + "privateKey": var("FLE_GCP_PRIVATEKEY").unwrap(), + }, + None, + ) +}); +pub(crate) static GCP_KMS_NAME1: Lazy = Lazy::new(|| { + let gcp_info = GCP_KMS.clone(); + (gcp_info.0.with_name("name1"), gcp_info.1, gcp_info.2) +}); +pub(crate) static LOCAL_KMS: Lazy = Lazy::new(|| { + ( + KmsProvider::local(), + doc! { + "key": bson::Binary { + subtype: bson::spec::BinarySubtype::Generic, + bytes: base64::decode(var("CSFLE_LOCAL_KEY").unwrap()).unwrap(), }, - ), + }, + None, + ) +}); +pub(crate) static LOCAL_KMS_NAME1: Lazy = Lazy::new(|| { + let local_info = LOCAL_KMS.clone(); + (local_info.0.with_name("name1"), local_info.1, local_info.2) +}); +pub(crate) static KMIP_KMS: Lazy = Lazy::new(|| { + let cert_dir = PathBuf::from(var("CSFLE_TLS_CERT_DIR").unwrap()); + let tls_options = TlsOptions::builder() + .ca_file_path(cert_dir.join("ca.pem")) + .cert_key_file_path(cert_dir.join("client.pem")) + .build(); + ( + KmsProvider::kmip(), + doc! { + "endpoint": "localhost:5698", + }, + Some(tls_options), + ) +}); +pub(crate) static KMIP_KMS_NAME1: Lazy = Lazy::new(|| { + let kmip_info = KMIP_KMS.clone(); + (kmip_info.0.with_name("name1"), kmip_info.1, kmip_info.2) +}); + +pub(crate) static UNNAMED_KMS_PROVIDERS: Lazy = Lazy::new(|| { + vec![ + AWS_KMS.clone(), + AZURE_KMS.clone(), + GCP_KMS.clone(), + LOCAL_KMS.clone(), + KMIP_KMS.clone(), ] }); -static LOCAL_KMS: Lazy = Lazy::new(|| { - KMS_PROVIDERS - .iter() - .filter(|(p, ..)| p == &KmsProvider::Local) - .cloned() - .collect() +pub(crate) static NAME1_KMS_PROVIDERS: Lazy = Lazy::new(|| { + vec![ + AWS_KMS_NAME1.clone(), + AZURE_KMS_NAME1.clone(), + GCP_KMS_NAME1.clone(), + LOCAL_KMS_NAME1.clone(), + KMIP_KMS_NAME1.clone(), + ] }); -pub(crate) static KMS_PROVIDERS_MAP: Lazy< - HashMap< - mongocrypt::ctx::KmsProvider, - (bson::Document, Option), - >, -> = Lazy::new(|| { - let mut map = HashMap::new(); - for (prov, conf, tls) in KMS_PROVIDERS.clone() { - map.insert(prov, (conf, tls)); - } - map +pub(crate) static ALL_KMS_PROVIDERS: Lazy = Lazy::new(|| { + let mut providers = UNNAMED_KMS_PROVIDERS.clone(); + providers.extend(NAME1_KMS_PROVIDERS.clone()); + providers.push(AWS_KMS_NAME2.clone()); + providers }); + static EXTRA_OPTIONS: Lazy = Lazy::new(|| doc! { "cryptSharedLibPath": std::env::var("CRYPT_SHARED_LIB_PATH").unwrap() }); static KV_NAMESPACE: Lazy = @@ -196,7 +248,7 @@ async fn custom_key_material() -> Result<()> { let enc = ClientEncryption::new( client.into_client(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )?; let key = base64::decode( @@ -205,7 +257,7 @@ async fn custom_key_material() -> Result<()> { ) .unwrap(); let id = enc - .create_data_key(MasterKey::Local) + .create_data_key(LocalMasterKey::builder().build()) .key_material(key) .await?; let mut key_doc = datakeys @@ -259,7 +311,7 @@ async fn data_key_double_encryption() -> Result<()> { let client_encrypted = Client::encrypted_builder( get_client_options().await.clone(), KV_NAMESPACE.clone(), - KMS_PROVIDERS.clone(), + UNNAMED_KMS_PROVIDERS.clone(), )? .schema_map(schema_map) .extra_options(EXTRA_OPTIONS.clone()) @@ -271,56 +323,50 @@ async fn data_key_double_encryption() -> Result<()> { let client_encryption = ClientEncryption::new( client.clone().into_client(), KV_NAMESPACE.clone(), - KMS_PROVIDERS.clone(), + UNNAMED_KMS_PROVIDERS.clone(), )?; // Testing each provider: let mut events = client.events.subscribe(); - let provider_keys = [ + let provider_keys: [(KmsProvider, MasterKey); 5] = [ ( - KmsProvider::Aws, - MasterKey::Aws { - region: "us-east-1".to_string(), - key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" - .to_string(), - endpoint: None, - }, + KmsProvider::aws(), + AwsMasterKey::builder() + .region("us-east-1") + .key("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0") + .build() + .into(), ), ( - KmsProvider::Azure, - MasterKey::Azure { - key_vault_endpoint: "key-vault-csfle.vault.azure.net".to_string(), - key_name: "key-name-csfle".to_string(), - key_version: None, - }, + KmsProvider::azure(), + AzureMasterKey::builder() + .key_vault_endpoint("key-vault-csfle.vault.azure.net") + .key_name("key-name-csfle") + .build() + .into(), ), ( - KmsProvider::Gcp, - MasterKey::Gcp { - project_id: "devprod-drivers".to_string(), - location: "global".to_string(), - key_ring: "key-ring-csfle".to_string(), - key_name: "key-name-csfle".to_string(), - key_version: None, - endpoint: None, - }, + KmsProvider::gcp(), + GcpMasterKey::builder() + .project_id("devprod-drivers") + .location("global") + .key_ring("key-ring-csfle") + .key_name("key-name-csfle") + .build() + .into(), ), - (KmsProvider::Local, MasterKey::Local), ( - KmsProvider::Kmip, - MasterKey::Kmip { - key_id: None, - delegated: None, - endpoint: None, - }, + KmsProvider::local(), + LocalMasterKey::builder().build().into(), ), + (KmsProvider::kmip(), KmipMasterKey::builder().build().into()), ]; for (provider, master_key) in provider_keys { // Create a data key let datakey_id = client_encryption .create_data_key(master_key) - .key_alt_names([format!("{}_altname", provider.name())]) + .key_alt_names([format!("{}_altname", provider.as_string())]) .await?; assert_eq!(datakey_id.subtype, BinarySubtype::Uuid); let docs: Vec<_> = client @@ -333,7 +379,7 @@ async fn data_key_double_encryption() -> Result<()> { assert_eq!(docs.len(), 1); assert_eq!( docs[0].get_document("masterKey")?.get_str("provider")?, - provider.name() + provider.as_string() ); let found = events .wait_for_event( @@ -364,7 +410,7 @@ async fn data_key_double_encryption() -> Result<()> { // Manually encrypt a value and automatically decrypt it. let encrypted = client_encryption .encrypt( - format!("hello {}", provider.name()), + format!("hello {}", provider.as_string()), EncryptKey::Id(datakey_id), Algorithm::Deterministic, ) @@ -373,19 +419,19 @@ async fn data_key_double_encryption() -> Result<()> { let coll = client_encrypted .database("db") .collection::("coll"); - coll.insert_one(doc! { "_id": provider.name(), "value": encrypted.clone() }) + coll.insert_one(doc! { "_id": provider.as_string(), "value": encrypted.clone() }) .await?; - let found = coll.find_one(doc! { "_id": provider.name() }).await?; + let found = coll.find_one(doc! { "_id": provider.as_string() }).await?; assert_eq!( found.as_ref().and_then(|doc| doc.get("value")), - Some(&Bson::String(format!("hello {}", provider.name()))), + Some(&Bson::String(format!("hello {}", provider.as_string()))), ); // Manually encrypt a value via key alt name. let other_encrypted = client_encryption .encrypt( - format!("hello {}", provider.name()), - EncryptKey::AltName(format!("{}_altname", provider.name())), + format!("hello {}", provider.as_string()), + EncryptKey::AltName(format!("{}_altname", provider.as_string())), Algorithm::Deterministic, ) .await?; @@ -451,7 +497,7 @@ async fn external_key_vault() -> Result<()> { let client_encrypted = Client::encrypted_builder( get_client_options().await.clone(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )? .key_vault_client(kv_client.clone()) .schema_map([("db.coll", load_testdata("external/external-schema.json")?)]) @@ -463,7 +509,7 @@ async fn external_key_vault() -> Result<()> { let client_encryption = ClientEncryption::new( kv_client.unwrap_or_else(|| client.into_client()), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )?; // Test: encrypted client. @@ -545,7 +591,7 @@ async fn bson_size_limits() -> Result<()> { let mut events = buffer.subscribe(); opts.command_event_handler = Some(buffer.handler()); let client_encrypted = - Client::encrypted_builder(opts, KV_NAMESPACE.clone(), LOCAL_KMS.clone())? + Client::encrypted_builder(opts, KV_NAMESPACE.clone(), vec![LOCAL_KMS.clone()])? .extra_options(EXTRA_OPTIONS.clone()) .disable_crypt_shared(*DISABLE_CRYPT_SHARED) .build() @@ -658,7 +704,7 @@ async fn views_prohibited() -> Result<()> { let client_encrypted = Client::encrypted_builder( get_client_options().await.clone(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )? .extra_options(EXTRA_OPTIONS.clone()) .disable_crypt_shared(*DISABLE_CRYPT_SHARED) @@ -754,7 +800,7 @@ async fn run_corpus_test(local_schema: bool) -> Result<()> { let mut enc_builder = Client::encrypted_builder( get_client_options().await.clone(), KV_NAMESPACE.clone(), - KMS_PROVIDERS.clone(), + UNNAMED_KMS_PROVIDERS.clone(), )? .extra_options(EXTRA_OPTIONS.clone()) .disable_crypt_shared(*DISABLE_CRYPT_SHARED); @@ -766,7 +812,7 @@ async fn run_corpus_test(local_schema: bool) -> Result<()> { let client_encryption = ClientEncryption::new( client.clone().into_client(), KV_NAMESPACE.clone(), - KMS_PROVIDERS.clone(), + UNNAMED_KMS_PROVIDERS.clone(), )?; // Test: build corpus. @@ -811,17 +857,17 @@ async fn run_corpus_test(local_schema: bool) -> Result<()> { "det" => Algorithm::Deterministic, s => return Err(failure!("Invalid algorithm {:?}", s)), }; - let kms = KmsProvider::from_name(subdoc.get_str("kms")?); + let kms = KmsProvider::from_string(subdoc.get_str("kms")?); let key = match subdoc.get_str("identifier")? { - "id" => EncryptKey::Id(base64_uuid(match kms { - KmsProvider::Local => "LOCALAAAAAAAAAAAAAAAAA==", - KmsProvider::Aws => "AWSAAAAAAAAAAAAAAAAAAA==", - KmsProvider::Azure => "AZUREAAAAAAAAAAAAAAAAA==", - KmsProvider::Gcp => "GCPAAAAAAAAAAAAAAAAAAA==", - KmsProvider::Kmip => "KMIPAAAAAAAAAAAAAAAAAA==", + "id" => EncryptKey::Id(base64_uuid(match kms.provider_type() { + KmsProviderType::Local => "LOCALAAAAAAAAAAAAAAAAA==", + KmsProviderType::Aws => "AWSAAAAAAAAAAAAAAAAAAA==", + KmsProviderType::Azure => "AZUREAAAAAAAAAAAAAAAAA==", + KmsProviderType::Gcp => "GCPAAAAAAAAAAAAAAAAAAA==", + KmsProviderType::Kmip => "KMIPAAAAAAAAAAAAAAAAAA==", _ => return Err(failure!("Invalid kms provider {:?}", kms)), })?), - "altname" => EncryptKey::AltName(kms.name().to_string()), + "altname" => EncryptKey::AltName(kms.as_string()), s => return Err(failure!("Invalid identifier {:?}", s)), }; let value: RawBson = subdoc @@ -913,8 +959,8 @@ async fn run_corpus_test(local_schema: bool) -> Result<()> { async fn custom_endpoint_setup(valid: bool) -> Result { let update_provider = |(provider, mut conf, tls): (KmsProvider, Document, Option)| { - match provider { - KmsProvider::Azure => { + match provider.provider_type() { + KmsProviderType::Azure => { conf.insert( "identityPlatformEndpoint", if valid { @@ -924,7 +970,7 @@ async fn custom_endpoint_setup(valid: bool) -> Result { }, ); } - KmsProvider::Gcp => { + KmsProviderType::Gcp => { conf.insert( "endpoint", if valid { @@ -934,7 +980,7 @@ async fn custom_endpoint_setup(valid: bool) -> Result { }, ); } - KmsProvider::Kmip => { + KmsProviderType::Kmip => { conf.insert( "endpoint", if valid { @@ -948,7 +994,7 @@ async fn custom_endpoint_setup(valid: bool) -> Result { } (provider, conf, tls) }; - let kms_providers: KmsProviderList = KMS_PROVIDERS + let kms_providers: KmsProviderList = UNNAMED_KMS_PROVIDERS .clone() .into_iter() .map(update_provider) @@ -981,12 +1027,13 @@ async fn custom_endpoint_aws_ok(endpoint: Option) -> Result<()> { let client_encryption = custom_endpoint_setup(true).await?; let key_id = client_encryption - .create_data_key(MasterKey::Aws { - region: "us-east-1".to_string(), - key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" - .to_string(), - endpoint, - }) + .create_data_key( + AwsMasterKey::builder() + .region("us-east-1") + .key("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0") + .endpoint(endpoint) + .build(), + ) .await?; validate_roundtrip(&client_encryption, key_id).await?; @@ -1033,12 +1080,13 @@ async fn custom_endpoint_aws_invalid_port() -> Result<()> { let client_encryption = custom_endpoint_setup(true).await?; let result = client_encryption - .create_data_key(MasterKey::Aws { - region: "us-east-1".to_string(), - key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" - .to_string(), - endpoint: Some("kms.us-east-1.amazonaws.com:12345".to_string()), - }) + .create_data_key( + AwsMasterKey::builder() + .region("us-east-1") + .key("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0") + .endpoint(Some("kms.us-east-1.amazonaws.com:12345".to_string())) + .build(), + ) .await; assert!(result.unwrap_err().is_network_error()); @@ -1055,12 +1103,13 @@ async fn custom_endpoint_aws_invalid_region() -> Result<()> { let client_encryption = custom_endpoint_setup(true).await?; let result = client_encryption - .create_data_key(MasterKey::Aws { - region: "us-east-1".to_string(), - key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" - .to_string(), - endpoint: Some("kms.us-east-2.amazonaws.com".to_string()), - }) + .create_data_key( + AwsMasterKey::builder() + .region("us-east-1") + .key("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0") + .endpoint(Some("kms.us-east-2.amazonaws.com".to_string())) + .build(), + ) .await; assert!(result.unwrap_err().is_csfle_error()); @@ -1077,12 +1126,13 @@ async fn custom_endpoint_aws_invalid_domain() -> Result<()> { let client_encryption = custom_endpoint_setup(true).await?; let result = client_encryption - .create_data_key(MasterKey::Aws { - region: "us-east-1".to_string(), - key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" - .to_string(), - endpoint: Some("doesnotexist.invalid".to_string()), - }) + .create_data_key( + AwsMasterKey::builder() + .region("us-east-1") + .key("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0") + .endpoint(Some("doesnotexist.invalid".to_string())) + .build(), + ) .await; assert!(result.unwrap_err().is_network_error()); @@ -1096,11 +1146,10 @@ async fn custom_endpoint_azure() -> Result<()> { return Ok(()); } - let master_key = MasterKey::Azure { - key_vault_endpoint: "key-vault-csfle.vault.azure.net".to_string(), - key_name: "key-name-csfle".to_string(), - key_version: None, - }; + let master_key = AzureMasterKey::builder() + .key_vault_endpoint("key-vault-csfle.vault.azure.net") + .key_name("key-name-csfle") + .build(); let client_encryption = custom_endpoint_setup(true).await?; let key_id = client_encryption @@ -1122,14 +1171,13 @@ async fn custom_endpoint_gcp_valid() -> Result<()> { return Ok(()); } - let master_key = MasterKey::Gcp { - project_id: "devprod-drivers".to_string(), - location: "global".to_string(), - key_ring: "key-ring-csfle".to_string(), - key_name: "key-name-csfle".to_string(), - key_version: None, - endpoint: Some("cloudkms.googleapis.com:443".to_string()), - }; + let master_key = GcpMasterKey::builder() + .project_id("devprod-drivers") + .location("global") + .key_ring("key-ring-csfle") + .key_name("key-name-csfle") + .endpoint(Some("cloudkms.googleapis.com:443".to_string())) + .build(); let client_encryption = custom_endpoint_setup(true).await?; let key_id = client_encryption @@ -1151,14 +1199,13 @@ async fn custom_endpoint_gcp_invalid() -> Result<()> { return Ok(()); } - let master_key = MasterKey::Gcp { - project_id: "devprod-drivers".to_string(), - location: "global".to_string(), - key_ring: "key-ring-csfle".to_string(), - key_name: "key-name-csfle".to_string(), - key_version: None, - endpoint: Some("doesnotexist.invalid:443".to_string()), - }; + let master_key = GcpMasterKey::builder() + .project_id("devprod-drivers") + .location("global") + .key_ring("key-ring-csfle") + .key_name("key-name-csfle") + .endpoint(Some("doesnotexist.invalid:443".to_string())) + .build(); let client_encryption = custom_endpoint_setup(true).await?; let result = client_encryption.create_data_key(master_key).await; @@ -1180,11 +1227,9 @@ async fn custom_endpoint_kmip_no_endpoint() -> Result<()> { return Ok(()); } - let master_key = MasterKey::Kmip { - key_id: Some("1".to_string()), - delegated: None, - endpoint: None, - }; + let master_key = KmipMasterKey::builder() + .key_id(Some("1".to_string())) + .build(); let client_encryption = custom_endpoint_setup(true).await?; let key_id = client_encryption @@ -1206,11 +1251,10 @@ async fn custom_endpoint_kmip_valid_endpoint() -> Result<()> { return Ok(()); } - let master_key = MasterKey::Kmip { - key_id: Some("1".to_string()), - delegated: None, - endpoint: Some("localhost:5698".to_string()), - }; + let master_key = KmipMasterKey::builder() + .key_id(Some("1".to_string())) + .endpoint(Some("localhost:5698".to_string())) + .build(); let client_encryption = custom_endpoint_setup(true).await?; let key_id = client_encryption.create_data_key(master_key).await?; @@ -1224,11 +1268,10 @@ async fn custom_endpoint_kmip_invalid_endpoint() -> Result<()> { return Ok(()); } - let master_key = MasterKey::Kmip { - key_id: Some("1".to_string()), - delegated: None, - endpoint: Some("doesnotexist.local:5698".to_string()), - }; + let master_key = KmipMasterKey::builder() + .key_id(Some("1".to_string())) + .endpoint(Some("doesnotexist.local:5698".to_string())) + .build(); let client_encryption = custom_endpoint_setup(true).await?; let result = client_encryption.create_data_key(master_key).await; @@ -1255,7 +1298,7 @@ async fn bypass_mongocryptd_via_shared_library() -> Result<()> { let client_encrypted = Client::encrypted_builder( get_client_options().await.clone(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )? .schema_map([("db.coll", load_testdata("external/external-schema.json")?)]) .extra_options(doc! { @@ -1300,7 +1343,7 @@ async fn bypass_mongocryptd_via_bypass_spawn() -> Result<()> { let client_encrypted = Client::encrypted_builder( get_client_options().await.clone(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )? .schema_map([("db.coll", load_testdata("external/external-schema.json")?)]) .extra_options(extra_options) @@ -1333,7 +1376,7 @@ async fn bypass_mongocryptd_unencrypted_insert(bypass: Bypass) -> Result<()> { let builder = Client::encrypted_builder( get_client_options().await.clone(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )? .extra_options(extra_options) .disable_crypt_shared(true); @@ -1533,7 +1576,7 @@ impl DeadlockTestCase { let client_encryption = ClientEncryption::new( client_test.clone().into_client(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )?; let ciphertext = client_encryption .encrypt( @@ -1552,7 +1595,7 @@ impl DeadlockTestCase { opts.command_event_handler = Some(event_buffer.handler()); opts.sdam_event_handler = Some(event_buffer.handler()); let client_encrypted = - Client::encrypted_builder(opts, KV_NAMESPACE.clone(), LOCAL_KMS.clone())? + Client::encrypted_builder(opts, KV_NAMESPACE.clone(), vec![LOCAL_KMS.clone()])? .bypass_auto_encryption(self.bypass_auto_encryption) .key_vault_client( if self.set_key_vault_client { @@ -1677,17 +1720,18 @@ async fn run_kms_tls_test(endpoint: impl Into) -> crate::error::Result<( let client_encryption = ClientEncryption::new( kv_client.clone().into_client(), KV_NAMESPACE.clone(), - KMS_PROVIDERS.clone(), + UNNAMED_KMS_PROVIDERS.clone(), )?; // Test client_encryption - .create_data_key(MasterKey::Aws { - region: "us-east-1".to_string(), - key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" - .to_string(), - endpoint: Some(endpoint.into()), - }) + .create_data_key( + AwsMasterKey::builder() + .region("us-east-1") + .key("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0") + .endpoint(Some(endpoint.into())) + .build(), + ) .await .map(|_| ()) } @@ -1699,118 +1743,126 @@ async fn kms_tls_options() -> Result<()> { return Ok(()); } - fn providers_with_tls( - mut providers: HashMap)>, - tls_opts: TlsOptions, + fn update_providers( + mut base_providers: KmsProviderList, + new_tls_options: TlsOptions, + mut update_credentials: impl FnMut(&KmsProvider, &mut Document), ) -> KmsProviderList { - for provider in [ - KmsProvider::Aws, - KmsProvider::Azure, - KmsProvider::Gcp, - KmsProvider::Kmip, - ] { - providers.get_mut(&provider).unwrap().1 = Some(tls_opts.clone()); + for (provider, credentials, tls_options) in base_providers.iter_mut() { + if provider != &KmsProvider::local() { + *tls_options = Some(new_tls_options.clone()); + } + update_credentials(provider, credentials); } - providers.into_iter().map(|(p, (o, t))| (p, o, t)).collect() + base_providers } - // Setup - let mut base_providers = KMS_PROVIDERS_MAP.clone(); - base_providers - .get_mut(&KmsProvider::Azure) - .unwrap() - .0 - .insert("identityPlatformEndpoint", KMS_CORRECT); - base_providers - .get_mut(&KmsProvider::Gcp) - .unwrap() - .0 - .insert("endpoint", KMS_CORRECT); - let cert_dir = PathBuf::from(std::env::var("CSFLE_TLS_CERT_DIR").unwrap()); let ca_path = cert_dir.join("ca.pem"); let key_path = cert_dir.join("client.pem"); + let add_correct_credentials = + |provider: &KmsProvider, credentials: &mut Document| match provider.provider_type() { + KmsProviderType::Azure => { + credentials.insert("identityPlatformEndpoint", KMS_CORRECT); + } + KmsProviderType::Gcp => { + credentials.insert("endpoint", KMS_CORRECT); + } + _ => {} + }; + let add_expired_credentials = + |provider: &KmsProvider, credentials: &mut Document| match provider.provider_type() { + KmsProviderType::Azure => { + credentials.insert("identityPlatformEndpoint", KMS_EXPIRED); + } + KmsProviderType::Gcp | KmsProviderType::Kmip => { + credentials.insert("endpoint", KMS_EXPIRED); + } + _ => {} + }; + let add_wrong_host_credentials = + |provider: &KmsProvider, credentials: &mut Document| match provider.provider_type() { + KmsProviderType::Azure => { + credentials.insert("identityPlatformEndpoint", KMS_WRONG_HOST); + } + KmsProviderType::Gcp | KmsProviderType::Kmip => { + credentials.insert("endpoint", KMS_WRONG_HOST); + } + _ => {} + }; + + let providers_no_client_cert = update_providers( + UNNAMED_KMS_PROVIDERS.clone(), + TlsOptions::builder().ca_file_path(ca_path.clone()).build(), + add_correct_credentials, + ); let client_encryption_no_client_cert = ClientEncryption::new( TestClient::new().await.into_client(), KV_NAMESPACE.clone(), - providers_with_tls( - base_providers.clone(), - TlsOptions::builder().ca_file_path(ca_path.clone()).build(), - ), + providers_no_client_cert.clone(), )?; + let providers_with_tls = update_providers( + UNNAMED_KMS_PROVIDERS.clone(), + TlsOptions::builder() + .ca_file_path(ca_path.clone()) + .cert_key_file_path(key_path.clone()) + .build(), + add_correct_credentials, + ); let client_encryption_with_tls = ClientEncryption::new( TestClient::new().await.into_client(), KV_NAMESPACE.clone(), - providers_with_tls( - base_providers.clone(), - TlsOptions::builder() - .ca_file_path(ca_path.clone()) - .cert_key_file_path(key_path.clone()) - .build(), - ), + providers_with_tls.clone(), )?; - let client_encryption_expired = { - let mut providers = base_providers.clone(); - providers - .get_mut(&KmsProvider::Azure) - .unwrap() - .0 - .insert("identityPlatformEndpoint", KMS_EXPIRED); - providers - .get_mut(&KmsProvider::Gcp) - .unwrap() - .0 - .insert("endpoint", KMS_EXPIRED); - providers - .get_mut(&KmsProvider::Kmip) - .unwrap() - .0 - .insert("endpoint", KMS_EXPIRED); - - ClientEncryption::new( - TestClient::new().await.into_client(), - KV_NAMESPACE.clone(), - providers_with_tls( - providers, - TlsOptions::builder().ca_file_path(ca_path.clone()).build(), - ), - )? - }; + let client_encryption_expired = ClientEncryption::new( + TestClient::new().await.into_client(), + KV_NAMESPACE.clone(), + update_providers( + UNNAMED_KMS_PROVIDERS.clone(), + TlsOptions::builder().ca_file_path(ca_path.clone()).build(), + add_expired_credentials, + ), + )?; - let client_encryption_invalid_hostname = { - let mut providers = base_providers.clone(); - providers - .get_mut(&KmsProvider::Azure) - .unwrap() - .0 - .insert("identityPlatformEndpoint", KMS_WRONG_HOST); - providers - .get_mut(&KmsProvider::Gcp) - .unwrap() - .0 - .insert("endpoint", KMS_WRONG_HOST); - providers - .get_mut(&KmsProvider::Kmip) - .unwrap() - .0 - .insert("endpoint", KMS_WRONG_HOST); + let client_encryption_invalid_hostname = ClientEncryption::new( + TestClient::new().await.into_client(), + KV_NAMESPACE.clone(), + update_providers( + UNNAMED_KMS_PROVIDERS.clone(), + TlsOptions::builder().ca_file_path(ca_path.clone()).build(), + add_wrong_host_credentials, + ), + )?; - ClientEncryption::new( - TestClient::new().await.into_client(), - KV_NAMESPACE.clone(), - providers_with_tls( - providers, - TlsOptions::builder().ca_file_path(ca_path.clone()).build(), - ), - )? - }; + let mut named_providers = providers_no_client_cert + .into_iter() + .filter_map(|info| { + if !matches!(info.0.provider_type(), KmsProviderType::Local) { + Some(add_name_to_info(info, "no_client_cert")) + } else { + None + } + }) + .collect::>(); + named_providers.extend(providers_with_tls.into_iter().filter_map(|info| { + if !matches!(info.0.provider_type(), KmsProviderType::Local) { + Some(add_name_to_info(info, "with_tls")) + } else { + None + } + })); + let client_encryption_with_names = ClientEncryption::new( + TestClient::new().await.into_client(), + KV_NAMESPACE.clone(), + named_providers, + )?; async fn provider_test( client_encryption: &ClientEncryption, - master_key: MasterKey, + master_key: impl Into, expected_errs: &[&str], ) -> Result<()> { let err = client_encryption @@ -1825,13 +1877,12 @@ async fn kms_tls_options() -> Result<()> { } // Case 1: AWS - fn aws_key(endpoint: impl Into) -> MasterKey { - MasterKey::Aws { - region: "us-east-1".to_string(), - key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" - .to_string(), - endpoint: Some(endpoint.into()), - } + fn aws_key(endpoint: impl Into) -> AwsMasterKey { + AwsMasterKey::builder() + .region("us-east-1") + .key("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0") + .endpoint(Some(endpoint.into())) + .build() } provider_test( @@ -1860,11 +1911,10 @@ async fn kms_tls_options() -> Result<()> { .await?; // Case 2: Azure - let azure_key = MasterKey::Azure { - key_vault_endpoint: "doesnotexist.local".to_string(), - key_name: "foo".to_string(), - key_version: None, - }; + let azure_key = AzureMasterKey::builder() + .key_vault_endpoint("doesnotexist.local") + .key_name("foo") + .build(); provider_test( &client_encryption_no_client_cert, @@ -1892,14 +1942,12 @@ async fn kms_tls_options() -> Result<()> { .await?; // Case 3: GCP - let gcp_key = MasterKey::Gcp { - project_id: "foo".to_string(), - location: "bar".to_string(), - key_ring: "baz".to_string(), - key_name: "foo".to_string(), - endpoint: None, - key_version: None, - }; + let gcp_key = GcpMasterKey::builder() + .project_id("foo") + .location("bar") + .key_ring("baz") + .key_name("foo") + .build(); provider_test( &client_encryption_no_client_cert, @@ -1927,11 +1975,7 @@ async fn kms_tls_options() -> Result<()> { .await?; // Case 4: KMIP - let kmip_key = MasterKey::Kmip { - key_id: None, - delegated: None, - endpoint: None, - }; + let kmip_key = KmipMasterKey::builder().build(); provider_test( &client_encryption_no_client_cert, @@ -1956,6 +2000,75 @@ async fn kms_tls_options() -> Result<()> { ) .await?; + // Case 6: named KMS providers apply TLS options + // Named AWS + let mut master_key = aws_key("127.0.0.1:9002"); + master_key.name = Some("no_client_cert".to_string()); + provider_test( + &client_encryption_with_names, + master_key, + &["SSL routines", "connection was forcibly closed"], + ) + .await?; + + let mut master_key = aws_key("127.0.0.1:9002"); + master_key.name = Some("with_tls".to_string()); + provider_test(&client_encryption_with_names, master_key, &["parse error"]).await?; + + // Named Azure + let mut master_key = azure_key.clone(); + master_key.name = Some("no_client_cert".to_string()); + provider_test( + &client_encryption_with_names, + master_key, + &["SSL routines", "connection was forcibly closed"], + ) + .await?; + + let mut master_key = azure_key.clone(); + master_key.name = Some("with_tls".to_string()); + provider_test( + &client_encryption_with_names, + master_key, + &["HTTP status=404"], + ) + .await?; + + // Named GCP + let mut master_key = gcp_key.clone(); + master_key.name = Some("no_client_cert".to_string()); + provider_test( + &client_encryption_with_names, + master_key, + &["SSL routines", "connection was forcibly closed"], + ) + .await?; + + let mut master_key = gcp_key.clone(); + master_key.name = Some("with_tls".to_string()); + provider_test( + &client_encryption_with_names, + master_key, + &["HTTP status=404"], + ) + .await?; + + // Named KMIP + let mut master_key = kmip_key.clone(); + master_key.name = Some("no_client_cert".to_string()); + provider_test( + &client_encryption_with_names, + master_key, + &["SSL routines", "connection was forcibly closed"], + ) + .await?; + + let mut master_key = kmip_key.clone(); + master_key.name = Some("with_tls".to_string()); + client_encryption_with_names + .create_data_key(master_key) + .await?; + Ok(()) } @@ -2262,12 +2375,12 @@ async fn explicit_encryption_setup() -> Result Result<()> { // Succeeds client_encryption - .create_data_key(MasterKey::Local) + .create_data_key(LocalMasterKey::builder().build()) .key_alt_names(vec!["abc".to_string()]) .await?; // Fails: duplicate key let err = client_encryption - .create_data_key(MasterKey::Local) + .create_data_key(LocalMasterKey::builder().build()) .key_alt_names(vec!["abc".to_string()]) .await .unwrap_err(); @@ -2310,7 +2423,7 @@ async fn unique_index_keyaltnames_create_data_key() -> Result<()> { ); // Fails: duplicate key let err = client_encryption - .create_data_key(MasterKey::Local) + .create_data_key(LocalMasterKey::builder().build()) .key_alt_names(vec!["def".to_string()]) .await .unwrap_err(); @@ -2334,7 +2447,9 @@ async fn unique_index_keyaltnames_add_key_alt_name() -> Result<()> { let (client_encryption, key) = unique_index_keyaltnames_setup().await?; // Succeeds - let new_key = client_encryption.create_data_key(MasterKey::Local).await?; + let new_key = client_encryption + .create_data_key(LocalMasterKey::builder().build()) + .await?; client_encryption.add_key_alt_name(&new_key, "abc").await?; // Still succeeds, has alt name let prev_key = client_encryption @@ -2396,10 +2511,10 @@ async fn unique_index_keyaltnames_setup() -> Result<(ClientEncryption, Binary)> let client_encryption = ClientEncryption::new( client.into_client(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )?; let key = client_encryption - .create_data_key(MasterKey::Local) + .create_data_key(LocalMasterKey::builder().build()) .key_alt_names(vec!["def".to_string()]) .await?; Ok((client_encryption, key)) @@ -2543,9 +2658,11 @@ impl DecryptionEventsTestdata { let client_encryption = ClientEncryption::new( setup_client.clone().into_client(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )?; - let key_id = client_encryption.create_data_key(MasterKey::Local).await?; + let key_id = client_encryption + .create_data_key(LocalMasterKey::builder().build()) + .await?; let ciphertext = client_encryption .encrypt("hello", EncryptKey::Id(key_id), Algorithm::Deterministic) .await?; @@ -2558,7 +2675,7 @@ impl DecryptionEventsTestdata { opts.retry_reads = Some(false); opts.command_event_handler = Some(ev_handler.clone().into()); let encrypted_client = - Client::encrypted_builder(opts, KV_NAMESPACE.clone(), LOCAL_KMS.clone())? + Client::encrypted_builder(opts, KV_NAMESPACE.clone(), vec![LOCAL_KMS.clone()])? .extra_options(EXTRA_OPTIONS.clone()) .disable_crypt_shared(*DISABLE_CRYPT_SHARED) .build() @@ -2623,15 +2740,15 @@ async fn on_demand_aws_failure() -> Result<()> { let ce = ClientEncryption::new( Client::test_builder().build().await.into_client(), KV_NAMESPACE.clone(), - [(KmsProvider::Aws, doc! {}, None)], + [(KmsProvider::aws(), doc! {}, None)], )?; let result = ce - .create_data_key(MasterKey::Aws { - region: "us-east-1".to_string(), - key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" - .to_string(), - endpoint: None, - }) + .create_data_key( + AwsMasterKey::builder() + .region("us-east-1") + .key("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0") + .build(), + ) .await; assert!(result.is_err(), "Expected error, got {:?}", result); @@ -2649,14 +2766,14 @@ async fn on_demand_aws_success() -> Result<()> { let ce = ClientEncryption::new( Client::test_builder().build().await.into_client(), KV_NAMESPACE.clone(), - [(KmsProvider::Aws, doc! {}, None)], + [(KmsProvider::aws(), doc! {}, None)], )?; - ce.create_data_key(MasterKey::Aws { - region: "us-east-1".to_string(), - key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" - .to_string(), - endpoint: None, - }) + ce.create_data_key( + AwsMasterKey::builder() + .region("us-east-1") + .key("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0") + .build(), + ) .await?; Ok(()) @@ -2672,18 +2789,18 @@ async fn on_demand_gcp_credentials() -> Result<()> { let client_encryption = ClientEncryption::new( util_client, KV_NAMESPACE.clone(), - [(KmsProvider::Gcp, doc! {}, None)], + [(KmsProvider::gcp(), doc! {}, None)], )?; let result = client_encryption - .create_data_key(MasterKey::Gcp { - project_id: "devprod-drivers".into(), - location: "global".into(), - key_ring: "key-ring-csfle".into(), - key_name: "key-name-csfle".into(), - key_version: None, - endpoint: None, - }) + .create_data_key( + GcpMasterKey::builder() + .project_id("devprod-drivers") + .location("global") + .key_ring("key-ring-csfle") + .key_name("key-name-csfle") + .build(), + ) .await; if std::env::var("ON_DEMAND_GCP_CREDS_SHOULD_SUCCEED").is_ok() { @@ -2784,15 +2901,16 @@ async fn azure_imds_integration_failure() -> Result<()> { let c = ClientEncryption::new( Client::test_builder().build().await.into_client(), KV_NAMESPACE.clone(), - [(KmsProvider::Azure, doc! {}, None)], + [(KmsProvider::azure(), doc! {}, None)], )?; let result = c - .create_data_key(MasterKey::Azure { - key_vault_endpoint: "https://keyvault-drivers-2411.vault.azure.net/keys/".to_string(), - key_name: "KEY-NAME".to_string(), - key_version: None, - }) + .create_data_key( + AzureMasterKey::builder() + .key_vault_endpoint("https://keyvault-drivers-2411.vault.azure.net/keys/") + .key_name("KEY-NAME") + .build(), + ) .await; assert!(result.is_err(), "expected error, got {:?}", result); @@ -2827,7 +2945,7 @@ async fn bypass_mongocryptd_client() -> Result<()> { let client_encrypted = Client::encrypted_builder( get_client_options().await.clone(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )? .extra_options({ let mut extra_options = EXTRA_OPTIONS.clone(); @@ -2851,21 +2969,23 @@ async fn bypass_mongocryptd_client() -> Result<()> { // Prost test 21. Automatic Data Encryption Keys #[tokio::test] async fn auto_encryption_keys_local() -> Result<()> { - auto_encryption_keys(MasterKey::Local).await + auto_encryption_keys(LocalMasterKey::builder().build()).await } #[tokio::test] async fn auto_encryption_keys_aws() -> Result<()> { - auto_encryption_keys(MasterKey::Aws { - region: "us-east-1".to_string(), - key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" - .to_string(), - endpoint: None, - }) + auto_encryption_keys( + AwsMasterKey::builder() + .region("us-east-1") + .key("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0") + .build(), + ) .await } -async fn auto_encryption_keys(master_key: MasterKey) -> Result<()> { +async fn auto_encryption_keys(master_key: impl Into) -> Result<()> { + let master_key = master_key.into(); + if !check_env("custom_key_material", false) { return Ok(()); } @@ -2887,11 +3007,7 @@ async fn auto_encryption_keys(master_key: MasterKey) -> Result<()> { let ce = ClientEncryption::new( client.into_client(), KV_NAMESPACE.clone(), - KMS_PROVIDERS - .iter() - .filter(|(p, ..)| p == &KmsProvider::Local || p == &KmsProvider::Aws) - .cloned() - .collect::>(), + vec![AWS_KMS.clone(), LOCAL_KMS.clone()], )?; // Case 1: Simple Creation and Validation @@ -3096,13 +3212,13 @@ async fn range_explicit_encryption_test( let client_encryption = ClientEncryption::new( key_vault_client.into_client(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )?; let encrypted_client = Client::encrypted_builder( get_client_options().await.clone(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )? .bypass_query_analysis(true) .build() @@ -3350,10 +3466,14 @@ async fn fle2_example() -> Result<()> { let ce = ClientEncryption::new( test_client.clone().into_client(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )?; - let key1_id = ce.create_data_key(MasterKey::Local).await?; - let key2_id = ce.create_data_key(MasterKey::Local).await?; + let key1_id = ce + .create_data_key(LocalMasterKey::builder().build()) + .await?; + let key2_id = ce + .create_data_key(LocalMasterKey::builder().build()) + .await?; // Create an encryptedFieldsMap. let encrypted_fields_map = [( @@ -3379,7 +3499,7 @@ async fn fle2_example() -> Result<()> { let encrypted_client = Client::encrypted_builder( get_client_options().await.clone(), KV_NAMESPACE.clone(), - LOCAL_KMS.clone(), + vec![LOCAL_KMS.clone()], )? .encrypted_fields_map(encrypted_fields_map) .build() diff --git a/src/test/spec/client_side_encryption.rs b/src/test/spec/client_side_encryption.rs index ba3a60960..412a2d444 100644 --- a/src/test/spec/client_side_encryption.rs +++ b/src/test/spec/client_side_encryption.rs @@ -6,6 +6,7 @@ async fn run_unified() { if cfg!(not(feature = "openssl-tls")) { skipped_tests.push("create datakey with KMIP KMS provider"); skipped_tests.push("create datakey with KMIP delegated KMS provider"); + skipped_tests.push("create datakey with named KMIP KMS provider"); } run_unified_tests(&["client-side-encryption", "unified"]) diff --git a/src/test/spec/json/client-side-encryption/benchmarks.md b/src/test/spec/json/client-side-encryption/benchmarks.md new file mode 100644 index 000000000..453d60846 --- /dev/null +++ b/src/test/spec/json/client-side-encryption/benchmarks.md @@ -0,0 +1,51 @@ +# In-Use Encryption: Benchmarks + +______________________________________________________________________ + +## Benchmarking Bindings + +Drivers are encouraged to benchmark the bindings to libmongocrypt. Benchmarking may help to identify performance issues +due to the cost of calling between the native language and the C library. + +A handle to libmongocrypt (`mongocrypt_t`) is needed for the benchmark. In the public driver API, `mongocrypt_t` is an +implementation detail contained in a `MongoClient`. The bindings API may more directly interface `mongocrypt_t`. +Example: the Java bindings API contains a +[MongoCrypt class](https://github.com/mongodb/libmongocrypt/blob/master/bindings/java/mongocrypt/src/main/java/com/mongodb/crypt/capi/MongoCrypt.java) +closely wrapping the `mongocrypt_t`. + +If possible, drivers are encouraged to use the bindings API and mock responses from the MongoDB server. This may help to +narrow the scope of the benchmarked code. See +[BenchmarkRunner.java](https://github.com/mongodb/libmongocrypt/blob/b81e66e0208d13e07c2e5e60b3170f0cfc61e1e2/bindings/java/mongocrypt/benchmarks/src/main/java/com/mongodb/crypt/benchmark/BenchmarkRunner.java) +for an example using the Java bindings API. If that is not possible, the benchmark can be implemented using the +`MongoClient` API, and the benchmark will include the time spent communicating with the MongoDB server. + +### Benchmarking Bulk Decryption + +Set up the benchmark data: + +- Create a data key with the "local" KMS provider. +- Encrypt 1500 string values of the form `value 0001`, `value 0002`, `value 0003`, ... with the algorithm + `AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic`. +- Create a document of the form: + `{ "key0001": , "key0002": , "key0003": }`. +- Create a handle to `mongocrypt_t`. This may be through the bindings API (preferred) or through a `MongoClient` + configured with `AutoEncryptionOpts`. + +Warm up the benchmark: + +- Use the handle to decrypt the document repeatedly for one second. + +Run the benchmark. Repeat benchmark for thread counts: (1, 2, 8, 64): + +- Start threads. Use the same handle between all threads (`mongocrypt_t` is thread-safe). +- In each thread: decrypt the document repeatedly for one second. +- Count the number of decrypt operations performed (ops/sec). +- Repeat 10 times. + +Produce results: + +- Report the median result of the ops/sec for each thread count. + +**Note:** The `mongocrypt_t` handle caches the decrypted Data Encryption Key (DEK) for a fixed time period of one +minute. If the benchmark exceeds one minute, the DEK will be requested again from the key vault collection. Reporting +the median of trials is expected to prevent this impacting results. diff --git a/src/test/spec/json/client-side-encryption/benchmarks.rst b/src/test/spec/json/client-side-encryption/benchmarks.rst deleted file mode 100644 index bba8db4a0..000000000 --- a/src/test/spec/json/client-side-encryption/benchmarks.rst +++ /dev/null @@ -1,46 +0,0 @@ -============================= -In-Use Encryption: Benchmarks -============================= - -.. contents:: - ----- - -Benchmarking Bindings -===================== - -Drivers are encouraged to benchmark the bindings to libmongocrypt. Benchmarking may help to identify performance issues due to the cost of calling between the native language and the C library. - -A handle to libmongocrypt (``mongocrypt_t``) is needed for the benchmark. In the public driver API, ``mongocrypt_t`` is an implementation detail contained in a ``MongoClient``. The bindings API may more directly interface ``mongocrypt_t``. Example: the Java bindings API contains a `MongoCrypt class `_ closely wrapping the ``mongocrypt_t``. - -If possible, drivers are encouraged to use the bindings API and mock responses from the MongoDB server. This may help to narrow the scope of the benchmarked code. See `BenchmarkRunner.java `_ for an example using the Java bindings API. If that is not possible, the benchmark can be implemented using the ``MongoClient`` API, and the benchmark will include the time spent communicating with the MongoDB server. - -Benchmarking Bulk Decryption -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Set up the benchmark data: - -- Create a data key with the "local" KMS provider. -- Encrypt 1500 string values of the form ``value 0001``, ``value 0002``, ``value 0003``, ... with the algorithm ``AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic``. -- Create a document of the form: ``{ "key0001": , "key0002": , "key0003": }``. -- Create a handle to ``mongocrypt_t``. This may be through the bindings API (preferred) or through a ``MongoClient`` configured with ``AutoEncryptionOpts``. - -Warm up the benchmark: - -- Use the handle to decrypt the document repeatedly for one second. - -Run the benchmark. Repeat benchmark for thread counts: (1, 2, 8, 64): - -- Start threads. Use the same handle between all threads (``mongocrypt_t`` is thread-safe). -- In each thread: decrypt the document repeatedly for one second. -- Count the number of decrypt operations performed (ops/sec). -- Repeat 10 times. - -Produce results: - -- Report the median result of the ops/sec for each thread count. - -**Note:** -The ``mongocrypt_t`` handle caches the decrypted Data Encryption Key (DEK) for a fixed time period of one minute. -If the benchmark exceeds one minute, the DEK will be requested again from the key vault collection. -Reporting the median of trials is expected to prevent this impacting results. diff --git a/src/test/spec/json/client-side-encryption/legacy/fle2v2-MissingKey.json b/src/test/spec/json/client-side-encryption/legacy/fle2v2-MissingKey.json index 8812a1f0a..1e655f0a9 100644 --- a/src/test/spec/json/client-side-encryption/legacy/fle2v2-MissingKey.json +++ b/src/test/spec/json/client-side-encryption/legacy/fle2v2-MissingKey.json @@ -54,7 +54,7 @@ "key_vault_data": [], "tests": [ { - "description": "FLE2 encrypt fails with mising key", + "description": "FLE2 encrypt fails with missing key", "clientOptions": { "autoEncryptOpts": { "kmsProviders": { @@ -85,7 +85,7 @@ ] }, { - "description": "FLE2 decrypt fails with mising key", + "description": "FLE2 decrypt fails with missing key", "clientOptions": { "autoEncryptOpts": { "kmsProviders": { diff --git a/src/test/spec/json/client-side-encryption/legacy/fle2v2-MissingKey.yml b/src/test/spec/json/client-side-encryption/legacy/fle2v2-MissingKey.yml index 2d9eb6e58..f4fbbeb5c 100644 --- a/src/test/spec/json/client-side-encryption/legacy/fle2v2-MissingKey.yml +++ b/src/test/spec/json/client-side-encryption/legacy/fle2v2-MissingKey.yml @@ -19,7 +19,7 @@ data: [ encrypted_fields: {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedIndexed', 'bsonType': 'string', 'queries': {'queryType': 'equality', 'contention': {'$numberLong': '0'}}}, {'keyId': {'$binary': {'base64': 'q83vqxI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedUnindexed', 'bsonType': 'string'}]} key_vault_data: [] tests: - - description: "FLE2 encrypt fails with mising key" + - description: "FLE2 encrypt fails with missing key" clientOptions: autoEncryptOpts: kmsProviders: @@ -30,7 +30,7 @@ tests: document: { _id: 1, encryptedIndexed: "123" } result: errorContains: "not all keys requested were satisfied" - - description: "FLE2 decrypt fails with mising key" + - description: "FLE2 decrypt fails with missing key" clientOptions: autoEncryptOpts: kmsProviders: diff --git a/src/test/spec/json/client-side-encryption/legacy/kmipKMS.json b/src/test/spec/json/client-side-encryption/legacy/kmipKMS.json index 079605019..349328b43 100644 --- a/src/test/spec/json/client-side-encryption/legacy/kmipKMS.json +++ b/src/test/spec/json/client-side-encryption/legacy/kmipKMS.json @@ -359,4 +359,4 @@ } } ] -} \ No newline at end of file +} diff --git a/src/test/spec/json/client-side-encryption/legacy/kmipKMS.yml b/src/test/spec/json/client-side-encryption/legacy/kmipKMS.yml index c72e6362c..51fa42cc7 100644 --- a/src/test/spec/json/client-side-encryption/legacy/kmipKMS.yml +++ b/src/test/spec/json/client-side-encryption/legacy/kmipKMS.yml @@ -55,6 +55,7 @@ tests: arguments: document: &doc1 { _id: 1, encrypted_string_kmip_delegated: "string0" } expectations: + # Auto encryption will request the collection info. - command_started_event: command: listCollections: 1 diff --git a/src/test/spec/json/client-side-encryption/legacy/namedKMS.json b/src/test/spec/json/client-side-encryption/legacy/namedKMS.json new file mode 100644 index 000000000..c85944358 --- /dev/null +++ b/src/test/spec/json/client-side-encryption/legacy/namedKMS.json @@ -0,0 +1,197 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local:name2" + } + } + ], + "tests": [ + { + "description": "Automatically encrypt and decrypt with a named KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local:name2": { + "key": { + "$binary": { + "base64": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_id": 1, + "encrypted_string": "string0" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/src/test/spec/json/client-side-encryption/legacy/namedKMS.yml b/src/test/spec/json/client-side-encryption/legacy/namedKMS.yml new file mode 100644 index 000000000..7bd456aba --- /dev/null +++ b/src/test/spec/json/client-side-encryption/legacy/namedKMS.yml @@ -0,0 +1,56 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {'properties': {'encrypted_string': {'encrypt': {'keyId': [{'$binary': {'base64': 'local+name2+AAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} +key_vault_data: [{'_id': {'$binary': {'base64': 'local+name2+AAAAAAAAAA==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1552949630483'}}, 'updateDate': {'$date': {'$numberLong': '1552949630483'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local:name2'}}] + +tests: + - description: "Automatically encrypt and decrypt with a named KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + "local:name2": {'key': {'$binary': {'base64': 'local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'local+name2+AAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted \ No newline at end of file diff --git a/src/test/spec/json/client-side-encryption/legacy/timeoutMS.json b/src/test/spec/json/client-side-encryption/legacy/timeoutMS.json index 443aa0aa2..b667767cf 100644 --- a/src/test/spec/json/client-side-encryption/legacy/timeoutMS.json +++ b/src/test/spec/json/client-side-encryption/legacy/timeoutMS.json @@ -161,7 +161,7 @@ "failPoint": { "configureFailPoint": "failCommand", "mode": { - "times": 3 + "times": 2 }, "data": { "failCommands": [ @@ -169,7 +169,7 @@ "find" ], "blockConnection": true, - "blockTimeMS": 20 + "blockTimeMS": 30 } }, "clientOptions": { diff --git a/src/test/spec/json/client-side-encryption/legacy/timeoutMS.yml b/src/test/spec/json/client-side-encryption/legacy/timeoutMS.yml index 33321ad64..bb71d6765 100644 --- a/src/test/spec/json/client-side-encryption/legacy/timeoutMS.yml +++ b/src/test/spec/json/client-side-encryption/legacy/timeoutMS.yml @@ -38,8 +38,10 @@ tests: command_name: listCollections # Test that timeoutMS applies to the sum of all operations done for client-side encryption. This is done by blocking - # listCollections and find for 20ms each and running an insertOne with timeoutMS=50. There should be two - # listCollections commands and one "find" command, so the sum should take more than timeoutMS. + # listCollections and find for 30ms each and running an insertOne with timeoutMS=50. There should be one + # listCollections command and one "find" command, so the sum should take more than timeoutMS. A second listCollections + # event doesn't occur due to the internal MongoClient lacking configured auto encryption, plus libmongocrypt holds the + # collection schema in cache for a minute. # # This test does not include command monitoring expectations because the exact command sequence is dependent on the # amount of time taken by mongocryptd communication. In slow runs, mongocryptd communication can breach the timeout @@ -47,11 +49,11 @@ tests: - description: "remaining timeoutMS applied to find to get keyvault data" failPoint: configureFailPoint: failCommand - mode: { times: 3 } + mode: { times: 2 } data: failCommands: ["listCollections", "find"] blockConnection: true - blockTimeMS: 20 + blockTimeMS: 30 clientOptions: autoEncryptOpts: kmsProviders: diff --git a/src/test/spec/json/client-side-encryption/unified/createDataKey-kms_providers-invalid.yml b/src/test/spec/json/client-side-encryption/unified/createDataKey-kms_providers-invalid.yml index f692a0907..fee5a823f 100644 --- a/src/test/spec/json/client-side-encryption/unified/createDataKey-kms_providers-invalid.yml +++ b/src/test/spec/json/client-side-encryption/unified/createDataKey-kms_providers-invalid.yml @@ -58,7 +58,7 @@ tests: kmsProvider: aws opts: masterKey: - key: arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0 + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" region: invalid expectError: isClientError: true diff --git a/src/test/spec/json/client-side-encryption/unified/deleteKey.yml b/src/test/spec/json/client-side-encryption/unified/deleteKey.yml index c598e9469..56acf115a 100644 --- a/src/test/spec/json/client-side-encryption/unified/deleteKey.yml +++ b/src/test/spec/json/client-side-encryption/unified/deleteKey.yml @@ -39,7 +39,7 @@ initialData: status: 1 masterKey: provider: aws - key: arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0 + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" region: us-east-1 - &local_key_doc _id: &local_key_id { $binary: { base64: bG9jYWxrZXlsb2NhbGtleQ==, subType: "04" } } diff --git a/src/test/spec/json/client-side-encryption/unified/getKey.yml b/src/test/spec/json/client-side-encryption/unified/getKey.yml index 149922761..65022dc2a 100644 --- a/src/test/spec/json/client-side-encryption/unified/getKey.yml +++ b/src/test/spec/json/client-side-encryption/unified/getKey.yml @@ -39,7 +39,7 @@ initialData: status: 1 masterKey: provider: aws - key: arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0 + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" region: us-east-1 - &local_key_doc _id: &local_key_id { $binary: { base64: bG9jYWxrZXlsb2NhbGtleQ==, subType: "04" } } diff --git a/src/test/spec/json/client-side-encryption/unified/getKeyByAltName.yml b/src/test/spec/json/client-side-encryption/unified/getKeyByAltName.yml index b0cada879..e9da7231e 100644 --- a/src/test/spec/json/client-side-encryption/unified/getKeyByAltName.yml +++ b/src/test/spec/json/client-side-encryption/unified/getKeyByAltName.yml @@ -39,7 +39,7 @@ initialData: status: 1 masterKey: provider: aws - key: arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0 + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" region: us-east-1 - &local_key_doc _id: { $binary: { base64: bG9jYWxrZXlsb2NhbGtleQ==, subType: "04" } } diff --git a/src/test/spec/json/client-side-encryption/unified/namedKMS-createDataKey.json b/src/test/spec/json/client-side-encryption/unified/namedKMS-createDataKey.json new file mode 100644 index 000000000..4d75e4cf5 --- /dev/null +++ b/src/test/spec/json/client-side-encryption/unified/namedKMS-createDataKey.json @@ -0,0 +1,396 @@ +{ + "description": "namedKMS-createDataKey", + "schemaVersion": "1.18", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws:name1": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure:name1": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp:name1": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip:name1": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local:name1": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [] + } + ], + "tests": [ + { + "description": "create data key with named AWS KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "aws:name1", + "opts": { + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named Azure KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "azure:name1", + "opts": { + "masterKey": { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named GCP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "gcp:name1", + "opts": { + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named KMIP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "kmip:name1" + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named local KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local:name1" + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "local:name1" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/src/test/spec/json/client-side-encryption/unified/namedKMS-createDataKey.yml b/src/test/spec/json/client-side-encryption/unified/namedKMS-createDataKey.yml new file mode 100644 index 000000000..0e3a53b64 --- /dev/null +++ b/src/test/spec/json/client-side-encryption/unified/namedKMS-createDataKey.yml @@ -0,0 +1,175 @@ +description: namedKMS-createDataKey + +schemaVersion: "1.18" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + "aws:name1": { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + "azure:name1": { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } + "gcp:name1": { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } + "kmip:name1": { endpoint: { $$placeholder: 1 } } + "local:name1": { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + +tests: + - description: create data key with named AWS KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "aws:name1" + opts: + masterKey: &new_aws_masterkey + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + region: us-east-1 + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "aws:name1" + <<: *new_aws_masterkey + writeConcern: { w: majority } + + - description: create datakey with named Azure KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "azure:name1" + opts: + masterKey: &new_azure_masterkey + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "azure:name1" + <<: *new_azure_masterkey + writeConcern: { w: majority } + + - description: create datakey with named GCP KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "gcp:name1" + opts: + masterKey: &new_gcp_masterkey + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "gcp:name1" + <<: *new_gcp_masterkey + writeConcern: { w: majority } + + - description: create datakey with named KMIP KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "kmip:name1" + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "kmip:name1" + keyId: { $$type: string } + writeConcern: { w: majority } + + - description: create datakey with named local KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "local:name1" + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "local:name1" + writeConcern: { w: majority } diff --git a/src/test/spec/json/client-side-encryption/unified/namedKMS-explicit.json b/src/test/spec/json/client-side-encryption/unified/namedKMS-explicit.json new file mode 100644 index 000000000..e28d7e8b3 --- /dev/null +++ b/src/test/spec/json/client-side-encryption/unified/namedKMS-explicit.json @@ -0,0 +1,130 @@ +{ + "description": "namedKMS-explicit", + "schemaVersion": "1.18", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local:name2": { + "key": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + }, + "keyAltNames": [ + "local:name2" + ], + "keyMaterial": { + "$binary": { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local:name2" + } + } + ] + } + ], + "tests": [ + { + "description": "can explicitly encrypt with a named KMS provider", + "operations": [ + { + "name": "encrypt", + "object": "clientEncryption0", + "arguments": { + "value": "foobar", + "opts": { + "keyAltName": "local:name2", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "expectResult": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", + "subType": "06" + } + } + } + ] + }, + { + "description": "can explicitly decrypt with a named KMS provider", + "operations": [ + { + "name": "decrypt", + "object": "clientEncryption0", + "arguments": { + "value": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", + "subType": "06" + } + } + }, + "expectResult": "foobar" + } + ] + } + ] +} diff --git a/src/test/spec/json/client-side-encryption/unified/namedKMS-explicit.yml b/src/test/spec/json/client-side-encryption/unified/namedKMS-explicit.yml new file mode 100644 index 000000000..aa9fb3bc8 --- /dev/null +++ b/src/test/spec/json/client-side-encryption/unified/namedKMS-explicit.yml @@ -0,0 +1,72 @@ +description: namedKMS-explicit + +schemaVersion: "1.18" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + "local:name2" : { key: "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { + "_id": { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + }, + "keyAltNames": ["local:name2"], + "keyMaterial": { + "$binary": { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00" + } + }, + "creationDate": {"$date": {"$numberLong": "1552949630483"}}, + "updateDate": {"$date": {"$numberLong": "1552949630483"}}, + "status": {"$numberInt": "0"}, + "masterKey": {"provider": "local:name2"} + } + +tests: + - description: can explicitly encrypt with a named KMS provider + operations: + - name: encrypt + object: *clientEncryption0 + arguments: + value: "foobar" + opts: + keyAltName: "local:name2" + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + expectResult: { "$binary" : { "base64" : "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", "subType" : "06" } } + + - description: can explicitly decrypt with a named KMS provider + operations: + - name: decrypt + object: *clientEncryption0 + arguments: + value: { "$binary" : { "base64" : "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", "subType" : "06" } } + expectResult: "foobar" + \ No newline at end of file diff --git a/src/test/spec/json/client-side-encryption/unified/namedKMS-rewrapManyDataKey.json b/src/test/spec/json/client-side-encryption/unified/namedKMS-rewrapManyDataKey.json new file mode 100644 index 000000000..b3b9bd247 --- /dev/null +++ b/src/test/spec/json/client-side-encryption/unified/namedKMS-rewrapManyDataKey.json @@ -0,0 +1,1385 @@ +{ + "description": "namedKMS-rewrapManyDataKey", + "schemaVersion": "1.18", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws:name1": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure:name1": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp:name1": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip:name1": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local:name1": { + "key": { + "$$placeholder": 1 + } + }, + "local:name2": { + "key": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + }, + "aws:name2": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "YXp1cmVhenVyZWF6dXJlYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "azure:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", + "subType": "04" + } + }, + "keyAltNames": [ + "gcp:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "a21pcGttaXBrbWlwa21pcA==", + "subType": "04" + } + }, + "keyAltNames": [ + "kmip:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "kmip:name1", + "keyId": "1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local:name1" + } + } + ] + } + ], + "tests": [ + { + "description": "rewrap to aws:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "aws:name1_key" + } + }, + "opts": { + "provider": "aws:name1", + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "aws:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap to azure:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "azure:name1_key" + } + }, + "opts": { + "provider": "azure:name1", + "masterKey": { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "azure:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap to gcp:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "gcp:name1_key" + } + }, + "opts": { + "provider": "gcp:name1", + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "gcp:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap to kmip:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "kmip:name1_key" + } + }, + "opts": { + "provider": "kmip:name1" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "kmip:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap to local:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "local:name1_key" + } + }, + "opts": { + "provider": "local:name1" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "local:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap from local:name1 to local:name2", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$eq": "local:name1_key" + } + }, + "opts": { + "provider": "local:name2" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 1, + "modifiedCount": 1, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$eq": "local:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name2" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap from aws:name1 to aws:name2", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$eq": "aws:name1_key" + } + }, + "opts": { + "provider": "aws:name2", + "masterKey": { + "key": "arn:aws:kms:us-east-1:857654397073:key/0f8468f0-f135-4226-aa0b-bd05c4c30df5", + "region": "us-east-1" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 1, + "modifiedCount": 1, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$eq": "aws:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name2", + "key": "arn:aws:kms:us-east-1:857654397073:key/0f8468f0-f135-4226-aa0b-bd05c4c30df5", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/src/test/spec/json/client-side-encryption/unified/namedKMS-rewrapManyDataKey.yml b/src/test/spec/json/client-side-encryption/unified/namedKMS-rewrapManyDataKey.yml new file mode 100644 index 000000000..b776ed4dd --- /dev/null +++ b/src/test/spec/json/client-side-encryption/unified/namedKMS-rewrapManyDataKey.yml @@ -0,0 +1,432 @@ +description: namedKMS-rewrapManyDataKey + +schemaVersion: "1.18" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + "aws:name1": { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + "azure:name1": { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } + "gcp:name1": { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } + "kmip:name1": { endpoint: { $$placeholder: 1 } } + "local:name1": { key: { $$placeholder: 1 } } + # Use a different local master key for `local:name2`. + "local:name2": { key: "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" } + # Use a different AWS account to test wrapping between different AWS accounts. + "aws:name2": { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - _id: &aws:name1_key_id { $binary: { base64: YXdzYXdzYXdzYXdzYXdzYQ==, subType: "04" } } + keyAltNames: ["aws:name1_key"] + keyMaterial: { $binary: { base64: AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &aws:name1_masterkey + provider: aws:name1 + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + region: us-east-1 + - _id: &azure:name1_key_id { $binary: { base64: YXp1cmVhenVyZWF6dXJlYQ==, subType: "04" } } + keyAltNames: ["azure:name1_key"] + keyMaterial: { $binary: { base64: pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &azure:name1_masterkey + provider: "azure:name1" + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + - _id: &gcp:name1_key_id { $binary: { base64: Z2NwZ2NwZ2NwZ2NwZ2NwZw==, subType: "04" } } + keyAltNames: ["gcp:name1_key"] + keyMaterial: { $binary: { base64: CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &gcp:name1_masterkey + provider: "gcp:name1" + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + - _id: &kmip:name1_key_id { $binary: { base64: a21pcGttaXBrbWlwa21pcA==, subType: "04" } } + keyAltNames: ["kmip:name1_key"] + keyMaterial: { $binary: { base64: CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &kmip:name1_masterkey + provider: "kmip:name1" + keyId: "1" + - _id: &local:name1_key_id { $binary: { base64: bG9jYWxrZXlsb2NhbGtleQ==, subType: "04" } } + keyAltNames: ["local:name1_key"] + keyMaterial: { $binary: { base64: ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &local:name1_masterkey + provider: "local:name1" + +tests: + - description: "rewrap to aws:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: "aws:name1_key" } } + opts: + provider: "aws:name1" + # Different key: 89fcc2c4-08b0-4bd9-9f25-e30687b580d0 -> 061334ae-07a8-4ceb-a813-8135540e837d. + masterKey: &new_aws_masterkey + key: "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d" + region: us-east-1 + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "aws:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to azure:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: azure:name1_key } } + opts: + provider: "azure:name1" + masterKey: &new_azure_masterkey + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: azure:name1_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to gcp:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: gcp:name1_key } } + opts: + provider: "gcp:name1" + masterKey: &new_gcp_masterkey + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "gcp:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to kmip:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: "kmip:name1_key" } } + opts: + provider: "kmip:name1" + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "kmip:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to local:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: "local:name1_key" } } + opts: + provider: "local:name1" + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "local:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap from local:name1 to local:name2" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $eq: "local:name1_key" } } + opts: + provider: "local:name2" + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 1 + modifiedCount: 1 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $eq: "local:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "local:name2" }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap from aws:name1 to aws:name2" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $eq: "aws:name1_key" } } + opts: + provider: "aws:name2" + masterKey: &new_awsname2_masterkey + # aws:name1 account does not have permissions to access this key. + key: "arn:aws:kms:us-east-1:857654397073:key/0f8468f0-f135-4226-aa0b-bd05c4c30df5" + region: us-east-1 + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 1 + modifiedCount: 1 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $eq: "aws:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "aws:name2", <<: *new_awsname2_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } diff --git a/src/test/spec/json/client-side-encryption/unified/rewrapManyDataKey-decrypt_failure.yml b/src/test/spec/json/client-side-encryption/unified/rewrapManyDataKey-decrypt_failure.yml index 3a6e52188..d61912ecf 100644 --- a/src/test/spec/json/client-side-encryption/unified/rewrapManyDataKey-decrypt_failure.yml +++ b/src/test/spec/json/client-side-encryption/unified/rewrapManyDataKey-decrypt_failure.yml @@ -43,7 +43,7 @@ initialData: masterKey: provider: aws # "us-east-1" changed to "us-east-2" in both key and region. - key: arn:aws:kms:us-east-2:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0 + key: "arn:aws:kms:us-east-2:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" region: us-east-2 tests: diff --git a/src/test/spec/json/client-side-encryption/unified/rewrapManyDataKey-encrypt_failure.yml b/src/test/spec/json/client-side-encryption/unified/rewrapManyDataKey-encrypt_failure.yml index b947c8ce7..0ec3ebb56 100644 --- a/src/test/spec/json/client-side-encryption/unified/rewrapManyDataKey-encrypt_failure.yml +++ b/src/test/spec/json/client-side-encryption/unified/rewrapManyDataKey-encrypt_failure.yml @@ -54,7 +54,7 @@ tests: provider: aws masterKey: # "us-east-1" changed to "us-east-2" in both key and region. - key: arn:aws:kms:us-east-2:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0 + key: "arn:aws:kms:us-east-2:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" region: us-east-2 expectError: isClientError: true diff --git a/src/test/spec/unified_runner/operation.rs b/src/test/spec/unified_runner/operation.rs index 8475b57c3..88a45f026 100644 --- a/src/test/spec/unified_runner/operation.rs +++ b/src/test/spec/unified_runner/operation.rs @@ -430,6 +430,10 @@ impl<'de> Deserialize<'de> for Operation { "listSearchIndexes" => deserialize_op::(definition.arguments), "updateSearchIndex" => deserialize_op::(definition.arguments), "clientBulkWrite" => deserialize_op::(definition.arguments), + #[cfg(feature = "in-use-encryption-unstable")] + "encrypt" => deserialize_op::(definition.arguments), + #[cfg(feature = "in-use-encryption-unstable")] + "decrypt" => deserialize_op::(definition.arguments), s => Ok(Box::new(UnimplementedOperation { _name: s.to_string(), }) as Box), diff --git a/src/test/spec/unified_runner/operation/csfle.rs b/src/test/spec/unified_runner/operation/csfle.rs index 647e17314..f095949ae 100644 --- a/src/test/spec/unified_runner/operation/csfle.rs +++ b/src/test/spec/unified_runner/operation/csfle.rs @@ -1,14 +1,15 @@ use std::fmt::Debug; use futures::{future::BoxFuture, stream::TryStreamExt, FutureExt}; +use mongocrypt::ctx::Algorithm; use serde::Deserialize; use super::{Entity, TestOperation, TestRunner}; use crate::{ action::csfle::DataKeyOptions, - bson::{doc, Bson}, - client_encryption::MasterKey, + bson::{doc, Binary, Bson, RawBson}, + client_encryption::{LocalMasterKey, MasterKey}, error::Result, }; @@ -142,7 +143,7 @@ impl<'de> Deserialize<'de> for CreateDataKey { .as_ref() .and_then(|o| o.master_key.as_ref()) .cloned() - .unwrap_or(MasterKey::Local), + .unwrap_or(LocalMasterKey::builder().build().into()), opts: t_op.opts.map(|to| DataKeyOptions { key_alt_names: to.key_alt_names, key_material: to.key_material.map(|bin| bin.bytes), @@ -217,3 +218,70 @@ impl TestOperation for RemoveKeyAltName { .boxed() } } + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub(super) struct Encrypt { + value: RawBson, + opts: EncryptOptions, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct EncryptOptions { + key_alt_name: String, + algorithm: String, +} + +fn algorithm_from_string(algorithm: &str) -> Algorithm { + match algorithm { + "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" => Algorithm::Deterministic, + other => panic!("unsupported encrypt algorithm: {}", other), + } +} + +impl TestOperation for Encrypt { + fn execute_entity_operation<'a>( + &'a self, + id: &'a str, + test_runner: &'a TestRunner, + ) -> BoxFuture<'a, Result>> { + async move { + let client_encryption = test_runner.get_client_encryption(id).await; + let algorithm = algorithm_from_string(self.opts.algorithm.as_str()); + + let encrypted_value = client_encryption + .encrypt( + self.value.clone(), + self.opts.key_alt_name.clone(), + algorithm, + ) + .await?; + + Ok(Some(Bson::Binary(encrypted_value).into())) + } + .boxed() + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub(super) struct Decrypt { + value: Binary, +} + +impl TestOperation for Decrypt { + fn execute_entity_operation<'a>( + &'a self, + id: &'a str, + test_runner: &'a TestRunner, + ) -> BoxFuture<'a, Result>> { + async move { + let client_encryption = test_runner.get_client_encryption(id).await; + let raw_value = self.value.as_raw_binary(); + let decrypted_value: Bson = client_encryption.decrypt(raw_value).await?.try_into()?; + Ok(Some(decrypted_value.into())) + } + .boxed() + } +} diff --git a/src/test/spec/unified_runner/test_file.rs b/src/test/spec/unified_runner/test_file.rs index 51a5add30..0892c8e75 100644 --- a/src/test/spec/unified_runner/test_file.rs +++ b/src/test/spec/unified_runner/test_file.rs @@ -344,7 +344,7 @@ pub(crate) struct ClientEncryption { pub(crate) struct ClientEncryptionOpts { pub(crate) key_vault_client: String, pub(crate) key_vault_namespace: crate::Namespace, - pub(crate) kms_providers: Document, + pub(crate) kms_providers: HashMap, } /// Messages used for communicating with test runner "threads". diff --git a/src/test/spec/unified_runner/test_runner.rs b/src/test/spec/unified_runner/test_runner.rs index 702ef9776..de7b16020 100644 --- a/src/test/spec/unified_runner/test_runner.rs +++ b/src/test/spec/unified_runner/test_runner.rs @@ -48,9 +48,6 @@ use super::{ TestFileEntity, }; -#[cfg(feature = "in-use-encryption-unstable")] -use crate::test::KmsProviderList; - #[cfg(feature = "tracing-unstable")] use crate::test::{ spec::unified_runner::matcher::tracing_events_match, @@ -305,9 +302,9 @@ impl TestRunner { for (actual, expected) in actual_events.iter().zip(expected_events) { if let Err(e) = events_match(actual, expected, Some(&entities)) { panic!( - "event mismatch: expected = {:#?}, actual = {:#?}\nmismatch \ + "event mismatch in {}: expected = {:#?}, actual = {:#?}\nmismatch \ detail: {}", - expected, actual, e, + test_case.description, expected, actual, e, ); } } @@ -607,9 +604,7 @@ impl TestRunner { .client() .unwrap() .clone(); - let kms_providers: HashMap = - bson::from_document(opts.kms_providers.clone()).unwrap(); - let kms_providers = fill_kms_placeholders(kms_providers); + let kms_providers = fill_kms_placeholders(opts.kms_providers.clone()); let client_enc = crate::client_encryption::ClientEncryption::new( kv_client, opts.key_vault_namespace.clone(), @@ -749,32 +744,31 @@ impl TestRunner { #[cfg(feature = "in-use-encryption-unstable")] fn fill_kms_placeholders( - kms_providers: HashMap, -) -> KmsProviderList { - use crate::test::KMS_PROVIDERS_MAP; + kms_provider_map: HashMap, +) -> crate::test::csfle::KmsProviderList { + use crate::{bson::doc, test::csfle::ALL_KMS_PROVIDERS}; - let placeholder = bson::Bson::Document(doc! { "$$placeholder": 1 }); + let placeholder = doc! { "$$placeholder": 1 }; + let all_kms_providers = ALL_KMS_PROVIDERS.clone(); + + let mut kms_providers = Vec::new(); + for (provider, mut config) in kms_provider_map { + let test_kms_provider = all_kms_providers.iter().find(|(p, ..)| p == &provider); - let mut out = vec![]; - for (provider, mut config) in kms_providers { for (key, value) in config.iter_mut() { - if *value == placeholder { - let new_value = KMS_PROVIDERS_MAP - .get(&provider) - .unwrap_or_else(|| panic!("missing config for {:?}", provider)) - .0 - .get(key) - .unwrap_or_else(|| { - panic!("provider config {:?} missing key {:?}", provider, key) - }) - .clone(); - *value = new_value; + if value.as_document() == Some(&placeholder) { + let test_kms_provider = test_kms_provider + .unwrap_or_else(|| panic!("missing config for {:?}", provider)); + let placeholder_value = test_kms_provider.1.get(key).unwrap_or_else(|| { + panic!("provider config {:?} missing key {:?}", provider, key) + }); + *value = placeholder_value.clone(); } } - let tls = KMS_PROVIDERS_MAP - .get(&provider) - .and_then(|(_, t)| t.clone()); - out.push((provider, config, tls)); + + let tls_options = test_kms_provider.and_then(|(_, _, tls_options)| tls_options.clone()); + kms_providers.push((provider, config, tls_options)); } - out + + kms_providers } diff --git a/src/test/spec/v2_runner/csfle.rs b/src/test/spec/v2_runner/csfle.rs index f80e23172..3b6424006 100644 --- a/src/test/spec/v2_runner/csfle.rs +++ b/src/test/spec/v2_runner/csfle.rs @@ -4,7 +4,7 @@ use mongocrypt::ctx::KmsProvider; use crate::{ coll::options::CollectionOptions, options::WriteConcern, - test::{util::TestClientBuilder, KMS_PROVIDERS_MAP}, + test::{csfle::AWS_KMS, util::TestClientBuilder}, Client, }; @@ -37,55 +37,41 @@ pub(crate) fn set_auto_enc(builder: TestClientBuilder, test: &Test) -> TestClien } else { return builder; }; + let kms_providers = &mut enc_opts.kms_providers; - for prov in [ - KmsProvider::Aws, - KmsProvider::Azure, - KmsProvider::Gcp, - KmsProvider::Kmip, - ] { - if kms_providers.credentials().contains_key(&prov) { - let opts = KMS_PROVIDERS_MAP.get(&prov).unwrap().clone(); - kms_providers.set(prov, opts.0, opts.1); - } - } - let aws_tls = KMS_PROVIDERS_MAP - .get(&KmsProvider::Aws) - .and_then(|(_, t)| t.as_ref()); + kms_providers.set_test_options(); + let aws_id = std::env::var("CSFLE_AWS_TEMP_ACCESS_KEY_ID").ok(); let aws_key = std::env::var("CSFLE_AWS_TEMP_SECRET_ACCESS_KEY").ok(); let aws_token = std::env::var("CSFLE_AWS_TEMP_SESSION_TOKEN").ok(); + if kms_providers .credentials() - .contains_key(&KmsProvider::Other("awsTemporary".to_string())) + .contains_key(&KmsProvider::other("awsTemporary")) { kms_providers.set( - KmsProvider::Aws, + KmsProvider::aws(), doc! { "accessKeyId": aws_id.unwrap(), "secretAccessKey": aws_key.unwrap(), "sessionToken": aws_token.unwrap(), }, - aws_tls.cloned(), + AWS_KMS.clone().2, ); - kms_providers.clear(&KmsProvider::Other("awsTemporary".to_string())); + kms_providers.clear(&KmsProvider::other("awsTemporary")); } else if kms_providers .credentials() - .contains_key(&KmsProvider::Other( - "awsTemporaryNoSessionToken".to_string(), - )) + .contains_key(&KmsProvider::other("awsTemporaryNoSessionToken")) { kms_providers.set( - KmsProvider::Aws, + KmsProvider::aws(), doc! { "accessKeyId": aws_id.unwrap(), "secretAccessKey": aws_key.unwrap(), }, - aws_tls.cloned(), + AWS_KMS.clone().2, ); - kms_providers.clear(&KmsProvider::Other( - "awsTemporaryNoSessionToken".to_string(), - )); + kms_providers.clear(&KmsProvider::other("awsTemporaryNoSessionToken")); } builder.encrypted_options(enc_opts) }