Skip to content

Commit dfbfad8

Browse files
SERVER-95889 Make FLE2IndexedEqualityEncryptedValueV2 wrap mc_FLE2IndexedEncryptedValueV2_t (#910)
* libmc changes * libmc changes * Fix errors * add IEV validate test * add metadata validate test * Change validation to look at type * Change names * Windows + test --------- Co-authored-by: Joshua Siegel <[email protected]>
1 parent c00e02b commit dfbfad8

7 files changed

+281
-56
lines changed

src/mc-fle2-payload-iev-private-v2.h

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,39 @@
8080
*
8181
*/
8282

83-
typedef struct _mc_FLE2IndexedEncryptedValueV2_t mc_FLE2IndexedEncryptedValueV2_t;
83+
typedef enum {
84+
kFLE2IEVTypeInitV2,
85+
kFLE2IEVTypeEqualityV2,
86+
kFLE2IEVTypeRangeV2,
87+
} _mc_fle2_iev_v2_type;
88+
89+
typedef struct _mc_FLE2IndexedEncryptedValueV2_t {
90+
// Raw payload values
91+
uint8_t fle_blob_subtype;
92+
uint8_t bson_value_type;
93+
uint8_t edge_count;
94+
_mongocrypt_buffer_t S_KeyId;
95+
_mongocrypt_buffer_t ServerEncryptedValue;
96+
97+
// Decode State
98+
_mc_fle2_iev_v2_type type;
99+
bool ClientEncryptedValueDecoded;
100+
bool ClientValueDecoded;
101+
102+
// Populated during _add_S_Key
103+
// DecryptedServerEncryptedValue := DecryptCTR(S_Key, ServerEncryptedValue)
104+
_mongocrypt_buffer_t DecryptedServerEncryptedValue;
105+
106+
// Views on DecryptedServerEncryptedValue (DSEV)
107+
_mongocrypt_buffer_t K_KeyId; // First 16 octets, UUID
108+
_mongocrypt_buffer_t ClientEncryptedValue; // Remainder of DSEV
109+
110+
// Populated during _add_K_Key
111+
// ClientValue := DecryptCBCAEAD(K_Key, ClientEncryptedValue, AD=K_KeyId)
112+
_mongocrypt_buffer_t ClientValue;
113+
114+
mc_FLE2TagAndEncryptedMetadataBlock_t *metadata;
115+
} mc_FLE2IndexedEncryptedValueV2_t;
84116

85117
mc_FLE2IndexedEncryptedValueV2_t *mc_FLE2IndexedEncryptedValueV2_new(void);
86118
bson_type_t mc_FLE2IndexedEncryptedValueV2_get_bson_value_type(const mc_FLE2IndexedEncryptedValueV2_t *iev,
@@ -118,6 +150,12 @@ bool mc_FLE2IndexedEncryptedValueV2_serialize(const mc_FLE2IndexedEncryptedValue
118150
_mongocrypt_buffer_t *buf,
119151
mongocrypt_status_t *status);
120152

153+
/**
154+
* Validates that a mc_FLE2IndexedEncryptedValueV2_t is well-formed, i.e. values are in their valid
155+
* ranges and buffers are correctly sized. Returns an error if the input structure is invalid.
156+
*/
157+
bool mc_FLE2IndexedEncryptedValueV2_validate(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status);
158+
121159
const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValueV2_get_S_KeyId(const mc_FLE2IndexedEncryptedValueV2_t *iev,
122160
mongocrypt_status_t *status);
123161

src/mc-fle2-payload-iev-v2.c

Lines changed: 98 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -23,43 +23,9 @@
2323
#include "mc-reader-private.h"
2424
#include "mc-tokens-private.h"
2525
#include "mc-writer-private.h"
26+
#include <mongocrypt-util-private.h>
2627
#include <stdint.h>
2728

28-
typedef enum {
29-
kTypeInit,
30-
kTypeEquality,
31-
kTypeRange,
32-
} _mc_fle2_iev_v2_type;
33-
34-
struct _mc_FLE2IndexedEncryptedValueV2_t {
35-
// Raw payload values
36-
uint8_t fle_blob_subtype;
37-
uint8_t bson_value_type;
38-
uint8_t edge_count;
39-
_mongocrypt_buffer_t S_KeyId;
40-
_mongocrypt_buffer_t ServerEncryptedValue;
41-
42-
// Decode State
43-
_mc_fle2_iev_v2_type type;
44-
bool ClientEncryptedValueDecoded;
45-
bool ClientValueDecoded;
46-
47-
// Populated during _add_S_Key
48-
// DecryptedServerEncryptedValue := DecryptCTR(S_Key, ServerEncryptedValue)
49-
_mongocrypt_buffer_t DecryptedServerEncryptedValue;
50-
51-
// Views on DecryptedServerEncryptedValue (DSEV)
52-
_mongocrypt_buffer_t K_KeyId; // First 16 octets, UUID
53-
_mongocrypt_buffer_t ClientEncryptedValue; // Remainder of DSEV
54-
55-
// Populated during _add_K_Key
56-
// ClientValue := DecryptCBCAEAD(K_Key, ClientEncryptedValue, AD=K_KeyId)
57-
_mongocrypt_buffer_t ClientValue;
58-
59-
mc_FLE2TagAndEncryptedMetadataBlock_t *metadata;
60-
};
61-
62-
#define kMetadataLen 96U // encCount(32) + tag(32) + encZeros(32)
6329
#define kMinServerEncryptedValueLen 17U // IV(16) + EncryptCTR(1byte)
6430
#define kMinSEVAndMetadataLen (kMinServerEncryptedValueLen + kMetadataLen)
6531

@@ -76,7 +42,7 @@ bson_type_t mc_FLE2IndexedEncryptedValueV2_get_bson_value_type(const mc_FLE2Inde
7642
mongocrypt_status_t *status) {
7743
BSON_ASSERT_PARAM(iev);
7844

79-
if (iev->type == kTypeInit) {
45+
if (iev->type == kFLE2IEVTypeInitV2) {
8046
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_bson_value_type "
8147
"must be called after "
8248
"mc_FLE2IndexedEncryptedValueV2_parse");
@@ -90,7 +56,7 @@ const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValueV2_get_S_KeyId(const mc_
9056
mongocrypt_status_t *status) {
9157
BSON_ASSERT_PARAM(iev);
9258

93-
if (iev->type == kTypeInit) {
59+
if (iev->type == kFLE2IEVTypeInitV2) {
9460
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_S_KeyId "
9561
"must be called after "
9662
"mc_FLE2IndexedEncryptedValueV2_parse");
@@ -109,7 +75,7 @@ bool mc_FLE2IndexedEncryptedValueV2_add_S_Key(_mongocrypt_crypto_t *crypto,
10975
BSON_ASSERT_PARAM(S_Key);
11076
BSON_ASSERT_PARAM(status);
11177

112-
if (iev->type == kTypeInit) {
78+
if (iev->type == kFLE2IEVTypeInitV2) {
11379
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_add_S_Key must "
11480
"be called after "
11581
"mc_FLE2IndexedEncryptedValueV2_parse");
@@ -315,14 +281,14 @@ uint8_t mc_FLE2IndexedEncryptedValueV2_get_edge_count(const mc_FLE2IndexedEncryp
315281
mongocrypt_status_t *status) {
316282
BSON_ASSERT_PARAM(iev);
317283

318-
if (iev->type == kTypeInit) {
284+
if (iev->type == kFLE2IEVTypeInitV2) {
319285
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge_count "
320286
"must be called after "
321287
"mc_FLE2IndexedEncryptedValueV2_parse");
322288
return 0;
323289
}
324290

325-
if (iev->type != kTypeRange) {
291+
if (iev->type != kFLE2IEVTypeRangeV2) {
326292
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge_count must be called with type range");
327293
return 0;
328294
}
@@ -337,20 +303,21 @@ bool mc_FLE2IndexedEncryptedValueV2_get_edge(const mc_FLE2IndexedEncryptedValueV
337303
BSON_ASSERT_PARAM(iev);
338304
BSON_ASSERT_PARAM(out);
339305

340-
if (iev->type == kTypeInit) {
306+
if (iev->type == kFLE2IEVTypeInitV2) {
341307
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge "
342308
"must be called after "
343309
"mc_FLE2IndexedEncryptedValueV2_parse");
344310
return false;
345311
}
346312

347-
if (iev->type != kTypeRange) {
313+
if (iev->type != kFLE2IEVTypeRangeV2) {
348314
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge must be called with type range");
349315
return false;
350316
}
351317

352318
if (edge_index >= iev->edge_count) {
353-
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge must be called with index edge_index less than edge count");
319+
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge must be called with index edge_index less "
320+
"than edge count");
354321
return false;
355322
}
356323

@@ -365,14 +332,14 @@ bool mc_FLE2IndexedEncryptedValueV2_get_metadata(const mc_FLE2IndexedEncryptedVa
365332
BSON_ASSERT_PARAM(iev);
366333
BSON_ASSERT_PARAM(out);
367334

368-
if (iev->type == kTypeInit) {
335+
if (iev->type == kFLE2IEVTypeInitV2) {
369336
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_metadata "
370337
"must be called after "
371338
"mc_FLE2IndexedEncryptedValueV2_parse");
372339
return false;
373340
}
374341

375-
if (iev->type != kTypeEquality) {
342+
if (iev->type != kFLE2IEVTypeEqualityV2) {
376343
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_metadata must be called with type equality");
377344
return false;
378345
}
@@ -393,7 +360,7 @@ bool mc_FLE2IndexedEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
393360
return false;
394361
}
395362

396-
if (iev->type != kTypeInit) {
363+
if (iev->type != kFLE2IEVTypeInitV2) {
397364
CLIENT_ERR("mc_FLE2IndexedRangeEncryptedValueV2_parse must not be "
398365
"called twice");
399366
return false;
@@ -405,9 +372,9 @@ bool mc_FLE2IndexedEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
405372
CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->fle_blob_subtype, status));
406373

407374
if (iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2) {
408-
iev->type = kTypeEquality;
375+
iev->type = kFLE2IEVTypeEqualityV2;
409376
} else if (iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2) {
410-
iev->type = kTypeRange;
377+
iev->type = kFLE2IEVTypeRangeV2;
411378
} else {
412379
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_parse expected "
413380
"fle_blob_subtype MC_SUBTYPE_FLE2Indexed(Equality|Range)EncryptedValueV2 got: %" PRIu8,
@@ -424,7 +391,7 @@ bool mc_FLE2IndexedEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
424391
/* Read edge_count */
425392
// Set equality edge_count to 1 as it doesn't technically exist but
426393
// there will be a singular metadata block
427-
if (iev->type == kTypeEquality) {
394+
if (iev->type == kFLE2IEVTypeEqualityV2) {
428395
iev->edge_count = 1;
429396
} else {
430397
CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->edge_count, status));
@@ -462,18 +429,31 @@ bool mc_FLE2IndexedEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
462429
return true;
463430
}
464431

432+
static inline uint32_t mc_FLE2IndexedEncryptedValueV2_serialized_length(const mc_FLE2IndexedEncryptedValueV2_t *iev) {
433+
// fle_blob_subtype: 1 byte
434+
// S_KeyId: UUID_LEN bytes
435+
// bson_value_type: 1 byte
436+
// if range: edge_count: 1 byte
437+
// ServerEncryptedValue: ServerEncryptedValue.len bytes
438+
// metadata: edge_count * kMetadataLen bytes
439+
return iev->ServerEncryptedValue.len + 1 + UUID_LEN + 1 + (iev->type == kFLE2IEVTypeRangeV2 ? 1 : 0)
440+
+ iev->edge_count * kMetadataLen;
441+
}
442+
465443
bool mc_FLE2IndexedEncryptedValueV2_serialize(const mc_FLE2IndexedEncryptedValueV2_t *iev,
466444
_mongocrypt_buffer_t *buf,
467445
mongocrypt_status_t *status) {
468446
BSON_ASSERT_PARAM(iev);
469447
BSON_ASSERT_PARAM(buf);
470448

471-
if (iev->type != kTypeRange && iev->type != kTypeEquality) {
449+
if (iev->type != kFLE2IEVTypeRangeV2 && iev->type != kFLE2IEVTypeEqualityV2) {
472450
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_serialize must be called with type equality or range");
473451
return false;
474452
}
475453

454+
uint32_t expected_len = mc_FLE2IndexedEncryptedValueV2_serialized_length(iev);
476455
mc_writer_t writer;
456+
_mongocrypt_buffer_resize(buf, expected_len);
477457
mc_writer_init_from_buffer(&writer, buf, __FUNCTION__);
478458

479459
// Serialize fle_blob_subtype
@@ -486,7 +466,7 @@ bool mc_FLE2IndexedEncryptedValueV2_serialize(const mc_FLE2IndexedEncryptedValue
486466
CHECK_AND_RETURN(mc_writer_write_u8(&writer, iev->bson_value_type, status));
487467

488468
// Serialize edge_count (only serialized for type range)
489-
if (iev->type == kTypeRange) {
469+
if (iev->type == kFLE2IEVTypeRangeV2) {
490470
CHECK_AND_RETURN(mc_writer_write_u8(&writer, iev->edge_count, status));
491471
}
492472

@@ -497,7 +477,7 @@ bool mc_FLE2IndexedEncryptedValueV2_serialize(const mc_FLE2IndexedEncryptedValue
497477
// Serialize metadata
498478
for (int i = 0; i < iev->edge_count; ++i) {
499479
_mongocrypt_buffer_t tmp_buf;
500-
_mongocrypt_buffer_init_size(&tmp_buf, kMetadataLen);
480+
_mongocrypt_buffer_init(&tmp_buf);
501481

502482
CHECK_AND_RETURN(mc_FLE2TagAndEncryptedMetadataBlock_serialize(&iev->metadata[i], &tmp_buf, status));
503483
CHECK_AND_RETURN(mc_writer_write_buffer(&writer, &tmp_buf, kMetadataLen, status));
@@ -506,4 +486,69 @@ bool mc_FLE2IndexedEncryptedValueV2_serialize(const mc_FLE2IndexedEncryptedValue
506486
}
507487

508488
return true;
509-
}
489+
}
490+
491+
bool is_fle2_equality_indexed_supported_type(int bson_type) {
492+
switch (bson_type) {
493+
case BSON_TYPE_BINARY:
494+
case BSON_TYPE_CODE:
495+
case BSON_TYPE_REGEX:
496+
case BSON_TYPE_UTF8:
497+
498+
case BSON_TYPE_INT32:
499+
case BSON_TYPE_INT64:
500+
case BSON_TYPE_BOOL:
501+
case BSON_TYPE_TIMESTAMP:
502+
case BSON_TYPE_DATE_TIME:
503+
case BSON_TYPE_OID:
504+
505+
case BSON_TYPE_SYMBOL:
506+
case BSON_TYPE_DBPOINTER: return true;
507+
default: // All other defined types are non-deterministic or singletons.
508+
return false;
509+
}
510+
}
511+
512+
#define CHECK(condition, msg) \
513+
do { \
514+
if (!(condition)) { \
515+
CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_validate failed: " msg); \
516+
return false; \
517+
} \
518+
} while (0)
519+
520+
bool mc_FLE2IndexedEncryptedValueV2_validate(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) {
521+
BSON_ASSERT_PARAM(iev);
522+
CHECK(iev->type == kFLE2IEVTypeEqualityV2, "validate only supports type equality");
523+
CHECK(iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2,
524+
"fle_blob_subtype does not match type");
525+
CHECK(is_fle2_equality_indexed_supported_type(iev->bson_value_type), "bson_value_type is invalid");
526+
CHECK(iev->edge_count == 1, "edge_count must be 1 for equality");
527+
528+
CHECK(iev->ServerEncryptedValue.len >= kMinServerEncryptedValueLen, "SEV.len is less than minimum");
529+
CHECK(iev->S_KeyId.len == UUID_LEN, "S_KeyId is not the correct length for a UUID");
530+
531+
CHECK(!iev->ClientValueDecoded || iev->ClientEncryptedValueDecoded,
532+
"Found decrypted client value without encrypted client value");
533+
if (iev->ClientEncryptedValueDecoded) {
534+
const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm();
535+
const uint32_t DecryptedServerEncryptedValueLen =
536+
fle2alg->get_plaintext_len(iev->ServerEncryptedValue.len, status);
537+
if (!mongocrypt_status_ok(status)) {
538+
return false;
539+
}
540+
CHECK(iev->DecryptedServerEncryptedValue.len == DecryptedServerEncryptedValueLen, "DSEV.len was unexpected");
541+
CHECK(iev->ClientEncryptedValue.len == iev->DecryptedServerEncryptedValue.len - UUID_LEN,
542+
"CEV.len was unexpected");
543+
CHECK(iev->K_KeyId.len == UUID_LEN, "K_KeyId is not the correct length for a UUID");
544+
}
545+
if (iev->ClientValueDecoded) {
546+
const _mongocrypt_value_encryption_algorithm_t *fle2v2aead = _mcFLE2v2AEADAlgorithm();
547+
const uint32_t ClientValueLen = fle2v2aead->get_plaintext_len(iev->ClientEncryptedValue.len, status);
548+
if (!mongocrypt_status_ok(status)) {
549+
return false;
550+
}
551+
CHECK(iev->ClientValue.len == ClientValueLen, "ClientValue.len was unexpected");
552+
}
553+
return mc_FLE2TagAndEncryptedMetadataBlock_validate(iev->metadata, status);
554+
}

src/mc-fle2-tag-and-encrypted-metadata-block-private.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
#include "mc-writer-private.h"
2222
#include "mongocrypt-private.h"
2323

24-
typedef struct {
24+
#define kMetadataLen 96U // encCount(32) + tag(32) + encZeros(32)
25+
26+
typedef struct _mc_FLE2TagAndEncryptedMetadataBlock_t {
2527
_mongocrypt_buffer_t encryptedCount;
2628
_mongocrypt_buffer_t tag;
2729
_mongocrypt_buffer_t encryptedZeros;
@@ -41,4 +43,7 @@ bool mc_FLE2TagAndEncryptedMetadataBlock_serialize(const mc_FLE2TagAndEncryptedM
4143
_mongocrypt_buffer_t *buf,
4244
mongocrypt_status_t *status);
4345

44-
#endif /* MC_FLE2_TAG_AND_ENCRYPTED_METADATA_BLOCK_H */
46+
bool mc_FLE2TagAndEncryptedMetadataBlock_validate(const mc_FLE2TagAndEncryptedMetadataBlock_t *metadata,
47+
mongocrypt_status_t *status);
48+
49+
#endif /* MC_FLE2_TAG_AND_ENCRYPTED_METADATA_BLOCK_H */

src/mc-fle2-tag-and-encrypted-metadata-block.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ bool mc_FLE2TagAndEncryptedMetadataBlock_serialize(const mc_FLE2TagAndEncryptedM
6868
BSON_ASSERT_PARAM(metadata);
6969
BSON_ASSERT_PARAM(buf);
7070

71+
_mongocrypt_buffer_resize(buf, kMetadataLen);
7172
mc_writer_t writer;
7273
mc_writer_init_from_buffer(&writer, buf, __FUNCTION__);
7374

@@ -79,3 +80,19 @@ bool mc_FLE2TagAndEncryptedMetadataBlock_serialize(const mc_FLE2TagAndEncryptedM
7980

8081
return true;
8182
}
83+
84+
#define CHECK(condition, msg) \
85+
do { \
86+
if (!(condition)) { \
87+
CLIENT_ERR("mc_FLE2TagAndEncryptedMetadataBlock_validate failed: " msg); \
88+
return false; \
89+
} \
90+
} while (0)
91+
92+
bool mc_FLE2TagAndEncryptedMetadataBlock_validate(const mc_FLE2TagAndEncryptedMetadataBlock_t *metadata,
93+
mongocrypt_status_t *status) {
94+
CHECK(metadata->encryptedCount.len == kFieldLen, "Length of encrypted count was unexpected");
95+
CHECK(metadata->tag.len == kFieldLen, "Length of tag was unexpected");
96+
CHECK(metadata->encryptedZeros.len == kFieldLen, "Length of encrypted zeros was unexpected");
97+
return true;
98+
}

src/mlib/windows-lean.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#endif
1313

1414
#pragma push_macro("WIN32_LEAN_AND_MEAN")
15+
// Disable macro redefinition warning
16+
#undef WIN32_LEAN_AND_MEAN
1517
#define WIN32_LEAN_AND_MEAN 1
1618
#include <windows.h>
1719
#pragma pop_macro("WIN32_LEAN_AND_MEAN")

0 commit comments

Comments
 (0)