Skip to content

Commit

Permalink
MONGOCRYPT-699 Omit encryptionInformation in compact if no range fi…
Browse files Browse the repository at this point in the history
…elds are used (#852)
  • Loading branch information
kevinAlbs committed Jul 1, 2024
1 parent 654385b commit fccd269
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 25 deletions.
40 changes: 20 additions & 20 deletions bindings/python/test/data/compact/success/encrypted-payload.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
{
"compactStructuredEncryptionData": "test",
"compactionTokens": {
"nested.notindexed": {
"$binary": {
"base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
"subType": "00"
}
},
"nested.encrypted": {
"$binary": {
"base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=",
"subType": "00"
}
},
"encrypted": {
"$binary": {
"base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=",
"subType": "00"
}
"compactStructuredEncryptionData": "test",
"compactionTokens": {
"nested.notindexed": {
"$binary": {
"base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
"subType": "00"
}
}
},
"nested.encrypted": {
"$binary": {
"base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=",
"subType": "00"
}
},
"encrypted": {
"$binary": {
"base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=",
"subType": "00"
}
}
}
}
27 changes: 22 additions & 5 deletions src/mongocrypt-ctx-encrypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,7 @@ typedef struct {
static moe_result must_omit_encryptionInformation(const char *command_name,
const bson_t *command,
bool use_range_v2,
const mc_EncryptedFieldConfig_t *efc,
mongocrypt_status_t *status) {
// eligible_commands may omit encryptionInformation if the command does not
// contain payloads requiring encryption.
Expand All @@ -1382,10 +1383,23 @@ static moe_result must_omit_encryptionInformation(const char *command_name,

BSON_ASSERT_PARAM(command_name);
BSON_ASSERT_PARAM(command);
BSON_ASSERT_PARAM(efc);

// Before range v2, compact is a prohibited command. After, it must have encryption information.
if (!use_range_v2 && 0 == strcmp("compactStructuredEncryptionData", command_name)) {
return (moe_result){.ok = true, .must_omit = true};
if (0 == strcmp("compactStructuredEncryptionData", command_name)) {
// `compactStructuredEncryptionData` is a special case:
// - Server 7.0 prohibits `encryptionInformation`.
// - Server 8.0 requires `encryptionInformation` if "range" fields are referenced. Otherwise ignores.
// Only send `encryptionInformation` if "range" fields are present to support both server versions.
bool uses_range_fields = false;
if (use_range_v2) {
for (const mc_EncryptedField_t *ef = efc->fields; ef != NULL; ef = ef->next) {
if (ef->supported_queries & SUPPORTS_RANGE_QUERIES) {
uses_range_fields = true;
break;
}
}
}
return (moe_result){.ok = true, .must_omit = !uses_range_fields};
}

for (i = 0; i < sizeof(prohibited_commands) / sizeof(prohibited_commands[0]); i++) {
Expand Down Expand Up @@ -1727,8 +1741,11 @@ static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
}
}

moe_result result =
must_omit_encryptionInformation(command_name, &converted, ctx->crypt->opts.use_range_v2, ctx->status);
moe_result result = must_omit_encryptionInformation(command_name,
&converted,
ctx->crypt->opts.use_range_v2,
&ectx->efc,
ctx->status);
if (!result.ok) {
bson_destroy(&converted);
bson_destroy(deleteTokens);
Expand Down
1 change: 1 addition & 0 deletions test/data/compact/no-range/cmd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "compactStructuredEncryptionData": "test" }
49 changes: 49 additions & 0 deletions test/data/compact/no-range/collinfo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"options": {
"encryptedFields": {
"escCollection": "esc",
"eccCollection": "ecc",
"ecocCollection": "ecoc",
"fields": [
{
"keyId": {
"$binary": {
"base64": "EjRWeBI0mHYSNBI0VniQEg==",
"subType": "04"
}
},
"path": "encrypted",
"bsonType": "string",
"queries": {
"queryType": "equality",
"contention": 0
}
},
{
"keyId": {
"$binary": {
"base64": "q83vqxI0mHYSNBI0VniQEg==",
"subType": "04"
}
},
"path": "nested.encrypted",
"bsonType": "string",
"queries": {
"queryType": "equality",
"contention": 0
}
},
{
"keyId": {
"$binary": {
"base64": "EjRWeBI0mHYSNBI0VniQEw==",
"subType": "04"
}
},
"path": "nested.notindexed",
"bsonType": "string"
}
]
}
}
}
47 changes: 47 additions & 0 deletions test/data/compact/no-range/encrypted-field-config-map.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"db.test": {
"escCollection": "esc",
"eccCollection": "ecc",
"ecocCollection": "ecoc",
"fields": [
{
"keyId": {
"$binary": {
"base64": "EjRWeBI0mHYSNBI0VniQEg==",
"subType": "04"
}
},
"path": "encrypted",
"bsonType": "string",
"queries": {
"queryType": "equality",
"contention": 0
}
},
{
"keyId": {
"$binary": {
"base64": "q83vqxI0mHYSNBI0VniQEg==",
"subType": "04"
}
},
"path": "nested.encrypted",
"bsonType": "string",
"queries": {
"queryType": "equality",
"contention": 0
}
},
{
"keyId": {
"$binary": {
"base64": "EjRWeBI0mHYSNBI0VniQEw==",
"subType": "04"
}
},
"path": "nested.notindexed",
"bsonType": "string"
}
]
}
}
23 changes: 23 additions & 0 deletions test/data/compact/no-range/encrypted-payload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"compactStructuredEncryptionData": "test",
"compactionTokens": {
"nested.notindexed": {
"$binary": {
"base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
"subType": "00"
}
},
"nested.encrypted": {
"$binary": {
"base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=",
"subType": "00"
}
},
"encrypted": {
"$binary": {
"base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=",
"subType": "00"
}
}
}
}
58 changes: 58 additions & 0 deletions test/test-mongocrypt-compact.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,64 @@ static void _test_compact_success(_mongocrypt_tester_t *tester) {
mongocrypt_destroy(crypt);
}
}

// Test `compactStructuredEncryptionData` without range fields omits encryptionInformation.
// This is a regression test for MONGOCRYPT-699.
for (int use_range_v2 = 0; use_range_v2 <= 1; use_range_v2++) {
datapath[nullb] = 0;
strcat(datapath, "no-range/");
strcpy(cmdfile, datapath);
strcat(cmdfile, "cmd.json");
strcpy(collfile, datapath);
strcat(collfile, "collinfo.json");
strcpy(payloadfile, datapath);
strcat(payloadfile, "encrypted-payload.json"); // Expect same result regardless of range v2.

mongocrypt_t *crypt;
mongocrypt_ctx_t *ctx;

crypt =
_mongocrypt_tester_mongocrypt(use_range_v2 ? TESTER_MONGOCRYPT_WITH_RANGE_V2 : TESTER_MONGOCRYPT_DEFAULT);
ctx = mongocrypt_ctx_new(crypt);

ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE(cmdfile)), ctx);

ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
{
ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE(collfile)), ctx);
ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
}

ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
{
ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
TEST_FILE("./test/data/keys/"
"12345678123498761234123456789012-local-document.json")),
ctx);
ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
TEST_FILE("./test/data/keys/"
"ABCDEFAB123498761234123456789012-local-document.json")),
ctx);
ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
TEST_FILE("./test/data/keys/"
"12345678123498761234123456789013-local-document.json")),
ctx);
ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
}

ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
{
mongocrypt_binary_t *out = mongocrypt_binary_new();
ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE(payloadfile), out);
mongocrypt_binary_destroy(out);
}

ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

mongocrypt_ctx_destroy(ctx);
mongocrypt_destroy(crypt);
}
}

static void _test_compact_nonlocal_kms(_mongocrypt_tester_t *tester) {
Expand Down

0 comments on commit fccd269

Please sign in to comment.