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/mc-schema-broker-private.h b/src/mc-schema-broker-private.h index 9ffca9981..7a7d08db0 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 @@ -102,6 +103,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. @@ -118,7 +125,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 e3d65b497..21db44ad3 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" @@ -607,7 +608,25 @@ 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; +} + static bool append_encryptedFields(const bson_t *encryptedFields, + _mongocrypt_key_broker_t *kb, const char *coll, uint8_t default_strEncodeVersion, bson_t *out, @@ -633,7 +652,8 @@ static bool append_encryptedFields(const bson_t *encryptedFields, // 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) { @@ -645,8 +665,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; + 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)) { + 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; + } } } @@ -687,6 +791,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) { @@ -728,7 +833,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; } @@ -778,6 +883,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, @@ -841,7 +947,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)) { @@ -891,7 +997,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; } @@ -914,7 +1020,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; } @@ -1032,7 +1138,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) { @@ -1053,6 +1160,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; @@ -1061,7 +1180,7 @@ bool mc_schema_broker_add_schemas_to_cmd(const 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); } @@ -1078,7 +1197,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) { @@ -1089,7 +1208,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 bc57c3b2b..3133bcbfd 100644 --- a/src/mongocrypt-ctx-encrypt.c +++ b/src/mongocrypt-ctx-encrypt.c @@ -19,6 +19,7 @@ #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" @@ -133,6 +134,35 @@ 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_maybe_get_encryptedFields(ectx->sb, ectx->target_coll, ctx->status); + if (!efc) { + return 0; + } + + 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); + 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; @@ -153,7 +183,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; @@ -219,6 +248,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, @@ -373,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); } @@ -472,7 +506,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 @@ -1142,6 +1176,20 @@ 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? + + 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) { @@ -1158,7 +1206,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); } @@ -2580,6 +2628,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 @@ -2710,11 +2770,21 @@ 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; + } else if (need_keys == 1) { + ctx->need_keys_for_encryptedFields = true; + } + 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. */ + 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); return _mongocrypt_ctx_state_from_key_broker(ctx); } 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..4e7660862 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); @@ -326,8 +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; + ctx->need_keys_for_encryptedFields = false; (void)_mongocrypt_key_broker_docs_done(&ctx->kb); - return _mongocrypt_ctx_state_from_key_broker(ctx); + + if (used_keyaltname) { + ctx->kb.state = KB_REQUESTING; + ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS; + return _try_run_csfle_marking(ctx); + } else { + return _mongocrypt_ctx_state_from_key_broker(ctx); + } } static mongocrypt_kms_ctx_t *_next_kms_ctx(mongocrypt_ctx_t *ctx) { @@ -878,7 +889,12 @@ bool _mongocrypt_ctx_state_from_key_broker(mongocrypt_ctx_t *ctx) { ret = true; break; case KB_DONE: - new_state = MONGOCRYPT_CTX_READY; + if (ctx->need_keys_for_encryptedFields) { + kb->state = KB_REQUESTING; + new_state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS; + } else { + 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/src/mongocrypt-key-broker.c b/src/mongocrypt-key-broker.c index 5bf7ae234..8cc77a53f 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); } } @@ -1084,7 +1085,8 @@ 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) { + // 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"); } diff --git a/src/mongocrypt-private.h b/src/mongocrypt-private.h index fc161a672..c123f8fb9 100644 --- a/src/mongocrypt-private.h +++ b/src/mongocrypt-private.h @@ -175,4 +175,7 @@ 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); +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 6391f758f..ed0936833 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); @@ -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({ @@ -826,7 +826,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); @@ -848,7 +848,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", @@ -873,7 +873,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); @@ -892,7 +892,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", @@ -917,7 +917,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" : [ { @@ -951,7 +951,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}} @@ -974,7 +974,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}}} @@ -997,7 +997,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}}} @@ -1022,7 +1022,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" : { @@ -1059,7 +1059,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-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 67258096e..6151b7dd2 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -6170,6 +6170,179 @@ 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); + + // 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); + } + + // 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); + { + 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_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 +} + +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"}} ], "subType" : "06"})), + // &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); + { + 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)); + 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); @@ -6269,4 +6442,6 @@ 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); + INSTALL_TEST(_test_qe_keyAltName_cryptShared); } diff --git a/test/test-mongocrypt-key-broker.c b/test/test-mongocrypt-key-broker.c index 154784ab2..dc1fad4c6 100644 --- a/test/test-mongocrypt-key-broker.c +++ b/test/test-mongocrypt-key-broker.c @@ -16,21 +16,10 @@ #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) { - 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;