From 24e8f255b1f8b48594e6354912768682a04b8b1b Mon Sep 17 00:00:00 2001 From: "sergey.galtsev" Date: Thu, 31 Mar 2022 15:22:40 +0000 Subject: SERVER-63791 support unindexed fle2 encrypted fields --- .gitignore | 3 + jstests/fle2/libs/encrypted_client_util.js | 12 ++ src/mongo/crypto/encryption_fields_util.h | 35 ++++- src/mongo/crypto/fle_crypto.cpp | 118 +++++++++++---- src/mongo/crypto/fle_crypto.h | 22 +++ src/mongo/crypto/fle_crypto_test.cpp | 181 +++++++++++++++-------- src/mongo/db/catalog/collection_options_test.cpp | 84 +++++++---- 7 files changed, 341 insertions(+), 114 deletions(-) diff --git a/.gitignore b/.gitignore index 9b90923a4d9..0c227d5625b 100644 --- a/.gitignore +++ b/.gitignore @@ -231,3 +231,6 @@ default.profraw /buildscripts/antithesis/base_images/workload/src /buildscripts/antithesis/base_images/workload/mongo /buildscripts/resmokeconfig/suites/antithesis_*.yml + +# generated by gen_all_feature_flag_list.py +all_feature_flags.txt diff --git a/jstests/fle2/libs/encrypted_client_util.js b/jstests/fle2/libs/encrypted_client_util.js index 501fee781c6..4a6a3a5a4dc 100644 --- a/jstests/fle2/libs/encrypted_client_util.js +++ b/jstests/fle2/libs/encrypted_client_util.js @@ -293,3 +293,15 @@ function assertIsIndexedEncryptedField(value) { assert(value.hex().startsWith("07"), "Expected subtype 7 but found the wrong type: " + value.hex()); } + +/** + * Assert a field is an unindexed encrypted field + * + * @param {BinData} value bindata value + */ +function assertIsUnindexedEncryptedField(value) { + assert(value instanceof BinData, "Expected BinData, found: " + value); + assert.eq(value.subtype(), 6, "Expected Encrypted bindata: " + value); + assert(value.hex().startsWith("06"), + "Expected subtype 6 but found the wrong type: " + value.hex()); +} diff --git a/src/mongo/crypto/encryption_fields_util.h b/src/mongo/crypto/encryption_fields_util.h index 7230d08fa48..d89a7009ca3 100644 --- a/src/mongo/crypto/encryption_fields_util.h +++ b/src/mongo/crypto/encryption_fields_util.h @@ -79,7 +79,40 @@ inline bool isFLE2EqualityIndexedSupportedType(BSONType type) { // Unindexed is the same as equality inline bool isFLE2UnindexedSupportedType(BSONType type) { - return isFLE2EqualityIndexedSupportedType(type); + switch (type) { + case BinData: + case Code: + case RegEx: + case String: + + case NumberInt: + case NumberLong: + case Bool: + case bsonTimestamp: + case Date: + case jstOID: + + case Array: + case Object: + case NumberDecimal: + case NumberDouble: + + // Deprecated + case Symbol: + case CodeWScope: + case DBRef: + return true; + + // Singletons + case EOO: + case jstNULL: + case MaxKey: + case MinKey: + case Undefined: + return false; + default: + MONGO_UNREACHABLE; + } } struct EncryptedFieldMatchResult { diff --git a/src/mongo/crypto/fle_crypto.cpp b/src/mongo/crypto/fle_crypto.cpp index 4cfa6749f1d..8a189c9a1c5 100644 --- a/src/mongo/crypto/fle_crypto.cpp +++ b/src/mongo/crypto/fle_crypto.cpp @@ -926,6 +926,15 @@ void convertToFLE2Payload(FLEKeyVault* keyVault, } else { uasserted(6410100, "No other FLE2 placeholders supported at this time."); } + } else if (ep.getAlgorithm() == Fle2AlgorithmInt::kUnindexed) { + uassert(6379102, + str::stream() << "Type '" << typeName(el.type()) + << "' is not a valid type for FLE 2 encryption", + isFLE2UnindexedSupportedType(el.type())); + + auto payload = FLE2UnindexedEncryptedValue::serialize(userKey, el); + builder->appendBinData( + fieldNameToSerialize, payload.size(), BinDataType::Encrypt, payload.data()); } else { uasserted(6338603, "Only FLE 2 style encryption placeholders are supported"); } @@ -957,8 +966,6 @@ void collectEDCServerInfo(std::vector* pFields, ConstDataRange cdr, StringData fieldPath) { - // TODO - validate acceptable types - kFLE2InsertUpdatePayload or kFLE2UnindexedEncryptedValue - // or kFLE2EqualityIndexedValue // TODO - validate field is actually indexed in the schema? auto [encryptedTypeBinding, subCdr] = fromEncryptedConstDataRange(cdr); @@ -970,6 +977,9 @@ void collectEDCServerInfo(std::vector* pFields, } else if (encryptedType == EncryptedBinDataType::kFLE2FindEqualityPayload) { // No-op return; + } else if (encryptedType == EncryptedBinDataType::kFLE2UnindexedEncryptedValue) { + // No-op + return; } uasserted(6373503, str::stream() << "Unexpected encrypted payload type: " @@ -998,36 +1008,40 @@ void convertServerPayload(ConstDataRange cdr, if (encryptedTypeBinding == EncryptedBinDataType::kFLE2FindEqualityPayload) { builder->appendBinData(fieldPath, cdr.length(), BinDataType::Encrypt, cdr.data()); return; - } + } else if (encryptedTypeBinding == EncryptedBinDataType::kFLE2InsertUpdatePayload) { - if (it.it == it.end) { - return; - } + if (it.it == it.end) { + return; + } - // TODO : renable this when find is working on query (find server number) - // uassert(6373505, "Unexpected end of iterator", it.it != it.end); - auto payload = *(it.it); + uassert(6373505, "Unexpected end of iterator", it.it != it.end); + auto payload = *(it.it); - // TODO - validate acceptable types - kFLE2InsertUpdatePayload or kFLE2UnindexedEncryptedValue - // or kFLE2EqualityIndexedValue - // TODO - validate field is actually indexed in the schema? + // TODO - validate field is actually indexed in the schema? - FLE2IndexedEqualityEncryptedValue sp(payload.payload, payload.count); + FLE2IndexedEqualityEncryptedValue sp(payload.payload, payload.count); - uassert(6373506, - str::stream() << "Type '" << typeName(sp.bsonType) - << "' is not a valid type for FLE 2 encryption", - isFLE2EqualityIndexedSupportedType(sp.bsonType)); + uassert(6373506, + str::stream() << "Type '" << typeName(sp.bsonType) + << "' is not a valid type for FLE 2 encryption", + isFLE2EqualityIndexedSupportedType(sp.bsonType)); - auto swEncrypted = sp.serialize(FLETokenFromCDR( - payload.payload.getServerEncryptionToken())); - uassertStatusOK(swEncrypted); - toEncryptedBinData(fieldPath, - EncryptedBinDataType::kFLE2EqualityIndexedValue, - ConstDataRange(swEncrypted.getValue()), - builder); + auto swEncrypted = + sp.serialize(FLETokenFromCDR( + payload.payload.getServerEncryptionToken())); + uassertStatusOK(swEncrypted); + toEncryptedBinData(fieldPath, + EncryptedBinDataType::kFLE2EqualityIndexedValue, + ConstDataRange(swEncrypted.getValue()), + builder); - pTags->push_back({EDCServerCollection::generateTag(payload)}); + pTags->push_back({EDCServerCollection::generateTag(payload)}); + } else if (encryptedTypeBinding == EncryptedBinDataType::kFLE2UnindexedEncryptedValue) { + builder->appendBinData(fieldPath, cdr.length(), BinDataType::Encrypt, cdr.data()); + return; + } else { + uassert(6379103, "Unexpected type binding", false); + } it.it++; } @@ -1317,6 +1331,8 @@ std::pair> FLEClientCrypto::decrypt(ConstDataRang return {ieev.bsonType, userData}; + } else if (pair.first == EncryptedBinDataType::kFLE2UnindexedEncryptedValue) { + return FLE2UnindexedEncryptedValue::deserialize(keyVault, cdr); } else if (pair.first == EncryptedBinDataType::kRandom || pair.first == EncryptedBinDataType::kDeterministic) { return {EOO, std::vector()}; @@ -1404,9 +1420,11 @@ void FLEClientCrypto::validateDocument(const BSONObj& doc, auto tag = EDCServerCollection::generateTag(ieev); tags.insert({tag, field.first}); + } else { + uassert(6379105, + str::stream() << "Field '" << field.first << "' must be marked unindexed", + encryptedTypeBinding == EncryptedBinDataType::kFLE2UnindexedEncryptedValue); } - - // TODO - support unindexed } BSONElement safeContent = doc[kSafeContent]; @@ -1946,6 +1964,52 @@ StatusWith> FLE2IndexedEqualityEncryptedValue::serialize( return serializedServerValue; } +std::vector FLE2UnindexedEncryptedValue::serialize(const FLEUserKeyAndId& userKey, + const BSONElement& element) { + BSONType bsonType = element.type(); + uassert(6379107, "Invalid BSON data type", isFLE2UnindexedSupportedType(bsonType)); + + auto value = ConstDataRange(element.value(), element.value() + element.valuesize()); + auto cdrKeyId = userKey.keyId.toCDR(); + auto cdrKey = userKey.key.toCDR(); + + auto cipherTextSize = crypto::fle2AeadCipherOutputLength(value.length()); + std::vector buf(assocDataSize + cipherTextSize); + DataRangeCursor adc(buf); + adc.writeAndAdvance(static_cast(EncryptedBinDataType::kFLE2UnindexedEncryptedValue)); + adc.writeAndAdvance(cdrKeyId); + adc.writeAndAdvance(static_cast(bsonType)); + + ConstDataRange assocData(buf.data(), assocDataSize); + auto cipherText = uassertStatusOK(encryptDataWithAssociatedData(cdrKey, assocData, value)); + uassert(6379106, "Cipher text size mismatch", cipherTextSize == cipherText.size()); + adc.writeAndAdvance(ConstDataRange(cipherText)); + + return buf; +} + +std::pair> FLE2UnindexedEncryptedValue::deserialize( + FLEKeyVault* keyVault, ConstDataRange blob) { + + auto [assocDataCdr, cipherTextCdr] = blob.split(assocDataSize); + ConstDataRangeCursor adc(assocDataCdr); + + uint8_t marker = adc.readAndAdvance(); + uassert(6379110, + "Invalid data type", + static_cast(EncryptedBinDataType::kFLE2UnindexedEncryptedValue) == marker); + + UUID keyId = UUID::fromCDR(adc.readAndAdvance()); + auto userKey = keyVault->getUserKeyById(keyId); + + BSONType bsonType = static_cast(adc.read()); + uassert(6379111, "Invalid BSON data type", isFLE2UnindexedSupportedType(bsonType)); + + auto data = uassertStatusOK( + decryptDataWithAssociatedData(userKey.key.toCDR(), assocDataCdr, cipherTextCdr)); + return {bsonType, data}; +} + ESCDerivedFromDataTokenAndContentionFactorToken EDCServerPayloadInfo::getESCToken() const { return FLETokenFromCDR( payload.getEscDerivedToken()); diff --git a/src/mongo/crypto/fle_crypto.h b/src/mongo/crypto/fle_crypto.h index 649429ce790..3a93676826d 100644 --- a/src/mongo/crypto/fle_crypto.h +++ b/src/mongo/crypto/fle_crypto.h @@ -896,6 +896,28 @@ struct FLE2IndexedEqualityEncryptedValue { std::vector clientEncryptedValue; }; +/** + * Class to read/write FLE2 Unindexed Encrypted Values + * + * Fields are encrypted with the following: + * + * struct { + * uint8_t fle_blob_subtype = 6; + * uint8_t key_uuid[16]; + * uint8 original_bson_type; + * ciphertext[ciphertext_length]; + * } blob; + * + */ +struct FLE2UnindexedEncryptedValue { + static std::vector serialize(const FLEUserKeyAndId& userKey, + const BSONElement& element); + static std::pair> deserialize(FLEKeyVault* keyVault, + ConstDataRange blob); + + static constexpr size_t assocDataSize = sizeof(uint8_t) + sizeof(UUID) + sizeof(uint8_t); +}; + struct EDCServerPayloadInfo { ESCDerivedFromDataTokenAndContentionFactorToken getESCToken() const; diff --git a/src/mongo/crypto/fle_crypto_test.cpp b/src/mongo/crypto/fle_crypto_test.cpp index 434975bc699..c9142a4408e 100644 --- a/src/mongo/crypto/fle_crypto_test.cpp +++ b/src/mongo/crypto/fle_crypto_test.cpp @@ -578,12 +578,13 @@ TEST(FLE_ESC, EmuBinary_NullRecord) { ASSERT_FALSE(i.has_value()); } - enum class Operation { kFind, kInsert }; -std::vector generatePlaceholder(BSONElement value, - Operation operation, - boost::optional key = boost::none) { +std::vector generatePlaceholder( + BSONElement value, + Operation operation, + mongo::Fle2AlgorithmInt algorithm = mongo::Fle2AlgorithmInt::kEquality, + boost::optional key = boost::none) { FLE2EncryptionPlaceholder ep; if (operation == Operation::kFind) { @@ -592,7 +593,7 @@ std::vector generatePlaceholder(BSONElement value, ep.setType(mongo::Fle2PlaceholderType::kInsert); } - ep.setAlgorithm(mongo::Fle2AlgorithmInt::kEquality); + ep.setAlgorithm(algorithm); ep.setUserKeyId(userKeyId); ep.setIndexKeyId(key.value_or(indexKeyId)); ep.setValue(value); @@ -639,7 +640,7 @@ void assertPayload(BSONElement elem, Operation operation) { } } -void roundTripTest(BSONObj doc, BSONType type, Operation operation) { +void roundTripTest(BSONObj doc, BSONType type, Operation opType, Fle2AlgorithmInt algorithm) { auto element = doc.firstElement(); ASSERT_EQ(element.type(), type); @@ -649,7 +650,7 @@ void roundTripTest(BSONObj doc, BSONType type, Operation operation) { << "sample" << "encrypted" << element); - auto buf = generatePlaceholder(element, operation); + auto buf = generatePlaceholder(element, opType, algorithm); BSONObjBuilder builder; builder.append("plainText", "sample"); builder.appendBinData("encrypted", buf.size(), BinDataType::Encrypt, buf.data()); @@ -662,8 +663,8 @@ void roundTripTest(BSONObj doc, BSONType type, Operation operation) { // TODO : when query enables server side work for Find, remove this // if statement. - if (operation == Operation::kFind) { - assertPayload(finalDoc["encrypted"], operation); + if (opType == Operation::kFind && algorithm == Fle2AlgorithmInt::kEquality) { + assertPayload(finalDoc["encrypted"], opType); return; } @@ -676,6 +677,11 @@ void roundTripTest(BSONObj doc, BSONType type, Operation operation) { ASSERT_BSONOBJ_EQ(inputDoc, decryptedDoc); } +void roundTripTest(BSONObj doc, BSONType type, Operation opType) { + roundTripTest(doc, type, opType, Fle2AlgorithmInt::kEquality); + roundTripTest(doc, type, opType, Fle2AlgorithmInt::kUnindexed); +} + void roundTripMultiencrypted(BSONObj doc1, BSONObj doc2, Operation operation1, @@ -689,8 +695,8 @@ void roundTripMultiencrypted(BSONObj doc1, << "sample" << "encrypted1" << element1 << "encrypted2" << element2); - auto buf1 = generatePlaceholder(element1, operation1, indexKeyId); - auto buf2 = generatePlaceholder(element2, operation2, indexKey2Id); + auto buf1 = generatePlaceholder(element1, operation1, Fle2AlgorithmInt::kEquality, indexKeyId); + auto buf2 = generatePlaceholder(element2, operation2, Fle2AlgorithmInt::kEquality, indexKey2Id); BSONObjBuilder builder; builder.append("plaintext", "sample"); @@ -709,33 +715,54 @@ void roundTripMultiencrypted(BSONObj doc1, assertPayload(finalDoc["encrypted2"], operation2); } -const std::vector> objects{ - {BSON("sample" - << "value123"), - String}, - {BSON("sample" << BSONBinData(testValue.data(), testValue.size(), BinDataType::BinDataGeneral)), - BinData}, - {BSON("sample" << OID()), jstOID}, - {BSON("sample" << false), Bool}, - {BSON("sample" << true), Bool}, - {BSON("sample" << Date_t()), Date}, - {BSON("sample" << BSONRegEx("value1", "value2")), RegEx}, - {BSON("sample" << 123456), NumberInt}, - {BSON("sample" << Timestamp()), bsonTimestamp}, - {BSON("sample" << 12345678901234567LL), NumberLong}, - {BSON("sample" << BSONCode("value")), Code}}; - TEST(FLE_EDC, Allowed_Types) { + const std::vector> universallyAllowedObjects{ + {BSON("sample" + << "value123"), + String}, + {BSON("sample" << BSONBinData( + testValue.data(), testValue.size(), BinDataType::BinDataGeneral)), + BinData}, + {BSON("sample" << OID()), jstOID}, + {BSON("sample" << false), Bool}, + {BSON("sample" << true), Bool}, + {BSON("sample" << Date_t()), Date}, + {BSON("sample" << BSONRegEx("value1", "value2")), RegEx}, + {BSON("sample" << 123456), NumberInt}, + {BSON("sample" << Timestamp()), bsonTimestamp}, + {BSON("sample" << 12345678901234567LL), NumberLong}, + {BSON("sample" << BSONCode("value")), Code}}; + + const std::vector> unindexedAllowedObjects{ + {BSON("sample" << 123.456), NumberDouble}, + {BSON("sample" << Decimal128()), NumberDecimal}, + {BSON("sample" << BSON("nested" + << "value")), + Object}, + {BSON("sample" << BSON_ARRAY(1 << 23)), Array}, + {BSON("sample" << BSONDBRef("value1", OID())), DBRef}, + {BSON("sample" << BSONSymbol("value")), Symbol}, + {BSON("sample" << BSONCodeWScope("value", + BSON("code" + << "something"))), + CodeWScope}, + }; + + std::vector opTypes{Operation::kInsert, Operation::kFind}; for (const auto& opType : opTypes) { - for (const auto& [obj, objType] : objects) { - roundTripTest(obj, objType, opType); + for (const auto& [obj, objType] : universallyAllowedObjects) { + roundTripTest(obj, objType, opType, Fle2AlgorithmInt::kEquality); + roundTripTest(obj, objType, opType, Fle2AlgorithmInt::kUnindexed); + } + for (const auto& [obj, objType] : unindexedAllowedObjects) { + roundTripTest(obj, objType, opType, Fle2AlgorithmInt::kUnindexed); } }; - for (const auto& [obj1, _] : objects) { - for (const auto& [obj2, _] : objects) { + for (const auto& [obj1, _] : universallyAllowedObjects) { + for (const auto& [obj2, _] : universallyAllowedObjects) { roundTripMultiencrypted(obj1, obj2, Operation::kInsert, Operation::kInsert); roundTripMultiencrypted(obj1, obj2, Operation::kInsert, Operation::kFind); roundTripMultiencrypted(obj1, obj2, Operation::kFind, Operation::kInsert); @@ -744,50 +771,53 @@ TEST(FLE_EDC, Allowed_Types) { } } -void illegalBSONType(BSONObj doc, BSONType type) { +void illegalBSONType(BSONObj doc, BSONType type, Fle2AlgorithmInt algorithm, int expectCode) { auto element = doc.firstElement(); - if (isValidBSONType(type)) { - ASSERT_EQ(element.type(), type); - } + ASSERT_EQ(element.type(), type); TestKeyVault keyVault; - auto buf = generatePlaceholder(element, Operation::kInsert); + auto buf = generatePlaceholder(element, Operation::kInsert, algorithm, boost::none); BSONObjBuilder builder; builder.append("plainText", "sample"); builder.appendBinData("encrypted", buf.size(), BinDataType::Encrypt, buf.data()); BSONObj obj = builder.obj(); ASSERT_THROWS_CODE( - FLEClientCrypto::transformPlaceholders(obj, &keyVault), DBException, 6338602); + FLEClientCrypto::transformPlaceholders(obj, &keyVault), DBException, expectCode); +} + +void illegalBSONType(BSONObj doc, BSONType type, Fle2AlgorithmInt algorithm) { + const int expectCode = algorithm == Fle2AlgorithmInt::kEquality ? 6338602 : 6379102; + illegalBSONType(doc, type, algorithm, expectCode); } TEST(FLE_EDC, Disallowed_Types) { - illegalBSONType(BSON("sample" << 123.456), NumberDouble); - illegalBSONType(BSON("sample" << Decimal128()), NumberDecimal); + illegalBSONType(BSON("sample" << 123.456), NumberDouble, Fle2AlgorithmInt::kEquality); + illegalBSONType(BSON("sample" << Decimal128()), NumberDecimal, Fle2AlgorithmInt::kEquality); - illegalBSONType(BSON("sample" << MINKEY), MinKey); + illegalBSONType(BSON("sample" << MINKEY), MinKey, Fle2AlgorithmInt::kEquality); illegalBSONType(BSON("sample" << BSON("nested" << "value")), - Object); - illegalBSONType(BSON("sample" << BSON_ARRAY(1 << 23)), Array); - - illegalBSONType(BSON("sample" << BSONUndefined), Undefined); - illegalBSONType(BSON("sample" << BSONNULL), jstNULL); - illegalBSONType(BSON("sample" << BSONDBRef("value1", OID())), DBRef); - illegalBSONType(BSON("sample" << BSONSymbol("value")), Symbol); + Object, + Fle2AlgorithmInt::kEquality); + illegalBSONType(BSON("sample" << BSON_ARRAY(1 << 23)), Array, Fle2AlgorithmInt::kEquality); + + illegalBSONType(BSON("sample" << BSONUndefined), Undefined, Fle2AlgorithmInt::kEquality); + illegalBSONType(BSON("sample" << BSONUndefined), Undefined, Fle2AlgorithmInt::kUnindexed); + illegalBSONType(BSON("sample" << BSONNULL), jstNULL, Fle2AlgorithmInt::kEquality); + illegalBSONType(BSON("sample" << BSONNULL), jstNULL, Fle2AlgorithmInt::kUnindexed); + illegalBSONType( + BSON("sample" << BSONDBRef("value1", OID())), DBRef, Fle2AlgorithmInt::kEquality); + illegalBSONType(BSON("sample" << BSONSymbol("value")), Symbol, Fle2AlgorithmInt::kEquality); illegalBSONType(BSON("sample" << BSONCodeWScope("value", BSON("code" << "something"))), - CodeWScope); - - illegalBSONType(BSON("sample" << MAXKEY), MaxKey); - - - uint8_t fakeBSONType = 42; - ASSERT_FALSE(isValidBSONType(fakeBSONType)); - illegalBSONType(BSON("sample" << 123.456), static_cast(fakeBSONType)); + CodeWScope, + Fle2AlgorithmInt::kEquality); + illegalBSONType(BSON("sample" << MAXKEY), MaxKey, Fle2AlgorithmInt::kEquality); + illegalBSONType(BSON("sample" << MAXKEY), MaxKey, Fle2AlgorithmInt::kUnindexed); } @@ -1176,11 +1206,13 @@ TEST(IndexedFields, FetchTwoLevels) { builder.appendBinData("encrypted", buf.size(), BinDataType::Encrypt, buf.data()); { BSONObjBuilder sub(builder.subobjStart("nested")); - auto buf2 = generatePlaceholder(element, Operation::kInsert, indexKey2Id); + auto buf2 = generatePlaceholder( + element, Operation::kInsert, Fle2AlgorithmInt::kEquality, indexKey2Id); sub.appendBinData("encrypted", buf2.size(), BinDataType::Encrypt, buf2.data()); { BSONObjBuilder sub2(sub.subobjStart("nested2")); - auto buf3 = generatePlaceholder(element, Operation::kInsert, indexKey3Id); + auto buf3 = generatePlaceholder( + element, Operation::kInsert, Fle2AlgorithmInt::kEquality, indexKey3Id); sub2.appendBinData("encrypted", buf3.size(), BinDataType::Encrypt, buf3.data()); } } @@ -1358,6 +1390,25 @@ TEST(TagDelta, Basic) { } } +TEST(EDC, UnindexedEncryptDecrypt) { + TestKeyVault keyVault; + FLEUserKeyAndId userKey = keyVault.getUserKeyById(indexKey2Id); + + auto inputDoc = BSON("a" + << "sample"); + auto element = inputDoc.firstElement(); + auto const elementData = + std::vector(element.value(), element.value() + element.valuesize()); + + auto blob = FLE2UnindexedEncryptedValue::serialize(userKey, element); + ASSERT_EQ(blob[0], 6); + + auto [type, plainText] = FLE2UnindexedEncryptedValue::deserialize(&keyVault, {blob}); + ASSERT_EQ(type, element.type()); + ASSERT_TRUE( + std::equal(plainText.begin(), plainText.end(), elementData.begin(), elementData.end())); +} + TEST(EDC, ValidateDocument) { EncryptedFieldConfig efc = getTestEncryptedFieldConfig(); @@ -1378,9 +1429,18 @@ TEST(EDC, ValidateDocument) { auto element = doc.firstElement(); BSONObjBuilder sub(builder.subobjStart("nested")); - auto buf = generatePlaceholder(element, Operation::kInsert, indexKey2Id); + auto buf = generatePlaceholder( + element, Operation::kInsert, Fle2AlgorithmInt::kEquality, indexKey2Id); builder.appendBinData("encrypted", buf.size(), BinDataType::Encrypt, buf.data()); - // TODO - add support for unindexed + } + { + auto doc = BSON("a" + << "bottom secret"); + auto element = doc.firstElement(); + + BSONObjBuilder sub(builder.subobjStart("nested")); + auto buf = generatePlaceholder(element, Operation::kInsert, Fle2AlgorithmInt::kUnindexed); + builder.appendBinData("notindexed", buf.size(), BinDataType::Encrypt, buf.data()); } auto finalDoc = encryptDocument(builder.obj(), &keyVault); @@ -1571,7 +1631,8 @@ TEST(FLE_Update, PullTokens) { builder.appendBinData("encrypted", buf.size(), BinDataType::Encrypt, buf.data()); { BSONObjBuilder sub(builder.subobjStart("nested")); - auto buf2 = generatePlaceholder(element, Operation::kInsert, indexKey2Id); + auto buf2 = generatePlaceholder( + element, Operation::kInsert, Fle2AlgorithmInt::kEquality, indexKey2Id); sub.appendBinData("encrypted", buf2.size(), BinDataType::Encrypt, buf2.data()); } auto encDoc = encryptDocument(builder.obj(), &keyVault); diff --git a/src/mongo/db/catalog/collection_options_test.cpp b/src/mongo/db/catalog/collection_options_test.cpp index d6632c3456f..232a39763ad 100644 --- a/src/mongo/db/catalog/collection_options_test.cpp +++ b/src/mongo/db/catalog/collection_options_test.cpp @@ -482,7 +482,7 @@ TEST(FLECollectionOptions, DuplicateQueryTypes) { TEST(FLECollectionOptions, AllowedTypes) { RAIIServerParameterControllerForTest featureFlagController("featureFlagFLE2", true); - std::vector types({ + std::vector typesAllowedIndexed({ "string", "binData", "objectId", @@ -495,19 +495,27 @@ TEST(FLECollectionOptions, AllowedTypes) { "long", }); - for (const auto& type : types) { - ASSERT_OK(CollectionOptions::parse(fromjson(str::stream() << R"({ - encryptedFields: { - "fields": [ - { - "path": "name.first", - "keyId": { '$uuid': '5f34e99a-b214-451f-b6f6-d3d28e933d15' }, - "bsonType": ")" << type << R"(" - } - ] - }})")) - .getStatus()); + std::vector typesAllowedUnindexed({ + "string", + "binData", + "objectId", + "bool", + "date", + "regex", + "javascript", + "int", + "timestamp", + "long", + "double", + "object", + "array", + "decimal", + "dbPointer", + "symbol", + "javascriptWithScope", + }); + for (const auto& type : typesAllowedIndexed) { ASSERT_OK(CollectionOptions::parse(fromjson(str::stream() << R"({ encryptedFields: { "fields": [ @@ -522,13 +530,27 @@ TEST(FLECollectionOptions, AllowedTypes) { }})")) .getStatus()); } + + for (const auto& type : typesAllowedUnindexed) { + ASSERT_OK(CollectionOptions::parse(fromjson(str::stream() << R"({ + encryptedFields: { + "fields": [ + { + "path": "name.first", + "keyId": { '$uuid': '5f34e99a-b214-451f-b6f6-d3d28e933d15' }, + "bsonType": ")" << type << R"(" + } + ] + }})")) + .getStatus()); + } } TEST(FLECollectionOptions, DisAllowedTypes) { RAIIServerParameterControllerForTest featureFlagController("featureFlagFLE2", true); - std::vector types({ + std::vector typesDisallowedIndexed({ "minKey", "missing", "double", @@ -543,19 +565,15 @@ TEST(FLECollectionOptions, DisAllowedTypes) { "maxKey", }); - for (const auto& type : types) { - ASSERT_NOT_OK(CollectionOptions::parse(fromjson(str::stream() << R"({ - encryptedFields: { - "fields": [ - { - "path": "name.first", - "keyId": { '$uuid': '5f34e99a-b214-451f-b6f6-d3d28e933d15' }, - "bsonType": ")" << type << R"(" - } - ] - }})")) - .getStatus()); + std::vector typesDisallowedUnindexed({ + "minKey", + "missing", + "null", + "undefined", + "maxKey", + }); + for (const auto& type : typesDisallowedIndexed) { ASSERT_NOT_OK(CollectionOptions::parse(fromjson(str::stream() << R"({ encryptedFields: { "fields": [ @@ -570,6 +588,20 @@ TEST(FLECollectionOptions, DisAllowedTypes) { }})")) .getStatus()); } + + for (const auto& type : typesDisallowedUnindexed) { + ASSERT_NOT_OK(CollectionOptions::parse(fromjson(str::stream() << R"({ + encryptedFields: { + "fields": [ + { + "path": "name.first", + "keyId": { '$uuid': '5f34e99a-b214-451f-b6f6-d3d28e933d15' }, + "bsonType": ")" << type << R"(" + } + ] + }})")) + .getStatus()); + } } } // namespace mongo -- cgit v1.2.1