From 248d35be8e0b826068db147058eacc7d6a18fbde Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Tue, 5 Aug 2025 18:24:36 -0700 Subject: [PATCH 1/6] prose tests draft --- .../mongoc/mongoc-client-side-encryption.c | 148 +++++++++++- .../mongoc/mongoc-client-side-encryption.h | 36 +++ .../src/mongoc/mongoc-crypt-private.h | 2 + src/libmongoc/src/mongoc/mongoc-crypt.c | 23 +- .../encryptedFields-text.json | 48 ++++ src/libmongoc/tests/json-test.c | 2 +- src/libmongoc/tests/json-test.h | 2 +- .../test-mongoc-client-side-encryption.c | 225 +++++++++++++++++- 8 files changed, 476 insertions(+), 10 deletions(-) create mode 100644 src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-text.json diff --git a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c index c5918190cf2..47c19abea9f 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c +++ b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #ifndef _WIN32 #include @@ -463,6 +464,26 @@ struct _mongoc_client_encryption_encrypt_range_opts_t { } precision; }; +struct _mongoc_client_encryption_encrypt_text_per_index_opts_t { + bool set; + struct { + bool set; + int32_t value; + } str_max_length; + int32_t str_max_query_length; + int32_t str_min_query_length; +}; + +struct _mongoc_client_encryption_encrypt_text_opts_t { + bool set; + bool case_sensitive; + bool diacritic_sensitive; + + mongoc_client_encryption_encrypt_text_per_index_opts_t substring; + mongoc_client_encryption_encrypt_text_per_index_opts_t prefix; + mongoc_client_encryption_encrypt_text_per_index_opts_t suffix; +}; + struct _mongoc_client_encryption_encrypt_opts_t { bson_value_t keyid; char *algorithm; @@ -473,6 +494,7 @@ struct _mongoc_client_encryption_encrypt_opts_t { } contention_factor; char *query_type; mongoc_client_encryption_encrypt_range_opts_t *range_opts; + mongoc_client_encryption_encrypt_text_opts_t text_opts; }; mongoc_client_encryption_encrypt_opts_t * @@ -481,6 +503,18 @@ mongoc_client_encryption_encrypt_opts_new (void) return bson_malloc0 (sizeof (mongoc_client_encryption_encrypt_opts_t)); } +mongoc_client_encryption_encrypt_text_per_index_opts_t * +mongoc_client_encryption_encrypt_text_per_index_opts_new (void) +{ + return bson_malloc0 (sizeof (mongoc_client_encryption_encrypt_text_per_index_opts_t)); +} + +mongoc_client_encryption_encrypt_text_opts_t * +mongoc_client_encryption_encrypt_text_opts_new (void) +{ + return bson_malloc0 (sizeof (mongoc_client_encryption_encrypt_text_opts_t)); +} + void mongoc_client_encryption_encrypt_range_opts_destroy (mongoc_client_encryption_encrypt_range_opts_t *range_opts) { @@ -672,6 +706,67 @@ mongoc_client_encryption_encrypt_opts_set_range_opts (mongoc_client_encryption_e opts->range_opts = copy_range_opts (range_opts); } +/*-------------------------------------------------------------------------- + * Explicit Encryption TextPreview Options + *-------------------------------------------------------------------------- + */ +void +mongoc_client_encryption_encrypt_opts_set_text_opts (mongoc_client_encryption_encrypt_opts_t *opts, + const mongoc_client_encryption_encrypt_text_opts_t *text_opts) +{ + BSON_ASSERT_PARAM(opts); + opts->text_opts = *text_opts; + opts->text_opts.set = true; +} + +void +mongoc_client_encryption_encrypt_text_opts_set_case_sensitive(mongoc_client_encryption_encrypt_text_opts_t *opts, bool case_sensitive) { + BSON_ASSERT_PARAM(opts); + opts->case_sensitive = case_sensitive; +} + +void +mongoc_client_encryption_encrypt_text_opts_set_diacritic_sensitive(mongoc_client_encryption_encrypt_text_opts_t *opts, bool diacritic_sensitive) { + BSON_ASSERT_PARAM(opts); + opts->diacritic_sensitive = diacritic_sensitive; +} + +void +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_length) { + BSON_ASSERT_PARAM(opts); + opts->str_max_length.set = true; + opts->str_max_length.value = str_max_length; +} + +void +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_query_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_query_length) { + BSON_ASSERT_PARAM(opts); + opts->str_max_query_length = str_max_query_length; +} + +void +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_min_query_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_min_query_length) { + BSON_ASSERT_PARAM(opts); + opts->str_min_query_length = str_min_query_length; +} + +void mongoc_client_encryption_encrypt_text_opts_set_prefix(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts) { + BSON_ASSERT_PARAM(opts); + opts->prefix = *per_index_opts; + opts->prefix.set = true; +} + +void mongoc_client_encryption_encrypt_text_opts_set_suffix(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts) { + BSON_ASSERT_PARAM(opts); + opts->suffix = *per_index_opts; + opts->suffix.set = true; +} + +void mongoc_client_encryption_encrypt_text_opts_set_substring(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts) { + BSON_ASSERT_PARAM(opts); + opts->substring = *per_index_opts; + opts->substring.set = true; +} /*-------------------------------------------------------------------------- * RewrapManyDataKeyResult. *-------------------------------------------------------------------------- @@ -1039,6 +1134,44 @@ append_bson_range_opts (bson_t *bson_range_opts, const mongoc_client_encryption_ } } +static void +append_bson_text_per_index_opts(bson_t *out, const mongoc_client_encryption_encrypt_text_per_index_opts_t *opts) { + BSON_ASSERT_PARAM (out); + if (opts->str_max_length.set) { + BSON_ASSERT(bson_append_int32(out, "strMaxLength", -1, opts->str_max_length.value)); + } + BSON_ASSERT(bson_append_int32(out, "strMaxQueryLength", -1, opts->str_max_query_length)); + BSON_ASSERT(bson_append_int32(out, "strMinQueryLength", -1, opts->str_min_query_length)); +} + +static void +append_bson_text_opts(bson_t *bson_text_opts, const mongoc_client_encryption_encrypt_text_opts_t *opts) { + BSON_ASSERT_PARAM (bson_text_opts); + BSON_ASSERT_PARAM (opts); + + BSON_ASSERT(BSON_APPEND_BOOL(bson_text_opts, "caseSensitive", opts->case_sensitive)); + BSON_ASSERT(BSON_APPEND_BOOL(bson_text_opts, "diacriticSensitive", opts->diacritic_sensitive)); + + if (opts->prefix.set) { + bson_t per_index_spec; + BSON_ASSERT(BSON_APPEND_DOCUMENT_BEGIN(bson_text_opts, "prefix", &per_index_spec)); + append_bson_text_per_index_opts(&per_index_spec, &opts->prefix); + BSON_ASSERT(bson_append_document_end(bson_text_opts, &per_index_spec)); + } + if (opts->suffix.set) { + bson_t per_index_spec; + BSON_ASSERT(BSON_APPEND_DOCUMENT_BEGIN(bson_text_opts, "suffix", &per_index_spec)); + append_bson_text_per_index_opts(&per_index_spec, &opts->suffix); + BSON_ASSERT(bson_append_document_end(bson_text_opts, &per_index_spec)); + } + if (opts->substring.set) { + bson_t per_index_spec; + BSON_ASSERT(BSON_APPEND_DOCUMENT_BEGIN(bson_text_opts, "substring", &per_index_spec)); + append_bson_text_per_index_opts(&per_index_spec, &opts->substring); + BSON_ASSERT(bson_append_document_end(bson_text_opts, &per_index_spec)); + } +} + /*-------------------------------------------------------------------------- * * _prep_for_auto_encryption -- @@ -2642,7 +2775,7 @@ mongoc_client_encryption_encrypt (mongoc_client_encryption_t *client_encryption, bson_error_t *error) { bool ret = false; - bson_t *range_opts = NULL; + bson_t *range_opts = NULL, *text_opts = NULL; ENTRY; @@ -2667,6 +2800,11 @@ mongoc_client_encryption_encrypt (mongoc_client_encryption_t *client_encryption, range_opts = bson_new (); append_bson_range_opts (range_opts, opts); } + + if (opts->text_opts.set) { + text_opts = bson_new(); + append_bson_text_opts(text_opts, &opts->text_opts); + } if (!_mongoc_crypt_explicit_encrypt (client_encryption->crypt, client_encryption->keyvault_coll, @@ -2676,6 +2814,7 @@ mongoc_client_encryption_encrypt (mongoc_client_encryption_t *client_encryption, opts->query_type, opts->contention_factor.set ? &opts->contention_factor.value : NULL, range_opts, + text_opts, value, ciphertext, error)) { @@ -2712,6 +2851,12 @@ mongoc_client_encryption_encrypt_expression (mongoc_client_encryption_t *client_ append_bson_range_opts (range_opts, opts); } + bson_t *text_opts = NULL; + if (opts->text_opts.set) { + text_opts = bson_new(); + append_bson_text_opts(text_opts, &opts->text_opts); + } + if (!_mongoc_crypt_explicit_encrypt_expression (client_encryption->crypt, client_encryption->keyvault_coll, opts->algorithm, @@ -2720,6 +2865,7 @@ mongoc_client_encryption_encrypt_expression (mongoc_client_encryption_t *client_ opts->query_type, opts->contention_factor.set ? &opts->contention_factor.value : NULL, range_opts, + text_opts, expr, expr_out, error)) { diff --git a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h index 5dcd919441d..9a3423d8605 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h +++ b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h @@ -37,10 +37,14 @@ struct _mongoc_database_t; #define MONGOC_ENCRYPT_ALGORITHM_UNINDEXED "Unindexed" #define MONGOC_ENCRYPT_ALGORITHM_RANGE "Range" #define MONGOC_ENCRYPT_ALGORITHM_RANGEPREVIEW "RangePreview" +#define MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW "TextPreview" #define MONGOC_ENCRYPT_QUERY_TYPE_EQUALITY "equality" #define MONGOC_ENCRYPT_QUERY_TYPE_RANGE "range" #define MONGOC_ENCRYPT_QUERY_TYPE_RANGEPREVIEW "rangePreview" +#define MONGOC_ENCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW "substringPreview" +#define MONGOC_ENCRYPT_QUERY_TYPE_PREFIXPREVIEW "prefixPreview" +#define MONGOC_ENCRYPT_QUERY_TYPE_SUFFIXPREVIEW "suffixPreview" BSON_BEGIN_DECLS @@ -104,6 +108,8 @@ mongoc_auto_encryption_opts_set_kms_credential_provider_callback (mongoc_auto_en typedef struct _mongoc_client_encryption_opts_t mongoc_client_encryption_opts_t; typedef struct _mongoc_client_encryption_t mongoc_client_encryption_t; typedef struct _mongoc_client_encryption_encrypt_range_opts_t mongoc_client_encryption_encrypt_range_opts_t; +typedef struct _mongoc_client_encryption_encrypt_text_per_index_opts_t mongoc_client_encryption_encrypt_text_per_index_opts_t; +typedef struct _mongoc_client_encryption_encrypt_text_opts_t mongoc_client_encryption_encrypt_text_opts_t; typedef struct _mongoc_client_encryption_encrypt_opts_t mongoc_client_encryption_encrypt_opts_t; typedef struct _mongoc_client_encryption_datakey_opts_t mongoc_client_encryption_datakey_opts_t; typedef struct _mongoc_client_encryption_rewrap_many_datakey_result_t @@ -228,6 +234,12 @@ mongoc_client_encryption_decrypt (mongoc_client_encryption_t *client_encryption, MONGOC_EXPORT (mongoc_client_encryption_encrypt_opts_t *) mongoc_client_encryption_encrypt_opts_new (void) BSON_GNUC_WARN_UNUSED_RESULT; +MONGOC_EXPORT (mongoc_client_encryption_encrypt_text_per_index_opts_t *) +mongoc_client_encryption_encrypt_text_per_index_opts_new (void); + +MONGOC_EXPORT (mongoc_client_encryption_encrypt_text_opts_t *) +mongoc_client_encryption_encrypt_text_opts_new (void); + MONGOC_EXPORT (void) mongoc_client_encryption_encrypt_opts_destroy (mongoc_client_encryption_encrypt_opts_t *opts); @@ -280,6 +292,30 @@ mongoc_client_encryption_encrypt_range_opts_set_precision (mongoc_client_encrypt MONGOC_EXPORT (void) mongoc_client_encryption_encrypt_opts_set_range_opts (mongoc_client_encryption_encrypt_opts_t *opts, const mongoc_client_encryption_encrypt_range_opts_t *range_opts); +MONGOC_EXPORT(void) +mongoc_client_encryption_encrypt_opts_set_text_opts (mongoc_client_encryption_encrypt_opts_t *opts, + const mongoc_client_encryption_encrypt_text_opts_t *text_opts); + +MONGOC_EXPORT(void) +mongoc_client_encryption_encrypt_text_opts_set_case_sensitive(mongoc_client_encryption_encrypt_text_opts_t *opts, bool case_sensitive); + +MONGOC_EXPORT(void) +mongoc_client_encryption_encrypt_text_opts_set_diacritic_sensitive(mongoc_client_encryption_encrypt_text_opts_t *opts, bool diacritic_sensitive); + +MONGOC_EXPORT(void) +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_length); + +MONGOC_EXPORT(void) +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_query_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_query_length); + +MONGOC_EXPORT(void) +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_min_query_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_min_query_length); + +MONGOC_EXPORT(void) mongoc_client_encryption_encrypt_text_opts_set_prefix(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts); + +MONGOC_EXPORT(void) mongoc_client_encryption_encrypt_text_opts_set_suffix(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts); + +MONGOC_EXPORT(void) mongoc_client_encryption_encrypt_text_opts_set_substring(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts); MONGOC_EXPORT (mongoc_client_encryption_datakey_opts_t *) mongoc_client_encryption_datakey_opts_new (void) BSON_GNUC_WARN_UNUSED_RESULT; diff --git a/src/libmongoc/src/mongoc/mongoc-crypt-private.h b/src/libmongoc/src/mongoc/mongoc-crypt-private.h index 8b72c8b3807..5259301a462 100644 --- a/src/libmongoc/src/mongoc/mongoc-crypt-private.h +++ b/src/libmongoc/src/mongoc/mongoc-crypt-private.h @@ -95,6 +95,7 @@ _mongoc_crypt_explicit_encrypt (_mongoc_crypt_t *crypt, const char *query_type /* may be NULL */, const int64_t *contention_factor /* may be NULL */, const bson_t *range_opts /* may be NULL */, + const bson_t *text_opts /* may be NULL */, const bson_value_t *value_in, bson_value_t *value_out, bson_error_t *error); @@ -112,6 +113,7 @@ _mongoc_crypt_explicit_encrypt_expression (_mongoc_crypt_t *crypt, const char *query_type /* may be NULL */, const int64_t *contention_factor /* may be NULL */, const bson_t *range_opts /* may be NULL */, + const bson_t *text_opts /* may be NULL */, const bson_t *expr_in, bson_t *expr_out, bson_error_t *error); diff --git a/src/libmongoc/src/mongoc/mongoc-crypt.c b/src/libmongoc/src/mongoc/mongoc-crypt.c index a884bf67137..dab207f3c89 100644 --- a/src/libmongoc/src/mongoc/mongoc-crypt.c +++ b/src/libmongoc/src/mongoc/mongoc-crypt.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "bson/bson.h" #define MONGOC_LOG_DOMAIN "client-side-encryption" #include @@ -1629,6 +1630,7 @@ _create_explicit_state_machine (_mongoc_crypt_t *crypt, const char *query_type, const int64_t *contention_factor, const bson_t *range_opts, + const bson_t *text_opts, bson_error_t *error) { BSON_ASSERT_PARAM (crypt); @@ -1638,6 +1640,7 @@ _create_explicit_state_machine (_mongoc_crypt_t *crypt, BSON_OPTIONAL_PARAM (keyaltname); BSON_OPTIONAL_PARAM (query_type); BSON_OPTIONAL_PARAM (range_opts); + BSON_OPTIONAL_PARAM (text_opts); BSON_OPTIONAL_PARAM (error); _state_machine_t *state_machine = NULL; @@ -1669,6 +1672,18 @@ _create_explicit_state_machine (_mongoc_crypt_t *crypt, mongocrypt_binary_destroy (binary_range_opts); } + if (text_opts != NULL) { + /* mongocrypt error checks and parses range options */ + mongocrypt_binary_t *binary_text_opts = + mongocrypt_binary_new_from_data ((uint8_t *) bson_get_data (text_opts), text_opts->len); + if (!mongocrypt_ctx_setopt_algorithm_text (state_machine->ctx, binary_text_opts)) { + mongocrypt_binary_destroy (binary_text_opts); + _ctx_check_error (state_machine->ctx, error, true); + goto fail; + } + mongocrypt_binary_destroy (binary_text_opts); + } + if (query_type != NULL) { if (!mongocrypt_ctx_setopt_query_type (state_machine->ctx, query_type, -1)) { goto fail; @@ -1736,6 +1751,7 @@ _mongoc_crypt_explicit_encrypt (_mongoc_crypt_t *crypt, const char *query_type, const int64_t *contention_factor, const bson_t *range_opts, + const bson_t *text_opts, const bson_value_t *value_in, bson_value_t *value_out, bson_error_t *error) @@ -1761,7 +1777,7 @@ _mongoc_crypt_explicit_encrypt (_mongoc_crypt_t *crypt, value_out->value_type = BSON_TYPE_EOD; state_machine = _create_explicit_state_machine ( - crypt, keyvault_coll, algorithm, keyid, keyaltname, query_type, contention_factor, range_opts, error); + crypt, keyvault_coll, algorithm, keyid, keyaltname, query_type, contention_factor, range_opts, text_opts, error); if (!state_machine) { goto fail; } @@ -1780,6 +1796,7 @@ _mongoc_crypt_explicit_encrypt (_mongoc_crypt_t *crypt, } /* extract value */ + printf("%s\n", bson_as_canonical_extended_json(&result,NULL)); if (!bson_iter_init_find (&iter, &result, "v")) { _mongoc_set_error (error, MONGOC_ERROR_CLIENT, @@ -1811,6 +1828,7 @@ _mongoc_crypt_explicit_encrypt_expression (_mongoc_crypt_t *crypt, const char *query_type, const int64_t *contention_factor, const bson_t *range_opts, + const bson_t *text_opts, const bson_t *expr_in, bson_t *expr_out, bson_error_t *error) @@ -1822,6 +1840,7 @@ _mongoc_crypt_explicit_encrypt_expression (_mongoc_crypt_t *crypt, BSON_OPTIONAL_PARAM (keyaltname); BSON_OPTIONAL_PARAM (query_type); BSON_OPTIONAL_PARAM (range_opts); + BSON_OPTIONAL_PARAM (text_opts); BSON_ASSERT_PARAM (expr_in); BSON_ASSERT_PARAM (expr_out); BSON_OPTIONAL_PARAM (error); @@ -1836,7 +1855,7 @@ _mongoc_crypt_explicit_encrypt_expression (_mongoc_crypt_t *crypt, bson_init (expr_out); state_machine = _create_explicit_state_machine ( - crypt, keyvault_coll, algorithm, keyid, keyaltname, query_type, contention_factor, range_opts, error); + crypt, keyvault_coll, algorithm, keyid, keyaltname, query_type, contention_factor, range_opts, text_opts, error); if (!state_machine) { goto fail; } diff --git a/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-text.json b/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-text.json new file mode 100644 index 00000000000..567eb467e6c --- /dev/null +++ b/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-text.json @@ -0,0 +1,48 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encrypted-textPreview", + "bsonType": "string", + "queries": [ + { + "queryType": "prefixPreview", + "strMinQueryLength": { + "$numberInt": "1" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "caseSensitive": true, + "diacriticSensitive": true + }, + { + "queryType": "suffixPreview", + "strMinQueryLength": { + "$numberInt": "1" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] +} diff --git a/src/libmongoc/tests/json-test.c b/src/libmongoc/tests/json-test.c index b8a192522c9..6536c75f986 100644 --- a/src/libmongoc/tests/json-test.c +++ b/src/libmongoc/tests/json-test.c @@ -655,7 +655,7 @@ collect_tests_from_dir (char (*paths)[MAX_TEST_NAME_LENGTH] /* OUT */, *----------------------------------------------------------------------- */ bson_t * -get_bson_from_json_file (char *filename) +get_bson_from_json_file (const char *filename) { FILE *const file = fopen (filename, "rb"); if (!file) { diff --git a/src/libmongoc/tests/json-test.h b/src/libmongoc/tests/json-test.h index 549b93b5f70..b21aaacd02e 100644 --- a/src/libmongoc/tests/json-test.h +++ b/src/libmongoc/tests/json-test.h @@ -54,7 +54,7 @@ typedef struct _json_test_config_t { #define JSON_TEST_CONFIG_INIT {NULL, NULL, NULL, NULL, NULL, NULL, false, false, NULL, NULL} bson_t * -get_bson_from_json_file (char *filename); +get_bson_from_json_file (const char *filename); int collect_tests_from_dir (char (*paths)[MAX_TEST_NAME_LENGTH] /* OUT */, diff --git a/src/libmongoc/tests/test-mongoc-client-side-encryption.c b/src/libmongoc/tests/test-mongoc-client-side-encryption.c index 91225dc5878..c1c3d1edb5d 100644 --- a/src/libmongoc/tests/test-mongoc-client-side-encryption.c +++ b/src/libmongoc/tests/test-mongoc-client-side-encryption.c @@ -14,6 +14,9 @@ * limitations under the License. */ +#include "bson/bson.h" +#include "bson/macros.h" +#include "mongoc/mongoc.h" #include #include @@ -3284,15 +3287,13 @@ typedef struct { } ee_fixture; static ee_fixture * -explicit_encryption_setup (void) +explicit_encryption_setup_full (const char* encrypted_fields_path, const char* key_path) { ee_fixture *eef = (ee_fixture *) bson_malloc0 (sizeof (ee_fixture)); bson_t *encryptedFields = - get_bson_from_json_file ("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" - "encryptedFields.json"); + get_bson_from_json_file (encrypted_fields_path); bson_t *key1Document = - get_bson_from_json_file ("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" - "key1-document.json"); + get_bson_from_json_file (key_path); mongoc_client_t *setupClient = test_framework_new_default_client (); @@ -3403,6 +3404,13 @@ explicit_encryption_setup (void) return eef; } +static ee_fixture * +explicit_encryption_setup (void) { + return explicit_encryption_setup_full("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" + "encryptedFields.json", "./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" + "key1-document.json"); +} + static void explicit_encryption_destroy (ee_fixture *eef) { @@ -4410,6 +4418,205 @@ test_explicit_encryption_case5 (void *unused) explicit_encryption_destroy (eef); } +static void +test_explicit_encryption_text(void *unused) +{ + bson_error_t error; + bool ok; + mongoc_client_encryption_encrypt_opts_t *eopts; + bson_value_t plaintext = {0}; + ee_fixture *eef = explicit_encryption_setup_full ("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" + "encryptedFields-text.json", "./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" + "key1-document.json"); + + BSON_UNUSED (unused); + + plaintext.value_type = BSON_TYPE_UTF8; + plaintext.value.v_utf8.str = "foobarbaz"; + plaintext.value.v_utf8.len = (uint32_t) strlen (plaintext.value.v_utf8.str); + + mongoc_client_encryption_encrypt_text_per_index_opts_t *iopts = mongoc_client_encryption_encrypt_text_per_index_opts_new(); + mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_query_length(iopts, 3); + mongoc_client_encryption_encrypt_text_per_index_opts_set_str_min_query_length(iopts, 1); + + /* Insert 'foobarbaz' with both prefix and suffix indexing */ + { + bson_value_t insertPayload; + bson_t to_insert = BSON_INITIALIZER; + + eopts = mongoc_client_encryption_encrypt_opts_new (); + mongoc_client_encryption_encrypt_opts_set_keyid (eopts, &eef->key1ID); + mongoc_client_encryption_encrypt_opts_set_algorithm (eopts, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); + + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new(); + mongoc_client_encryption_encrypt_text_opts_set_prefix(topts, iopts); + mongoc_client_encryption_encrypt_text_opts_set_suffix(topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts(eopts, topts); + + ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eopts, &insertPayload, &error); + ASSERT_OR_PRINT (ok, error); + + ASSERT (BSON_APPEND_VALUE (&to_insert, "encrypted-textPreview", &insertPayload)); + + ok = mongoc_collection_insert_one (eef->encryptedColl, &to_insert, NULL /* opts */, NULL /* reply */, &error); + ASSERT_OR_PRINT (ok, error); + + bson_value_destroy (&insertPayload); + bson_destroy (&to_insert); + mongoc_client_encryption_encrypt_opts_destroy (eopts); + } + + // /* Find the document using the 'foo' prefix */ + { + bson_value_t findPayload; + mongoc_client_encryption_encrypt_opts_t *eo = mongoc_client_encryption_encrypt_opts_new (); + mongoc_client_encryption_encrypt_opts_set_keyid (eo, &eef->key1ID); + mongoc_client_encryption_encrypt_opts_set_algorithm (eo, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); + mongoc_client_encryption_encrypt_opts_set_query_type (eo, MONGOC_ENCRYPT_QUERY_TYPE_PREFIXPREVIEW); + mongoc_client_encryption_encrypt_opts_set_contention_factor (eo, 0); + + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new(); + mongoc_client_encryption_encrypt_text_opts_set_prefix(topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts(eo, topts); + + plaintext.value.v_utf8.str = "foo"; + plaintext.value.v_utf8.len = 3; + ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &findPayload, &error); + + bsonBuildDecl (expr, + kv ("$expr", + doc ( kv ("encStrStartsWith", + doc (kv ("input", cstr("$encrypted-textPreview")), + kv ("prefix", value(findPayload))))))); + ASSERT_OR_PRINT (ok, error); + + mongoc_cursor_t *cursor; + const bson_t *got; + + cursor = mongoc_collection_find_with_opts (eef->encryptedColl, &expr, NULL /* opts */, NULL /* read_prefs */); + ASSERT (mongoc_cursor_next (cursor, &got)); + ASSERT_OR_PRINT (!mongoc_cursor_error (cursor, &error), error); + ASSERT_MATCH (got, "{ 'encrypted-textPreview': 'foobarbaz' }"); + ASSERT (!mongoc_cursor_next (cursor, &got) && "expected one document to be returned, got more than one"); + + mongoc_cursor_destroy (cursor); + bson_destroy (&expr); + } + + /* Find the document using the 'baz' suffix */ + { + bson_value_t findPayload; + mongoc_client_encryption_encrypt_opts_t *eo = mongoc_client_encryption_encrypt_opts_new (); + mongoc_client_encryption_encrypt_opts_set_keyid (eo, &eef->key1ID); + mongoc_client_encryption_encrypt_opts_set_algorithm (eo, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); + mongoc_client_encryption_encrypt_opts_set_query_type (eo, MONGOC_ENCRYPT_QUERY_TYPE_SUFFIXPREVIEW); + mongoc_client_encryption_encrypt_opts_set_contention_factor (eo, 0); + + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new(); + mongoc_client_encryption_encrypt_text_opts_set_suffix(topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts(eo, topts); + + plaintext.value.v_utf8.str = "baz"; + plaintext.value.v_utf8.len = 3; + ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &findPayload, &error); + + bsonBuildDecl (expr, + kv ("$expr", + doc ( kv ("encStrEndsWith", + doc (kv ("input", cstr("$encrypted-textPreview")), + kv ("suffix", value(findPayload))))))); + ASSERT_OR_PRINT (ok, error); + + mongoc_cursor_t *cursor; + const bson_t *got; + + cursor = mongoc_collection_find_with_opts (eef->encryptedColl, &expr, NULL /* opts */, NULL /* read_prefs */); + ASSERT (mongoc_cursor_next (cursor, &got)); + ASSERT_OR_PRINT (!mongoc_cursor_error (cursor, &error), error); + ASSERT_MATCH (got, "{ 'encrypted-textPreview': 'foobarbaz' }"); + ASSERT (!mongoc_cursor_next (cursor, &got) && "expected one document to be returned, got more than one"); + + mongoc_cursor_destroy (cursor); + bson_destroy (&expr); + } + + /* Ensure querying for a 'foo' suffix returns no documents */ + { + bson_value_t findPayload; + mongoc_client_encryption_encrypt_opts_t *eo = mongoc_client_encryption_encrypt_opts_new (); + mongoc_client_encryption_encrypt_opts_set_keyid (eo, &eef->key1ID); + mongoc_client_encryption_encrypt_opts_set_algorithm (eo, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); + mongoc_client_encryption_encrypt_opts_set_query_type (eo, MONGOC_ENCRYPT_QUERY_TYPE_SUFFIXPREVIEW); + mongoc_client_encryption_encrypt_opts_set_contention_factor (eo, 0); + + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new(); + mongoc_client_encryption_encrypt_text_opts_set_suffix(topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts(eo, topts); + + plaintext.value.v_utf8.str = "foo"; + plaintext.value.v_utf8.len = 3; + ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &findPayload, &error); + + bsonBuildDecl (expr, + kv ("$expr", + doc ( kv ("encStrEndsWith", + doc (kv ("input", cstr("$encrypted-textPreview")), + kv ("suffix", value(findPayload))))))); + ASSERT_OR_PRINT (ok, error); + printf("%s\n", bson_as_canonical_extended_json(&expr, NULL)); + + mongoc_cursor_t *cursor; + const bson_t *got; + + cursor = mongoc_collection_find_with_opts (eef->encryptedColl, &expr, NULL /* opts */, NULL /* read_prefs */); + // ASSERT (!mongoc_cursor_next (cursor, &got) && "expected no documents to be returned, got some"); + mongoc_cursor_next (cursor, &got); + printf("%s\n", bson_as_canonical_extended_json(got, NULL)); + + mongoc_cursor_destroy (cursor); + bson_destroy (&expr); + } + + /* Ensure querying for a 'baz' prefix returns no documents */ + { + bson_value_t findPayload; + mongoc_client_encryption_encrypt_opts_t *eo = mongoc_client_encryption_encrypt_opts_new (); + mongoc_client_encryption_encrypt_opts_set_keyid (eo, &eef->key1ID); + mongoc_client_encryption_encrypt_opts_set_algorithm (eo, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); + mongoc_client_encryption_encrypt_opts_set_query_type (eo, MONGOC_ENCRYPT_QUERY_TYPE_PREFIXPREVIEW); + mongoc_client_encryption_encrypt_opts_set_contention_factor (eo, 0); + + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new(); + mongoc_client_encryption_encrypt_text_opts_set_prefix(topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts(eo, topts); + + plaintext.value.v_utf8.str = "baz"; + plaintext.value.v_utf8.len = 3; + ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &findPayload, &error); + + bsonBuildDecl (expr, + kv ("$expr", + doc ( kv ("encStrStartsWith", + doc (kv ("input", cstr("$encrypted-textPreview")), + kv ("prefix", value(findPayload))))))); + ASSERT_OR_PRINT (ok, error); + + mongoc_cursor_t *cursor; + const bson_t *got; + + cursor = mongoc_collection_find_with_opts (eef->encryptedColl, &expr, NULL /* opts */, NULL /* read_prefs */); + ASSERT (!mongoc_cursor_next (cursor, &got) && "expected no documents to be returned, got some"); + + mongoc_cursor_destroy (cursor); + bson_destroy (&expr); + } + + // TODO missing and present prefix/suffix + // substring is encStrContains + + explicit_encryption_destroy (eef); +} + static void _test_unique_index_on_keyaltnames_setup (void (*test_case) (mongoc_client_encryption_t *, const bson_value_t *keyid)) { @@ -7332,5 +7539,13 @@ test_client_side_encryption_install (TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_21 /* require server > 7.0 for QE support */, test_framework_skip_if_single, /* QE not supported on standalone */ test_framework_skip_if_no_client_side_encryption); + TestSuite_AddFull (suite, + "/client_side_encryption/text", + test_explicit_encryption_text, + NULL, + NULL, + test_framework_skip_if_max_wire_version_less_than_25 /* require server > 8.1 for QE support */, + test_framework_skip_if_single, /* QE not supported on standalone */ + test_framework_skip_if_no_client_side_encryption); } } From 6c83fe8199f763fae75f9885db9ccc7e52df6881 Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Tue, 5 Aug 2025 18:32:08 -0700 Subject: [PATCH 2/6] syntax fix --- src/libmongoc/src/mongoc/mongoc-crypt.c | 1 - .../tests/test-mongoc-client-side-encryption.c | 12 +++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-crypt.c b/src/libmongoc/src/mongoc/mongoc-crypt.c index dab207f3c89..515616e5fd0 100644 --- a/src/libmongoc/src/mongoc/mongoc-crypt.c +++ b/src/libmongoc/src/mongoc/mongoc-crypt.c @@ -1796,7 +1796,6 @@ _mongoc_crypt_explicit_encrypt (_mongoc_crypt_t *crypt, } /* extract value */ - printf("%s\n", bson_as_canonical_extended_json(&result,NULL)); if (!bson_iter_init_find (&iter, &result, "v")) { _mongoc_set_error (error, MONGOC_ERROR_CLIENT, diff --git a/src/libmongoc/tests/test-mongoc-client-side-encryption.c b/src/libmongoc/tests/test-mongoc-client-side-encryption.c index c1c3d1edb5d..3aedc87edfe 100644 --- a/src/libmongoc/tests/test-mongoc-client-side-encryption.c +++ b/src/libmongoc/tests/test-mongoc-client-side-encryption.c @@ -4485,7 +4485,7 @@ test_explicit_encryption_text(void *unused) bsonBuildDecl (expr, kv ("$expr", - doc ( kv ("encStrStartsWith", + doc ( kv ("$encStrStartsWith", doc (kv ("input", cstr("$encrypted-textPreview")), kv ("prefix", value(findPayload))))))); ASSERT_OR_PRINT (ok, error); @@ -4522,7 +4522,7 @@ test_explicit_encryption_text(void *unused) bsonBuildDecl (expr, kv ("$expr", - doc ( kv ("encStrEndsWith", + doc ( kv ("$encStrEndsWith", doc (kv ("input", cstr("$encrypted-textPreview")), kv ("suffix", value(findPayload))))))); ASSERT_OR_PRINT (ok, error); @@ -4559,19 +4559,17 @@ test_explicit_encryption_text(void *unused) bsonBuildDecl (expr, kv ("$expr", - doc ( kv ("encStrEndsWith", + doc ( kv ("$encStrEndsWith", doc (kv ("input", cstr("$encrypted-textPreview")), kv ("suffix", value(findPayload))))))); ASSERT_OR_PRINT (ok, error); - printf("%s\n", bson_as_canonical_extended_json(&expr, NULL)); mongoc_cursor_t *cursor; const bson_t *got; cursor = mongoc_collection_find_with_opts (eef->encryptedColl, &expr, NULL /* opts */, NULL /* read_prefs */); - // ASSERT (!mongoc_cursor_next (cursor, &got) && "expected no documents to be returned, got some"); + ASSERT (!mongoc_cursor_next (cursor, &got) && "expected no documents to be returned, got some"); mongoc_cursor_next (cursor, &got); - printf("%s\n", bson_as_canonical_extended_json(got, NULL)); mongoc_cursor_destroy (cursor); bson_destroy (&expr); @@ -4596,7 +4594,7 @@ test_explicit_encryption_text(void *unused) bsonBuildDecl (expr, kv ("$expr", - doc ( kv ("encStrStartsWith", + doc ( kv ("$encStrStartsWith", doc (kv ("input", cstr("$encrypted-textPreview")), kv ("prefix", value(findPayload))))))); ASSERT_OR_PRINT (ok, error); From 6a7dda8d81e133891426c0ef2dd3adb1e848f619 Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:05:42 -0700 Subject: [PATCH 3/6] substring tests + format --- .../mongoc/mongoc-client-side-encryption.c | 102 +++++--- .../mongoc/mongoc-client-side-encryption.h | 47 ++-- src/libmongoc/src/mongoc/mongoc-crypt.c | 2 +- ...son => encryptedFields-prefix-suffix.json} | 14 +- .../encryptedFields-substring.json | 30 +++ .../test-mongoc-client-side-encryption.c | 231 +++++++++++++----- 6 files changed, 297 insertions(+), 129 deletions(-) rename src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/{encryptedFields-text.json => encryptedFields-prefix-suffix.json} (78%) create mode 100644 src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-substring.json diff --git a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c index 47c19abea9f..b779a2d2835 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c +++ b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c @@ -712,58 +712,80 @@ mongoc_client_encryption_encrypt_opts_set_range_opts (mongoc_client_encryption_e */ void mongoc_client_encryption_encrypt_opts_set_text_opts (mongoc_client_encryption_encrypt_opts_t *opts, - const mongoc_client_encryption_encrypt_text_opts_t *text_opts) + const mongoc_client_encryption_encrypt_text_opts_t *text_opts) { - BSON_ASSERT_PARAM(opts); + BSON_ASSERT_PARAM (opts); opts->text_opts = *text_opts; opts->text_opts.set = true; } void -mongoc_client_encryption_encrypt_text_opts_set_case_sensitive(mongoc_client_encryption_encrypt_text_opts_t *opts, bool case_sensitive) { - BSON_ASSERT_PARAM(opts); +mongoc_client_encryption_encrypt_text_opts_set_case_sensitive (mongoc_client_encryption_encrypt_text_opts_t *opts, + bool case_sensitive) +{ + BSON_ASSERT_PARAM (opts); opts->case_sensitive = case_sensitive; } void -mongoc_client_encryption_encrypt_text_opts_set_diacritic_sensitive(mongoc_client_encryption_encrypt_text_opts_t *opts, bool diacritic_sensitive) { - BSON_ASSERT_PARAM(opts); +mongoc_client_encryption_encrypt_text_opts_set_diacritic_sensitive (mongoc_client_encryption_encrypt_text_opts_t *opts, + bool diacritic_sensitive) +{ + BSON_ASSERT_PARAM (opts); opts->diacritic_sensitive = diacritic_sensitive; } void -mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_length) { - BSON_ASSERT_PARAM(opts); +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_length ( + mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_length) +{ + BSON_ASSERT_PARAM (opts); opts->str_max_length.set = true; opts->str_max_length.value = str_max_length; } void -mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_query_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_query_length) { - BSON_ASSERT_PARAM(opts); +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_query_length ( + mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_query_length) +{ + BSON_ASSERT_PARAM (opts); opts->str_max_query_length = str_max_query_length; } void -mongoc_client_encryption_encrypt_text_per_index_opts_set_str_min_query_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_min_query_length) { - BSON_ASSERT_PARAM(opts); +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_min_query_length ( + mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_min_query_length) +{ + BSON_ASSERT_PARAM (opts); opts->str_min_query_length = str_min_query_length; } -void mongoc_client_encryption_encrypt_text_opts_set_prefix(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts) { - BSON_ASSERT_PARAM(opts); +void +mongoc_client_encryption_encrypt_text_opts_set_prefix ( + mongoc_client_encryption_encrypt_text_opts_t *opts, + mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts) +{ + BSON_ASSERT_PARAM (opts); opts->prefix = *per_index_opts; opts->prefix.set = true; } -void mongoc_client_encryption_encrypt_text_opts_set_suffix(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts) { - BSON_ASSERT_PARAM(opts); +void +mongoc_client_encryption_encrypt_text_opts_set_suffix ( + mongoc_client_encryption_encrypt_text_opts_t *opts, + mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts) +{ + BSON_ASSERT_PARAM (opts); opts->suffix = *per_index_opts; opts->suffix.set = true; } -void mongoc_client_encryption_encrypt_text_opts_set_substring(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts) { - BSON_ASSERT_PARAM(opts); +void +mongoc_client_encryption_encrypt_text_opts_set_substring ( + mongoc_client_encryption_encrypt_text_opts_t *opts, + mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts) +{ + BSON_ASSERT_PARAM (opts); opts->substring = *per_index_opts; opts->substring.set = true; } @@ -1135,40 +1157,42 @@ append_bson_range_opts (bson_t *bson_range_opts, const mongoc_client_encryption_ } static void -append_bson_text_per_index_opts(bson_t *out, const mongoc_client_encryption_encrypt_text_per_index_opts_t *opts) { +append_bson_text_per_index_opts (bson_t *out, const mongoc_client_encryption_encrypt_text_per_index_opts_t *opts) +{ BSON_ASSERT_PARAM (out); if (opts->str_max_length.set) { - BSON_ASSERT(bson_append_int32(out, "strMaxLength", -1, opts->str_max_length.value)); + BSON_ASSERT (bson_append_int32 (out, "strMaxLength", -1, opts->str_max_length.value)); } - BSON_ASSERT(bson_append_int32(out, "strMaxQueryLength", -1, opts->str_max_query_length)); - BSON_ASSERT(bson_append_int32(out, "strMinQueryLength", -1, opts->str_min_query_length)); + BSON_ASSERT (bson_append_int32 (out, "strMaxQueryLength", -1, opts->str_max_query_length)); + BSON_ASSERT (bson_append_int32 (out, "strMinQueryLength", -1, opts->str_min_query_length)); } static void -append_bson_text_opts(bson_t *bson_text_opts, const mongoc_client_encryption_encrypt_text_opts_t *opts) { +append_bson_text_opts (bson_t *bson_text_opts, const mongoc_client_encryption_encrypt_text_opts_t *opts) +{ BSON_ASSERT_PARAM (bson_text_opts); BSON_ASSERT_PARAM (opts); - BSON_ASSERT(BSON_APPEND_BOOL(bson_text_opts, "caseSensitive", opts->case_sensitive)); - BSON_ASSERT(BSON_APPEND_BOOL(bson_text_opts, "diacriticSensitive", opts->diacritic_sensitive)); + BSON_ASSERT (BSON_APPEND_BOOL (bson_text_opts, "caseSensitive", opts->case_sensitive)); + BSON_ASSERT (BSON_APPEND_BOOL (bson_text_opts, "diacriticSensitive", opts->diacritic_sensitive)); if (opts->prefix.set) { bson_t per_index_spec; - BSON_ASSERT(BSON_APPEND_DOCUMENT_BEGIN(bson_text_opts, "prefix", &per_index_spec)); - append_bson_text_per_index_opts(&per_index_spec, &opts->prefix); - BSON_ASSERT(bson_append_document_end(bson_text_opts, &per_index_spec)); + BSON_ASSERT (BSON_APPEND_DOCUMENT_BEGIN (bson_text_opts, "prefix", &per_index_spec)); + append_bson_text_per_index_opts (&per_index_spec, &opts->prefix); + BSON_ASSERT (bson_append_document_end (bson_text_opts, &per_index_spec)); } if (opts->suffix.set) { bson_t per_index_spec; - BSON_ASSERT(BSON_APPEND_DOCUMENT_BEGIN(bson_text_opts, "suffix", &per_index_spec)); - append_bson_text_per_index_opts(&per_index_spec, &opts->suffix); - BSON_ASSERT(bson_append_document_end(bson_text_opts, &per_index_spec)); + BSON_ASSERT (BSON_APPEND_DOCUMENT_BEGIN (bson_text_opts, "suffix", &per_index_spec)); + append_bson_text_per_index_opts (&per_index_spec, &opts->suffix); + BSON_ASSERT (bson_append_document_end (bson_text_opts, &per_index_spec)); } if (opts->substring.set) { bson_t per_index_spec; - BSON_ASSERT(BSON_APPEND_DOCUMENT_BEGIN(bson_text_opts, "substring", &per_index_spec)); - append_bson_text_per_index_opts(&per_index_spec, &opts->substring); - BSON_ASSERT(bson_append_document_end(bson_text_opts, &per_index_spec)); + BSON_ASSERT (BSON_APPEND_DOCUMENT_BEGIN (bson_text_opts, "substring", &per_index_spec)); + append_bson_text_per_index_opts (&per_index_spec, &opts->substring); + BSON_ASSERT (bson_append_document_end (bson_text_opts, &per_index_spec)); } } @@ -2800,10 +2824,10 @@ mongoc_client_encryption_encrypt (mongoc_client_encryption_t *client_encryption, range_opts = bson_new (); append_bson_range_opts (range_opts, opts); } - + if (opts->text_opts.set) { - text_opts = bson_new(); - append_bson_text_opts(text_opts, &opts->text_opts); + text_opts = bson_new (); + append_bson_text_opts (text_opts, &opts->text_opts); } if (!_mongoc_crypt_explicit_encrypt (client_encryption->crypt, @@ -2853,8 +2877,8 @@ mongoc_client_encryption_encrypt_expression (mongoc_client_encryption_t *client_ bson_t *text_opts = NULL; if (opts->text_opts.set) { - text_opts = bson_new(); - append_bson_text_opts(text_opts, &opts->text_opts); + text_opts = bson_new (); + append_bson_text_opts (text_opts, &opts->text_opts); } if (!_mongoc_crypt_explicit_encrypt_expression (client_encryption->crypt, diff --git a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h index 9a3423d8605..53970f94661 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h +++ b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h @@ -108,7 +108,8 @@ mongoc_auto_encryption_opts_set_kms_credential_provider_callback (mongoc_auto_en typedef struct _mongoc_client_encryption_opts_t mongoc_client_encryption_opts_t; typedef struct _mongoc_client_encryption_t mongoc_client_encryption_t; typedef struct _mongoc_client_encryption_encrypt_range_opts_t mongoc_client_encryption_encrypt_range_opts_t; -typedef struct _mongoc_client_encryption_encrypt_text_per_index_opts_t mongoc_client_encryption_encrypt_text_per_index_opts_t; +typedef struct _mongoc_client_encryption_encrypt_text_per_index_opts_t + mongoc_client_encryption_encrypt_text_per_index_opts_t; typedef struct _mongoc_client_encryption_encrypt_text_opts_t mongoc_client_encryption_encrypt_text_opts_t; typedef struct _mongoc_client_encryption_encrypt_opts_t mongoc_client_encryption_encrypt_opts_t; typedef struct _mongoc_client_encryption_datakey_opts_t mongoc_client_encryption_datakey_opts_t; @@ -292,30 +293,44 @@ mongoc_client_encryption_encrypt_range_opts_set_precision (mongoc_client_encrypt MONGOC_EXPORT (void) mongoc_client_encryption_encrypt_opts_set_range_opts (mongoc_client_encryption_encrypt_opts_t *opts, const mongoc_client_encryption_encrypt_range_opts_t *range_opts); -MONGOC_EXPORT(void) +MONGOC_EXPORT (void) mongoc_client_encryption_encrypt_opts_set_text_opts (mongoc_client_encryption_encrypt_opts_t *opts, - const mongoc_client_encryption_encrypt_text_opts_t *text_opts); + const mongoc_client_encryption_encrypt_text_opts_t *text_opts); -MONGOC_EXPORT(void) -mongoc_client_encryption_encrypt_text_opts_set_case_sensitive(mongoc_client_encryption_encrypt_text_opts_t *opts, bool case_sensitive); +MONGOC_EXPORT (void) +mongoc_client_encryption_encrypt_text_opts_set_case_sensitive (mongoc_client_encryption_encrypt_text_opts_t *opts, + bool case_sensitive); -MONGOC_EXPORT(void) -mongoc_client_encryption_encrypt_text_opts_set_diacritic_sensitive(mongoc_client_encryption_encrypt_text_opts_t *opts, bool diacritic_sensitive); +MONGOC_EXPORT (void) +mongoc_client_encryption_encrypt_text_opts_set_diacritic_sensitive (mongoc_client_encryption_encrypt_text_opts_t *opts, + bool diacritic_sensitive); -MONGOC_EXPORT(void) -mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_length); +MONGOC_EXPORT (void) +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_length ( + mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_length); -MONGOC_EXPORT(void) -mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_query_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_query_length); +MONGOC_EXPORT (void) +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_query_length ( + mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_max_query_length); -MONGOC_EXPORT(void) -mongoc_client_encryption_encrypt_text_per_index_opts_set_str_min_query_length(mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_min_query_length); +MONGOC_EXPORT (void) +mongoc_client_encryption_encrypt_text_per_index_opts_set_str_min_query_length ( + mongoc_client_encryption_encrypt_text_per_index_opts_t *opts, int32_t str_min_query_length); -MONGOC_EXPORT(void) mongoc_client_encryption_encrypt_text_opts_set_prefix(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts); +MONGOC_EXPORT (void) +mongoc_client_encryption_encrypt_text_opts_set_prefix ( + mongoc_client_encryption_encrypt_text_opts_t *opts, + mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts); -MONGOC_EXPORT(void) mongoc_client_encryption_encrypt_text_opts_set_suffix(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts); +MONGOC_EXPORT (void) +mongoc_client_encryption_encrypt_text_opts_set_suffix ( + mongoc_client_encryption_encrypt_text_opts_t *opts, + mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts); -MONGOC_EXPORT(void) mongoc_client_encryption_encrypt_text_opts_set_substring(mongoc_client_encryption_encrypt_text_opts_t *opts, mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts); +MONGOC_EXPORT (void) +mongoc_client_encryption_encrypt_text_opts_set_substring ( + mongoc_client_encryption_encrypt_text_opts_t *opts, + mongoc_client_encryption_encrypt_text_per_index_opts_t *per_index_opts); MONGOC_EXPORT (mongoc_client_encryption_datakey_opts_t *) mongoc_client_encryption_datakey_opts_new (void) BSON_GNUC_WARN_UNUSED_RESULT; diff --git a/src/libmongoc/src/mongoc/mongoc-crypt.c b/src/libmongoc/src/mongoc/mongoc-crypt.c index 515616e5fd0..c5a646bc4f4 100644 --- a/src/libmongoc/src/mongoc/mongoc-crypt.c +++ b/src/libmongoc/src/mongoc/mongoc-crypt.c @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "bson/bson.h" +#include #define MONGOC_LOG_DOMAIN "client-side-encryption" #include diff --git a/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-text.json b/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-prefix-suffix.json similarity index 78% rename from src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-text.json rename to src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-prefix-suffix.json index 567eb467e6c..8c52b6dd0ad 100644 --- a/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-text.json +++ b/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-prefix-suffix.json @@ -13,7 +13,7 @@ { "queryType": "prefixPreview", "strMinQueryLength": { - "$numberInt": "1" + "$numberInt": "2" }, "strMaxQueryLength": { "$numberInt": "10" @@ -24,7 +24,7 @@ { "queryType": "suffixPreview", "strMinQueryLength": { - "$numberInt": "1" + "$numberInt": "2" }, "strMaxQueryLength": { "$numberInt": "10" @@ -33,16 +33,6 @@ "diacriticSensitive": true } ] - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } ] } diff --git a/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-substring.json b/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-substring.json new file mode 100644 index 00000000000..cb97a167175 --- /dev/null +++ b/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-substring.json @@ -0,0 +1,30 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encrypted-textPreview", + "bsonType": "string", + "queries": [ + { + "queryType": "substringPreview", + "strMaxLength": { + "$numberInt": "10" + }, + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] +} diff --git a/src/libmongoc/tests/test-mongoc-client-side-encryption.c b/src/libmongoc/tests/test-mongoc-client-side-encryption.c index 3aedc87edfe..f1b9231a0b8 100644 --- a/src/libmongoc/tests/test-mongoc-client-side-encryption.c +++ b/src/libmongoc/tests/test-mongoc-client-side-encryption.c @@ -14,12 +14,14 @@ * limitations under the License. */ -#include "bson/bson.h" -#include "bson/macros.h" -#include "mongoc/mongoc.h" #include #include +#include + +#include +#include + #include #include @@ -3287,13 +3289,11 @@ typedef struct { } ee_fixture; static ee_fixture * -explicit_encryption_setup_full (const char* encrypted_fields_path, const char* key_path) +explicit_encryption_setup_full (const char *encrypted_fields_path, const char *key_path) { ee_fixture *eef = (ee_fixture *) bson_malloc0 (sizeof (ee_fixture)); - bson_t *encryptedFields = - get_bson_from_json_file (encrypted_fields_path); - bson_t *key1Document = - get_bson_from_json_file (key_path); + bson_t *encryptedFields = get_bson_from_json_file (encrypted_fields_path); + bson_t *key1Document = get_bson_from_json_file (key_path); mongoc_client_t *setupClient = test_framework_new_default_client (); @@ -3405,10 +3405,12 @@ explicit_encryption_setup_full (const char* encrypted_fields_path, const char* k } static ee_fixture * -explicit_encryption_setup (void) { - return explicit_encryption_setup_full("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" - "encryptedFields.json", "./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" - "key1-document.json"); +explicit_encryption_setup (void) +{ + return explicit_encryption_setup_full ("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" + "encryptedFields.json", + "./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" + "key1-document.json"); } static void @@ -4419,15 +4421,17 @@ test_explicit_encryption_case5 (void *unused) } static void -test_explicit_encryption_text(void *unused) +test_explicit_encryption_text (void *unused) { bson_error_t error; bool ok; mongoc_client_encryption_encrypt_opts_t *eopts; bson_value_t plaintext = {0}; - ee_fixture *eef = explicit_encryption_setup_full ("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" - "encryptedFields-text.json", "./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" - "key1-document.json"); + ee_fixture *eef = + explicit_encryption_setup_full ("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" + "encryptedFields-prefix-suffix.json", + "./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" + "key1-document.json"); BSON_UNUSED (unused); @@ -4435,10 +4439,12 @@ test_explicit_encryption_text(void *unused) plaintext.value.v_utf8.str = "foobarbaz"; plaintext.value.v_utf8.len = (uint32_t) strlen (plaintext.value.v_utf8.str); - mongoc_client_encryption_encrypt_text_per_index_opts_t *iopts = mongoc_client_encryption_encrypt_text_per_index_opts_new(); - mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_query_length(iopts, 3); - mongoc_client_encryption_encrypt_text_per_index_opts_set_str_min_query_length(iopts, 1); + mongoc_client_encryption_encrypt_text_per_index_opts_t *iopts = + mongoc_client_encryption_encrypt_text_per_index_opts_new (); + mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_query_length (iopts, 3); + mongoc_client_encryption_encrypt_text_per_index_opts_set_str_min_query_length (iopts, 1); + /* Prefix and suffix tests */ /* Insert 'foobarbaz' with both prefix and suffix indexing */ { bson_value_t insertPayload; @@ -4448,10 +4454,10 @@ test_explicit_encryption_text(void *unused) mongoc_client_encryption_encrypt_opts_set_keyid (eopts, &eef->key1ID); mongoc_client_encryption_encrypt_opts_set_algorithm (eopts, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); - mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new(); - mongoc_client_encryption_encrypt_text_opts_set_prefix(topts, iopts); - mongoc_client_encryption_encrypt_text_opts_set_suffix(topts, iopts); - mongoc_client_encryption_encrypt_opts_set_text_opts(eopts, topts); + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new (); + mongoc_client_encryption_encrypt_text_opts_set_prefix (topts, iopts); + mongoc_client_encryption_encrypt_text_opts_set_suffix (topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts (eopts, topts); ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eopts, &insertPayload, &error); ASSERT_OR_PRINT (ok, error); @@ -4475,19 +4481,19 @@ test_explicit_encryption_text(void *unused) mongoc_client_encryption_encrypt_opts_set_query_type (eo, MONGOC_ENCRYPT_QUERY_TYPE_PREFIXPREVIEW); mongoc_client_encryption_encrypt_opts_set_contention_factor (eo, 0); - mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new(); - mongoc_client_encryption_encrypt_text_opts_set_prefix(topts, iopts); - mongoc_client_encryption_encrypt_opts_set_text_opts(eo, topts); + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new (); + mongoc_client_encryption_encrypt_text_opts_set_prefix (topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts (eo, topts); plaintext.value.v_utf8.str = "foo"; plaintext.value.v_utf8.len = 3; ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &findPayload, &error); - bsonBuildDecl (expr, - kv ("$expr", - doc ( kv ("$encStrStartsWith", - doc (kv ("input", cstr("$encrypted-textPreview")), - kv ("prefix", value(findPayload))))))); + bsonBuildDecl ( + expr, + kv ("$expr", + doc (kv ("$encStrStartsWith", + doc (kv ("input", cstr ("$encrypted-textPreview")), kv ("prefix", value (findPayload))))))); ASSERT_OR_PRINT (ok, error); mongoc_cursor_t *cursor; @@ -4512,19 +4518,19 @@ test_explicit_encryption_text(void *unused) mongoc_client_encryption_encrypt_opts_set_query_type (eo, MONGOC_ENCRYPT_QUERY_TYPE_SUFFIXPREVIEW); mongoc_client_encryption_encrypt_opts_set_contention_factor (eo, 0); - mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new(); - mongoc_client_encryption_encrypt_text_opts_set_suffix(topts, iopts); - mongoc_client_encryption_encrypt_opts_set_text_opts(eo, topts); + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new (); + mongoc_client_encryption_encrypt_text_opts_set_suffix (topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts (eo, topts); plaintext.value.v_utf8.str = "baz"; plaintext.value.v_utf8.len = 3; ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &findPayload, &error); - bsonBuildDecl (expr, - kv ("$expr", - doc ( kv ("$encStrEndsWith", - doc (kv ("input", cstr("$encrypted-textPreview")), - kv ("suffix", value(findPayload))))))); + bsonBuildDecl ( + expr, + kv ("$expr", + doc (kv ("$encStrEndsWith", + doc (kv ("input", cstr ("$encrypted-textPreview")), kv ("suffix", value (findPayload))))))); ASSERT_OR_PRINT (ok, error); mongoc_cursor_t *cursor; @@ -4549,19 +4555,19 @@ test_explicit_encryption_text(void *unused) mongoc_client_encryption_encrypt_opts_set_query_type (eo, MONGOC_ENCRYPT_QUERY_TYPE_SUFFIXPREVIEW); mongoc_client_encryption_encrypt_opts_set_contention_factor (eo, 0); - mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new(); - mongoc_client_encryption_encrypt_text_opts_set_suffix(topts, iopts); - mongoc_client_encryption_encrypt_opts_set_text_opts(eo, topts); + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new (); + mongoc_client_encryption_encrypt_text_opts_set_suffix (topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts (eo, topts); plaintext.value.v_utf8.str = "foo"; plaintext.value.v_utf8.len = 3; ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &findPayload, &error); - bsonBuildDecl (expr, - kv ("$expr", - doc ( kv ("$encStrEndsWith", - doc (kv ("input", cstr("$encrypted-textPreview")), - kv ("suffix", value(findPayload))))))); + bsonBuildDecl ( + expr, + kv ("$expr", + doc (kv ("$encStrEndsWith", + doc (kv ("input", cstr ("$encrypted-textPreview")), kv ("suffix", value (findPayload))))))); ASSERT_OR_PRINT (ok, error); mongoc_cursor_t *cursor; @@ -4584,19 +4590,19 @@ test_explicit_encryption_text(void *unused) mongoc_client_encryption_encrypt_opts_set_query_type (eo, MONGOC_ENCRYPT_QUERY_TYPE_PREFIXPREVIEW); mongoc_client_encryption_encrypt_opts_set_contention_factor (eo, 0); - mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new(); - mongoc_client_encryption_encrypt_text_opts_set_prefix(topts, iopts); - mongoc_client_encryption_encrypt_opts_set_text_opts(eo, topts); + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new (); + mongoc_client_encryption_encrypt_text_opts_set_prefix (topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts (eo, topts); plaintext.value.v_utf8.str = "baz"; plaintext.value.v_utf8.len = 3; ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &findPayload, &error); - bsonBuildDecl (expr, - kv ("$expr", - doc ( kv ("$encStrStartsWith", - doc (kv ("input", cstr("$encrypted-textPreview")), - kv ("prefix", value(findPayload))))))); + bsonBuildDecl ( + expr, + kv ("$expr", + doc (kv ("$encStrStartsWith", + doc (kv ("input", cstr ("$encrypted-textPreview")), kv ("prefix", value (findPayload))))))); ASSERT_OR_PRINT (ok, error); mongoc_cursor_t *cursor; @@ -4609,9 +4615,112 @@ test_explicit_encryption_text(void *unused) bson_destroy (&expr); } - // TODO missing and present prefix/suffix - // substring is encStrContains + /* Substring tests */ + explicit_encryption_destroy (eef); + eef = explicit_encryption_setup_full ("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" + "encryptedFields-substring.json", + "./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" + "key1-document.json"); + /* Insert 'foobarbaz' with substring indexing */ + { + bson_value_t insertPayload; + bson_t to_insert = BSON_INITIALIZER; + + eopts = mongoc_client_encryption_encrypt_opts_new (); + mongoc_client_encryption_encrypt_opts_set_keyid (eopts, &eef->key1ID); + mongoc_client_encryption_encrypt_opts_set_algorithm (eopts, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); + + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new (); + mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_length (iopts, 10); + mongoc_client_encryption_encrypt_text_opts_set_substring (topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts (eopts, topts); + + plaintext.value.v_utf8.str = "foobarbaz"; + plaintext.value.v_utf8.len = 9; + ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eopts, &insertPayload, &error); + ASSERT_OR_PRINT (ok, error); + + ASSERT (BSON_APPEND_VALUE (&to_insert, "encrypted-textPreview", &insertPayload)); + + ok = mongoc_collection_insert_one (eef->encryptedColl, &to_insert, NULL /* opts */, NULL /* reply */, &error); + ASSERT_OR_PRINT (ok, error); + + bson_value_destroy (&insertPayload); + bson_destroy (&to_insert); + mongoc_client_encryption_encrypt_opts_destroy (eopts); + } + + /* Find the document using the 'bar' substring */ + { + bson_value_t findPayload; + mongoc_client_encryption_encrypt_opts_t *eo = mongoc_client_encryption_encrypt_opts_new (); + mongoc_client_encryption_encrypt_opts_set_keyid (eo, &eef->key1ID); + mongoc_client_encryption_encrypt_opts_set_algorithm (eo, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); + mongoc_client_encryption_encrypt_opts_set_query_type (eo, MONGOC_ENCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW); + mongoc_client_encryption_encrypt_opts_set_contention_factor (eo, 0); + + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new (); + mongoc_client_encryption_encrypt_text_opts_set_substring (topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts (eo, topts); + + plaintext.value.v_utf8.str = "bar"; + plaintext.value.v_utf8.len = 3; + ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &findPayload, &error); + + bsonBuildDecl ( + expr, + kv ("$expr", + doc (kv ("$encStrContains", + doc (kv ("input", cstr ("$encrypted-textPreview")), kv ("substring", value (findPayload))))))); + ASSERT_OR_PRINT (ok, error); + + mongoc_cursor_t *cursor; + const bson_t *got; + + cursor = mongoc_collection_find_with_opts (eef->encryptedColl, &expr, NULL /* opts */, NULL /* read_prefs */); + ASSERT (mongoc_cursor_next (cursor, &got)); + ASSERT_OR_PRINT (!mongoc_cursor_error (cursor, &error), error); + ASSERT_MATCH (got, "{ 'encrypted-textPreview': 'foobarbaz' }"); + ASSERT (!mongoc_cursor_next (cursor, &got) && "expected one document to be returned, got more than one"); + + mongoc_cursor_destroy (cursor); + bson_destroy (&expr); + } + + /* Ensure querying for a 'qux' substring returns no documents */ + { + bson_value_t findPayload; + mongoc_client_encryption_encrypt_opts_t *eo = mongoc_client_encryption_encrypt_opts_new (); + mongoc_client_encryption_encrypt_opts_set_keyid (eo, &eef->key1ID); + mongoc_client_encryption_encrypt_opts_set_algorithm (eo, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); + mongoc_client_encryption_encrypt_opts_set_query_type (eo, MONGOC_ENCRYPT_QUERY_TYPE_PREFIXPREVIEW); + mongoc_client_encryption_encrypt_opts_set_contention_factor (eo, 0); + + mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new (); + mongoc_client_encryption_encrypt_text_opts_set_substring (topts, iopts); + mongoc_client_encryption_encrypt_opts_set_text_opts (eo, topts); + + plaintext.value.v_utf8.str = "qux"; + plaintext.value.v_utf8.len = 3; + ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &findPayload, &error); + + bsonBuildDecl ( + expr, + kv ("$expr", + doc (kv ("$encStrContains", + doc (kv ("input", cstr ("$encrypted-textPreview")), kv ("substring", value (findPayload))))))); + ASSERT_OR_PRINT (ok, error); + + mongoc_cursor_t *cursor; + const bson_t *got; + + cursor = mongoc_collection_find_with_opts (eef->encryptedColl, &expr, NULL /* opts */, NULL /* read_prefs */); + ASSERT (!mongoc_cursor_next (cursor, &got) && "expected no documents to be returned, got some"); + + mongoc_cursor_destroy (cursor); + bson_destroy (&expr); + } explicit_encryption_destroy (eef); } @@ -7538,10 +7647,10 @@ test_client_side_encryption_install (TestSuite *suite) test_framework_skip_if_single, /* QE not supported on standalone */ test_framework_skip_if_no_client_side_encryption); TestSuite_AddFull (suite, - "/client_side_encryption/text", - test_explicit_encryption_text, - NULL, - NULL, + "/client_side_encryption/text", + test_explicit_encryption_text, + NULL, + NULL, test_framework_skip_if_max_wire_version_less_than_25 /* require server > 8.1 for QE support */, test_framework_skip_if_single, /* QE not supported on standalone */ test_framework_skip_if_no_client_side_encryption); From 772abde6dc4fa044ed515930ef715cc89cf3c45a Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:10:03 -0700 Subject: [PATCH 4/6] update libmongocrypt --- .evergreen/scripts/compile-libmongocrypt.sh | 6 +++--- src/libmongoc/CMakeLists.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.evergreen/scripts/compile-libmongocrypt.sh b/.evergreen/scripts/compile-libmongocrypt.sh index 297e374423a..3c1b9b22cd6 100755 --- a/.evergreen/scripts/compile-libmongocrypt.sh +++ b/.evergreen/scripts/compile-libmongocrypt.sh @@ -10,15 +10,15 @@ compile_libmongocrypt() { # `.evergreen/scripts/kms-divergence-check.sh` to ensure that there is no # divergence in the copied files. - # Clone libmongocrypt and check-out 1.13.0. - git clone -q --depth=1 https://github.com/mongodb/libmongocrypt --branch 1.13.0 || return + # Clone libmongocrypt and check-out 1.15.0. + git clone -q --depth=1 https://github.com/mongodb/libmongocrypt --branch 1.15.0 || return declare -a crypt_cmake_flags=( "-DMONGOCRYPT_MONGOC_DIR=${mongoc_dir}" "-DBUILD_TESTING=OFF" "-DENABLE_ONLINE_TESTS=OFF" "-DENABLE_MONGOC=OFF" - "-DBUILD_VERSION=1.13.0" + "-DBUILD_VERSION=1.15.0" ) . "$(dirname "${BASH_SOURCE[0]}")/find-ccache.sh" diff --git a/src/libmongoc/CMakeLists.txt b/src/libmongoc/CMakeLists.txt index 1df9ec49930..ec7776eac97 100644 --- a/src/libmongoc/CMakeLists.txt +++ b/src/libmongoc/CMakeLists.txt @@ -489,10 +489,10 @@ elseif (NOT ENABLE_CLIENT_SIDE_ENCRYPTION STREQUAL OFF) find_package (mongocrypt QUIET) endif () - if (mongocrypt_FOUND AND "${mongocrypt_VERSION}" VERSION_LESS 1.13.0) + if (mongocrypt_FOUND AND "${mongocrypt_VERSION}" VERSION_LESS 1.15.0) message (STATUS " libmongocrypt found at ${mongocrypt_DIR}") message (STATUS " libmongocrypt version ${mongocrypt_VERSION} found") - message (STATUS " libmongocrypt version 1.13.0 is required to enable In-Use Encryption Support.") + message (STATUS " libmongocrypt version 1.15.0 is required to enable In-Use Encryption Support.") set (REQUIRED_MONGOCRYPT_VERSION_FOUND OFF) elseif (mongocrypt_FOUND) set (REQUIRED_MONGOCRYPT_VERSION_FOUND ON) From 854c7ce8827913da0bd3ac32ed7e7ff949ec3af7 Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:45:50 -0700 Subject: [PATCH 5/6] require 8.2 --- src/libmongoc/tests/test-libmongoc.c | 2 ++ src/libmongoc/tests/test-libmongoc.h | 2 ++ src/libmongoc/tests/test-mongoc-client-side-encryption.c | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libmongoc/tests/test-libmongoc.c b/src/libmongoc/tests/test-libmongoc.c index 5e642a218ae..b713d8f9327 100644 --- a/src/libmongoc/tests/test-libmongoc.c +++ b/src/libmongoc/tests/test-libmongoc.c @@ -2272,6 +2272,8 @@ WIRE_VERSION_CHECKS (24) WIRE_VERSION_CHECKS (25) /* wire version 26 begins with the 8.1 release. */ WIRE_VERSION_CHECKS (26) +/* wire version 27 begins with the 8.2 release. */ +WIRE_VERSION_CHECKS (27) int test_framework_skip_if_no_dual_ip_hostname (void) diff --git a/src/libmongoc/tests/test-libmongoc.h b/src/libmongoc/tests/test-libmongoc.h index 861b74dd998..6f218a22d37 100644 --- a/src/libmongoc/tests/test-libmongoc.h +++ b/src/libmongoc/tests/test-libmongoc.h @@ -219,6 +219,8 @@ WIRE_VERSION_CHECK_DECLS (24) WIRE_VERSION_CHECK_DECLS (25) /* wire version 26 begins with the 8.1 release. */ WIRE_VERSION_CHECK_DECLS (26) +/* wire version 27 begins with the 8.2 release. */ +WIRE_VERSION_CHECK_DECLS (27) #undef WIRE_VERSION_CHECK_DECLS diff --git a/src/libmongoc/tests/test-mongoc-client-side-encryption.c b/src/libmongoc/tests/test-mongoc-client-side-encryption.c index f1b9231a0b8..124f4ad2d29 100644 --- a/src/libmongoc/tests/test-mongoc-client-side-encryption.c +++ b/src/libmongoc/tests/test-mongoc-client-side-encryption.c @@ -7651,7 +7651,7 @@ test_client_side_encryption_install (TestSuite *suite) test_explicit_encryption_text, NULL, NULL, - test_framework_skip_if_max_wire_version_less_than_25 /* require server > 8.1 for QE support */, + test_framework_skip_if_max_wire_version_less_than_27 /* require server > 8.2 for QE support */, test_framework_skip_if_single, /* QE not supported on standalone */ test_framework_skip_if_no_client_side_encryption); } From ba2f5e6b1a2ad90b7a6ee35d03f262903c3285da Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Wed, 6 Aug 2025 19:44:48 -0700 Subject: [PATCH 6/6] address memory leaks --- .../mongoc/mongoc-client-side-encryption.c | 14 ++++++ .../mongoc/mongoc-client-side-encryption.h | 7 +++ .../test-mongoc-client-side-encryption.c | 50 +++++++++++++------ 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c index b779a2d2835..857cda7f9b2 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c +++ b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c @@ -509,12 +509,25 @@ mongoc_client_encryption_encrypt_text_per_index_opts_new (void) return bson_malloc0 (sizeof (mongoc_client_encryption_encrypt_text_per_index_opts_t)); } +void +mongoc_client_encryption_encrypt_text_per_index_opts_destroy ( + mongoc_client_encryption_encrypt_text_per_index_opts_t *iopts) +{ + bson_free (iopts); +} + mongoc_client_encryption_encrypt_text_opts_t * mongoc_client_encryption_encrypt_text_opts_new (void) { return bson_malloc0 (sizeof (mongoc_client_encryption_encrypt_text_opts_t)); } +void +mongoc_client_encryption_encrypt_text_opts_destroy (mongoc_client_encryption_encrypt_text_opts_t *topts) +{ + bson_free (topts); +} + void mongoc_client_encryption_encrypt_range_opts_destroy (mongoc_client_encryption_encrypt_range_opts_t *range_opts) { @@ -2847,6 +2860,7 @@ mongoc_client_encryption_encrypt (mongoc_client_encryption_t *client_encryption, ret = true; fail: + bson_destroy (text_opts); bson_destroy (range_opts); RETURN (ret); } diff --git a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h index 53970f94661..bee247c663e 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h +++ b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.h @@ -241,6 +241,13 @@ mongoc_client_encryption_encrypt_text_per_index_opts_new (void); MONGOC_EXPORT (mongoc_client_encryption_encrypt_text_opts_t *) mongoc_client_encryption_encrypt_text_opts_new (void); +MONGOC_EXPORT (void) +mongoc_client_encryption_encrypt_text_per_index_opts_destroy ( + mongoc_client_encryption_encrypt_text_per_index_opts_t *iopts); + +MONGOC_EXPORT (void) +mongoc_client_encryption_encrypt_text_opts_destroy (mongoc_client_encryption_encrypt_text_opts_t *topts); + MONGOC_EXPORT (void) mongoc_client_encryption_encrypt_opts_destroy (mongoc_client_encryption_encrypt_opts_t *opts); diff --git a/src/libmongoc/tests/test-mongoc-client-side-encryption.c b/src/libmongoc/tests/test-mongoc-client-side-encryption.c index 124f4ad2d29..387fe029bc0 100644 --- a/src/libmongoc/tests/test-mongoc-client-side-encryption.c +++ b/src/libmongoc/tests/test-mongoc-client-side-encryption.c @@ -4425,7 +4425,6 @@ test_explicit_encryption_text (void *unused) { bson_error_t error; bool ok; - mongoc_client_encryption_encrypt_opts_t *eopts; bson_value_t plaintext = {0}; ee_fixture *eef = explicit_encryption_setup_full ("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" @@ -4450,16 +4449,16 @@ test_explicit_encryption_text (void *unused) bson_value_t insertPayload; bson_t to_insert = BSON_INITIALIZER; - eopts = mongoc_client_encryption_encrypt_opts_new (); - mongoc_client_encryption_encrypt_opts_set_keyid (eopts, &eef->key1ID); - mongoc_client_encryption_encrypt_opts_set_algorithm (eopts, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); + mongoc_client_encryption_encrypt_opts_t *eo = mongoc_client_encryption_encrypt_opts_new (); + mongoc_client_encryption_encrypt_opts_set_keyid (eo, &eef->key1ID); + mongoc_client_encryption_encrypt_opts_set_algorithm (eo, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new (); mongoc_client_encryption_encrypt_text_opts_set_prefix (topts, iopts); mongoc_client_encryption_encrypt_text_opts_set_suffix (topts, iopts); - mongoc_client_encryption_encrypt_opts_set_text_opts (eopts, topts); + mongoc_client_encryption_encrypt_opts_set_text_opts (eo, topts); - ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eopts, &insertPayload, &error); + ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &insertPayload, &error); ASSERT_OR_PRINT (ok, error); ASSERT (BSON_APPEND_VALUE (&to_insert, "encrypted-textPreview", &insertPayload)); @@ -4469,10 +4468,11 @@ test_explicit_encryption_text (void *unused) bson_value_destroy (&insertPayload); bson_destroy (&to_insert); - mongoc_client_encryption_encrypt_opts_destroy (eopts); + mongoc_client_encryption_encrypt_text_opts_destroy (topts); + mongoc_client_encryption_encrypt_opts_destroy (eo); } - // /* Find the document using the 'foo' prefix */ + /* Find the document using the 'foo' prefix */ { bson_value_t findPayload; mongoc_client_encryption_encrypt_opts_t *eo = mongoc_client_encryption_encrypt_opts_new (); @@ -4505,8 +4505,11 @@ test_explicit_encryption_text (void *unused) ASSERT_MATCH (got, "{ 'encrypted-textPreview': 'foobarbaz' }"); ASSERT (!mongoc_cursor_next (cursor, &got) && "expected one document to be returned, got more than one"); + bson_value_destroy (&findPayload); mongoc_cursor_destroy (cursor); bson_destroy (&expr); + mongoc_client_encryption_encrypt_text_opts_destroy (topts); + mongoc_client_encryption_encrypt_opts_destroy (eo); } /* Find the document using the 'baz' suffix */ @@ -4542,8 +4545,11 @@ test_explicit_encryption_text (void *unused) ASSERT_MATCH (got, "{ 'encrypted-textPreview': 'foobarbaz' }"); ASSERT (!mongoc_cursor_next (cursor, &got) && "expected one document to be returned, got more than one"); + bson_value_destroy (&findPayload); mongoc_cursor_destroy (cursor); bson_destroy (&expr); + mongoc_client_encryption_encrypt_text_opts_destroy (topts); + mongoc_client_encryption_encrypt_opts_destroy (eo); } /* Ensure querying for a 'foo' suffix returns no documents */ @@ -4577,8 +4583,11 @@ test_explicit_encryption_text (void *unused) ASSERT (!mongoc_cursor_next (cursor, &got) && "expected no documents to be returned, got some"); mongoc_cursor_next (cursor, &got); + bson_value_destroy (&findPayload); mongoc_cursor_destroy (cursor); bson_destroy (&expr); + mongoc_client_encryption_encrypt_text_opts_destroy (topts); + mongoc_client_encryption_encrypt_opts_destroy (eo); } /* Ensure querying for a 'baz' prefix returns no documents */ @@ -4611,8 +4620,11 @@ test_explicit_encryption_text (void *unused) cursor = mongoc_collection_find_with_opts (eef->encryptedColl, &expr, NULL /* opts */, NULL /* read_prefs */); ASSERT (!mongoc_cursor_next (cursor, &got) && "expected no documents to be returned, got some"); + bson_value_destroy (&findPayload); mongoc_cursor_destroy (cursor); bson_destroy (&expr); + mongoc_client_encryption_encrypt_text_opts_destroy (topts); + mongoc_client_encryption_encrypt_opts_destroy (eo); } @@ -4627,18 +4639,18 @@ test_explicit_encryption_text (void *unused) bson_value_t insertPayload; bson_t to_insert = BSON_INITIALIZER; - eopts = mongoc_client_encryption_encrypt_opts_new (); - mongoc_client_encryption_encrypt_opts_set_keyid (eopts, &eef->key1ID); - mongoc_client_encryption_encrypt_opts_set_algorithm (eopts, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); + mongoc_client_encryption_encrypt_opts_t *eo = mongoc_client_encryption_encrypt_opts_new (); + mongoc_client_encryption_encrypt_opts_set_keyid (eo, &eef->key1ID); + mongoc_client_encryption_encrypt_opts_set_algorithm (eo, MONGOC_ENCRYPT_ALGORITHM_TEXTPREVIEW); mongoc_client_encryption_encrypt_text_opts_t *topts = mongoc_client_encryption_encrypt_text_opts_new (); mongoc_client_encryption_encrypt_text_per_index_opts_set_str_max_length (iopts, 10); mongoc_client_encryption_encrypt_text_opts_set_substring (topts, iopts); - mongoc_client_encryption_encrypt_opts_set_text_opts (eopts, topts); + mongoc_client_encryption_encrypt_opts_set_text_opts (eo, topts); plaintext.value.v_utf8.str = "foobarbaz"; plaintext.value.v_utf8.len = 9; - ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eopts, &insertPayload, &error); + ok = mongoc_client_encryption_encrypt (eef->clientEncryption, &plaintext, eo, &insertPayload, &error); ASSERT_OR_PRINT (ok, error); ASSERT (BSON_APPEND_VALUE (&to_insert, "encrypted-textPreview", &insertPayload)); @@ -4648,7 +4660,8 @@ test_explicit_encryption_text (void *unused) bson_value_destroy (&insertPayload); bson_destroy (&to_insert); - mongoc_client_encryption_encrypt_opts_destroy (eopts); + mongoc_client_encryption_encrypt_text_opts_destroy (topts); + mongoc_client_encryption_encrypt_opts_destroy (eo); } /* Find the document using the 'bar' substring */ @@ -4684,8 +4697,11 @@ test_explicit_encryption_text (void *unused) ASSERT_MATCH (got, "{ 'encrypted-textPreview': 'foobarbaz' }"); ASSERT (!mongoc_cursor_next (cursor, &got) && "expected one document to be returned, got more than one"); + bson_value_destroy (&findPayload); mongoc_cursor_destroy (cursor); bson_destroy (&expr); + mongoc_client_encryption_encrypt_text_opts_destroy (topts); + mongoc_client_encryption_encrypt_opts_destroy (eo); } /* Ensure querying for a 'qux' substring returns no documents */ @@ -4718,9 +4734,13 @@ test_explicit_encryption_text (void *unused) cursor = mongoc_collection_find_with_opts (eef->encryptedColl, &expr, NULL /* opts */, NULL /* read_prefs */); ASSERT (!mongoc_cursor_next (cursor, &got) && "expected no documents to be returned, got some"); + bson_value_destroy (&findPayload); mongoc_cursor_destroy (cursor); bson_destroy (&expr); + mongoc_client_encryption_encrypt_text_opts_destroy (topts); + mongoc_client_encryption_encrypt_opts_destroy (eo); } + mongoc_client_encryption_encrypt_text_per_index_opts_destroy (iopts); explicit_encryption_destroy (eef); } @@ -7647,7 +7667,7 @@ test_client_side_encryption_install (TestSuite *suite) test_framework_skip_if_single, /* QE not supported on standalone */ test_framework_skip_if_no_client_side_encryption); TestSuite_AddFull (suite, - "/client_side_encryption/text", + "/client_side_encryption/explicit_encryption/text", test_explicit_encryption_text, NULL, NULL,