Skip to content

Commit 5ad1c61

Browse files
authored
RUST-1386 Implement the first batch of FLE prose tests (#758)
1 parent 7b62ff3 commit 5ad1c61

29 files changed

+27755
-51
lines changed

.evergreen/config.yml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ functions:
5555
export MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin"
5656
export UPLOAD_BUCKET="${project}"
5757
export PROJECT_DIRECTORY="$(pwd)"
58+
export MONGOCRYPT_LIB_DIR="$PROJECT_DIRECTORY/libmongocrypt/${LIBMONGOCRYPT_OS}/lib"
59+
export LD_LIBRARY_PATH="$MONGOCRYPT_LIB_DIR:$LD_LIBRARY_PATH"
5860
5961
cat <<EOT > expansion.yml
6062
CURRENT_VERSION: "$CURRENT_VERSION"
@@ -72,6 +74,8 @@ functions:
7274
export UPLOAD_BUCKET="$UPLOAD_BUCKET"
7375
export PROJECT_DIRECTORY="$PROJECT_DIRECTORY"
7476
export DRIVERS_TOOLS_X509="$DRIVERS_TOOLS/.evergreen/x509gen"
77+
export MONGOCRYPT_LIB_DIR="$MONGOCRYPT_LIB_DIR"
78+
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH"
7579
7680
export TMPDIR="$MONGO_ORCHESTRATION_HOME/db"
7781
export PATH="$MONGODB_BINARIES:$PATH"
@@ -81,6 +85,7 @@ functions:
8185
export SSL=${SSL}
8286
export TOPOLOGY=${TOPOLOGY}
8387
export MONGODB_VERSION=${MONGODB_VERSION}
88+
export CRYPT_SHARED_VERSION=${CRYPT_SHARED_VERSION}
8489
8590
if [ "Windows_NT" != "$OS" ]; then
8691
ulimit -n 64000
@@ -344,6 +349,14 @@ functions:
344349
${PREPARE_SHELL}
345350
.evergreen/install-dependencies.sh rust
346351
352+
"install libmongocrypt":
353+
command: shell.exec
354+
params:
355+
working_dir: "src"
356+
script: |
357+
${PREPARE_SHELL}
358+
.evergreen/install-dependencies.sh libmongocrypt
359+
347360
"install junit dependencies":
348361
command: shell.exec
349362
params:
@@ -460,6 +473,52 @@ functions:
460473
MONGO_OCSP_TESTS=1 \
461474
.evergreen/run-ocsp-test.sh
462475
476+
"fetch crypt_shared":
477+
- command: shell.exec
478+
type: test
479+
params:
480+
shell: bash
481+
working_dir: "src"
482+
script: |
483+
${PREPARE_SHELL}
484+
python3 ${DRIVERS_TOOLS}/.evergreen/mongodl.py --component=crypt_shared --version=${CRYPT_SHARED_VERSION} --out=./crypt_shared/
485+
ls -R ${PROJECT_DIRECTORY}/crypt_shared
486+
487+
"run kmip server":
488+
- command: shell.exec
489+
type: test
490+
params:
491+
shell: bash
492+
working_dir: "src"
493+
background: true
494+
script: |
495+
${PREPARE_SHELL}
496+
cd ${DRIVERS_TOOLS}/.evergreen/csfle
497+
. ./activate_venv.sh
498+
# TMPDIR is required to avoid "AF_UNIX path too long" errors.
499+
TMPDIR="$(dirname ${DRIVERS_TOOLS})" python3 kms_kmip_server.py
500+
501+
"run csfle tests":
502+
- command: shell.exec
503+
type: test
504+
params:
505+
shell: bash
506+
working_dir: "src"
507+
script: |
508+
${PREPARE_SHELL}
509+
export ASYNC_RUNTIME=${ASYNC_RUNTIME}
510+
export CSFLE_TLS_CERT_DIR=$DRIVERS_TOOLS_X509
511+
export CSFLE_SHARED_LIB_PATH=$(.evergreen/find-crypt_shared.sh "$PROJECT_DIRECTORY/crypt_shared/lib/")
512+
# Exported without xtrace to avoid leaking credentials
513+
set +o xtrace
514+
export KMS_PROVIDERS=$(cat << "EOF"
515+
${kms_providers}
516+
EOF
517+
)
518+
set -o xtrace
519+
520+
.evergreen/run-csfle-tests.sh
521+
463522
run-valid-ocsp-server:
464523
- command: shell.exec
465524
params:
@@ -713,6 +772,7 @@ pre:
713772
- func: "init test-results"
714773
- func: "make files executable"
715774
- func: "install rust"
775+
- func: "install libmongocrypt"
716776

717777
post:
718778
- func: "stop load balancer"
@@ -1111,6 +1171,17 @@ tasks:
11111171
- func: "install junit dependencies"
11121172
- func: "run serverless tests"
11131173

1174+
- name: "test-csfle"
1175+
tags: ["csfle"]
1176+
commands:
1177+
- func: "install junit dependencies"
1178+
- func: "fetch crypt_shared"
1179+
- func: "bootstrap mongo-orchestration"
1180+
vars:
1181+
TOPOLOGY: "server"
1182+
- func: "run kmip server"
1183+
- func: "run csfle tests"
1184+
11141185
- name: "test-atlas-connectivity"
11151186
tags: ["atlas-connect"]
11161187
commands:
@@ -1506,6 +1577,7 @@ axes:
15061577
display_name: "6.0"
15071578
variables:
15081579
MONGODB_VERSION: "6.0"
1580+
CRYPT_SHARED_VERSION: "6.0.0"
15091581
- id: "5.0"
15101582
display_name: "5.0"
15111583
variables:
@@ -1608,31 +1680,36 @@ axes:
16081680
variables:
16091681
PYTHON: "/opt/mongodbtoolchain/v3/bin/python"
16101682
VENV_BIN_DIR: "bin"
1683+
LIBMONGOCRYPT_OS: "ubuntu1804-64"
16111684
- id: ubuntu-20.04
16121685
display_name: "Ubuntu 20.04"
16131686
run_on: ubuntu2004-test
16141687
variables:
16151688
PYTHON: "/opt/mongodbtoolchain/v3/bin/python"
16161689
VENV_BIN_DIR: "bin"
1690+
LIBMONGOCRYPT_OS: "ubuntu2004-64"
16171691
- id: ubuntu-18.04-arm64
16181692
display_name: "ARM64 Ubuntu 18.04"
16191693
run_on: ubuntu1804-arm64-test
16201694
variables:
16211695
PYTHON: "/opt/mongodbtoolchain/v3/bin/python"
16221696
VENV_BIN_DIR: "bin"
1697+
LIBMONGOCRYPT_OS: "ubuntu1804-arm64"
16231698
- id: macos-11.00
16241699
display_name: "MacOS 11.00"
16251700
run_on: macos-1100
16261701
variables:
16271702
SINGLE_THREAD: true
16281703
PYTHON: "/opt/mongodbtoolchain/v3/bin/python"
16291704
VENV_BIN_DIR: "bin"
1705+
LIBMONGOCRYPT_OS: "macos"
16301706
- id: windows-64-vs2017
16311707
display_name: "Windows (VS 2017)"
16321708
run_on: windows-64-vs2017-test
16331709
variables:
16341710
PYTHON: "/cygdrive/c/python/Python36/python"
16351711
VENV_BIN_DIR: "Scripts"
1712+
LIBMONGOCRYPT_OS: "windows-test"
16361713

16371714
- id: "versioned-api"
16381715
display_name: "Versioned API"
@@ -1791,6 +1868,17 @@ buildvariants:
17911868
tasks:
17921869
- "serverless_task_group"
17931870

1871+
# TODO(RUST-1386): Expand the os and version listing
1872+
- matrix_name: "csfle"
1873+
matrix_spec:
1874+
os:
1875+
- ubuntu-20.04
1876+
mongodb-version:
1877+
- "6.0"
1878+
display_name: "CSFLE on mongodb ${mongodb-version} / ${os}"
1879+
tasks:
1880+
- "test-csfle"
1881+
17941882
- matrix_name: "atlas-connect"
17951883
matrix_spec:
17961884
os:

.evergreen/find-crypt_shared.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env bash
2+
3+
if [ "$1" = "" ]; then
4+
echo "crypt_shared library directory required"
5+
exit 1
6+
fi
7+
8+
crypt_shared_glob=("$1"/*)
9+
10+
if [ "${#crypt_shared_glob[@]}" != "1" ]; then
11+
echo "Wrong number of files found: ${crypt_shared_glob[@]}"
12+
exit 1
13+
fi
14+
15+
echo ${crypt_shared_glob[0]}

.evergreen/install-dependencies.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ for arg; do
5858
if [ $RESULT -ne 0 ]; then
5959
exit $RESULT
6060
fi
61+
elif [ $arg == "libmongocrypt" ]; then
62+
mkdir ${PROJECT_DIRECTORY}/libmongocrypt
63+
cd ${PROJECT_DIRECTORY}/libmongocrypt
64+
curl -sSfO https://s3.amazonaws.com/mciuploads/libmongocrypt/all/master/latest/libmongocrypt-all.tar.gz
65+
tar xzf libmongocrypt-all.tar.gz
66+
if [ "Windows_NT" == "$OS" ]; then
67+
chmod +x ${MONGOCRYPT_LIB_DIR}/../bin/*.dll
68+
fi
6169
else
6270
echo Missing/unknown install option: "$arg"
6371
exit 1

.evergreen/run-csfle-tests.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
3+
set -o errexit
4+
set -o pipefail
5+
6+
source ./.evergreen/env.sh
7+
8+
set -o xtrace
9+
10+
FEATURE_FLAGS="openssl-tls,csfle"
11+
OPTIONS="-- -Z unstable-options --format json --report-time"
12+
13+
if [ "$SINGLE_THREAD" = true ]; then
14+
OPTIONS="$OPTIONS --test-threads=1"
15+
fi
16+
17+
echo "cargo test options: --features ${FEATURE_FLAGS} ${OPTIONS}"
18+
19+
CARGO_RESULT=0
20+
21+
cargo_test() {
22+
RUST_BACKTRACE=1 \
23+
cargo test --features ${FEATURE_FLAGS} $1 ${OPTIONS} | cargo2junit
24+
(( CARGO_RESULT = ${CARGO_RESULT} || $? ))
25+
}
26+
27+
set +o errexit
28+
29+
cargo_test test::csfle > results.xml
30+
31+
exit ${CARGO_RESULT}

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ version = "1.1.2"
165165
features = ["v4"]
166166

167167
[dev-dependencies]
168+
anyhow = { version = "1.0", features = ["backtrace"] }
168169
approx = "0.5.1"
169170
async_once = "0.2.6"
170171
ctrlc = "3.2.2"

src/client/csfle.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,11 @@ impl ClientState {
7878
}
7979

8080
fn make_crypt(opts: &AutoEncryptionOptions) -> Result<Crypt> {
81-
let mut builder = Crypt::builder();
81+
let mut builder =
82+
Crypt::builder().kms_providers(&bson::to_document(&opts.kms_providers)?)?;
83+
if let Some(m) = &opts.schema_map {
84+
builder = builder.schema_map(&bson::to_document(m)?)?;
85+
}
8286
if Some(true) != opts.bypass_auto_encryption {
8387
builder = builder.append_crypt_shared_lib_search_path(Path::new("$SYSTEM"))?;
8488
}

src/client/csfle/client_encryption.rs

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ pub struct ClientEncryption {
3535
impl ClientEncryption {
3636
/// Create a new key vault handle with the given options.
3737
pub fn new(options: ClientEncryptionOptions) -> Result<Self> {
38-
let crypt = Crypt::builder().build()?;
38+
let crypt = Crypt::builder()
39+
.kms_providers(&bson::to_document(&options.kms_providers)?)?
40+
.build()?;
3941
let exec = CryptExecutor::new_explicit(
4042
options.key_vault_client.weak(),
4143
options.key_vault_namespace.clone(),
@@ -62,10 +64,10 @@ impl ClientEncryption {
6264
/// Returns the _id of the created document as a UUID (BSON binary subtype 0x04).
6365
pub async fn create_data_key(
6466
&self,
65-
kms_provider: KmsProvider,
67+
kms_provider: &KmsProvider,
6668
opts: impl Into<Option<DataKeyOptions>>,
6769
) -> Result<Binary> {
68-
let ctx = self.create_data_key_ctx(kms_provider, opts.into())?;
70+
let ctx = self.create_data_key_ctx(kms_provider, opts.into().as_ref())?;
6971
let data_key = self.exec.run_ctx(ctx, None).await?;
7072
self.key_vault.insert_one(&data_key, None).await?;
7173
let bin_ref = data_key
@@ -76,8 +78,8 @@ impl ClientEncryption {
7678

7779
fn create_data_key_ctx(
7880
&self,
79-
kms_provider: KmsProvider,
80-
opts: Option<DataKeyOptions>,
81+
kms_provider: &KmsProvider,
82+
opts: Option<&DataKeyOptions>,
8183
) -> Result<Ctx> {
8284
let mut builder = self.crypt.ctx_builder();
8385
let mut key_doc = doc! { "provider": kms_provider.name() };
@@ -86,13 +88,13 @@ impl ClientEncryption {
8688
let master_doc = bson::to_document(&opts.master_key)?;
8789
key_doc.extend(master_doc);
8890
}
89-
if let Some(alt_names) = opts.key_alt_names {
91+
if let Some(alt_names) = &opts.key_alt_names {
9092
for name in alt_names {
91-
builder = builder.key_alt_name(&name)?;
93+
builder = builder.key_alt_name(name)?;
9294
}
9395
}
94-
if let Some(material) = opts.key_material {
95-
builder = builder.key_material(&material)?;
96+
if let Some(material) = &opts.key_material {
97+
builder = builder.key_material(material)?;
9698
}
9799
}
98100
builder = builder.key_encryption_key(&key_doc)?;
@@ -181,30 +183,30 @@ impl ClientEncryption {
181183
/// Encrypts a BsonValue with a given key and algorithm.
182184
/// Returns an encrypted value (BSON binary of subtype 6).
183185
pub async fn encrypt(&self, value: bson::RawBson, opts: EncryptOptions) -> Result<Binary> {
184-
let ctx = self.encrypt_ctx(value, opts)?;
186+
let ctx = self.encrypt_ctx(value, &opts)?;
185187
let result = self.exec.run_ctx(ctx, None).await?;
186188
let bin_ref = result
187189
.get_binary("v")
188190
.map_err(|e| Error::internal(format!("invalid encryption result: {}", e)))?;
189191
Ok(bin_ref.to_binary())
190192
}
191193

192-
fn encrypt_ctx(&self, value: bson::RawBson, opts: EncryptOptions) -> Result<Ctx> {
194+
fn encrypt_ctx(&self, value: bson::RawBson, opts: &EncryptOptions) -> Result<Ctx> {
193195
let mut builder = self.crypt.ctx_builder();
194-
match opts.key {
196+
match &opts.key {
195197
EncryptKey::Id(id) => {
196198
builder = builder.key_id(&id.bytes)?;
197199
}
198200
EncryptKey::AltName(name) => {
199-
builder = builder.key_alt_name(&name)?;
201+
builder = builder.key_alt_name(name)?;
200202
}
201203
}
202204
builder = builder.algorithm(opts.algorithm)?;
203205
if let Some(factor) = opts.contention_factor {
204206
builder = builder.contention_factor(factor)?;
205207
}
206-
if let Some(qtype) = opts.query_type {
207-
builder = builder.query_type(&qtype)?;
208+
if let Some(qtype) = &opts.query_type {
209+
builder = builder.query_type(qtype)?;
208210
}
209211
Ok(builder.build_explicit_encrypt(value)?)
210212
}
@@ -270,26 +272,30 @@ pub struct DataKeyOptions {
270272
}
271273

272274
/// A KMS-specific key used to encrypt data keys.
275+
#[serde_with::skip_serializing_none]
273276
#[derive(Debug, Clone, Serialize)]
274-
#[serde(rename_all = "camelCase", untagged)]
277+
#[serde(untagged)]
275278
#[non_exhaustive]
276279
#[allow(missing_docs)]
277280
pub enum MasterKey {
281+
#[serde(rename_all = "camelCase")]
278282
Aws {
279283
region: String,
280284
/// The Amazon Resource Name (ARN) to the AWS customer master key (CMK).
281285
key: String,
282286
/// An alternate host identifier to send KMS requests to. May include port number. Defaults
283-
/// to "kms.<region>.amazonaws.com"
287+
/// to "kms.REGION.amazonaws.com"
284288
endpoint: Option<String>,
285289
},
290+
#[serde(rename_all = "camelCase")]
286291
Azure {
287292
/// Host with optional port. Example: "example.vault.azure.net".
288293
key_vault_endpoint: String,
289294
key_name: String,
290295
/// A specific version of the named key, defaults to using the key's primary version.
291296
key_version: Option<String>,
292297
},
298+
#[serde(rename_all = "camelCase")]
293299
Gcp {
294300
project_id: String,
295301
location: String,
@@ -302,6 +308,7 @@ pub enum MasterKey {
302308
},
303309
/// Master keys are not applicable to `KmsProvider::Local`.
304310
Local,
311+
#[serde(rename_all = "camelCase")]
305312
Kmip {
306313
/// keyId is the KMIP Unique Identifier to a 96 byte KMIP Secret Data managed object. If
307314
/// keyId is omitted, the driver creates a random 96 byte KMIP Secret Data managed object.

0 commit comments

Comments
 (0)