From d8cf297d187352154f5c1c6c42b9f4430c542b2f Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Mon, 15 Sep 2025 14:01:44 -0400 Subject: [PATCH 01/10] add failing test --- test/test-mongocrypt-ctx-encrypt.c | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/test-mongocrypt-ctx-encrypt.c b/test/test-mongocrypt-ctx-encrypt.c index 1a61c3cd9..81d62a371 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -6058,6 +6058,40 @@ static void _test_deterministic_contention(_mongocrypt_tester_t *tester) { mongocrypt_status_destroy(status); } +static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) { +#define TF(suffix) TEST_FILE("./test/data/qe_keyAltName/" suffix) + { + mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT); + + // Q: Do we need the server to support keyAltName in encryptedFields? A: + + // Specify a local encryptedFieldsMap with keyAltName: + mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR( + BSON_STR({"db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "foo"} ]}})); + mongocrypt_setopt_encrypted_field_config_map(crypt, encrypted_fields_map); + ASSERT_OK(mongocrypt_init(crypt), crypt); + + mongocrypt_binary_t *cmd = TEST_BSON_STR(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : "bar"} ]})); + + mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + + ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx); + + // TODO: expect the key is requested with keyAltName "foo". + ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS); + { + mongocrypt_binary_t *filter = mongocrypt_binary_new(); + ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx); + ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON_STR(BSON_STR({"keyAltName" : {"$in" : ["foo"]}})), filter); + mongocrypt_binary_destroy(filter); + } + + mongocrypt_ctx_destroy(ctx); + mongocrypt_destroy(crypt); + } +#undef TF +} + void _mongocrypt_tester_install_ctx_encrypt(_mongocrypt_tester_t *tester) { INSTALL_TEST(_test_explicit_encrypt_init); INSTALL_TEST(_test_encrypt_init); @@ -6157,4 +6191,5 @@ void _mongocrypt_tester_install_ctx_encrypt(_mongocrypt_tester_t *tester) { INSTALL_TEST(_test_fle2_collinfo_with_bad_str_encode_version); INSTALL_TEST(_test_lookup); INSTALL_TEST(_test_deterministic_contention); + INSTALL_TEST(_test_qe_keyAltName); } From a23b6fedf284074319dc3a77cabe2a97c5a8aa44 Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Mon, 29 Sep 2025 10:37:49 -0700 Subject: [PATCH 02/10] keyAltName prototype --- src/mc-efc-private.h | 1 + src/mc-efc.c | 49 +++++++++++++++++++++++++----- src/mongocrypt-ctx-encrypt.c | 34 ++++++++++++++++++++- src/mongocrypt-private.h | 2 ++ test/test-mongocrypt-cleanup.c | 2 +- test/test-mongocrypt-compact.c | 2 +- test/test-mongocrypt-ctx-encrypt.c | 2 -- test/test-mongocrypt-key-broker.c | 3 +- 8 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/mc-efc-private.h b/src/mc-efc-private.h index 79b8676d9..438e1d982 100644 --- a/src/mc-efc-private.h +++ b/src/mc-efc-private.h @@ -37,6 +37,7 @@ typedef enum _supported_query_type_flags { typedef struct _mc_EncryptedField_t { supported_query_type_flags supported_queries; _mongocrypt_buffer_t keyId; + const char *keyAltName; const char *path; struct _mc_EncryptedField_t *next; } mc_EncryptedField_t; diff --git a/src/mc-efc.c b/src/mc-efc.c index a5e25db98..cb167c2b1 100644 --- a/src/mc-efc.c +++ b/src/mc-efc.c @@ -85,18 +85,46 @@ static bool _parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocry BSON_ASSERT_PARAM(efc); BSON_ASSERT_PARAM(field); - if (!bson_iter_init_find(&field_iter, field, "keyId")) { - CLIENT_ERR("unable to find 'keyId' in 'field' document"); + bool has_keyid = false; + bool has_keyaltname = false; + if (bson_iter_init_find(&field_iter, field, "keyId")) { + has_keyid = true; + } + if (bson_iter_init_find(&field_iter, field, "keyAltName")) { + has_keyaltname = true; + } + if (!(has_keyid || has_keyaltname)) { + CLIENT_ERR("unable to find 'keyId' or 'keyAltName' in 'field' document"); return false; } - if (!BSON_ITER_HOLDS_BINARY(&field_iter)) { - CLIENT_ERR("expected 'fields.keyId' to be type binary, got: %d", (int)bson_iter_type(&field_iter)); + if (has_keyid && has_keyaltname) { + CLIENT_ERR("only one of 'keyId' or 'keyAltName may be in 'field' document"); return false; } + _mongocrypt_buffer_t field_keyid; - if (!_mongocrypt_buffer_from_uuid_iter(&field_keyid, &field_iter)) { - CLIENT_ERR("unable to parse uuid key from 'fields.keyId'"); - return false; + if (has_keyid) { + BSON_ASSERT(bson_iter_init_find(&field_iter, field, "keyId")); + if (!BSON_ITER_HOLDS_BINARY(&field_iter)) { + CLIENT_ERR("expected 'fields.keyId' to be type binary, got: %d", (int)bson_iter_type(&field_iter)); + return false; + } + if (!_mongocrypt_buffer_from_uuid_iter(&field_keyid, &field_iter)) { + CLIENT_ERR("unable to parse uuid key from 'fields.keyId'"); + return false; + } + } else if (has_keyaltname) { + BSON_ASSERT(bson_iter_init_find(&field_iter, field, "keyAltName")); + } + + const char *keyAltName; + if (has_keyaltname) { + BSON_ASSERT(bson_iter_init_find(&field_iter, field, "keyAltName")); + if (!BSON_ITER_HOLDS_UTF8(&field_iter)) { + CLIENT_ERR("expected 'fields.keyAltName' to be type UTF-8, got: %d", (int)bson_iter_type(&field_iter)); + return false; + } + keyAltName = bson_iter_utf8(&field_iter, NULL); } const char *field_path; @@ -151,7 +179,12 @@ static bool _parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocry /* Prepend a new mc_EncryptedField_t */ mc_EncryptedField_t *ef = bson_malloc0(sizeof(mc_EncryptedField_t)); - _mongocrypt_buffer_copy_to(&field_keyid, &ef->keyId); + if (has_keyid) { + _mongocrypt_buffer_copy_to(&field_keyid, &ef->keyId); + } + if (has_keyaltname) { + ef->keyAltName = bson_strdup(keyAltName); + } ef->path = bson_strdup(field_path); ef->next = efc->fields; ef->supported_queries = query_types; diff --git a/src/mongocrypt-ctx-encrypt.c b/src/mongocrypt-ctx-encrypt.c index 81c8c713b..2138ec628 100644 --- a/src/mongocrypt-ctx-encrypt.c +++ b/src/mongocrypt-ctx-encrypt.c @@ -132,6 +132,33 @@ static bool _fle2_collect_keys_for_compaction(mongocrypt_ctx_t *ctx) { return true; } +// Return value: 1 = keys needed, 0 = no keys needed, -1 = error +static int _fle2_collect_keys_for_encrypted_fields(mongocrypt_ctx_t *ctx) { + int need_keys = 0; + _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx; + BSON_ASSERT_PARAM(ctx); + + const mc_EncryptedFieldConfig_t *efc = + mc_schema_broker_get_encryptedFields(ectx->sb, ectx->target_coll, ctx->status); + if (!efc) { + _mongocrypt_ctx_fail(ctx); + return -1; + } + + for (const mc_EncryptedField_t *field = efc->fields; field != NULL && field->keyAltName; field = field->next) { + need_keys = 1; + bson_value_t keyAltName; + _bson_value_from_string(field->keyAltName, &keyAltName); + if (!_mongocrypt_key_broker_request_name(&ctx->kb, &keyAltName)) { + _mongocrypt_key_broker_status(&ctx->kb, ctx->status); + _mongocrypt_ctx_fail(ctx); + return -1; + } + } + + return need_keys; +} + static bool _mongo_feed_collinfo(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in) { bson_t as_bson; @@ -2699,8 +2726,13 @@ static bool mongocrypt_ctx_encrypt_ismaster_done(mongocrypt_ctx_t *ctx) { return false; } + const int need_keys = _fle2_collect_keys_for_encrypted_fields(ctx); + if (need_keys == -1) { + return false; + } + if (ctx->state == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS) { - if (ectx->bypass_query_analysis) { + if (ectx->bypass_query_analysis || need_keys == 1) { /* Keys may have been requested for compactionTokens. * Finish key requests. */ diff --git a/src/mongocrypt-private.h b/src/mongocrypt-private.h index fc161a672..17ca3a70a 100644 --- a/src/mongocrypt-private.h +++ b/src/mongocrypt-private.h @@ -175,4 +175,6 @@ bool _mongocrypt_needs_credentials_for_provider(mongocrypt_t *crypt, _mongocrypt_kms_provider_t provider, const char *name); +void _bson_value_from_string(const char *string, bson_value_t *value); + #endif /* MONGOCRYPT_PRIVATE_H */ diff --git a/test/test-mongocrypt-cleanup.c b/test/test-mongocrypt-cleanup.c index c37be0f6f..b653fa87f 100644 --- a/test/test-mongocrypt-cleanup.c +++ b/test/test-mongocrypt-cleanup.c @@ -169,7 +169,7 @@ static void _test_cleanup_missing_key_id(_mongocrypt_tester_t *tester) { { ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/cleanup/missing-key-id/collinfo.json")), ctx, - "unable to find 'keyId' in 'field' document"); + "unable to find 'keyId' or 'keyAltName' in 'field' document"); } mongocrypt_ctx_destroy(ctx); diff --git a/test/test-mongocrypt-compact.c b/test/test-mongocrypt-compact.c index 2537a708a..2ad9d50d1 100644 --- a/test/test-mongocrypt-compact.c +++ b/test/test-mongocrypt-compact.c @@ -226,7 +226,7 @@ static void _test_compact_missing_key_id(_mongocrypt_tester_t *tester) { { ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/compact/missing-key-id/collinfo.json")), ctx, - "unable to find 'keyId' in 'field' document"); + "unable to find 'keyId' or 'keyAltName' in 'field' document"); } mongocrypt_ctx_destroy(ctx); diff --git a/test/test-mongocrypt-ctx-encrypt.c b/test/test-mongocrypt-ctx-encrypt.c index 81d62a371..d4f5fd923 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -6063,8 +6063,6 @@ static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) { { mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT); - // Q: Do we need the server to support keyAltName in encryptedFields? A: - // Specify a local encryptedFieldsMap with keyAltName: mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR( BSON_STR({"db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "foo"} ]}})); diff --git a/test/test-mongocrypt-key-broker.c b/test/test-mongocrypt-key-broker.c index 154784ab2..cdeadbdeb 100644 --- a/test/test-mongocrypt-key-broker.c +++ b/test/test-mongocrypt-key-broker.c @@ -16,11 +16,12 @@ #include "mongocrypt-key-broker-private.h" #include "mongocrypt-key-private.h" +#include "mongocrypt-private.h" #include "mongocrypt.h" #include "test-mongocrypt.h" /* Given a string, populate a bson_value_t for that string */ -static void _bson_value_from_string(char *string, bson_value_t *value) { +void _bson_value_from_string(const char *string, bson_value_t *value) { bson_t *bson; bson_iter_t iter; From 55f4a656f1045a8660e605b218ba9fc060c28187 Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:30:13 -0700 Subject: [PATCH 03/10] WIP state --- src/mc-schema-broker-private.h | 10 +- src/mc-schema-broker.c | 154 +++++++++++++++++++++++++++-- src/mongocrypt-ctx-encrypt.c | 27 +++-- src/mongocrypt-ctx-private.h | 1 + src/mongocrypt-ctx.c | 15 +++ src/mongocrypt-key-broker.c | 3 +- src/mongocrypt-private.h | 1 + src/mongocrypt.c | 13 +++ test/test-mc-schema-broker.c | 32 +++--- test/test-mongocrypt-ctx-encrypt.c | 16 ++- test/test-mongocrypt-key-broker.c | 12 --- 11 files changed, 234 insertions(+), 50 deletions(-) diff --git a/src/mc-schema-broker-private.h b/src/mc-schema-broker-private.h index 287d29ac6..b3bacbce9 100644 --- a/src/mc-schema-broker-private.h +++ b/src/mc-schema-broker-private.h @@ -19,6 +19,7 @@ #include "mc-efc-private.h" // mc_EncryptedFieldConfig_t #include "mongocrypt-cache-collinfo-private.h" +#include "mongocrypt-key-broker-private.h" #include "mongocrypt.h" #include @@ -100,6 +101,12 @@ bool mc_schema_broker_need_more_schemas(const mc_schema_broker_t *sb); const mc_EncryptedFieldConfig_t * mc_schema_broker_get_encryptedFields(const mc_schema_broker_t *sb, const char *coll, mongocrypt_status_t *status); +// mc_schema_broker_get_encryptedFields returns encryptedFields for a collection if any exists. +// +// Returns NULL if none is found. +const mc_EncryptedFieldConfig_t * +mc_schema_broker_maybe_get_encryptedFields(const mc_schema_broker_t *sb, const char *coll, mongocrypt_status_t *status); + typedef enum { MC_CMD_SCHEMAS_FOR_CRYPT_SHARED, // target the crypt_shared library. MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, // target mongocryptd process. @@ -116,7 +123,8 @@ typedef enum { // - encryptionInformation: for QE. // // Set cmd_target to the intended command destination. This impacts if/how schema information is added. -bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb, +bool mc_schema_broker_add_schemas_to_cmd(mc_schema_broker_t *sb, + _mongocrypt_key_broker_t *kb, bson_t *cmd /* in and out */, mc_cmd_target_t cmd_target, mongocrypt_status_t *status); diff --git a/src/mc-schema-broker.c b/src/mc-schema-broker.c index 461692235..0a80157b9 100644 --- a/src/mc-schema-broker.c +++ b/src/mc-schema-broker.c @@ -17,6 +17,7 @@ #include "mc-schema-broker-private.h" #include "mc-efc-private.h" // mc_EncryptedFieldConfig_t +#include "mongocrypt-buffer-private.h" #include "mongocrypt-cache-collinfo-private.h" #include "mongocrypt-key-broker-private.h" #include "mongocrypt-private.h" @@ -601,7 +602,39 @@ mc_schema_broker_get_encryptedFields(const mc_schema_broker_t *sb, const char *c return NULL; } +const mc_EncryptedFieldConfig_t * +mc_schema_broker_maybe_get_encryptedFields(const mc_schema_broker_t *sb, const char *coll, mongocrypt_status_t *status) { + BSON_ASSERT_PARAM(sb); + BSON_ASSERT_PARAM(coll); + + for (const mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { + if (0 != strcmp(it->coll, coll)) { + continue; + } + if (!it->encryptedFields.set) { + return NULL; + } + return &it->encryptedFields.efc; + } + return NULL; +} + +// TODO un-duplicate from test-mongocrypt-util.c +static void bson_iter_bson(bson_iter_t *iter, bson_t *bson) { + uint32_t len; + const uint8_t *data = NULL; + if (BSON_ITER_HOLDS_DOCUMENT(iter)) { + bson_iter_document(iter, &len, &data); + } + if (BSON_ITER_HOLDS_ARRAY(iter)) { + bson_iter_array(iter, &len, &data); + } + BSON_ASSERT(data); + bson_init_static(bson, data, len); +} + static bool append_encryptedFields(const bson_t *encryptedFields, + _mongocrypt_key_broker_t *kb, const char *coll, uint8_t default_strEncodeVersion, bson_t *out, @@ -625,9 +658,11 @@ static bool append_encryptedFields(const bson_t *encryptedFields, goto fail; } + // TODO rewriting encryptedFields goes here somewhere // Copy all values. Check if state collections are present. while (bson_iter_next(&iter)) { - if (strcmp(bson_iter_key(&iter), "escCollection") == 0) { + const char *iter_key = bson_iter_key(&iter); + if (strcmp(iter_key, "escCollection") == 0) { has_escCollection = true; } if (strcmp(bson_iter_key(&iter), "ecocCollection") == 0) { @@ -639,8 +674,92 @@ static bool append_encryptedFields(const bson_t *encryptedFields, if (strcmp(bson_iter_key(&iter), "strEncodeVersion") == 0) { has_strEncodeVersion = true; } - TRY_BSON_OR(BSON_APPEND_VALUE(out, bson_iter_key(&iter), bson_iter_value(&iter))) { - goto fail; + /* Special-case the "fields" array: copy each element but omit the + * "keyAltName" key from each subdocument. For other keys, copy as-is. */ + if (0 == strcmp(iter_key, "fields") && BSON_ITER_HOLDS_ARRAY(&iter)) { + uint32_t array_len = 0; + const uint8_t *array_data = NULL; + bson_t array_bson; + + bson_iter_array(&iter, &array_len, &array_data); + bson_init_static(&array_bson, array_data, array_len); + + bson_t new_array; + TRY_BSON_OR(BSON_APPEND_ARRAY_BEGIN(out, "fields", &new_array)) { + goto fail; + } + + bson_iter_t arr_it; + if (!bson_iter_init(&arr_it, &array_bson)) { + CLIENT_ERR("failed to iterate 'fields' array"); + goto fail; + } + + size_t idx = 0; + while (bson_iter_next(&arr_it)) { + char idx_str[32]; + const char *idx_str_ptr; + int ret = bson_uint32_to_string((uint32_t)idx, &idx_str_ptr, idx_str, sizeof idx_str); + BSON_ASSERT(ret > 0 && ret <= (int)sizeof idx_str); + + if (BSON_ITER_HOLDS_DOCUMENT(&arr_it)) { + uint32_t doc_len = 0; + const uint8_t *doc_data = NULL; + bson_iter_document(&arr_it, &doc_len, &doc_data); + bson_t elem_doc; + bson_init_static(&elem_doc, doc_data, doc_len); + + bson_t new_doc; + char *keyAltName_dup = NULL; + + /* Extract keyAltName (if present) and strdup it so caller can + * derive a keyId from it. */ + bson_iter_t doc_it2; + if (bson_iter_init(&doc_it2, &elem_doc)) { + if (bson_iter_find(&doc_it2, "keyAltName") && BSON_ITER_HOLDS_UTF8(&doc_it2)) { + const char *kan = bson_iter_utf8(&doc_it2, NULL); + if (kan) { + keyAltName_dup = bson_strdup(kan); + } + } + } + + bson_init(&new_doc); + /* Copy elem_doc into new_doc excluding "keyAltName". */ + bson_copy_to_excluding_noinit(&elem_doc, &new_doc, "keyAltName", NULL); + + if (keyAltName_dup) { + _mongocrypt_buffer_t unused, key_id_out; + bson_value_t key_alt_name_v; + _bson_value_from_string(keyAltName_dup, &key_alt_name_v); + BSON_ASSERT(_mongocrypt_key_broker_decrypted_key_by_name(kb, &key_alt_name_v, &unused, &key_id_out)); + bson_append_binary(&new_doc, "keyId", -1, key_id_out.subtype, key_id_out.data, key_id_out.len); + } + + TRY_BSON_OR(bson_append_document(&new_array, idx_str_ptr, -1, &new_doc)) { + bson_destroy(&new_doc); + bson_free(keyAltName_dup); + goto fail; + } + bson_destroy(&new_doc); + bson_free(keyAltName_dup); + } else { + /* Non-document elements: copy as-is. */ + TRY_BSON_OR(BSON_APPEND_VALUE(&new_array, idx_str, bson_iter_value(&arr_it))) { + goto fail; + } + } + + idx++; + } + + TRY_BSON_OR(bson_append_array_end(out, &new_array)) { + goto fail; + } + } else { + TRY_BSON_OR(BSON_APPEND_VALUE(out, iter_key, bson_iter_value(&iter))) { + goto fail; + } } } @@ -681,6 +800,7 @@ static bool append_encryptedFields(const bson_t *encryptedFields, } static bool append_encryptionInformation(const mc_schema_broker_t *sb, + _mongocrypt_key_broker_t *kb, const char *cmd_name, bson_t *out, mongocrypt_status_t *status) { @@ -717,7 +837,7 @@ static bool append_encryptionInformation(const mc_schema_broker_t *sb, encryptedFields = &se->encryptedFields.bson; default_strEncodeVersion = se->encryptedFields.efc.str_encode_version; } - if (!append_encryptedFields(encryptedFields, se->coll, default_strEncodeVersion, &ns_to_schema_bson, status)) { + if (!append_encryptedFields(encryptedFields, kb, se->coll, default_strEncodeVersion, &ns_to_schema_bson, status)) { goto loop_fail; } @@ -767,6 +887,7 @@ static const char *get_cmd_name(const bson_t *cmd, mongocrypt_status_t *status) } static bool insert_encryptionInformation(const mc_schema_broker_t *sb, + _mongocrypt_key_broker_t *kb, const char *cmd_name, bson_t *cmd /* in and out */, mc_cmd_target_t cmd_target, @@ -830,7 +951,7 @@ static bool insert_encryptionInformation(const mc_schema_broker_t *sb, goto fail; } // And append `encryptionInformation`. - if (!append_encryptionInformation(sb, cmd_name, &nsInfo_array_0, status)) { + if (!append_encryptionInformation(sb, kb, cmd_name, &nsInfo_array_0, status)) { goto fail; } if (!bson_append_document_end(&nsInfo_array, &nsInfo_array_0)) { @@ -880,7 +1001,7 @@ static bool insert_encryptionInformation(const mc_schema_broker_t *sb, bson_copy_to(&tmp, &explain); } - if (!append_encryptionInformation(sb, cmd_name, &explain, status)) { + if (!append_encryptionInformation(sb, kb, cmd_name, &explain, status)) { goto fail; } @@ -903,7 +1024,7 @@ static bool insert_encryptionInformation(const mc_schema_broker_t *sb, // "": { ... } // "encryptionInformation": {} // } - if (!append_encryptionInformation(sb, cmd_name, cmd, status)) { + if (!append_encryptionInformation(sb, kb, cmd_name, cmd, status)) { goto fail; } @@ -1005,7 +1126,8 @@ static bool insert_csfleEncryptionSchemas(const mc_schema_broker_t *sb, return true; } -bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb, +bool mc_schema_broker_add_schemas_to_cmd(mc_schema_broker_t *sb, + _mongocrypt_key_broker_t *kb, bson_t *cmd /* in and out */, mc_cmd_target_t cmd_target, mongocrypt_status_t *status) { @@ -1026,6 +1148,18 @@ bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb, if (it->encryptedFields.set) { has_encryptedFields = true; coll_with_encryptedFields = it->coll; + for (mc_EncryptedField_t *f = it->encryptedFields.efc.fields; f != NULL; f = f->next) { + if (f->keyAltName) { + bson_value_t key_alt_name; + _mongocrypt_buffer_t unused; + _bson_value_from_string(f->keyAltName, &key_alt_name); + const bool r = _mongocrypt_key_broker_decrypted_key_by_name(kb, &key_alt_name, &unused, &f->keyId); + if (!r) { + CLIENT_ERR("Could not find key by keyAltName: %s", f->keyAltName); + return false; + } + } + } } else if (it->jsonSchema.set) { has_jsonSchema = true; coll_with_jsonSchema = it->coll; @@ -1046,7 +1180,7 @@ bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb, if (has_encryptedFields) { // Use encryptionInformation. - return insert_encryptionInformation(sb, cmd_name, cmd, cmd_target, status); + return insert_encryptionInformation(sb, kb, cmd_name, cmd, cmd_target, status); } if (has_jsonSchema) { @@ -1057,7 +1191,7 @@ bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb, // Collections have no QE or CSFLE schemas. if (0 == strcmp(cmd_name, "bulkWrite")) { // "bulkWrite" does not support the jsonSchema field. Use encryptionInformation with empty schemas. - return insert_encryptionInformation(sb, cmd_name, cmd, cmd_target, status); + return insert_encryptionInformation(sb, kb, cmd_name, cmd, cmd_target, status); } // Use csfleEncryptionSchemas / jsonSchema with empty schemas. diff --git a/src/mongocrypt-ctx-encrypt.c b/src/mongocrypt-ctx-encrypt.c index 2138ec628..5cd61ae73 100644 --- a/src/mongocrypt-ctx-encrypt.c +++ b/src/mongocrypt-ctx-encrypt.c @@ -17,7 +17,9 @@ #include "mc-efc-private.h" #include "mc-fle-blob-subtype-private.h" #include "mc-fle2-rfds-private.h" +#include "mc-schema-broker-private.h" #include "mc-tokens-private.h" +#include "mongocrypt-buffer-private.h" #include "mongocrypt-ciphertext-private.h" #include "mongocrypt-crypto-private.h" #include "mongocrypt-ctx-private.h" @@ -139,10 +141,9 @@ static int _fle2_collect_keys_for_encrypted_fields(mongocrypt_ctx_t *ctx) { BSON_ASSERT_PARAM(ctx); const mc_EncryptedFieldConfig_t *efc = - mc_schema_broker_get_encryptedFields(ectx->sb, ectx->target_coll, ctx->status); + mc_schema_broker_maybe_get_encryptedFields(ectx->sb, ectx->target_coll, ctx->status); if (!efc) { - _mongocrypt_ctx_fail(ctx); - return -1; + return 0; } for (const mc_EncryptedField_t *field = efc->fields; field != NULL && field->keyAltName; field = field->next) { @@ -179,7 +180,6 @@ static bool _mongo_feed_collinfo(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in) return true; } -static bool _try_run_csfle_marking(mongocrypt_ctx_t *ctx); static bool _mongo_done_collinfo(mongocrypt_ctx_t *ctx) { _mongocrypt_ctx_encrypt_t *ectx; @@ -245,6 +245,7 @@ static bool _create_markings_cmd_bson(mongocrypt_ctx_t *ctx, bson_t *out) { // used to send the command. bson_copy_to_excluding_noinit(&bson_view, out, "$db", NULL); if (!mc_schema_broker_add_schemas_to_cmd(ectx->sb, + &ctx->kb, out, ctx->crypt->csfle.okay ? MC_CMD_SCHEMAS_FOR_CRYPT_SHARED : MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, @@ -498,7 +499,7 @@ static bool _add_dollar_db(const char *cmd_name, bson_t *cmd, const char *cmd_db * to generate the markings by passing a special command to a mongocryptd daemon * process. Instead, we'll do it ourselves here, if possible. */ -static bool _try_run_csfle_marking(mongocrypt_ctx_t *ctx) { +bool _try_run_csfle_marking(mongocrypt_ctx_t *ctx) { BSON_ASSERT_PARAM(ctx); BSON_ASSERT(ctx->state == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS @@ -1168,6 +1169,18 @@ static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) { // single target collection. For other commands, encryptedFields may not be on the target collection. const mc_EncryptedFieldConfig_t *target_efc = mc_schema_broker_get_encryptedFields(ectx->sb, ectx->target_coll, NULL); + // TODO I think here we have everything needed to rewrite the target encryptedFields with keyID + // note: kb->key_requests contains only the keyAltName for returned key? + + for (mc_EncryptedField_t *f = target_efc->fields; f != NULL; f = f->next) { + if (f->keyId.data == NULL) { + BSON_ASSERT(f->keyAltName); + bson_value_t key_alt_name; + _mongocrypt_buffer_t _unused; + _bson_value_from_string(f->keyAltName, &key_alt_name); + BSON_ASSERT(_mongocrypt_key_broker_decrypted_key_by_name(&ctx->kb, &key_alt_name, &_unused, &f->keyId)); + } + } moe_result result = must_omit_encryptionInformation(command_name, &converted, target_efc, ctx->status); if (!result.ok) { @@ -1184,7 +1197,7 @@ static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) { /* Append a new 'encryptionInformation'. */ if (!result.must_omit) { - if (!mc_schema_broker_add_schemas_to_cmd(ectx->sb, &converted, MC_CMD_SCHEMAS_FOR_SERVER, ctx->status)) { + if (!mc_schema_broker_add_schemas_to_cmd(ectx->sb, &ctx->kb, &converted, MC_CMD_SCHEMAS_FOR_SERVER, ctx->status)) { bson_destroy(&converted); return _mongocrypt_ctx_fail(ctx); } @@ -2729,6 +2742,8 @@ static bool mongocrypt_ctx_encrypt_ismaster_done(mongocrypt_ctx_t *ctx) { const int need_keys = _fle2_collect_keys_for_encrypted_fields(ctx); if (need_keys == -1) { return false; + } else if (need_keys == 1) { + ctx->need_keys_for_encryptedFields = true; } if (ctx->state == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS) { diff --git a/src/mongocrypt-ctx-private.h b/src/mongocrypt-ctx-private.h index 4d0b2e65a..f986fea47 100644 --- a/src/mongocrypt-ctx-private.h +++ b/src/mongocrypt-ctx-private.h @@ -127,6 +127,7 @@ struct _mongocrypt_ctx_t { _mongocrypt_opts_kms_providers_t per_ctx_kms_providers; /* owned */ _mongocrypt_opts_kms_providers_t kms_providers; /* not owned, is merged from per-ctx / per-mongocrypt_t */ bool initialized; + bool need_keys_for_encryptedFields; /* nothing_to_do is set to true under these conditions: * 1. No keys are requested * 2. The command is bypassed for automatic encryption (e.g. ping). diff --git a/src/mongocrypt-ctx.c b/src/mongocrypt-ctx.c index 15f4f372d..b4609e085 100644 --- a/src/mongocrypt-ctx.c +++ b/src/mongocrypt-ctx.c @@ -15,6 +15,7 @@ */ #include +#include #include "mc-mlib/str.h" #include "mc-textopts-private.h" @@ -22,6 +23,7 @@ #include "mongocrypt-ctx-private.h" #include "mongocrypt-key-broker-private.h" #include "mongocrypt-private.h" +#include "mongocrypt.h" bool _mongocrypt_ctx_fail_w_msg(mongocrypt_ctx_t *ctx, const char *msg) { BSON_ASSERT_PARAM(ctx); @@ -327,6 +329,13 @@ static bool _mongo_done_keys(mongocrypt_ctx_t *ctx) { BSON_ASSERT_PARAM(ctx); (void)_mongocrypt_key_broker_docs_done(&ctx->kb); + const bool r = _mongocrypt_ctx_state_from_key_broker(ctx); + if (!r) return false; + + if (ctx->state == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS) { + // return _try_run_csfle_marking(ctx); + return true; + } return _mongocrypt_ctx_state_from_key_broker(ctx); } @@ -878,6 +887,12 @@ bool _mongocrypt_ctx_state_from_key_broker(mongocrypt_ctx_t *ctx) { ret = true; break; case KB_DONE: + if (ctx->need_keys_for_encryptedFields) { + ctx->need_keys_for_encryptedFields = false; + new_state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS; + ret = true; + break; + } new_state = MONGOCRYPT_CTX_READY; if (kb->key_requests == NULL) { /* No key requests were ever added. */ diff --git a/src/mongocrypt-key-broker.c b/src/mongocrypt-key-broker.c index 5bf7ae234..441abe39f 100644 --- a/src/mongocrypt-key-broker.c +++ b/src/mongocrypt-key-broker.c @@ -455,7 +455,7 @@ bool _mongocrypt_key_broker_filter(_mongocrypt_key_broker_t *kb, mongocrypt_bina /* * This is our final query: * { $or: [ { _id: { $in : [ids] }}, - * { keyAltName : { $in : [names] }} ] } + * { keyAltNames : { $in : [names] }} ] } */ filter = BCON_NEW("$or", "[", @@ -727,6 +727,7 @@ bool _mongocrypt_key_broker_add_doc(_mongocrypt_key_broker_t *kb, } if (_mongocrypt_key_alt_name_intersects(key_doc->key_alt_names, key_request->alt_name)) { key_request->satisfied = true; + _mongocrypt_buffer_copy_to(&key_doc->id, &key_request->id); } } diff --git a/src/mongocrypt-private.h b/src/mongocrypt-private.h index 17ca3a70a..c123f8fb9 100644 --- a/src/mongocrypt-private.h +++ b/src/mongocrypt-private.h @@ -176,5 +176,6 @@ bool _mongocrypt_needs_credentials_for_provider(mongocrypt_t *crypt, const char *name); void _bson_value_from_string(const char *string, bson_value_t *value); +bool _try_run_csfle_marking(mongocrypt_ctx_t *ctx); #endif /* MONGOCRYPT_PRIVATE_H */ diff --git a/src/mongocrypt.c b/src/mongocrypt.c index 93fafbbd0..e5822437a 100644 --- a/src/mongocrypt.c +++ b/src/mongocrypt.c @@ -1188,6 +1188,19 @@ bool _mongocrypt_needs_credentials_for_provider(mongocrypt_t *crypt, return (crypt->opts.kms_providers.need_credentials & (int)provider) != 0; } +/* Given a string, populate a bson_value_t for that string */ +void _bson_value_from_string(const char *string, bson_value_t *value) { + bson_t *bson; + bson_iter_t iter; + + bson = BCON_NEW("key", string); + BSON_ASSERT(bson_iter_init_find(&iter, bson, "key")); + bson_value_copy(bson_iter_value(&iter), value); + + bson_destroy(bson); +} + + void mongocrypt_setopt_bypass_query_analysis(mongocrypt_t *crypt) { BSON_ASSERT_PARAM(crypt); diff --git a/test/test-mc-schema-broker.c b/test/test-mc-schema-broker.c index 29049a62a..ba4126362 100644 --- a/test/test-mc-schema-broker.c +++ b/test/test-mc-schema-broker.c @@ -592,7 +592,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); bson_t *expect = TMP_BSONF(BSON_STR({"find" : "coll", "jsonSchema" : MC_BSON, "isRemoteSchema" : false}), jsonSchema); ASSERT_EQUAL_BSON(expect, cmd); @@ -613,7 +613,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); bson_t *expect = TMP_BSONF(BSON_STR({ "find" : "coll", "csfleEncryptionSchemas" : { @@ -640,7 +640,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); bson_t *expect = TMP_BSON(BSON_STR({"find" : "coll", "jsonSchema" : {}, "isRemoteSchema" : true})); ASSERT_EQUAL_BSON(expect, cmd); @@ -665,7 +665,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); bson_t *expect = TMP_BSONF(BSON_STR({ "find" : "coll", "csfleEncryptionSchemas" : { @@ -693,7 +693,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); bson_t *expect = TMP_BSON(BSON_STR({ "find" : "coll", "csfleEncryptionSchemas" : { @@ -724,7 +724,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_FAILS_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), + ASSERT_FAILS_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status, "'coll2' has an encryptedFields configured, but collection 'coll' has a JSON schema"); _mongocrypt_cache_cleanup(&cache); @@ -743,7 +743,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); bson_t *expect = TMP_BSONF( BSON_STR({"find" : "coll", "encryptionInformation" : {"type" : 1, "schema" : {"db.coll" : MC_BSON}}}), encryptedFields); @@ -765,7 +765,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); bson_t *expect = TMP_BSONF(BSON_STR({ "find" : "coll", @@ -790,7 +790,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); bson_t *expect = TMP_BSON(BSON_STR({"find" : "coll"})); ASSERT_EQUAL_BSON(expect, cmd); @@ -809,7 +809,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"bulkWrite" : "coll", "nsInfo" : [ {"ns" : "db.coll"} ]})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); bson_t *expect = TMP_BSONF( BSON_STR({ "bulkWrite" : "coll", @@ -834,7 +834,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"bulkWrite" : "coll", "nsInfo" : [ {"ns" : "db.coll"} ]})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); bson_t *expect = TMP_BSON(BSON_STR({ "bulkWrite" : "coll", "nsInfo" : [ { @@ -868,7 +868,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"explain" : {"find" : "coll"}})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); bson_t *expect = TMP_BSONF(BSON_STR({ "explain" : {"find" : "coll"}, "encryptionInformation" : {"type" : 1, "schema" : {"db.coll" : MC_BSON}} @@ -891,7 +891,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"explain" : {"find" : "coll"}})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); bson_t *expect = TMP_BSONF( BSON_STR({ "explain" : {"find" : "coll", "encryptionInformation" : {"type" : 1, "schema" : {"db.coll" : MC_BSON}}} @@ -914,7 +914,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"explain" : {"find" : "coll"}})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_CRYPT_SHARED, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_CRYPT_SHARED, status), status); bson_t *expect = TMP_BSONF( BSON_STR({ "explain" : {"find" : "coll", "encryptionInformation" : {"type" : 1, "schema" : {"db.coll" : MC_BSON}}} @@ -939,7 +939,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); bson_t *expect = TMP_BSONF(BSON_STR({ "find" : "coll", "encryptionInformation" : { @@ -976,7 +976,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status); bson_t *expect = TMP_BSONF(BSON_STR({ "find" : "coll", "encryptionInformation" : { diff --git a/test/test-mongocrypt-ctx-encrypt.c b/test/test-mongocrypt-ctx-encrypt.c index d4f5fd923..1131dfd47 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -6065,7 +6065,7 @@ static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) { // Specify a local encryptedFieldsMap with keyAltName: mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR( - BSON_STR({"db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "foo"} ]}})); + BSON_STR({"db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "keyDocumentName"} ]}})); mongocrypt_setopt_encrypted_field_config_map(crypt, encrypted_fields_map); ASSERT_OK(mongocrypt_init(crypt), crypt); @@ -6074,15 +6074,23 @@ static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) { mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx); - - // TODO: expect the key is requested with keyAltName "foo". ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS); { mongocrypt_binary_t *filter = mongocrypt_binary_new(); ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx); - ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON_STR(BSON_STR({"keyAltName" : {"$in" : ["foo"]}})), filter); + ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON_STR(BSON_STR({ "$or" : [ { "_id" : { "$in" : [ ] } }, { "keyAltNames" : { "$in" : [ "keyDocumentName" ] } } ] })), filter); mongocrypt_binary_destroy(filter); } + // check state that talks to mongocryptd, translate before then + _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE); + + { + // Make the same request again and ensure that the key is fulfilled from cache (not requested again) + mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx); + ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY); + _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE); + } mongocrypt_ctx_destroy(ctx); mongocrypt_destroy(crypt); diff --git a/test/test-mongocrypt-key-broker.c b/test/test-mongocrypt-key-broker.c index cdeadbdeb..dc1fad4c6 100644 --- a/test/test-mongocrypt-key-broker.c +++ b/test/test-mongocrypt-key-broker.c @@ -20,18 +20,6 @@ #include "mongocrypt.h" #include "test-mongocrypt.h" -/* Given a string, populate a bson_value_t for that string */ -void _bson_value_from_string(const char *string, bson_value_t *value) { - bson_t *bson; - bson_iter_t iter; - - bson = BCON_NEW("key", string); - BSON_ASSERT(bson_iter_init_find(&iter, bson, "key")); - bson_value_copy(bson_iter_value(&iter), value); - - bson_destroy(bson); -} - static void _key_broker_add_name(_mongocrypt_key_broker_t *kb, char *string) { bson_value_t key_name; From 072857fa65442a273bb85fc97d5e0fe5c997d6c3 Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Thu, 16 Oct 2025 21:41:41 -0700 Subject: [PATCH 04/10] c driver tests passing --- src/mongocrypt-ctx.c | 18 +++++++++--------- src/mongocrypt-key-broker.c | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/mongocrypt-ctx.c b/src/mongocrypt-ctx.c index b4609e085..34fb014d0 100644 --- a/src/mongocrypt-ctx.c +++ b/src/mongocrypt-ctx.c @@ -328,15 +328,17 @@ static bool _mongo_feed_keys(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in) { static bool _mongo_done_keys(mongocrypt_ctx_t *ctx) { BSON_ASSERT_PARAM(ctx); + const bool used_keyaltname = ctx->need_keys_for_encryptedFields; (void)_mongocrypt_key_broker_docs_done(&ctx->kb); - const bool r = _mongocrypt_ctx_state_from_key_broker(ctx); - if (!r) return false; + // const bool r = _mongocrypt_ctx_state_from_key_broker(ctx); + // if (!r) return false; - if (ctx->state == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS) { - // return _try_run_csfle_marking(ctx); - return true; + if (used_keyaltname) { + ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS; + return _try_run_csfle_marking(ctx); + } else { + return _mongocrypt_ctx_state_from_key_broker(ctx); } - return _mongocrypt_ctx_state_from_key_broker(ctx); } static mongocrypt_kms_ctx_t *_next_kms_ctx(mongocrypt_ctx_t *ctx) { @@ -889,9 +891,7 @@ bool _mongocrypt_ctx_state_from_key_broker(mongocrypt_ctx_t *ctx) { case KB_DONE: if (ctx->need_keys_for_encryptedFields) { ctx->need_keys_for_encryptedFields = false; - new_state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS; - ret = true; - break; + kb->state = KB_REQUESTING; } new_state = MONGOCRYPT_CTX_READY; if (kb->key_requests == NULL) { diff --git a/src/mongocrypt-key-broker.c b/src/mongocrypt-key-broker.c index 441abe39f..0f12c025d 100644 --- a/src/mongocrypt-key-broker.c +++ b/src/mongocrypt-key-broker.c @@ -302,9 +302,9 @@ bool _mongocrypt_key_broker_request_id(_mongocrypt_key_broker_t *kb, const _mong BSON_ASSERT_PARAM(kb); BSON_ASSERT_PARAM(key_id); - if (kb->state != KB_REQUESTING) { - return _key_broker_fail_w_msg(kb, "attempting to request a key id, but in wrong state"); - } + // if (kb->state != KB_REQUESTING) { + // return _key_broker_fail_w_msg(kb, "attempting to request a key id, but in wrong state"); + // } if (!_mongocrypt_buffer_is_uuid((_mongocrypt_buffer_t *)key_id)) { return _key_broker_fail_w_msg(kb, "expected UUID for key id"); @@ -376,9 +376,9 @@ bool _mongocrypt_key_broker_request_any(_mongocrypt_key_broker_t *kb) { bool _mongocrypt_key_broker_requests_done(_mongocrypt_key_broker_t *kb) { BSON_ASSERT_PARAM(kb); - if (kb->state != KB_REQUESTING) { - return _key_broker_fail_w_msg(kb, "attempting to finish adding requests, but in wrong state"); - } + // if (kb->state != KB_REQUESTING) { + // return _key_broker_fail_w_msg(kb, "attempting to finish adding requests, but in wrong state"); + // } if (kb->key_requests) { if (_all_key_requests_satisfied(kb)) { @@ -1085,9 +1085,9 @@ bool _mongocrypt_key_broker_decrypted_key_by_name(_mongocrypt_key_broker_t *kb, BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(key_id_out); - if (kb->state != KB_DONE) { - return _key_broker_fail_w_msg(kb, "attempting retrieve decrypted key material, but in wrong state"); - } + // if (kb->state != KB_DONE) { + // return _key_broker_fail_w_msg(kb, "attempting retrieve decrypted key material, but in wrong state"); + // } key_alt_name = _mongocrypt_key_alt_name_new(key_alt_name_value); ret = _get_decrypted_key_material(kb, NULL, key_alt_name, out, key_id_out); From 31bbd30ad74105197569a4f2019591c5fdc13222 Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:09:08 -0700 Subject: [PATCH 05/10] test including cache + passing --- src/mongocrypt-ctx-encrypt.c | 9 +++- src/mongocrypt-ctx.c | 6 ++- test/test-mongocrypt-ctx-encrypt.c | 76 ++++++++++++++++++++++++++---- 3 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/mongocrypt-ctx-encrypt.c b/src/mongocrypt-ctx-encrypt.c index 5cd61ae73..d7554389d 100644 --- a/src/mongocrypt-ctx-encrypt.c +++ b/src/mongocrypt-ctx-encrypt.c @@ -146,7 +146,10 @@ static int _fle2_collect_keys_for_encrypted_fields(mongocrypt_ctx_t *ctx) { return 0; } - for (const mc_EncryptedField_t *field = efc->fields; field != NULL && field->keyAltName; field = field->next) { + for (const mc_EncryptedField_t *field = efc->fields; field != NULL; field = field->next) { + if (!field->keyAltName) { + continue; + } need_keys = 1; bson_value_t keyAltName; _bson_value_from_string(field->keyAltName, &keyAltName); @@ -400,6 +403,10 @@ static bool _mongo_done_markings(mongocrypt_ctx_t *ctx) { return mongocrypt_ctx_encrypt_ismaster_done(ctx); } (void)_mongocrypt_key_broker_requests_done(&ctx->kb); + // We can get here without going through NEED_MONGO_KEYS if the key is cached + if (ctx->need_keys_for_encryptedFields) { + ctx->need_keys_for_encryptedFields = false; + } return _mongocrypt_ctx_state_from_key_broker(ctx); } diff --git a/src/mongocrypt-ctx.c b/src/mongocrypt-ctx.c index 34fb014d0..82f350ab1 100644 --- a/src/mongocrypt-ctx.c +++ b/src/mongocrypt-ctx.c @@ -329,6 +329,7 @@ static bool _mongo_done_keys(mongocrypt_ctx_t *ctx) { BSON_ASSERT_PARAM(ctx); const bool used_keyaltname = ctx->need_keys_for_encryptedFields; + ctx->need_keys_for_encryptedFields = false; (void)_mongocrypt_key_broker_docs_done(&ctx->kb); // const bool r = _mongocrypt_ctx_state_from_key_broker(ctx); // if (!r) return false; @@ -890,10 +891,11 @@ bool _mongocrypt_ctx_state_from_key_broker(mongocrypt_ctx_t *ctx) { break; case KB_DONE: if (ctx->need_keys_for_encryptedFields) { - ctx->need_keys_for_encryptedFields = false; kb->state = KB_REQUESTING; + new_state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS; + } else { + new_state = MONGOCRYPT_CTX_READY; } - new_state = MONGOCRYPT_CTX_READY; if (kb->key_requests == NULL) { /* No key requests were ever added. */ ctx->nothing_to_do = true; /* nothing to encrypt/decrypt */ diff --git a/test/test-mongocrypt-ctx-encrypt.c b/test/test-mongocrypt-ctx-encrypt.c index 1131dfd47..b5f925884 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -6064,8 +6064,9 @@ static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) { mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT); // Specify a local encryptedFieldsMap with keyAltName: - mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR( - BSON_STR({"db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "keyDocumentName"} ]}})); + mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR(BSON_STR({ + "db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "keyDocumentName"} ]} + })); mongocrypt_setopt_encrypted_field_config_map(crypt, encrypted_fields_map); ASSERT_OK(mongocrypt_init(crypt), crypt); @@ -6074,22 +6075,81 @@ static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) { mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx); + // Keys requested to translate the keyAltNames: ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS); { mongocrypt_binary_t *filter = mongocrypt_binary_new(); ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx); - ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON_STR(BSON_STR({ "$or" : [ { "_id" : { "$in" : [ ] } }, { "keyAltNames" : { "$in" : [ "keyDocumentName" ] } } ] })), filter); + ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON( + TEST_BSON_STR( + BSON_STR({"$or" : [ {"_id" : {"$in" : []}}, {"keyAltNames" : {"$in" : ["keyDocumentName"]}} ]})), + filter); mongocrypt_binary_destroy(filter); + + // Feed requested key: + mongocrypt_binary_t *key = TF("key-document.json"); + ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key), ctx); + ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx); } - // check state that talks to mongocryptd, translate before then - _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE); + // MONGOCRYPT_CTX_MARKINGS is entered to send command to mongocryptd. + ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS); { - // Make the same request again and ensure that the key is fulfilled from cache (not requested again) - mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + mongocrypt_binary_t *cmd = mongocrypt_binary_new(); + ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd), ctx); + // Command to mongocryptd contains keyId (not keyAltName) + ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TF("cmd-to-mongocryptd.json"), cmd); + mongocrypt_binary_destroy(cmd); + + // Feed command with markings: + mongocrypt_binary_t *reply = TF("reply-from-mongocryptd.json"); + ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, reply), ctx); + ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx); + } + + ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY); + { + bson_t result_bson; + mongocrypt_binary_t *result = mongocrypt_binary_new(); + ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx); + ASSERT(_mongocrypt_binary_to_bson(result, &result_bson)); + // _assert_match_bson( + // TMP_BSON(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : {"$$type" : "binData"}} ]})), + // &result_bson); + mongocrypt_binary_destroy(result); + } + + // COPY + ctx = mongocrypt_ctx_new(crypt); ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx); + + // MONGOCRYPT_CTX_MARKINGS is entered to send command to mongocryptd. + ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS); + { + mongocrypt_binary_t *cmd = mongocrypt_binary_new(); + ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd), ctx); + // Command to mongocryptd contains keyId (not keyAltName) + ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TF("cmd-to-mongocryptd.json"), cmd); + mongocrypt_binary_destroy(cmd); + + // Feed command with markings: + mongocrypt_binary_t *reply = TF("reply-from-mongocryptd.json"); + ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, reply), ctx); + ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx); + } + ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY); - _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE); + { + mongocrypt_binary_t *result = mongocrypt_binary_new(); + ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx); + ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON( + // TODO: update expected result 'AAAA' with ciphertext. + TEST_BSON_STR(BSON_STR({ + "insert" : "coll", + "documents" : [ {"secret" : {"$binary" : {"base64" : "EGFhYWFhYWFhYWFhYWFhYWECZsXiTFAY0XXprCZjSggTgzFb+cy0/epNKDjEMZ3HaDBjVDIXHZQH8ye3hKBoKD5pDY8SERVzu070rWOU7EIw3g==", "subType" : "06"}}} ] + })), + result); + mongocrypt_binary_destroy(result); } mongocrypt_ctx_destroy(ctx); From c4d566f51596b53b4d5eb8f324f0a234f8642a5b Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Tue, 21 Oct 2025 12:08:52 -0700 Subject: [PATCH 06/10] working in C driver --- src/mongocrypt-ctx-encrypt.c | 15 +++++++ test/test-mongocrypt-ctx-encrypt.c | 71 ++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/mongocrypt-ctx-encrypt.c b/src/mongocrypt-ctx-encrypt.c index d7554389d..81b8b1084 100644 --- a/src/mongocrypt-ctx-encrypt.c +++ b/src/mongocrypt-ctx-encrypt.c @@ -2626,6 +2626,18 @@ bool mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t *ctx, const char *db, int32_t return mongocrypt_ctx_encrypt_ismaster_done(ctx); } +static bool _all_key_requests_satisfied(_mongocrypt_key_broker_t *kb) { + key_request_t *key_request; + + BSON_ASSERT_PARAM(kb); + + for (key_request = kb->key_requests; NULL != key_request; key_request = key_request->next) { + if (!key_request->satisfied) { + return false; + } + } + return true; +} #define WIRE_VERSION_SERVER_6 17 #define WIRE_VERSION_SERVER_8_1 26 @@ -2758,6 +2770,9 @@ static bool mongocrypt_ctx_encrypt_ismaster_done(mongocrypt_ctx_t *ctx) { /* Keys may have been requested for compactionTokens. * Finish key requests. */ + if (_all_key_requests_satisfied(&ctx->kb)) { + return _try_run_csfle_marking(ctx); + } _mongocrypt_key_broker_requests_done(&ctx->kb); return _mongocrypt_ctx_state_from_key_broker(ctx); } diff --git a/test/test-mongocrypt-ctx-encrypt.c b/test/test-mongocrypt-ctx-encrypt.c index b5f925884..cbdcc08e6 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -6158,6 +6158,76 @@ static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) { #undef TF } +static void _test_qe_keyAltName_cryptShared(_mongocrypt_tester_t *tester) { +#define TF(suffix) TEST_FILE("./test/data/qe_keyAltName/" suffix) + { + mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT | TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB); + + // Specify a local encryptedFieldsMap with keyAltName: + mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR(BSON_STR({ + "db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "keyDocumentName"} ]} + })); + mongocrypt_setopt_encrypted_field_config_map(crypt, encrypted_fields_map); + ASSERT_OK(mongocrypt_init(crypt), crypt); + + mongocrypt_binary_t *cmd = TEST_BSON_STR(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : "bar"} ]})); + + mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + + ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx); + // Keys requested to translate the keyAltNames: + ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS); + { + mongocrypt_binary_t *filter = mongocrypt_binary_new(); + ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx); + ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON( + TEST_BSON_STR( + BSON_STR({"$or" : [ {"_id" : {"$in" : []}}, {"keyAltNames" : {"$in" : ["keyDocumentName"]}} ]})), + filter); + mongocrypt_binary_destroy(filter); + + // Feed requested key: + mongocrypt_binary_t *key = TF("key-document.json"); + ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key), ctx); + ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx); + } + + ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY); + { + bson_t result_bson; + mongocrypt_binary_t *result = mongocrypt_binary_new(); + ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx); + ASSERT(_mongocrypt_binary_to_bson(result, &result_bson)); + // _assert_match_bson( + // TMP_BSON(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : {"$$type" : "binData"}} ]})), + // &result_bson); + mongocrypt_binary_destroy(result); + } + + // COPY + ctx = mongocrypt_ctx_new(crypt); + ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx); + + ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY); + { + mongocrypt_binary_t *result = mongocrypt_binary_new(); + ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx); + ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON( + // TODO: update expected result 'AAAA' with ciphertext. + TEST_BSON_STR(BSON_STR({ + "insert" : "coll", + "documents" : [ {"secret" : {"$binary" : {"base64" : "EGFhYWFhYWFhYWFhYWFhYWECZsXiTFAY0XXprCZjSggTgzFb+cy0/epNKDjEMZ3HaDBjVDIXHZQH8ye3hKBoKD5pDY8SERVzu070rWOU7EIw3g==", "subType" : "06"}}} ] + })), + result); + mongocrypt_binary_destroy(result); + } + + mongocrypt_ctx_destroy(ctx); + mongocrypt_destroy(crypt); + } +#undef TF +} + void _mongocrypt_tester_install_ctx_encrypt(_mongocrypt_tester_t *tester) { INSTALL_TEST(_test_explicit_encrypt_init); INSTALL_TEST(_test_encrypt_init); @@ -6258,4 +6328,5 @@ void _mongocrypt_tester_install_ctx_encrypt(_mongocrypt_tester_t *tester) { INSTALL_TEST(_test_lookup); INSTALL_TEST(_test_deterministic_contention); INSTALL_TEST(_test_qe_keyAltName); + INSTALL_TEST(_test_qe_keyAltName_cryptShared); } From bd76c71b9a65ec110d02ca703df6b49b26b5d79a Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:20:02 -0700 Subject: [PATCH 07/10] cleanup --- src/mc-schema-broker.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/mc-schema-broker.c b/src/mc-schema-broker.c index 0a80157b9..558f3654a 100644 --- a/src/mc-schema-broker.c +++ b/src/mc-schema-broker.c @@ -619,20 +619,6 @@ mc_schema_broker_maybe_get_encryptedFields(const mc_schema_broker_t *sb, const c return NULL; } -// TODO un-duplicate from test-mongocrypt-util.c -static void bson_iter_bson(bson_iter_t *iter, bson_t *bson) { - uint32_t len; - const uint8_t *data = NULL; - if (BSON_ITER_HOLDS_DOCUMENT(iter)) { - bson_iter_document(iter, &len, &data); - } - if (BSON_ITER_HOLDS_ARRAY(iter)) { - bson_iter_array(iter, &len, &data); - } - BSON_ASSERT(data); - bson_init_static(bson, data, len); -} - static bool append_encryptedFields(const bson_t *encryptedFields, _mongocrypt_key_broker_t *kb, const char *coll, @@ -658,7 +644,6 @@ static bool append_encryptedFields(const bson_t *encryptedFields, goto fail; } - // TODO rewriting encryptedFields goes here somewhere // Copy all values. Check if state collections are present. while (bson_iter_next(&iter)) { const char *iter_key = bson_iter_key(&iter); @@ -699,7 +684,7 @@ static bool append_encryptedFields(const bson_t *encryptedFields, while (bson_iter_next(&arr_it)) { char idx_str[32]; const char *idx_str_ptr; - int ret = bson_uint32_to_string((uint32_t)idx, &idx_str_ptr, idx_str, sizeof idx_str); + const size_t ret = bson_uint32_to_string((uint32_t)idx, &idx_str_ptr, idx_str, sizeof idx_str); BSON_ASSERT(ret > 0 && ret <= (int)sizeof idx_str); if (BSON_ITER_HOLDS_DOCUMENT(&arr_it)) { From e07769d029b62935c9b11004353030e570a98784 Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:38:02 -0700 Subject: [PATCH 08/10] test fixes --- src/mongocrypt-ctx-encrypt.c | 22 ++++++++++++---------- test/test-mongocrypt-ctx-encrypt.c | 26 +++++++++++++++++++++----- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/mongocrypt-ctx-encrypt.c b/src/mongocrypt-ctx-encrypt.c index 81b8b1084..a8ae5c256 100644 --- a/src/mongocrypt-ctx-encrypt.c +++ b/src/mongocrypt-ctx-encrypt.c @@ -1179,15 +1179,17 @@ static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) { // TODO I think here we have everything needed to rewrite the target encryptedFields with keyID // note: kb->key_requests contains only the keyAltName for returned key? - for (mc_EncryptedField_t *f = target_efc->fields; f != NULL; f = f->next) { - if (f->keyId.data == NULL) { - BSON_ASSERT(f->keyAltName); - bson_value_t key_alt_name; - _mongocrypt_buffer_t _unused; - _bson_value_from_string(f->keyAltName, &key_alt_name); - BSON_ASSERT(_mongocrypt_key_broker_decrypted_key_by_name(&ctx->kb, &key_alt_name, &_unused, &f->keyId)); - } - } + if (target_efc) { + for (mc_EncryptedField_t *f = target_efc->fields; f != NULL; f = f->next) { + if (f->keyId.data == NULL) { + BSON_ASSERT(f->keyAltName); + bson_value_t key_alt_name; + _mongocrypt_buffer_t _unused; + _bson_value_from_string(f->keyAltName, &key_alt_name); + BSON_ASSERT(_mongocrypt_key_broker_decrypted_key_by_name(&ctx->kb, &key_alt_name, &_unused, &f->keyId)); + } + } + } moe_result result = must_omit_encryptionInformation(command_name, &converted, target_efc, ctx->status); if (!result.ok) { @@ -2770,7 +2772,7 @@ static bool mongocrypt_ctx_encrypt_ismaster_done(mongocrypt_ctx_t *ctx) { /* Keys may have been requested for compactionTokens. * Finish key requests. */ - if (_all_key_requests_satisfied(&ctx->kb)) { + if (_all_key_requests_satisfied(&ctx->kb) && ctx->need_keys_for_encryptedFields) { return _try_run_csfle_marking(ctx); } _mongocrypt_key_broker_requests_done(&ctx->kb); diff --git a/test/test-mongocrypt-ctx-encrypt.c b/test/test-mongocrypt-ctx-encrypt.c index cbdcc08e6..27784008b 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -6122,7 +6122,6 @@ static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) { // COPY ctx = mongocrypt_ctx_new(crypt); ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx); - // MONGOCRYPT_CTX_MARKINGS is entered to send command to mongocryptd. ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS); { @@ -6146,7 +6145,15 @@ static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) { // TODO: update expected result 'AAAA' with ciphertext. TEST_BSON_STR(BSON_STR({ "insert" : "coll", - "documents" : [ {"secret" : {"$binary" : {"base64" : "EGFhYWFhYWFhYWFhYWFhYWECZsXiTFAY0XXprCZjSggTgzFb+cy0/epNKDjEMZ3HaDBjVDIXHZQH8ye3hKBoKD5pDY8SERVzu070rWOU7EIw3g==", "subType" : "06"}}} ] + "documents" : [ { + "secret" : { + "$binary" : { + "base64" : "EGFhYWFhYWFhYWFhYWFhYWECZsXiTFAY0XXprCZjSggTgzFb+cy0/" + "epNKDjEMZ3HaDBjVDIXHZQH8ye3hKBoKD5pDY8SERVzu070rWOU7EIw3g==", + "subType" : "06" + } + } + } ] })), result); mongocrypt_binary_destroy(result); @@ -6161,7 +6168,8 @@ static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) { static void _test_qe_keyAltName_cryptShared(_mongocrypt_tester_t *tester) { #define TF(suffix) TEST_FILE("./test/data/qe_keyAltName/" suffix) { - mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT | TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB); + mongocrypt_t *crypt = + _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT | TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB); // Specify a local encryptedFieldsMap with keyAltName: mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR(BSON_STR({ @@ -6207,7 +6215,7 @@ static void _test_qe_keyAltName_cryptShared(_mongocrypt_tester_t *tester) { // COPY ctx = mongocrypt_ctx_new(crypt); ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx); - + ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY); { mongocrypt_binary_t *result = mongocrypt_binary_new(); @@ -6216,7 +6224,15 @@ static void _test_qe_keyAltName_cryptShared(_mongocrypt_tester_t *tester) { // TODO: update expected result 'AAAA' with ciphertext. TEST_BSON_STR(BSON_STR({ "insert" : "coll", - "documents" : [ {"secret" : {"$binary" : {"base64" : "EGFhYWFhYWFhYWFhYWFhYWECZsXiTFAY0XXprCZjSggTgzFb+cy0/epNKDjEMZ3HaDBjVDIXHZQH8ye3hKBoKD5pDY8SERVzu070rWOU7EIw3g==", "subType" : "06"}}} ] + "documents" : [ { + "secret" : { + "$binary" : { + "base64" : "EGFhYWFhYWFhYWFhYWFhYWECZsXiTFAY0XXprCZjSggTgzFb+cy0/" + "epNKDjEMZ3HaDBjVDIXHZQH8ye3hKBoKD5pDY8SERVzu070rWOU7EIw3g==", + "subType" : "06" + } + } + } ] })), result); mongocrypt_binary_destroy(result); From 58892676d348807244ae2d9bf5bd360546d1b6de Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Wed, 29 Oct 2025 22:15:38 -0700 Subject: [PATCH 09/10] fix schema broker tests --- src/mc-schema-broker.c | 2 +- test/test-mc-schema-broker.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mc-schema-broker.c b/src/mc-schema-broker.c index f45cc3f28..21db44ad3 100644 --- a/src/mc-schema-broker.c +++ b/src/mc-schema-broker.c @@ -1180,7 +1180,7 @@ bool mc_schema_broker_add_schemas_to_cmd(mc_schema_broker_t *sb, if (has_encryptedFields && has_jsonSchema) { if (sb->schema_mixing_is_supported) { - return insert_encryptionInformation(sb, cmd_name, cmd, cmd_target, status) + return insert_encryptionInformation(sb, kb, cmd_name, cmd, cmd_target, status) && insert_csfleEncryptionSchemas(sb, cmd, cmd_target, status); } diff --git a/test/test-mc-schema-broker.c b/test/test-mc-schema-broker.c index e16d5bce8..ed0936833 100644 --- a/test/test-mc-schema-broker.c +++ b/test/test-mc-schema-broker.c @@ -751,7 +751,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); // Expect command has both 'encryptionInformation' and 'csfleEncryptionSchemas': bson_t *expect = TMP_BSONF(BSON_STR({ @@ -789,7 +789,7 @@ static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *teste ASSERT(!mc_schema_broker_need_more_schemas(sb)); bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"})); - ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); + ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status), status); // Expect db.coll3 is only included in encryptionInformation, not csfleEncryptionSchemas: bson_t *expect = TMP_BSONF(BSON_STR({ From b516f7d347f3d259cbd671ea65dc697468dcadc3 Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Wed, 29 Oct 2025 23:01:18 -0700 Subject: [PATCH 10/10] reset key broker state --- src/mongocrypt-ctx.c | 3 +-- src/mongocrypt-key-broker.c | 19 ++++++++++--------- test/test-mongocrypt-ctx-encrypt.c | 19 +++---------------- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/mongocrypt-ctx.c b/src/mongocrypt-ctx.c index 82f350ab1..4e7660862 100644 --- a/src/mongocrypt-ctx.c +++ b/src/mongocrypt-ctx.c @@ -331,10 +331,9 @@ static bool _mongo_done_keys(mongocrypt_ctx_t *ctx) { const bool used_keyaltname = ctx->need_keys_for_encryptedFields; ctx->need_keys_for_encryptedFields = false; (void)_mongocrypt_key_broker_docs_done(&ctx->kb); - // const bool r = _mongocrypt_ctx_state_from_key_broker(ctx); - // if (!r) return false; if (used_keyaltname) { + ctx->kb.state = KB_REQUESTING; ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS; return _try_run_csfle_marking(ctx); } else { diff --git a/src/mongocrypt-key-broker.c b/src/mongocrypt-key-broker.c index 0f12c025d..8cc77a53f 100644 --- a/src/mongocrypt-key-broker.c +++ b/src/mongocrypt-key-broker.c @@ -302,9 +302,9 @@ bool _mongocrypt_key_broker_request_id(_mongocrypt_key_broker_t *kb, const _mong BSON_ASSERT_PARAM(kb); BSON_ASSERT_PARAM(key_id); - // if (kb->state != KB_REQUESTING) { - // return _key_broker_fail_w_msg(kb, "attempting to request a key id, but in wrong state"); - // } + if (kb->state != KB_REQUESTING) { + return _key_broker_fail_w_msg(kb, "attempting to request a key id, but in wrong state"); + } if (!_mongocrypt_buffer_is_uuid((_mongocrypt_buffer_t *)key_id)) { return _key_broker_fail_w_msg(kb, "expected UUID for key id"); @@ -376,9 +376,9 @@ bool _mongocrypt_key_broker_request_any(_mongocrypt_key_broker_t *kb) { bool _mongocrypt_key_broker_requests_done(_mongocrypt_key_broker_t *kb) { BSON_ASSERT_PARAM(kb); - // if (kb->state != KB_REQUESTING) { - // return _key_broker_fail_w_msg(kb, "attempting to finish adding requests, but in wrong state"); - // } + if (kb->state != KB_REQUESTING) { + return _key_broker_fail_w_msg(kb, "attempting to finish adding requests, but in wrong state"); + } if (kb->key_requests) { if (_all_key_requests_satisfied(kb)) { @@ -1085,9 +1085,10 @@ bool _mongocrypt_key_broker_decrypted_key_by_name(_mongocrypt_key_broker_t *kb, BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(key_id_out); - // if (kb->state != KB_DONE) { - // return _key_broker_fail_w_msg(kb, "attempting retrieve decrypted key material, but in wrong state"); - // } + // We may be in KB_REQUESTING and need keys after requesting keys for keyAltName + if (kb->state != KB_DONE && kb->state != KB_REQUESTING) { + return _key_broker_fail_w_msg(kb, "attempting retrieve decrypted key material, but in wrong state"); + } key_alt_name = _mongocrypt_key_alt_name_new(key_alt_name_value); ret = _get_decrypted_key_material(kb, NULL, key_alt_name, out, key_id_out); diff --git a/test/test-mongocrypt-ctx-encrypt.c b/test/test-mongocrypt-ctx-encrypt.c index 60ed2f495..6151b7dd2 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -6319,7 +6319,7 @@ static void _test_qe_keyAltName_cryptShared(_mongocrypt_tester_t *tester) { ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx); ASSERT(_mongocrypt_binary_to_bson(result, &result_bson)); // _assert_match_bson( - // TMP_BSON(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : {"$$type" : "binData"}} ]})), + // TMP_BSON(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : {"$$type" : "binData"}} ], "subType" : "06"})), // &result_bson); mongocrypt_binary_destroy(result); } @@ -6330,23 +6330,10 @@ static void _test_qe_keyAltName_cryptShared(_mongocrypt_tester_t *tester) { ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY); { + bson_t result_bson; mongocrypt_binary_t *result = mongocrypt_binary_new(); ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx); - ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON( - // TODO: update expected result 'AAAA' with ciphertext. - TEST_BSON_STR(BSON_STR({ - "insert" : "coll", - "documents" : [ { - "secret" : { - "$binary" : { - "base64" : "EGFhYWFhYWFhYWFhYWFhYWECZsXiTFAY0XXprCZjSggTgzFb+cy0/" - "epNKDjEMZ3HaDBjVDIXHZQH8ye3hKBoKD5pDY8SERVzu070rWOU7EIw3g==", - "subType" : "06" - } - } - } ] - })), - result); + ASSERT(_mongocrypt_binary_to_bson(result, &result_bson)); mongocrypt_binary_destroy(result); }