summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsergey.galtsev <sergey.galtsev@mongodb.com>2022-03-31 15:22:40 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-03-31 17:38:01 +0000
commit24e8f255b1f8b48594e6354912768682a04b8b1b (patch)
treef401c7facd48d2569fa17e557e36140bd9ce2757
parent2d78530f2b590d205232bd3d65cb8f66500aa86f (diff)
downloadmongo-24e8f255b1f8b48594e6354912768682a04b8b1b.tar.gz
SERVER-63791 support unindexed fle2 encrypted fields
-rw-r--r--.gitignore3
-rw-r--r--jstests/fle2/libs/encrypted_client_util.js12
-rw-r--r--src/mongo/crypto/encryption_fields_util.h35
-rw-r--r--src/mongo/crypto/fle_crypto.cpp118
-rw-r--r--src/mongo/crypto/fle_crypto.h22
-rw-r--r--src/mongo/crypto/fle_crypto_test.cpp181
-rw-r--r--src/mongo/db/catalog/collection_options_test.cpp84
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<EDCServerPayloadInfo>* 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<EDCServerPayloadInfo>* 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<char>());
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<FLETokenType::ServerDataEncryptionLevel1Token>(
- payload.payload.getServerEncryptionToken()));
- uassertStatusOK(swEncrypted);
- toEncryptedBinData(fieldPath,
- EncryptedBinDataType::kFLE2EqualityIndexedValue,
- ConstDataRange(swEncrypted.getValue()),
- builder);
+ auto swEncrypted =
+ sp.serialize(FLETokenFromCDR<FLETokenType::ServerDataEncryptionLevel1Token>(
+ 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<BSONType, std::vector<uint8_t>> 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<uint8_t>()};
@@ -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<std::vector<uint8_t>> FLE2IndexedEqualityEncryptedValue::serialize(
return serializedServerValue;
}
+std::vector<uint8_t> 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<uint8_t> buf(assocDataSize + cipherTextSize);
+ DataRangeCursor adc(buf);
+ adc.writeAndAdvance(static_cast<uint8_t>(EncryptedBinDataType::kFLE2UnindexedEncryptedValue));
+ adc.writeAndAdvance(cdrKeyId);
+ adc.writeAndAdvance(static_cast<uint8_t>(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<BSONType, std::vector<uint8_t>> FLE2UnindexedEncryptedValue::deserialize(
+ FLEKeyVault* keyVault, ConstDataRange blob) {
+
+ auto [assocDataCdr, cipherTextCdr] = blob.split(assocDataSize);
+ ConstDataRangeCursor adc(assocDataCdr);
+
+ uint8_t marker = adc.readAndAdvance<uint8_t>();
+ uassert(6379110,
+ "Invalid data type",
+ static_cast<uint8_t>(EncryptedBinDataType::kFLE2UnindexedEncryptedValue) == marker);
+
+ UUID keyId = UUID::fromCDR(adc.readAndAdvance<UUIDBuf>());
+ auto userKey = keyVault->getUserKeyById(keyId);
+
+ BSONType bsonType = static_cast<BSONType>(adc.read<uint8_t>());
+ 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<FLETokenType::ESCDerivedFromDataTokenAndContentionFactorToken>(
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<uint8_t> 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<uint8_t> serialize(const FLEUserKeyAndId& userKey,
+ const BSONElement& element);
+ static std::pair<BSONType, std::vector<uint8_t>> 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<char> generatePlaceholder(BSONElement value,
- Operation operation,
- boost::optional<UUID> key = boost::none) {
+std::vector<char> generatePlaceholder(
+ BSONElement value,
+ Operation operation,
+ mongo::Fle2AlgorithmInt algorithm = mongo::Fle2AlgorithmInt::kEquality,
+ boost::optional<UUID> key = boost::none) {
FLE2EncryptionPlaceholder ep;
if (operation == Operation::kFind) {
@@ -592,7 +593,7 @@ std::vector<char> 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<std::pair<BSONObj, BSONType>> 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<std::pair<BSONObj, BSONType>> 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<std::pair<BSONObj, BSONType>> 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<Operation> 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<BSONType>(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<uint8_t>(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<std::string> types({
+ std::vector<std::string> 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<std::string> 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<std::string> types({
+ std::vector<std::string> 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<std::string> 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