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) diff --git a/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c b/src/libmongoc/src/mongoc/mongoc-client-side-encryption.c index c5918190cf2..857cda7f9b2 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,31 @@ 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)); +} + +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) { @@ -672,6 +719,89 @@ 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 +1169,46 @@ 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 +2812,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; @@ -2668,6 +2838,11 @@ mongoc_client_encryption_encrypt (mongoc_client_encryption_t *client_encryption, 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, opts->algorithm, @@ -2676,6 +2851,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)) { @@ -2684,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); } @@ -2712,6 +2889,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 +2903,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..bee247c663e 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,9 @@ 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 +235,19 @@ 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_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); @@ -280,6 +300,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_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..c5a646bc4f4 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 #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; } @@ -1811,6 +1827,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 +1839,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 +1854,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-prefix-suffix.json b/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-prefix-suffix.json new file mode 100644 index 00000000000..8c52b6dd0ad --- /dev/null +++ b/src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/encryptedFields-prefix-suffix.json @@ -0,0 +1,38 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encrypted-textPreview", + "bsonType": "string", + "queries": [ + { + "queryType": "prefixPreview", + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "caseSensitive": true, + "diacriticSensitive": true + }, + { + "queryType": "suffixPreview", + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] +} 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/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-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 91225dc5878..387fe029bc0 100644 --- a/src/libmongoc/tests/test-mongoc-client-side-encryption.c +++ b/src/libmongoc/tests/test-mongoc-client-side-encryption.c @@ -17,6 +17,11 @@ #include #include +#include + +#include +#include + #include #include @@ -3284,15 +3289,11 @@ 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"); - bson_t *key1Document = - get_bson_from_json_file ("./src/libmongoc/tests/client_side_encryption_prose/explicit_encryption/" - "key1-document.json"); + 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 (); @@ -3403,6 +3404,15 @@ 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 +4420,330 @@ test_explicit_encryption_case5 (void *unused) explicit_encryption_destroy (eef); } +static void +test_explicit_encryption_text (void *unused) +{ + bson_error_t error; + bool ok; + bson_value_t plaintext = {0}; + 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); + + 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); + + /* Prefix and suffix tests */ + /* Insert 'foobarbaz' with both prefix and suffix indexing */ + { + bson_value_t insertPayload; + bson_t to_insert = BSON_INITIALIZER; + + 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 (eo, topts); + + 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)); + + 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_text_opts_destroy (topts); + mongoc_client_encryption_encrypt_opts_destroy (eo); + } + + /* 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"); + + 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 */ + { + 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"); + + 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 */ + { + 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); + + 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); + + 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 */ + { + 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"); + + 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); + } + + + /* 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; + + 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 (eo, topts); + + plaintext.value.v_utf8.str = "foobarbaz"; + plaintext.value.v_utf8.len = 9; + 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)); + + 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_text_opts_destroy (topts); + mongoc_client_encryption_encrypt_opts_destroy (eo); + } + + /* 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"); + + 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 */ + { + 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"); + + 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); +} + static void _test_unique_index_on_keyaltnames_setup (void (*test_case) (mongoc_client_encryption_t *, const bson_value_t *keyid)) { @@ -7332,5 +7666,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/explicit_encryption/text", + test_explicit_encryption_text, + NULL, + NULL, + 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); } }