Skip to content

Commit 08e2923

Browse files
RUST-1813 Support named KMS providers (#1141)
1 parent e6e1827 commit 08e2923

36 files changed

+3839
-650
lines changed

src/action/csfle/create_data_key.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ impl ClientEncryption {
88
/// `await` will return d[`Result<Binary>`] (subtype 0x04) with the _id of the created
99
/// document as a UUID.
1010
#[deeplink]
11-
pub fn create_data_key(&self, master_key: MasterKey) -> CreateDataKey {
11+
pub fn create_data_key(&self, master_key: impl Into<MasterKey>) -> CreateDataKey {
1212
CreateDataKey {
1313
client_enc: self,
14-
master_key,
14+
master_key: master_key.into(),
1515
options: None,
1616
#[cfg(test)]
1717
test_kms_provider: None,

src/client/csfle/client_encryption.rs

Lines changed: 159 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod encrypt;
55

66
use mongocrypt::{ctx::KmsProvider, Crypt};
77
use serde::{Deserialize, Serialize};
8+
use typed_builder::TypedBuilder;
89

910
use crate::{
1011
bson::{doc, spec::BinarySubtype, Binary, RawBinaryRef, RawDocumentBuf},
@@ -187,63 +188,174 @@ impl ClientEncryption {
187188
}
188189

189190
/// A KMS-specific key used to encrypt data keys.
190-
#[serde_with::skip_serializing_none]
191191
#[derive(Debug, Clone, Serialize, Deserialize)]
192192
#[serde(untagged)]
193193
#[non_exhaustive]
194194
#[allow(missing_docs)]
195195
pub enum MasterKey {
196-
#[serde(rename_all = "camelCase")]
197-
Aws {
198-
region: String,
199-
/// The Amazon Resource Name (ARN) to the AWS customer master key (CMK).
200-
key: String,
201-
/// An alternate host identifier to send KMS requests to. May include port number. Defaults
202-
/// to "kms.REGION.amazonaws.com"
203-
endpoint: Option<String>,
204-
},
205-
#[serde(rename_all = "camelCase")]
206-
Azure {
207-
/// Host with optional port. Example: "example.vault.azure.net".
208-
key_vault_endpoint: String,
209-
key_name: String,
210-
/// A specific version of the named key, defaults to using the key's primary version.
211-
key_version: Option<String>,
212-
},
213-
#[serde(rename_all = "camelCase")]
214-
Gcp {
215-
project_id: String,
216-
location: String,
217-
key_ring: String,
218-
key_name: String,
219-
/// A specific version of the named key, defaults to using the key's primary version.
220-
key_version: Option<String>,
221-
/// Host with optional port. Defaults to "cloudkms.googleapis.com".
222-
endpoint: Option<String>,
223-
},
224-
/// Master keys are not applicable to `KmsProvider::Local`.
225-
Local,
226-
#[serde(rename_all = "camelCase")]
227-
Kmip {
228-
/// keyId is the KMIP Unique Identifier to a 96 byte KMIP Secret Data managed object. If
229-
/// keyId is omitted, the driver creates a random 96 byte KMIP Secret Data managed object.
230-
key_id: Option<String>,
231-
/// If true (recommended), the KMIP server must decrypt this key. Defaults to false.
232-
delegated: Option<bool>,
233-
/// Host with optional port.
234-
endpoint: Option<String>,
235-
},
196+
Aws(AwsMasterKey),
197+
Azure(AzureMasterKey),
198+
Gcp(GcpMasterKey),
199+
Kmip(KmipMasterKey),
200+
Local(LocalMasterKey),
201+
}
202+
203+
/// An AWS master key.
204+
#[serde_with::skip_serializing_none]
205+
#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
206+
#[builder(field_defaults(default, setter(into)))]
207+
#[serde(rename_all = "camelCase")]
208+
#[non_exhaustive]
209+
pub struct AwsMasterKey {
210+
/// The name for the key. The value for this field must be the same as the corresponding
211+
/// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name.
212+
#[serde(skip)]
213+
pub name: Option<String>,
214+
215+
/// The region.
216+
pub region: String,
217+
218+
/// The Amazon Resource Name (ARN) to the AWS customer master key (CMK).
219+
pub key: String,
220+
221+
/// An alternate host identifier to send KMS requests to. May include port number. Defaults to
222+
/// "kms.\<region\>.amazonaws.com".
223+
pub endpoint: Option<String>,
224+
}
225+
226+
impl From<AwsMasterKey> for MasterKey {
227+
fn from(aws_master_key: AwsMasterKey) -> Self {
228+
Self::Aws(aws_master_key)
229+
}
230+
}
231+
232+
/// An Azure master key.
233+
#[serde_with::skip_serializing_none]
234+
#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
235+
#[builder(field_defaults(default, setter(into)))]
236+
#[serde(rename_all = "camelCase")]
237+
#[non_exhaustive]
238+
pub struct AzureMasterKey {
239+
/// The name for the key. The value for this field must be the same as the corresponding
240+
/// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name.
241+
#[serde(skip)]
242+
pub name: Option<String>,
243+
244+
/// Host with optional port. Example: "example.vault.azure.net".
245+
pub key_vault_endpoint: String,
246+
247+
/// The key name.
248+
pub key_name: String,
249+
250+
/// A specific version of the named key, defaults to using the key's primary version.
251+
pub key_version: Option<String>,
252+
}
253+
254+
impl From<AzureMasterKey> for MasterKey {
255+
fn from(azure_master_key: AzureMasterKey) -> Self {
256+
Self::Azure(azure_master_key)
257+
}
258+
}
259+
260+
/// A GCP master key.
261+
#[serde_with::skip_serializing_none]
262+
#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
263+
#[builder(field_defaults(default, setter(into)))]
264+
#[serde(rename_all = "camelCase")]
265+
#[non_exhaustive]
266+
pub struct GcpMasterKey {
267+
/// The name for the key. The value for this field must be the same as the corresponding
268+
/// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name.
269+
#[serde(skip)]
270+
pub name: Option<String>,
271+
272+
/// The project ID.
273+
pub project_id: String,
274+
275+
/// The location.
276+
pub location: String,
277+
278+
/// The key ring.
279+
pub key_ring: String,
280+
281+
/// The key name.
282+
pub key_name: String,
283+
284+
/// A specific version of the named key. Defaults to using the key's primary version.
285+
pub key_version: Option<String>,
286+
287+
/// Host with optional port. Defaults to "cloudkms.googleapis.com".
288+
pub endpoint: Option<String>,
289+
}
290+
291+
impl From<GcpMasterKey> for MasterKey {
292+
fn from(gcp_master_key: GcpMasterKey) -> Self {
293+
Self::Gcp(gcp_master_key)
294+
}
295+
}
296+
297+
/// A local master key.
298+
#[serde_with::skip_serializing_none]
299+
#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
300+
#[builder(field_defaults(default, setter(into)))]
301+
#[serde(rename_all = "camelCase")]
302+
#[non_exhaustive]
303+
pub struct LocalMasterKey {
304+
/// The name for the key. The value for this field must be the same as the corresponding
305+
/// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name.
306+
#[serde(skip)]
307+
pub name: Option<String>,
308+
}
309+
310+
impl From<LocalMasterKey> for MasterKey {
311+
fn from(local_master_key: LocalMasterKey) -> Self {
312+
Self::Local(local_master_key)
313+
}
314+
}
315+
316+
/// A KMIP master key.
317+
#[serde_with::skip_serializing_none]
318+
#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
319+
#[builder(field_defaults(default, setter(into)))]
320+
#[serde(rename_all = "camelCase")]
321+
#[non_exhaustive]
322+
pub struct KmipMasterKey {
323+
/// The name for the key. The value for this field must be the same as the corresponding
324+
/// [`KmsProvider`](mongocrypt::ctx::KmsProvider)'s name.
325+
#[serde(skip)]
326+
pub name: Option<String>,
327+
328+
/// The KMIP Unique Identifier to a 96 byte KMIP Secret Data managed object. If this field is
329+
/// not specified, the driver creates a random 96 byte KMIP Secret Data managed object.
330+
pub key_id: Option<String>,
331+
332+
/// If true (recommended), the KMIP server must decrypt this key. Defaults to false.
333+
pub delegated: Option<bool>,
334+
335+
/// Host with optional port.
336+
pub endpoint: Option<String>,
337+
}
338+
339+
impl From<KmipMasterKey> for MasterKey {
340+
fn from(kmip_master_key: KmipMasterKey) -> Self {
341+
Self::Kmip(kmip_master_key)
342+
}
236343
}
237344

238345
impl MasterKey {
239346
/// Returns the `KmsProvider` associated with this key.
240347
pub fn provider(&self) -> KmsProvider {
241-
match self {
242-
MasterKey::Aws { .. } => KmsProvider::Aws,
243-
MasterKey::Azure { .. } => KmsProvider::Azure,
244-
MasterKey::Gcp { .. } => KmsProvider::Gcp,
245-
MasterKey::Kmip { .. } => KmsProvider::Kmip,
246-
MasterKey::Local => KmsProvider::Local,
348+
let (provider, name) = match self {
349+
MasterKey::Aws(AwsMasterKey { name, .. }) => (KmsProvider::aws(), name.clone()),
350+
MasterKey::Azure(AzureMasterKey { name, .. }) => (KmsProvider::azure(), name.clone()),
351+
MasterKey::Gcp(GcpMasterKey { name, .. }) => (KmsProvider::gcp(), name.clone()),
352+
MasterKey::Kmip(KmipMasterKey { name, .. }) => (KmsProvider::kmip(), name.clone()),
353+
MasterKey::Local(LocalMasterKey { name, .. }) => (KmsProvider::local(), name.clone()),
354+
};
355+
if let Some(name) = name {
356+
provider.with_name(name)
357+
} else {
358+
provider
247359
}
248360
}
249361
}

src/client/csfle/client_encryption/create_data_key.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ impl ClientEncryption {
4242
opts: Option<DataKeyOptions>,
4343
) -> Result<Ctx> {
4444
let mut builder = self.crypt.ctx_builder();
45-
let mut key_doc = doc! { "provider": kms_provider.name() };
46-
if !matches!(master_key, MasterKey::Local) {
45+
let mut key_doc = doc! { "provider": kms_provider.as_string() };
46+
if !matches!(master_key, MasterKey::Local(_)) {
4747
let master_doc = bson::to_document(&master_key)?;
4848
key_doc.extend(master_doc);
4949
}

src/client/csfle/options.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,49 @@ impl KmsProviders {
130130
&self.credentials
131131
}
132132

133+
#[cfg(test)]
134+
pub(crate) fn set_test_options(&mut self) {
135+
use mongocrypt::ctx::KmsProviderType;
136+
137+
use crate::{
138+
bson::doc,
139+
test::csfle::{ALL_KMS_PROVIDERS, AWS_KMS},
140+
};
141+
142+
let all_kms_providers = ALL_KMS_PROVIDERS.clone();
143+
for (provider, test_credentials, tls_options) in all_kms_providers {
144+
if self.credentials.contains_key(&provider)
145+
&& !matches!(provider.provider_type(), KmsProviderType::Local)
146+
{
147+
self.set(provider, test_credentials, tls_options);
148+
}
149+
}
150+
151+
let aws_temp_provider = KmsProvider::other("awsTemporary".to_string());
152+
if self.credentials.contains_key(&aws_temp_provider) {
153+
let aws_credentials = doc! {
154+
"accessKeyId": std::env::var("CSFLE_AWS_TEMP_ACCESS_KEY_ID").unwrap(),
155+
"secretAccessKey": std::env::var("CSFLE_AWS_TEMP_SECRET_ACCESS_KEY").unwrap(),
156+
"sessionToken": std::env::var("CSFLE_AWS_TEMP_SESSION_TOKEN").unwrap()
157+
};
158+
self.set(KmsProvider::aws(), aws_credentials, AWS_KMS.clone().2);
159+
self.clear(&aws_temp_provider);
160+
}
161+
162+
let aws_temp_no_session_token_provider = KmsProvider::other("awsTemporaryNoSessionToken");
163+
if self
164+
.credentials
165+
.contains_key(&aws_temp_no_session_token_provider)
166+
{
167+
let aws_credentials = doc! {
168+
"accessKeyId": std::env::var("CSFLE_AWS_TEMP_ACCESS_KEY_ID").unwrap(),
169+
"secretAccessKey": std::env::var("CSFLE_AWS_TEMP_SECRET_ACCESS_KEY").unwrap(),
170+
};
171+
self.set(KmsProvider::aws(), aws_credentials, AWS_KMS.clone().2);
172+
self.clear(&aws_temp_no_session_token_provider);
173+
}
174+
}
175+
133176
#[cfg(test)]
134177
pub(crate) fn set(&mut self, provider: KmsProvider, creds: Document, tls: Option<TlsOptions>) {
135178
self.credentials.insert(provider.clone(), creds);

0 commit comments

Comments
 (0)