|
21 | 21 | #include "mc-fle2-find-equality-payload-private.h"
|
22 | 22 | #include "mc-fle2-find-range-payload-private-v2.h"
|
23 | 23 | #include "mc-fle2-find-range-payload-private.h"
|
| 24 | +#include "mc-fle2-find-text-payload-private.h" |
24 | 25 | #include "mc-fle2-insert-update-payload-private-v2.h"
|
25 | 26 | #include "mc-fle2-insert-update-payload-private.h"
|
26 | 27 | #include "mc-fle2-payload-uev-private.h"
|
@@ -1132,6 +1133,46 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
|
1132 | 1133 | return false; \
|
1133 | 1134 | } \
|
1134 | 1135 | return true; \
|
| 1136 | + } \ |
| 1137 | + static bool _fle2_generate_Text##Type##FindTokenSet( \ |
| 1138 | + _mongocrypt_key_broker_t *kb, \ |
| 1139 | + mc_Text##Type##FindTokenSet_t *out, \ |
| 1140 | + const _mongocrypt_buffer_t *value, \ |
| 1141 | + const mc_CollectionsLevel1Token_t *collLevel1Token, \ |
| 1142 | + const mc_ServerTokenDerivationLevel1Token_t *serverLevel1Token, \ |
| 1143 | + mongocrypt_status_t *status) { \ |
| 1144 | + BSON_ASSERT_PARAM(kb); \ |
| 1145 | + BSON_ASSERT_PARAM(kb->crypt); \ |
| 1146 | + BSON_ASSERT_PARAM(out); \ |
| 1147 | + BSON_ASSERT_PARAM(value); \ |
| 1148 | + BSON_ASSERT_PARAM(collLevel1Token); \ |
| 1149 | + BSON_ASSERT_PARAM(serverLevel1Token); \ |
| 1150 | + if (!_fle2_derive_EDCText##Type##_token(kb->crypt->crypto, \ |
| 1151 | + &out->edcDerivedToken, \ |
| 1152 | + collLevel1Token, \ |
| 1153 | + value, \ |
| 1154 | + false, \ |
| 1155 | + 0, \ |
| 1156 | + status)) { \ |
| 1157 | + return false; \ |
| 1158 | + } \ |
| 1159 | + if (!_fle2_derive_ESCText##Type##_token(kb->crypt->crypto, \ |
| 1160 | + &out->escDerivedToken, \ |
| 1161 | + collLevel1Token, \ |
| 1162 | + value, \ |
| 1163 | + false, \ |
| 1164 | + 0, \ |
| 1165 | + status)) { \ |
| 1166 | + return false; \ |
| 1167 | + } \ |
| 1168 | + if (!_fle2_derive_serverText##Type##DerivedFromDataToken(kb->crypt->crypto, \ |
| 1169 | + &out->serverDerivedFromDataToken, \ |
| 1170 | + serverLevel1Token, \ |
| 1171 | + value, \ |
| 1172 | + status)) { \ |
| 1173 | + return false; \ |
| 1174 | + } \ |
| 1175 | + return true; \ |
1135 | 1176 | }
|
1136 | 1177 | GENERATE_TEXT_SEARCH_TOKEN_SET_FOR_TYPE_IMPL(Exact)
|
1137 | 1178 | GENERATE_TEXT_SEARCH_TOKEN_SET_FOR_TYPE_IMPL(Substring)
|
@@ -1328,6 +1369,98 @@ static bool _fle2_generate_TextSearchTokenSets(_mongocrypt_key_broker_t *kb,
|
1328 | 1369 | return res;
|
1329 | 1370 | }
|
1330 | 1371 |
|
| 1372 | +static bool _fle2_generate_TextSearchFindTokenSets(_mongocrypt_key_broker_t *kb, |
| 1373 | + mc_TextSearchFindTokenSets_t *out, |
| 1374 | + const _mongocrypt_buffer_t *indexKeyId, |
| 1375 | + const mc_FLE2TextSearchInsertSpec_t *spec, |
| 1376 | + mongocrypt_status_t *status) { |
| 1377 | + BSON_ASSERT_PARAM(kb); |
| 1378 | + BSON_ASSERT_PARAM(kb->crypt); |
| 1379 | + BSON_ASSERT_PARAM(out); |
| 1380 | + BSON_ASSERT_PARAM(indexKeyId); |
| 1381 | + BSON_ASSERT_PARAM(spec); |
| 1382 | + |
| 1383 | + _mongocrypt_crypto_t *crypto = kb->crypt->crypto; |
| 1384 | + _FLE2EncryptedPayloadCommon_t common = {{0}}; |
| 1385 | + _mongocrypt_buffer_t asBsonValue = {0}; |
| 1386 | + bool res = false; |
| 1387 | + |
| 1388 | + int operator_count = (int)spec->substr.set + (int)spec->suffix.set + (int)spec->prefix.set; |
| 1389 | + if (operator_count > 1) { |
| 1390 | + CLIENT_ERR("Text search query specification cannot contain multiple query type specifications"); |
| 1391 | + goto fail; |
| 1392 | + } |
| 1393 | + |
| 1394 | + if (!mc_text_search_str_query(spec, &asBsonValue, status)) { |
| 1395 | + goto fail; |
| 1396 | + } |
| 1397 | + |
| 1398 | + // Start the token derivations |
| 1399 | + if (!_get_tokenKey(kb, indexKeyId, &common.tokenKey, status)) { |
| 1400 | + goto fail; |
| 1401 | + } |
| 1402 | + |
| 1403 | + common.collectionsLevel1Token = mc_CollectionsLevel1Token_new(crypto, &common.tokenKey, status); |
| 1404 | + if (!common.collectionsLevel1Token) { |
| 1405 | + CLIENT_ERR("unable to derive collectionLevel1Token"); |
| 1406 | + goto fail; |
| 1407 | + } |
| 1408 | + |
| 1409 | + common.serverTokenDerivationLevel1Token = mc_ServerTokenDerivationLevel1Token_new(crypto, &common.tokenKey, status); |
| 1410 | + if (!common.serverTokenDerivationLevel1Token) { |
| 1411 | + CLIENT_ERR("unable to derive serverTokenDerivationLevel1Token"); |
| 1412 | + goto fail; |
| 1413 | + } |
| 1414 | + |
| 1415 | + if (spec->substr.set) { |
| 1416 | + if (!_fle2_generate_TextSubstringFindTokenSet(kb, |
| 1417 | + &out->substring.value, |
| 1418 | + &asBsonValue, |
| 1419 | + common.collectionsLevel1Token, |
| 1420 | + common.serverTokenDerivationLevel1Token, |
| 1421 | + status)) { |
| 1422 | + goto fail; |
| 1423 | + } |
| 1424 | + out->substring.set = true; |
| 1425 | + } else if (spec->suffix.set) { |
| 1426 | + if (!_fle2_generate_TextSuffixFindTokenSet(kb, |
| 1427 | + &out->suffix.value, |
| 1428 | + &asBsonValue, |
| 1429 | + common.collectionsLevel1Token, |
| 1430 | + common.serverTokenDerivationLevel1Token, |
| 1431 | + status)) { |
| 1432 | + goto fail; |
| 1433 | + } |
| 1434 | + out->suffix.set = true; |
| 1435 | + |
| 1436 | + } else if (spec->prefix.set) { |
| 1437 | + if (!_fle2_generate_TextPrefixFindTokenSet(kb, |
| 1438 | + &out->prefix.value, |
| 1439 | + &asBsonValue, |
| 1440 | + common.collectionsLevel1Token, |
| 1441 | + common.serverTokenDerivationLevel1Token, |
| 1442 | + status)) { |
| 1443 | + goto fail; |
| 1444 | + } |
| 1445 | + out->prefix.set = true; |
| 1446 | + } else { |
| 1447 | + if (!_fle2_generate_TextExactFindTokenSet(kb, |
| 1448 | + &out->exact.value, |
| 1449 | + &asBsonValue, |
| 1450 | + common.collectionsLevel1Token, |
| 1451 | + common.serverTokenDerivationLevel1Token, |
| 1452 | + status)) { |
| 1453 | + goto fail; |
| 1454 | + } |
| 1455 | + out->exact.set = true; |
| 1456 | + } |
| 1457 | + res = true; |
| 1458 | +fail: |
| 1459 | + _mongocrypt_buffer_cleanup(&asBsonValue); |
| 1460 | + _FLE2EncryptedPayloadCommon_cleanup(&common); |
| 1461 | + return res; |
| 1462 | +} |
| 1463 | + |
1331 | 1464 | /**
|
1332 | 1465 | * Payload subtype 11: FLE2InsertUpdatePayloadV2 for text search inserts/updates
|
1333 | 1466 | *
|
@@ -1366,6 +1499,12 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForTextSearc
|
1366 | 1499 | goto fail;
|
1367 | 1500 | }
|
1368 | 1501 |
|
| 1502 | + // One of substr/suffix/prefix must be set for inserts |
| 1503 | + if (!(insertSpec.substr.set || insertSpec.suffix.set || insertSpec.prefix.set)) { |
| 1504 | + CLIENT_ERR("FLE2TextSearchInsertSpec is missing a substring, suffix, or prefix index specification"); |
| 1505 | + goto fail; |
| 1506 | + } |
| 1507 | + |
1369 | 1508 | // t
|
1370 | 1509 | payload.valueType = BSON_TYPE_UTF8;
|
1371 | 1510 |
|
@@ -1830,9 +1969,59 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForTextSearch(_mongoc
|
1830 | 1969 | _mongocrypt_marking_t *marking,
|
1831 | 1970 | _mongocrypt_ciphertext_t *ciphertext,
|
1832 | 1971 | mongocrypt_status_t *status) {
|
1833 |
| - // TODO MONGOCRYPT-761 implement find support for text search fields |
1834 |
| - CLIENT_ERR("Text search find is not yet supported"); |
1835 |
| - return false; |
| 1972 | + BSON_ASSERT_PARAM(kb); |
| 1973 | + BSON_ASSERT_PARAM(marking); |
| 1974 | + BSON_ASSERT_PARAM(ciphertext); |
| 1975 | + BSON_ASSERT(kb->crypt); |
| 1976 | + BSON_ASSERT(marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION); |
| 1977 | + |
| 1978 | + bool res = false; |
| 1979 | + mc_FLE2EncryptionPlaceholder_t *placeholder = &marking->u.fle2; |
| 1980 | + BSON_ASSERT(placeholder->type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND); |
| 1981 | + BSON_ASSERT(placeholder->algorithm == MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH); |
| 1982 | + |
| 1983 | + mc_FLE2FindTextPayload_t payload; |
| 1984 | + mc_FLE2FindTextPayload_init(&payload); |
| 1985 | + |
| 1986 | + mc_FLE2TextSearchInsertSpec_t spec; |
| 1987 | + if (!mc_FLE2TextSearchInsertSpec_parse(&spec, &placeholder->v_iter, status)) { |
| 1988 | + goto fail; |
| 1989 | + } |
| 1990 | + |
| 1991 | + if (!_fle2_generate_TextSearchFindTokenSets(kb, &payload.tokenSets, &placeholder->index_key_id, &spec, status)) { |
| 1992 | + goto fail; |
| 1993 | + } |
| 1994 | + |
| 1995 | + payload.caseFold = spec.casef; |
| 1996 | + payload.diacriticFold = spec.diacf; |
| 1997 | + payload.maxContentionFactor = placeholder->maxContentionFactor; |
| 1998 | + if (spec.substr.set) { |
| 1999 | + payload.substringSpec.set = true; |
| 2000 | + payload.substringSpec.value = spec.substr.value; |
| 2001 | + } else if (spec.suffix.set) { |
| 2002 | + payload.suffixSpec.set = true; |
| 2003 | + payload.suffixSpec.value = spec.suffix.value; |
| 2004 | + } else if (spec.prefix.set) { |
| 2005 | + payload.prefixSpec.set = true; |
| 2006 | + payload.prefixSpec.value = spec.prefix.value; |
| 2007 | + } |
| 2008 | + |
| 2009 | + // Serialize. |
| 2010 | + { |
| 2011 | + bson_t out = BSON_INITIALIZER; |
| 2012 | + mc_FLE2FindTextPayload_serialize(&payload, &out); |
| 2013 | + _mongocrypt_buffer_steal_from_bson(&ciphertext->data, &out); |
| 2014 | + } |
| 2015 | + |
| 2016 | + // Do not set ciphertext->original_bson_type and ciphertext->key_id. They are |
| 2017 | + // not used for FLE2FindTextPayload. |
| 2018 | + ciphertext->blob_subtype = MC_SUBTYPE_FLE2FindTextPayload; |
| 2019 | + |
| 2020 | + res = true; |
| 2021 | + |
| 2022 | +fail: |
| 2023 | + mc_FLE2FindTextPayload_cleanup(&payload); |
| 2024 | + return res; |
1836 | 2025 | }
|
1837 | 2026 |
|
1838 | 2027 | static bool _mongocrypt_fle2_placeholder_to_FLE2UnindexedEncryptedValue(_mongocrypt_key_broker_t *kb,
|
|
0 commit comments