@@ -512,14 +512,21 @@ static bool _fle2_placeholder_aes_aead_encrypt(_mongocrypt_key_broker_t *kb,
512512// ECCDerivedFromDataTokenAndContentionFactor)
513513// FLE V2: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor)
514514// Range V2: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || isLeaf)
515+ // Text search: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || msize)
516+ struct encrypted_token_metadata {
517+ mc_optional_bool_t is_leaf ; // isLeaf for Range V2, none for all other cases
518+ mc_optional_uint32_t msize ; // msize for text search, none for all other cases
519+ };
520+
515521static bool _fle2_derive_encrypted_token (_mongocrypt_crypto_t * crypto ,
516522 _mongocrypt_buffer_t * out ,
517- bool concatentate_leaf ,
518523 const mc_CollectionsLevel1Token_t * collectionsLevel1Token ,
519524 const _mongocrypt_buffer_t * escDerivedToken ,
520525 const _mongocrypt_buffer_t * eccDerivedToken ,
521- mc_optional_bool_t is_leaf ,
526+ struct encrypted_token_metadata token_metadata ,
522527 mongocrypt_status_t * status ) {
528+ // isLeaf and msize should never both be set.
529+ BSON_ASSERT (!token_metadata .is_leaf .set || !token_metadata .msize .set );
523530 mc_ECOCToken_t * ecocToken = mc_ECOCToken_new (crypto , collectionsLevel1Token , status );
524531 if (!ecocToken ) {
525532 return false;
@@ -531,10 +538,10 @@ static bool _fle2_derive_encrypted_token(_mongocrypt_crypto_t *crypto,
531538 const _mongocrypt_buffer_t * p = & tmp ;
532539 if (!eccDerivedToken ) {
533540 // FLE2v2
534- if (concatentate_leaf && is_leaf .set ) {
541+ if (token_metadata . is_leaf .set ) {
535542 // Range V2; concat isLeaf
536543 _mongocrypt_buffer_t isLeafBuf ;
537- if (!_mongocrypt_buffer_copy_from_data_and_size (& isLeafBuf , (uint8_t []){is_leaf .value }, 1 )) {
544+ if (!_mongocrypt_buffer_copy_from_data_and_size (& isLeafBuf , (uint8_t []){token_metadata . is_leaf .value }, 1 )) {
538545 CLIENT_ERR ("failed to create is_leaf buffer" );
539546 goto fail ;
540547 }
@@ -544,6 +551,21 @@ static bool _fle2_derive_encrypted_token(_mongocrypt_crypto_t *crypto,
544551 goto fail ;
545552 }
546553 _mongocrypt_buffer_cleanup (& isLeafBuf );
554+ } else if (token_metadata .msize .set ) {
555+ // Text search; concat msize
556+ _mongocrypt_buffer_t msizeBuf ;
557+ // msize is a 3-byte value, so copy the 3 least significant bytes into the buffer in little-endian order.
558+ uint32_t le_msize = BSON_UINT32_TO_LE (token_metadata .msize .value );
559+ if (!_mongocrypt_buffer_copy_from_data_and_size (& msizeBuf , (uint8_t * )& le_msize , 3 )) {
560+ CLIENT_ERR ("failed to create msize buffer" );
561+ goto fail ;
562+ }
563+ if (!_mongocrypt_buffer_concat (& tmp , (_mongocrypt_buffer_t []){* escDerivedToken , msizeBuf }, 2 )) {
564+ CLIENT_ERR ("failed to allocate buffer" );
565+ _mongocrypt_buffer_cleanup (& msizeBuf );
566+ goto fail ;
567+ }
568+ _mongocrypt_buffer_cleanup (& msizeBuf );
547569 } else {
548570 p = escDerivedToken ;
549571 }
@@ -753,16 +775,23 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_common(_mongocrypt_key
753775
754776 // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor)
755777 // Or in Range V2, when using range: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || 0x00)
756- if (!_fle2_derive_encrypted_token (
757- crypto ,
758- & out -> encryptedTokens ,
759- true,
760- common -> collectionsLevel1Token ,
761- & out -> escDerivedToken ,
762- NULL , // unused in v2
763- // If this is a range insert, we append isLeaf to the encryptedTokens. Otherwise, we don't.
764- placeholder -> algorithm == MONGOCRYPT_FLE2_ALGORITHM_RANGE ? OPT_BOOL (false) : (mc_optional_bool_t ){0 },
765- status )) {
778+ // Or in Text Search, when using msize: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor ||
779+ // 0x000000)
780+ struct encrypted_token_metadata et_meta = {{0 }};
781+ if (placeholder -> algorithm == MONGOCRYPT_FLE2_ALGORITHM_RANGE ) {
782+ // For range, we append isLeaf to the encryptedTokens.
783+ et_meta .is_leaf = OPT_BOOL (false);
784+ } else if (placeholder -> algorithm == MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH ) {
785+ // For text search, we append msize to the encryptedTokens.
786+ et_meta .msize = OPT_U32 (0 );
787+ }
788+ if (!_fle2_derive_encrypted_token (crypto ,
789+ & out -> encryptedTokens ,
790+ common -> collectionsLevel1Token ,
791+ & out -> escDerivedToken ,
792+ NULL , // unused in v2
793+ et_meta ,
794+ status )) {
766795 goto fail ;
767796 }
768797
@@ -1021,11 +1050,10 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
10211050 // Or in Range V2: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || isLeaf)
10221051 if (!_fle2_derive_encrypted_token (kb -> crypt -> crypto ,
10231052 & etc .encryptedTokens ,
1024- true,
10251053 edge_tokens .collectionsLevel1Token ,
10261054 & etc .escDerivedToken ,
10271055 NULL , // ecc unsed in FLE2v2
1028- OPT_BOOL (is_leaf ),
1056+ ( struct encrypted_token_metadata ){. is_leaf = OPT_BOOL (is_leaf )} ,
10291057 status )) {
10301058 goto fail_loop ;
10311059 }
@@ -1087,6 +1115,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
10871115 mc_Text##Type##TokenSet_t *out, \
10881116 const _mongocrypt_buffer_t *value, \
10891117 int64_t contentionFactor, \
1118+ uint32_t msize, \
10901119 const mc_CollectionsLevel1Token_t *collLevel1Token, \
10911120 const mc_ServerTokenDerivationLevel1Token_t *serverLevel1Token, \
10921121 mongocrypt_status_t *status) { \
@@ -1124,11 +1153,10 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
11241153 } \
11251154 if (!_fle2_derive_encrypted_token(kb->crypt->crypto, \
11261155 &out->encryptedTokens, \
1127- false, \
11281156 collLevel1Token, \
11291157 &out->escDerivedToken, \
11301158 NULL, \
1131- (mc_optional_bool_t){0}, \
1159+ (struct encrypted_token_metadata){.msize = OPT_U32(msize)}, \
11321160 status)) { \
11331161 return false; \
11341162 } \
@@ -1230,6 +1258,8 @@ static bool _fle2_generate_TextSearchTokenSets(_mongocrypt_key_broker_t *kb,
12301258 & tsts -> exact ,
12311259 & asBsonValue ,
12321260 contentionFactor ,
1261+ // For the exact token, report total msize of the token set.
1262+ encodeSets -> msize ,
12331263 common .collectionsLevel1Token ,
12341264 common .serverTokenDerivationLevel1Token ,
12351265 status )) {
@@ -1258,10 +1288,12 @@ static bool _fle2_generate_TextSearchTokenSets(_mongocrypt_key_broker_t *kb,
12581288 _mongocrypt_buffer_init (& asBsonValue );
12591289 _mongocrypt_buffer_copy_from_string_as_bson_value (& asBsonValue , substring , (int )bytelen );
12601290
1291+ // For substring, prefix, and suffix tokens, report 0 as the msize.
12611292 if (!_fle2_generate_TextSubstringTokenSet (kb ,
12621293 & tset ,
12631294 & asBsonValue ,
12641295 contentionFactor ,
1296+ 0 /* msize */ ,
12651297 common .collectionsLevel1Token ,
12661298 common .serverTokenDerivationLevel1Token ,
12671299 status )) {
@@ -1302,6 +1334,7 @@ static bool _fle2_generate_TextSearchTokenSets(_mongocrypt_key_broker_t *kb,
13021334 & tset ,
13031335 & asBsonValue ,
13041336 contentionFactor ,
1337+ 0 /* msize */ ,
13051338 common .collectionsLevel1Token ,
13061339 common .serverTokenDerivationLevel1Token ,
13071340 status )) {
@@ -1342,6 +1375,7 @@ static bool _fle2_generate_TextSearchTokenSets(_mongocrypt_key_broker_t *kb,
13421375 & tset ,
13431376 & asBsonValue ,
13441377 contentionFactor ,
1378+ 0 /* msize */ ,
13451379 common .collectionsLevel1Token ,
13461380 common .serverTokenDerivationLevel1Token ,
13471381 status )) {
@@ -1543,16 +1577,15 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForTextSearc
15431577 _mongocrypt_buffer_copy_to (& payload .edcDerivedToken , & payload .escDerivedToken );
15441578 _mongocrypt_buffer_copy_to (& payload .edcDerivedToken , & payload .serverDerivedFromDataToken );
15451579
1546- // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor)
1580+ // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor | 0x000000 )
15471581 // Since p is never used for text search, this just sets p to a bogus ciphertext of
15481582 // the correct length.
15491583 if (!_fle2_derive_encrypted_token (kb -> crypt -> crypto ,
15501584 & payload .encryptedTokens ,
1551- false,
15521585 common .collectionsLevel1Token ,
15531586 & payload .escDerivedToken , // bogus
15541587 NULL , // unused in FLE2v2
1555- (mc_optional_bool_t ){ 0 },
1588+ (struct encrypted_token_metadata ){. msize = OPT_U32 ( 0 ) },
15561589 status )) {
15571590 goto fail ;
15581591 }
0 commit comments