diff --git a/README.third_party.md b/README.third_party.md
index 8658d2ecced..42576c7aeca 100644
--- a/README.third_party.md
+++ b/README.third_party.md
@@ -44,7 +44,7 @@ a notice will be included in
| [Intel Decimal Floating-Point Math Library] | BSD-3-Clause | v2.0 U1 | | ✗ |
| [jbeder/yaml-cpp] | MIT | 0.6.3 | | ✗ |
| [JSON-Schema-Test-Suite] | Unknown License | Unknown | | |
-| [libmongocrypt] | Apache-2.0 | 1.8.4 | ✗ | ✗ |
+| [libmongocrypt] | Apache-2.0 | 1.12.0 | ✗ | ✗ |
| [librdkafka - the Apache Kafka C/C++ client library] | BSD-3-Clause, Xmlproc License, ISC, MIT, Public Domain, Zlib, BSD-2-Clause, Andreas Stolcke License | 2.0.2 | | ✗ |
| [LibTomCrypt] | WTFPL, Public Domain | 1.18.2 | ✗ | ✗ |
| [libunwind/libunwind] | MIT | v1.6.2 | | ✗ |
diff --git a/sbom.json b/sbom.json
index 8b9c9dac415..be429e5d58f 100644
--- a/sbom.json
+++ b/sbom.json
@@ -894,7 +894,7 @@
"name": "Organization: github"
},
"name": "libmongocrypt",
- "version": "1.8.4",
+ "version": "1.12.0",
"licenses": [
{
"license": {
@@ -902,7 +902,7 @@
}
}
],
- "purl": "pkg:github/mongodb/libmongocrypt@8354ba537d11494cb0dee21ec6d32fe8ab548c52",
+ "purl": "pkg:github/mongodb/libmongocrypt@085a0ce6538a28179da6bfd2927aea106924443a",
"properties": [
{
"name": "internal:team_responsible",
diff --git a/src/third_party/libmongocrypt/BUILD.bazel b/src/third_party/libmongocrypt/BUILD.bazel
index 5c493112a69..2ad18005607 100644
--- a/src/third_party/libmongocrypt/BUILD.bazel
+++ b/src/third_party/libmongocrypt/BUILD.bazel
@@ -90,6 +90,7 @@ mongo_cc_library(
"dist/src/mc-fle2-payload-uev-common.c",
"dist/src/mc-fle2-payload-uev-v2.c",
"dist/src/mc-fle2-rfds.c",
+ "dist/src/mc-fle2-tag-and-encrypted-metadata-block.c",
"dist/src/mc-range-edge-generation.c",
"dist/src/mc-range-encoding.c",
"dist/src/mc-range-mincover.c",
diff --git a/src/third_party/libmongocrypt/dist/kms-message/README.md b/src/third_party/libmongocrypt/dist/kms-message/README.md
index 2155f5f7449..d45e005e35c 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/README.md
+++ b/src/third_party/libmongocrypt/dist/kms-message/README.md
@@ -11,7 +11,7 @@ implements the request format.
- `test_kms_azure_online` makes live requests, and has additional requirements (must have working credentials).
### Requirements
-- A complete installation of the C driver. (libbson is needed for parsing JSON, and libmongoc is used for creating TLS streams). See http://mongoc.org/libmongoc/current/installing.html for installation instructions. For macOS, `brew install mongo-c-driver` will suffice.
+- A complete installation of the C driver. (libbson is needed for parsing JSON, and libmongoc is used for creating TLS streams). See the [C Driver Manual](https://www.mongodb.com/docs/languages/c/c-driver/current/libmongoc/tutorials/obtaining-libraries/) for installation instructions. For macOS, `brew install mongo-c-driver` will suffice.
- An Azure key vault, and a service principal with an access policy allowing encrypt / decrypt key operations. The following environment variables must be set:
- AZURE_TENANT_ID
- AZURE_CLIENT_ID
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_gcp_request.c b/src/third_party/libmongocrypt/dist/kms-message/src/kms_gcp_request.c
index 564cacc6113..960141bd225 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_gcp_request.c
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_gcp_request.c
@@ -87,7 +87,7 @@ kms_gcp_request_oauth_new (const char *host,
req->crypto.sign_ctx = opt->crypto.sign_ctx;
}
- jwt_signature = malloc (SIGNATURE_LEN);
+ jwt_signature = calloc (1, SIGNATURE_LEN);
if (!req->crypto.sign_rsaes_pkcs1_v1_5 (
req->crypto.sign_ctx,
private_key_data,
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_reader_writer.c b/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_reader_writer.c
index 08affaa948c..56ca01fce49 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_reader_writer.c
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_reader_writer.c
@@ -178,6 +178,14 @@ kmip_writer_write_enumeration (kmip_writer_t *writer, kmip_tag_type_t tag, int32
kmip_writer_write_u32 (writer, 0);
}
+void kmip_writer_write_bool (kmip_writer_t *writer, kmip_tag_type_t tag, bool value)
+{
+ kmip_writer_write_tag_enum (writer, tag);
+ kmip_writer_write_u8 (writer, KMIP_ITEM_TYPE_Boolean);
+ kmip_writer_write_u32 (writer, 8);
+ kmip_writer_write_u64(writer, (uint64_t) value);
+}
+
void
kmip_writer_write_datetime (kmip_writer_t *writer, kmip_tag_type_t tag, int64_t value)
{
@@ -384,6 +392,15 @@ kmip_reader_read_enumeration (kmip_reader_t *reader, uint32_t *enum_value)
return kmip_reader_read_u32 (reader, &ignored);
}
+bool
+kmip_reader_read_bool (kmip_reader_t *reader, bool *value)
+{
+ uint64_t u64;
+ CHECK_AND_RET (kmip_reader_read_u64 (reader, &u64));
+ *value = (bool) u64;
+ return true;
+}
+
bool
kmip_reader_read_integer (kmip_reader_t *reader, int32_t *value)
{
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_reader_writer_private.h b/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_reader_writer_private.h
index 314bf394522..a31aa1c6e7d 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_reader_writer_private.h
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_reader_writer_private.h
@@ -59,6 +59,9 @@ kmip_writer_write_long_integer (kmip_writer_t *writer, kmip_tag_type_t tag, int6
void
kmip_writer_write_enumeration (kmip_writer_t *writer, kmip_tag_type_t tag, int32_t value);
+void
+kmip_writer_write_bool (kmip_writer_t *writer, kmip_tag_type_t tag, bool value);
+
void
kmip_writer_write_datetime (kmip_writer_t *writer, kmip_tag_type_t tag, int64_t value);
@@ -112,6 +115,9 @@ kmip_reader_read_type (kmip_reader_t *reader, kmip_item_type_t *type);
bool
kmip_reader_read_enumeration (kmip_reader_t *reader, uint32_t *enum_value);
+bool
+kmip_reader_read_bool (kmip_reader_t *reader, bool *value);
+
bool
kmip_reader_read_integer (kmip_reader_t *reader, int32_t *value);
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_request.c b/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_request.c
index c59bff7c5fa..3b9541032b2 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_request.c
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_request.c
@@ -16,6 +16,7 @@
#include "kms_message/kms_kmip_request.h"
+#include "kms_kmip_tag_type_private.h"
#include "kms_message_private.h"
#include "kms_kmip_reader_writer_private.h"
@@ -181,7 +182,7 @@ kms_kmip_request_activate_new (void *reserved, const char *unique_identifer)
kmip_writer_close_struct (writer); /* KMIP_TAG_RequestHeader */
kmip_writer_begin_struct (writer, KMIP_TAG_BatchItem);
- /* 0x0A == Get */
+ /* 0x12 == Activate */
kmip_writer_write_enumeration (writer, KMIP_TAG_Operation, 0x12);
kmip_writer_begin_struct (writer, KMIP_TAG_RequestPayload);
kmip_writer_write_string (writer,
@@ -254,3 +255,212 @@ kms_kmip_request_get_new (void *reserved, const char *unique_identifer)
kmip_writer_destroy (writer);
return req;
}
+
+kms_request_t *
+kms_kmip_request_create_new (void *reserved) {
+ /*
+ Create a KMIP Create request of this form:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ */
+ kmip_writer_t *writer;
+ kms_request_t *req;
+
+ req = calloc (1, sizeof (kms_request_t));
+ req->provider = KMS_REQUEST_PROVIDER_KMIP;
+
+ writer = kmip_writer_new();
+ kmip_writer_begin_struct(writer, KMIP_TAG_RequestMessage);
+
+ kmip_writer_begin_struct (writer, KMIP_TAG_RequestHeader);
+ kmip_writer_begin_struct (writer, KMIP_TAG_ProtocolVersion);
+ kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMajor, 1);
+ kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMinor, 2);
+ kmip_writer_close_struct (writer); /* KMIP_TAG_ProtocolVersion */
+ kmip_writer_write_integer (writer, KMIP_TAG_BatchCount, 1);
+ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestHeader */
+
+ kmip_writer_begin_struct (writer, KMIP_TAG_BatchItem);
+ /* 0x01 == Create */
+ kmip_writer_write_enumeration (writer, KMIP_TAG_Operation, 0x01);
+ kmip_writer_begin_struct (writer, KMIP_TAG_RequestPayload);
+ /* 0x02 == symmetric key */
+ kmip_writer_write_enumeration(writer, KMIP_TAG_ObjectType, 0x02);
+
+ {
+ kmip_writer_begin_struct (writer, KMIP_TAG_TemplateAttribute);
+
+ kmip_writer_begin_struct (writer, KMIP_TAG_Attribute);
+ const char *cryptographicAlgorithmStr = "Cryptographic Algorithm";
+ kmip_writer_write_string (writer,
+ KMIP_TAG_AttributeName,
+ cryptographicAlgorithmStr,
+ strlen (cryptographicAlgorithmStr));
+ kmip_writer_write_enumeration (writer, KMIP_TAG_AttributeValue, 3 /* AES */);
+ kmip_writer_close_struct (writer);
+ kmip_writer_begin_struct (writer, KMIP_TAG_Attribute);
+ const char *cryptographicLengthStr = "Cryptographic Length";
+ kmip_writer_write_string (writer,
+ KMIP_TAG_AttributeName,
+ cryptographicLengthStr,
+ strlen (cryptographicLengthStr));
+ kmip_writer_write_integer (writer, KMIP_TAG_AttributeValue, 256);
+ kmip_writer_close_struct (writer);
+ kmip_writer_begin_struct (writer, KMIP_TAG_Attribute);
+ const char *cryptographicUsageMaskStr = "Cryptographic Usage Mask";
+ kmip_writer_write_string (writer,
+ KMIP_TAG_AttributeName,
+ cryptographicUsageMaskStr,
+ strlen (cryptographicUsageMaskStr));
+ kmip_writer_write_integer (writer, KMIP_TAG_AttributeValue, 4 | 8 /* Encrypt | Decrypt */);
+ kmip_writer_close_struct (writer);
+
+ kmip_writer_close_struct (writer); /* KMIP_TAG_TemplateAttribute */
+ }
+
+ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestPayload */
+ kmip_writer_close_struct (writer); /* KMIP_TAG_BatchItem */
+ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestMessage */
+
+ /* Copy the KMIP writer buffer to a KMIP request. */
+ copy_writer_buffer (req, writer);
+ kmip_writer_destroy (writer);
+ return req;
+}
+
+static kms_request_t *
+kmip_encrypt_decrypt (const char* unique_identifer, const uint8_t *data, size_t len,
+ const uint8_t *iv_data, size_t iv_len, bool encrypt) {
+ kmip_writer_t *writer;
+ kms_request_t *req;
+
+ req = calloc (1, sizeof (kms_request_t));
+ req->provider = KMS_REQUEST_PROVIDER_KMIP;
+
+ writer = kmip_writer_new();
+ kmip_writer_begin_struct(writer, KMIP_TAG_RequestMessage);
+
+ kmip_writer_begin_struct (writer, KMIP_TAG_RequestHeader);
+ kmip_writer_begin_struct (writer, KMIP_TAG_ProtocolVersion);
+ kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMajor, 1);
+ kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMinor, 2);
+ kmip_writer_close_struct (writer); /* KMIP_TAG_ProtocolVersion */
+ kmip_writer_write_integer (writer, KMIP_TAG_BatchCount, 1);
+ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestHeader */
+
+ kmip_writer_begin_struct (writer, KMIP_TAG_BatchItem);
+ /* 0x1F == Encrypt, 0x20 == Decrypt*/
+ kmip_writer_write_enumeration (writer, KMIP_TAG_Operation, encrypt ? 0x1F : 0x20);
+ kmip_writer_begin_struct (writer, KMIP_TAG_RequestPayload);
+ kmip_writer_write_string (writer,
+ KMIP_TAG_UniqueIdentifier,
+ unique_identifer,
+ strlen (unique_identifer));
+
+ kmip_writer_begin_struct (writer, KMIP_TAG_CryptographicParameters);
+ kmip_writer_write_enumeration(writer, KMIP_TAG_BlockCipherMode, 1 /* CBC */);
+ kmip_writer_write_enumeration(writer, KMIP_TAG_PaddingMethod, 3 /* PKCS5 */);
+ kmip_writer_write_enumeration(writer, KMIP_TAG_CryptographicAlgorithm, 3 /* AES */);
+ if (encrypt) kmip_writer_write_bool(writer, KMIP_TAG_RandomIV, true);
+ kmip_writer_close_struct(writer); /* KMIP_TAG_CryptographicParameters */
+
+ kmip_writer_write_bytes(writer, KMIP_TAG_Data, (char *) data, len);
+ if (!encrypt) kmip_writer_write_bytes(writer, KMIP_TAG_IVCounterNonce, (char *) iv_data, iv_len);
+
+ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestPayload */
+ kmip_writer_close_struct (writer); /* KMIP_TAG_BatchItem */
+ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestMessage */
+
+ /* Copy the KMIP writer buffer to a KMIP request. */
+ copy_writer_buffer (req, writer);
+ kmip_writer_destroy (writer);
+ return req;
+}
+
+kms_request_t *
+kms_kmip_request_encrypt_new (void *reserved, const char* unique_identifer, const uint8_t *plaintext, size_t len) {
+ /*
+ Create a KMIP Encrypt request of this form:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ */
+ return kmip_encrypt_decrypt(unique_identifer, plaintext, len, NULL, 0, true);
+}
+
+kms_request_t *
+kms_kmip_request_decrypt_new (void *reserved, const char* unique_identifer, const uint8_t *ciphertext, size_t len, const uint8_t *iv_data, size_t iv_len) {
+ /*
+ Create a KMIP Decrypt request of this form:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ */
+ return kmip_encrypt_decrypt(unique_identifer, ciphertext, len, iv_data, iv_len, false);
+}
+
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_response.c b/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_response.c
index b4dc2c6fe07..ec389e60134 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_response.c
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_response.c
@@ -1,5 +1,7 @@
#include "kms_message/kms_kmip_response.h"
+#include "kms_kmip_item_type_private.h"
+#include "kms_kmip_tag_type_private.h"
#include "kms_message_private.h"
#include "kms_kmip_reader_writer_private.h"
#include "kms_kmip_result_reason_private.h"
@@ -209,6 +211,167 @@ fail:
return kms_request_str_detach (nullterminated);
}
+/*
+Example of a successful response to an Encrypt request:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+*/
+uint8_t *
+kms_kmip_response_get_iv (kms_response_t *res, size_t *datalen) {
+ kmip_reader_t *reader = NULL;
+ size_t pos;
+ size_t len;
+ uint8_t *data = NULL;
+ uint8_t *tmp;
+
+ if (!check_and_require_kmip (res)) {
+ goto fail;
+ }
+
+ if (!kms_kmip_response_ok (res)) {
+ goto fail;
+ }
+
+ reader = kmip_reader_new (res->kmip.data, res->kmip.len);
+
+ if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponseMessage)) {
+ KMS_ERROR (res,
+ "unable to find tag: %s",
+ kmip_tag_to_string (KMIP_TAG_ResponseMessage));
+ goto fail;
+ }
+
+ if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_BatchItem)) {
+ KMS_ERROR (res,
+ "unable to find tag: %s",
+ kmip_tag_to_string (KMIP_TAG_BatchItem));
+ goto fail;
+ }
+
+ if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponsePayload)) {
+ KMS_ERROR (res,
+ "unable to find tag: %s",
+ kmip_tag_to_string (KMIP_TAG_ResponsePayload));
+ goto fail;
+ }
+
+ if (!kmip_reader_find (reader, KMIP_TAG_IVCounterNonce, KMIP_ITEM_TYPE_ByteString, &pos, &len)) {
+ KMS_ERROR (res,
+ "unable to find tag: %s",
+ kmip_tag_to_string (KMIP_TAG_Data));
+ goto fail;
+ }
+
+ if (!kmip_reader_read_bytes (reader, &tmp, len)) {
+ KMS_ERROR (res, "unable to read data bytes");
+ goto fail;
+ }
+ data = malloc (len);
+ memcpy (data, tmp, len);
+ *datalen = len;
+
+ fail:
+ kmip_reader_destroy (reader);
+ return data;
+}
+
+/*
+Example of a successful response to a Decrypt request:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+*/
+uint8_t *
+kms_kmip_response_get_data (kms_response_t *res, size_t *datalen) {
+ kmip_reader_t *reader = NULL;
+ size_t pos;
+ size_t len;
+ uint8_t *data = NULL;
+ uint8_t *tmp;
+
+ if (!check_and_require_kmip (res)) {
+ goto fail;
+ }
+
+ if (!kms_kmip_response_ok (res)) {
+ goto fail;
+ }
+
+ reader = kmip_reader_new (res->kmip.data, res->kmip.len);
+
+ if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponseMessage)) {
+ KMS_ERROR (res,
+ "unable to find tag: %s",
+ kmip_tag_to_string (KMIP_TAG_ResponseMessage));
+ goto fail;
+ }
+
+ if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_BatchItem)) {
+ KMS_ERROR (res,
+ "unable to find tag: %s",
+ kmip_tag_to_string (KMIP_TAG_BatchItem));
+ goto fail;
+ }
+
+ if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponsePayload)) {
+ KMS_ERROR (res,
+ "unable to find tag: %s",
+ kmip_tag_to_string (KMIP_TAG_ResponsePayload));
+ goto fail;
+ }
+
+ if (!kmip_reader_find (reader, KMIP_TAG_Data, KMIP_ITEM_TYPE_ByteString, &pos, &len)) {
+ KMS_ERROR (res,
+ "unable to find tag: %s",
+ kmip_tag_to_string (KMIP_TAG_Data));
+ goto fail;
+ }
+
+ if (!kmip_reader_read_bytes (reader, &tmp, len)) {
+ KMS_ERROR (res, "unable to read data bytes");
+ goto fail;
+ }
+ data = malloc (len);
+ memcpy (data, tmp, len);
+ *datalen = len;
+
+ fail:
+ kmip_reader_destroy (reader);
+ return data;
+}
+
/*
Example of a successful response to a Get request:
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_tag_type_private.h b/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_tag_type_private.h
index 293ff1598ae..91df9f9daee 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_tag_type_private.h
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_kmip_tag_type_private.h
@@ -312,7 +312,8 @@
KMS_X (AlwaysSensitive, 0x420121) \
KMS_X (Extractable, 0x420122) \
KMS_X (NeverExtractable, 0x420123) \
- KMS_X_LAST (ReplaceExisting, 0x420124)
+ KMS_X (ReplaceExisting, 0x420124) \
+ KMS_X_LAST (Attributes, 0x420125)
/* clang-format on */
/* Generate an enum with each tag value. */
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_kmip_request.h b/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_kmip_request.h
index 68eac812089..c688ebcba3b 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_kmip_request.h
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_kmip_request.h
@@ -51,6 +51,23 @@ kms_kmip_request_activate_new (void *reserved, const char *unique_identifier);
KMS_MSG_EXPORT (kms_request_t *)
kms_kmip_request_get_new (void *reserved, const char *unique_identifier);
+KMS_MSG_EXPORT (kms_request_t *)
+kms_kmip_request_create_new (void *reserved);
+
+KMS_MSG_EXPORT (kms_request_t *)
+kms_kmip_request_encrypt_new (void *reserved,
+ const char *unique_identifier,
+ const uint8_t *plaintext,
+ size_t len);
+
+KMS_MSG_EXPORT (kms_request_t *)
+kms_kmip_request_decrypt_new (void *reserved,
+ const char *unique_identifier,
+ const uint8_t *ciphertext,
+ size_t len,
+ const uint8_t *iv,
+ size_t iv_len);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_kmip_response.h b/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_kmip_response.h
index 4c24c5120e7..a29a87f4b8f 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_kmip_response.h
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_kmip_response.h
@@ -37,4 +37,10 @@ kms_kmip_response_get_unique_identifier (kms_response_t *res);
KMS_MSG_EXPORT (uint8_t *)
kms_kmip_response_get_secretdata (kms_response_t *res, size_t *secretdatalen);
+KMS_MSG_EXPORT (uint8_t *)
+kms_kmip_response_get_data (kms_response_t *res, size_t *datalen);
+
+KMS_MSG_EXPORT (uint8_t *)
+kms_kmip_response_get_iv (kms_response_t *res, size_t *datalen);
+
#endif /* KMS_KMIP_RESPONSE_H */
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_response_parser.h b/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_response_parser.h
index 8f134bcf722..915f0ac55a1 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_response_parser.h
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_message/kms_response_parser.h
@@ -57,6 +57,9 @@ kms_response_parser_error (kms_response_parser_t *parser);
KMS_MSG_EXPORT (void)
kms_response_parser_destroy (kms_response_parser_t *parser);
+KMS_MSG_EXPORT (void)
+kms_response_parser_reset (kms_response_parser_t *parser);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_port.c b/src/third_party/libmongocrypt/dist/kms-message/src/kms_port.c
index ee9e6ed9c90..cf5f52f1f56 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_port.c
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_port.c
@@ -18,7 +18,8 @@
#if defined(_WIN32)
#include
#include
-char * kms_strndup (const char *src, size_t len)
+char *
+kms_strndup (const char *src, size_t len)
{
char *dst = (char *) malloc (len + 1);
if (!dst) {
@@ -30,4 +31,4 @@ char * kms_strndup (const char *src, size_t len)
return dst;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_request.c b/src/third_party/libmongocrypt/dist/kms-message/src/kms_request.c
index d0978d682f4..86f90a1a069 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_request.c
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_request.c
@@ -181,10 +181,12 @@ kms_request_set_date (kms_request_t *request, const struct tm *tm)
/* use current time */
time_t t;
time (&t);
-#ifdef _WIN32
+#if defined(KMS_MESSAGE_HAVE_GMTIME_R)
+ gmtime_r (&t, &tmp_tm);
+#elif defined(_MSC_VER)
gmtime_s (&tmp_tm, &t);
#else
- gmtime_r (&t, &tmp_tm);
+ tmp_tm = *gmtime (&t);
#endif
tm = &tmp_tm;
}
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_request_str.c b/src/third_party/libmongocrypt/dist/kms-message/src/kms_request_str.c
index 48ab8103025..57b2e4f9d34 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_request_str.c
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_request_str.c
@@ -328,7 +328,7 @@ kms_request_str_append_stripped (kms_request_str_t *str,
kms_request_str_reserve (str, appended->len);
- // msvcrt is unhappy when it gets non-ANSI characters in isspace
+ /* msvcrt is unhappy when it gets non-ANSI characters in isspace */
while (*src >= 0 && isspace (*src)) {
++src;
}
@@ -366,7 +366,7 @@ kms_request_str_append_hashed (_kms_crypto_t *crypto,
kms_request_str_t *str,
kms_request_str_t *appended)
{
- uint8_t hash[32];
+ uint8_t hash[32] = {0};
char *hex_chars;
if (!crypto->sha256 (crypto->ctx, appended->str, appended->len, hash)) {
diff --git a/src/third_party/libmongocrypt/dist/kms-message/src/kms_response_parser.c b/src/third_party/libmongocrypt/dist/kms-message/src/kms_response_parser.c
index 4d2e7a129f4..67336d70293 100644
--- a/src/third_party/libmongocrypt/dist/kms-message/src/kms_response_parser.c
+++ b/src/third_party/libmongocrypt/dist/kms-message/src/kms_response_parser.c
@@ -38,6 +38,14 @@ _parser_init (kms_response_parser_t *parser)
parser->kmip = NULL;
}
+void
+kms_response_parser_reset (kms_response_parser_t *parser)
+{
+ KMS_ASSERT(!parser->kmip); // KMIP is not-yet supported.
+ _parser_destroy(parser);
+ _parser_init(parser);
+}
+
kms_response_parser_t *
kms_response_parser_new (void)
{
diff --git a/src/third_party/libmongocrypt/dist/src/crypto/libcrypto.c b/src/third_party/libmongocrypt/dist/src/crypto/libcrypto.c
index dd1b57873a1..2fee38475d6 100644
--- a/src/third_party/libmongocrypt/dist/src/crypto/libcrypto.c
+++ b/src/third_party/libmongocrypt/dist/src/crypto/libcrypto.c
@@ -61,7 +61,7 @@ void _native_crypto_init(void) {
static bool _encrypt_with_cipher(const EVP_CIPHER *cipher, aes_256_args_t args) {
EVP_CIPHER_CTX *ctx;
bool ret = false;
- int intermediate_bytes_written;
+ int intermediate_bytes_written = 0;
mongocrypt_status_t *status = args.status;
ctx = EVP_CIPHER_CTX_new();
@@ -71,8 +71,8 @@ static bool _encrypt_with_cipher(const EVP_CIPHER *cipher, aes_256_args_t args)
BSON_ASSERT(args.out);
BSON_ASSERT(ctx);
BSON_ASSERT(cipher);
- BSON_ASSERT(NULL == args.iv || EVP_CIPHER_iv_length(cipher) == args.iv->len);
- BSON_ASSERT(EVP_CIPHER_key_length(cipher) == args.key->len);
+ BSON_ASSERT(NULL == args.iv || (uint32_t)EVP_CIPHER_iv_length(cipher) == args.iv->len);
+ BSON_ASSERT((uint32_t)EVP_CIPHER_key_length(cipher) == args.key->len);
BSON_ASSERT(args.in->len <= INT_MAX);
if (!EVP_EncryptInit_ex(ctx, cipher, NULL /* engine */, args.key->data, NULL == args.iv ? NULL : args.iv->data)) {
@@ -89,6 +89,7 @@ static bool _encrypt_with_cipher(const EVP_CIPHER *cipher, aes_256_args_t args)
goto done;
}
+ BSON_ASSERT(intermediate_bytes_written >= 0 && (uint64_t)intermediate_bytes_written <= UINT32_MAX);
/* intermediate_bytes_written cannot be negative, so int -> uint32_t is OK */
*args.bytes_written = (uint32_t)intermediate_bytes_written;
@@ -97,7 +98,7 @@ static bool _encrypt_with_cipher(const EVP_CIPHER *cipher, aes_256_args_t args)
goto done;
}
- BSON_ASSERT(UINT32_MAX - *args.bytes_written >= intermediate_bytes_written);
+ BSON_ASSERT(UINT32_MAX - *args.bytes_written >= (uint32_t)intermediate_bytes_written);
*args.bytes_written += (uint32_t)intermediate_bytes_written;
ret = true;
@@ -116,18 +117,19 @@ done:
static bool _decrypt_with_cipher(const EVP_CIPHER *cipher, aes_256_args_t args) {
EVP_CIPHER_CTX *ctx;
bool ret = false;
- int intermediate_bytes_written;
+ int intermediate_bytes_written = 0;
mongocrypt_status_t *status = args.status;
ctx = EVP_CIPHER_CTX_new();
+ BSON_ASSERT(ctx);
BSON_ASSERT_PARAM(cipher);
BSON_ASSERT(args.iv);
BSON_ASSERT(args.key);
BSON_ASSERT(args.in);
BSON_ASSERT(args.out);
- BSON_ASSERT(EVP_CIPHER_iv_length(cipher) == args.iv->len);
- BSON_ASSERT(EVP_CIPHER_key_length(cipher) == args.key->len);
+ BSON_ASSERT((uint32_t)EVP_CIPHER_iv_length(cipher) == args.iv->len);
+ BSON_ASSERT((uint32_t)EVP_CIPHER_key_length(cipher) == args.key->len);
BSON_ASSERT(args.in->len <= INT_MAX);
if (!EVP_DecryptInit_ex(ctx, cipher, NULL /* engine */, args.key->data, args.iv->data)) {
@@ -145,6 +147,7 @@ static bool _decrypt_with_cipher(const EVP_CIPHER *cipher, aes_256_args_t args)
goto done;
}
+ BSON_ASSERT(intermediate_bytes_written >= 0 && (uint64_t)intermediate_bytes_written <= UINT32_MAX);
/* intermediate_bytes_written cannot be negative, so int -> uint32_t is OK */
*args.bytes_written = (uint32_t)intermediate_bytes_written;
@@ -153,7 +156,7 @@ static bool _decrypt_with_cipher(const EVP_CIPHER *cipher, aes_256_args_t args)
goto done;
}
- BSON_ASSERT(UINT32_MAX - *args.bytes_written >= intermediate_bytes_written);
+ BSON_ASSERT(UINT32_MAX - *args.bytes_written >= (uint32_t)intermediate_bytes_written);
*args.bytes_written += (uint32_t)intermediate_bytes_written;
ret = true;
@@ -203,9 +206,9 @@ static bool _hmac_with_hash(const EVP_MD *hash,
ctx = HMAC_CTX_new();
- if (out->len != EVP_MD_size(hash)) {
+ if (out->len != (uint32_t)EVP_MD_size(hash)) {
CLIENT_ERR("out does not contain %d bytes", EVP_MD_size(hash));
- return false;
+ goto done;
}
if (!HMAC_Init_ex(ctx, key->data, (int)key->len, hash, NULL /* engine */)) {
diff --git a/src/third_party/libmongocrypt/dist/src/mc-cmp-private.h b/src/third_party/libmongocrypt/dist/src/mc-cmp-private.h
new file mode 100644
index 00000000000..0ed34b076dc
--- /dev/null
+++ b/src/third_party/libmongocrypt/dist/src/mc-cmp-private.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2018-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// `cmp.h` is a modified copy of `bson-cmp.h` from libbson 1.28.0.
+#ifndef MC_CMP_H
+#define MC_CMP_H
+
+#include /* ssize_t + BSON_CONCAT */
+
+#include
+#include
+#include
+
+/* Sanity check: ensure ssize_t limits are as expected relative to size_t. */
+BSON_STATIC_ASSERT2(ssize_t_size_min_check, SSIZE_MIN + 1 == -SSIZE_MAX);
+BSON_STATIC_ASSERT2(ssize_t_size_max_check, (size_t)SSIZE_MAX <= SIZE_MAX);
+
+/* Based on the "Safe Integral Comparisons" proposal merged in C++20:
+ * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0586r2.html
+ *
+ * Due to lack of type deduction in C, relational comparison functions (e.g.
+ * `cmp_less`) are defined in sets of four "functions" according to the
+ * signedness of each value argument, e.g.:
+ * - mc_cmp_less_ss (signed-value, signed-value)
+ * - mc_cmp_less_uu (unsigned-value, unsigned-value)
+ * - mc_cmp_less_su (signed-value, unsigned-value)
+ * - mc_cmp_less_us (unsigned-value, signed-value)
+ *
+ * Similarly, the `in_range` function is defined as a set of two "functions"
+ * according to the signedness of the value argument:
+ * - mc_in_range_signed (Type, signed-value)
+ * - mc_in_range_unsigned (Type, unsigned-value)
+ *
+ * The user must take care to use the correct signedness for the provided
+ * argument(s). Enabling compiler warnings for implicit sign conversions is
+ * recommended.
+ */
+
+#define MC_CMP_SET(op, ss, uu, su, us) \
+ static BSON_INLINE bool BSON_CONCAT3(mc_cmp_, op, _ss)(int64_t t, int64_t u) { return (ss); } \
+ \
+ static BSON_INLINE bool BSON_CONCAT3(mc_cmp_, op, _uu)(uint64_t t, uint64_t u) { return (uu); } \
+ \
+ static BSON_INLINE bool BSON_CONCAT3(mc_cmp_, op, _su)(int64_t t, uint64_t u) { return (su); } \
+ \
+ static BSON_INLINE bool BSON_CONCAT3(mc_cmp_, op, _us)(uint64_t t, int64_t u) { return (us); }
+
+MC_CMP_SET(equal, t == u, t == u, t < 0 ? false : (uint64_t)(t) == u, u < 0 ? false : t == (uint64_t)(u))
+
+MC_CMP_SET(not_equal, !mc_cmp_equal_ss(t, u), !mc_cmp_equal_uu(t, u), !mc_cmp_equal_su(t, u), !mc_cmp_equal_us(t, u))
+
+MC_CMP_SET(less, t < u, t < u, t < 0 ? true : (uint64_t)(t) < u, u < 0 ? false : t < (uint64_t)(u))
+
+MC_CMP_SET(greater, mc_cmp_less_ss(u, t), mc_cmp_less_uu(u, t), mc_cmp_less_us(u, t), mc_cmp_less_su(u, t))
+
+MC_CMP_SET(less_equal,
+ !mc_cmp_greater_ss(t, u),
+ !mc_cmp_greater_uu(t, u),
+ !mc_cmp_greater_su(t, u),
+ !mc_cmp_greater_us(t, u))
+
+MC_CMP_SET(greater_equal, !mc_cmp_less_ss(t, u), !mc_cmp_less_uu(t, u), !mc_cmp_less_su(t, u), !mc_cmp_less_us(t, u))
+
+#undef MC_CMP_SET
+
+/* Return true if the given value is within the range of the corresponding
+ * signed type. The suffix must match the signedness of the given value. */
+#define MC_IN_RANGE_SET_SIGNED(Type, min, max) \
+ static BSON_INLINE bool BSON_CONCAT3(mc_in_range, _##Type, _signed)(int64_t value) { \
+ return mc_cmp_greater_equal_ss(value, min) && mc_cmp_less_equal_ss(value, max); \
+ } \
+ \
+ static BSON_INLINE bool BSON_CONCAT3(mc_in_range, _##Type, _unsigned)(uint64_t value) { \
+ return mc_cmp_greater_equal_us(value, min) && mc_cmp_less_equal_us(value, max); \
+ }
+
+/* Return true if the given value is within the range of the corresponding
+ * unsigned type. The suffix must match the signedness of the given value. */
+#define MC_IN_RANGE_SET_UNSIGNED(Type, max) \
+ static BSON_INLINE bool BSON_CONCAT3(mc_in_range, _##Type, _signed)(int64_t value) { \
+ return mc_cmp_greater_equal_su(value, 0u) && mc_cmp_less_equal_su(value, max); \
+ } \
+ \
+ static BSON_INLINE bool BSON_CONCAT3(mc_in_range, _##Type, _unsigned)(uint64_t value) { \
+ return mc_cmp_less_equal_uu(value, max); \
+ }
+
+MC_IN_RANGE_SET_SIGNED(signed_char, SCHAR_MIN, SCHAR_MAX)
+MC_IN_RANGE_SET_SIGNED(short, SHRT_MIN, SHRT_MAX)
+MC_IN_RANGE_SET_SIGNED(int, INT_MIN, INT_MAX)
+MC_IN_RANGE_SET_SIGNED(long, LONG_MIN, LONG_MAX)
+MC_IN_RANGE_SET_SIGNED(long_long, LLONG_MIN, LLONG_MAX)
+
+MC_IN_RANGE_SET_UNSIGNED(unsigned_char, UCHAR_MAX)
+MC_IN_RANGE_SET_UNSIGNED(unsigned_short, USHRT_MAX)
+MC_IN_RANGE_SET_UNSIGNED(unsigned_int, UINT_MAX)
+MC_IN_RANGE_SET_UNSIGNED(unsigned_long, ULONG_MAX)
+MC_IN_RANGE_SET_UNSIGNED(unsigned_long_long, ULLONG_MAX)
+
+MC_IN_RANGE_SET_SIGNED(int8_t, INT8_MIN, INT8_MAX)
+MC_IN_RANGE_SET_SIGNED(int16_t, INT16_MIN, INT16_MAX)
+MC_IN_RANGE_SET_SIGNED(int32_t, INT32_MIN, INT32_MAX)
+MC_IN_RANGE_SET_SIGNED(int64_t, INT64_MIN, INT64_MAX)
+
+MC_IN_RANGE_SET_UNSIGNED(uint8_t, UINT8_MAX)
+MC_IN_RANGE_SET_UNSIGNED(uint16_t, UINT16_MAX)
+MC_IN_RANGE_SET_UNSIGNED(uint32_t, UINT32_MAX)
+MC_IN_RANGE_SET_UNSIGNED(uint64_t, UINT64_MAX)
+
+MC_IN_RANGE_SET_SIGNED(ssize_t, SSIZE_MIN, SSIZE_MAX)
+MC_IN_RANGE_SET_UNSIGNED(size_t, SIZE_MAX)
+
+#undef MC_IN_RANGE_SET_SIGNED
+#undef MC_IN_RANGE_SET_UNSIGNED
+
+/* Return true if the value with *signed* type is in the representable range of
+ * Type and false otherwise. */
+#define mc_in_range_signed(Type, value) BSON_CONCAT3(mc_in_range, _##Type, _signed)(value)
+
+/* Return true if the value with *unsigned* type is in the representable range
+ * of Type and false otherwise. */
+#define mc_in_range_unsigned(Type, value) BSON_CONCAT3(mc_in_range, _##Type, _unsigned)(value)
+
+#endif /* MC_CMP_H */
diff --git a/src/third_party/libmongocrypt/dist/src/mc-dec128.h b/src/third_party/libmongocrypt/dist/src/mc-dec128.h
index 4495714146e..fd994874fbf 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-dec128.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-dec128.h
@@ -607,7 +607,7 @@ static inline char *mc_dec128_to_new_decimal_string(mc_dec128 d) {
}
}
-static inline mc_dec128 mc_dec128_from_bson_iter(bson_iter_t *it) {
+static inline mc_dec128 mc_dec128_from_bson_iter(const bson_iter_t *it) {
bson_decimal128_t b;
if (!bson_iter_decimal128(it, &b)) {
mc_dec128 nan = MC_DEC128_POSITIVE_NAN;
diff --git a/src/third_party/libmongocrypt/dist/src/mc-efc-private.h b/src/third_party/libmongocrypt/dist/src/mc-efc-private.h
index c4ff8060585..f9e5c59faf5 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-efc-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-efc-private.h
@@ -19,8 +19,19 @@
#include "mongocrypt-buffer-private.h"
#include
+typedef enum _supported_query_type_flags {
+ // No queries supported
+ SUPPORTS_NO_QUERIES = 0,
+ // Equality query supported
+ SUPPORTS_EQUALITY_QUERIES = 1 << 0,
+ // Range query supported
+ SUPPORTS_RANGE_QUERIES = 1 << 1,
+ // Range preview query supported
+ SUPPORTS_RANGE_PREVIEW_DEPRECATED_QUERIES = 1 << 2,
+} supported_query_type_flags;
+
typedef struct _mc_EncryptedField_t {
- bool has_queries;
+ supported_query_type_flags supported_queries;
_mongocrypt_buffer_t keyId;
const char *path;
struct _mc_EncryptedField_t *next;
@@ -37,7 +48,10 @@ typedef struct {
* into @efc. Fields are copied from @efc_bson. It is OK to free efc_bson after
* this call. Fields are appended in reverse order to @efc->fields. Extra
* unrecognized fields are not considered an error for forward compatibility. */
-bool mc_EncryptedFieldConfig_parse(mc_EncryptedFieldConfig_t *efc, const bson_t *efc_bson, mongocrypt_status_t *status);
+bool mc_EncryptedFieldConfig_parse(mc_EncryptedFieldConfig_t *efc,
+ const bson_t *efc_bson,
+ mongocrypt_status_t *status,
+ bool use_range_v2);
void mc_EncryptedFieldConfig_cleanup(mc_EncryptedFieldConfig_t *efc);
diff --git a/src/third_party/libmongocrypt/dist/src/mc-efc.c b/src/third_party/libmongocrypt/dist/src/mc-efc.c
index dd75e0fc0f1..1a5e6f7dae2 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-efc.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-efc.c
@@ -16,12 +16,64 @@
#include "mc-efc-private.h"
+#include "mlib/str.h"
#include "mongocrypt-private.h"
#include "mongocrypt-util-private.h" // mc_iter_document_as_bson
+static bool _parse_query_type_string(const char *queryType, supported_query_type_flags *out) {
+ BSON_ASSERT_PARAM(queryType);
+ BSON_ASSERT_PARAM(out);
+
+ mstr_view qtv = mstrv_view_cstr(queryType);
+
+ if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_EQUALITY_STR), qtv)) {
+ *out = SUPPORTS_EQUALITY_QUERIES;
+ } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_RANGE_STR), qtv)) {
+ *out = SUPPORTS_RANGE_QUERIES;
+ } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED_STR), qtv)) {
+ *out = SUPPORTS_RANGE_PREVIEW_DEPRECATED_QUERIES;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+_parse_supported_query_types(bson_iter_t *iter, supported_query_type_flags *out, mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(iter);
+ BSON_ASSERT_PARAM(out);
+ if (!BSON_ITER_HOLDS_DOCUMENT(iter)) {
+ CLIENT_ERR("When parsing supported query types: Expected type document, got: %d", bson_iter_type(iter));
+ return false;
+ }
+
+ bson_t query_doc;
+ if (!mc_iter_document_as_bson(iter, &query_doc, status)) {
+ return false;
+ }
+ bson_iter_t query_type_iter;
+ if (!bson_iter_init_find(&query_type_iter, &query_doc, "queryType")) {
+ CLIENT_ERR("When parsing supported query types: Unable to find 'queryType' in query document");
+ return false;
+ }
+ if (!BSON_ITER_HOLDS_UTF8(&query_type_iter)) {
+ CLIENT_ERR("When parsing supported query types: Expected 'queryType' to be type UTF-8, got: %d",
+ bson_iter_type(&query_type_iter));
+ return false;
+ }
+ const char *queryType = bson_iter_utf8(&query_type_iter, NULL /* length */);
+ if (!_parse_query_type_string(queryType, out)) {
+ CLIENT_ERR("When parsing supported query types: Did not recognize query type '%s'", queryType);
+ return false;
+ }
+ return true;
+}
+
/* _parse_field parses and prepends one field document to efc->fields. */
-static bool _parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocrypt_status_t *status) {
- bool has_queries = false;
+static bool
+_parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocrypt_status_t *status, bool use_range_v2) {
+ supported_query_type_flags query_types = SUPPORTS_NO_QUERIES;
bson_iter_t field_iter;
BSON_ASSERT_PARAM(efc);
@@ -53,7 +105,42 @@ static bool _parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocry
field_path = bson_iter_utf8(&field_iter, NULL /* length */);
if (bson_iter_init_find(&field_iter, field, "queries")) {
- has_queries = true;
+ if (BSON_ITER_HOLDS_ARRAY(&field_iter)) {
+ // Multiple queries, iterate through and grab all query types.
+ uint32_t queries_buf_len;
+ const uint8_t *queries_buf;
+ bson_t queries_arr;
+ bson_iter_array(&field_iter, &queries_buf_len, &queries_buf);
+ if (!bson_init_static(&queries_arr, queries_buf, queries_buf_len)) {
+ CLIENT_ERR("Failed to parse 'queries' field");
+ return false;
+ }
+
+ bson_iter_t queries_iter;
+ bson_iter_init(&queries_iter, &queries_arr);
+ while (bson_iter_next(&queries_iter)) {
+ supported_query_type_flags flag;
+ if (!_parse_supported_query_types(&queries_iter, &flag, status)) {
+ return false;
+ }
+ query_types |= flag;
+ }
+ } else {
+ supported_query_type_flags flag;
+ if (!_parse_supported_query_types(&field_iter, &flag, status)) {
+ return false;
+ }
+ query_types |= flag;
+ }
+ }
+
+ if (query_types & SUPPORTS_RANGE_PREVIEW_DEPRECATED_QUERIES && use_range_v2) {
+ // When rangev2 is enabled ("range") error if "rangePreview" is included.
+ // This check is intended to give an easier-to-understand earlier error.
+ CLIENT_ERR("Cannot use field '%s' with 'rangePreview' queries. 'rangePreview' is unsupported. Use 'range' "
+ "instead. 'range' is not compatible with 'rangePreview' and requires recreating the collection.",
+ field_path);
+ return false;
}
/* Prepend a new mc_EncryptedField_t */
@@ -61,7 +148,7 @@ static bool _parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocry
_mongocrypt_buffer_copy_to(&field_keyid, &ef->keyId);
ef->path = bson_strdup(field_path);
ef->next = efc->fields;
- ef->has_queries = has_queries;
+ ef->supported_queries = query_types;
efc->fields = ef;
return true;
@@ -69,7 +156,8 @@ static bool _parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocry
bool mc_EncryptedFieldConfig_parse(mc_EncryptedFieldConfig_t *efc,
const bson_t *efc_bson,
- mongocrypt_status_t *status) {
+ mongocrypt_status_t *status,
+ bool use_range_v2) {
bson_iter_t iter;
BSON_ASSERT_PARAM(efc);
@@ -93,7 +181,7 @@ bool mc_EncryptedFieldConfig_parse(mc_EncryptedFieldConfig_t *efc,
if (!mc_iter_document_as_bson(&iter, &field, status)) {
return false;
}
- if (!_parse_field(efc, &field, status)) {
+ if (!_parse_field(efc, &field, status, use_range_v2)) {
return false;
}
}
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle-blob-subtype-private.h b/src/third_party/libmongocrypt/dist/src/mc-fle-blob-subtype-private.h
index 949b22f29d3..3fda8e97413 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle-blob-subtype-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle-blob-subtype-private.h
@@ -19,7 +19,7 @@
/* FLE Blob Subtype is the first byte of a BSON Binary Subtype 6.
* FLE1 Blob Subtypes are defined in:
- * https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/subtype6.rst
+ * https://github.com/mongodb/specifications/blob/master/source/bson-binary-encrypted/binary-encrypted.md
* FLE2 Blob Subtypes are currently defined in:
* https://github.com/markbenvenuto/mongo-enterprise-modules/blob/fle2/fle_protocol.md#reference-bindata-6-subtypes.
*/
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-encryption-placeholder-private.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-encryption-placeholder-private.h
index d1ec121375e..b2168dadaa3 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-encryption-placeholder-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-encryption-placeholder-private.h
@@ -49,9 +49,16 @@ typedef struct {
bson_iter_t indexMax;
// precision determines the number of digits after the decimal point for
// floating point values.
- mc_optional_uint32_t precision;
+ mc_optional_int32_t precision;
+ // trimFactor determines how many root levels of the hypergraph to trim.
+ mc_optional_int32_t trimFactor;
} mc_FLE2RangeFindSpecEdgesInfo_t;
+// `mc_FLE2RangeFindSpecEdgesInfo_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof_mc_FLE2RangeFindSpecEdgesInfo_t,
+ BSON_ALIGNOF(mc_FLE2RangeFindSpecEdgesInfo_t) >= BSON_ALIGNOF(bson_iter_t));
+
/** FLE2RangeFindSpec represents the range find specification that is encoded
* inside of a FLE2EncryptionPlaceholder. See
* https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/fle_field_schema.idl
@@ -74,7 +81,15 @@ typedef struct {
mc_FLE2RangeOperator_t secondOperator;
} mc_FLE2RangeFindSpec_t;
-bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status);
+// `mc_FLE2RangeFindSpec_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof_mc_FLE2RangeFindSpec_t,
+ BSON_ALIGNOF(mc_FLE2RangeFindSpec_t) >= BSON_ALIGNOF(mc_FLE2RangeFindSpecEdgesInfo_t));
+
+bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out,
+ const bson_iter_t *in,
+ bool use_range_v2,
+ mongocrypt_status_t *status);
/** mc_FLE2RangeInsertSpec_t represents the range insert specification that is
* encoded inside of a FLE2EncryptionPlaceholder. See
@@ -89,10 +104,20 @@ typedef struct {
bson_iter_t max;
// precision determines the number of digits after the decimal point for
// floating point values.
- mc_optional_uint32_t precision;
+ mc_optional_int32_t precision;
+ // trimFactor determines how many root levels of the hypergraph to trim.
+ mc_optional_int32_t trimFactor;
} mc_FLE2RangeInsertSpec_t;
-bool mc_FLE2RangeInsertSpec_parse(mc_FLE2RangeInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status);
+// `mc_FLE2RangeInsertSpec_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof_mc_FLE2RangeInsertSpec_t,
+ BSON_ALIGNOF(mc_FLE2RangeInsertSpec_t) >= BSON_ALIGNOF(bson_iter_t));
+
+bool mc_FLE2RangeInsertSpec_parse(mc_FLE2RangeInsertSpec_t *out,
+ const bson_iter_t *in,
+ bool use_range_v2,
+ mongocrypt_status_t *status);
/** FLE2EncryptionPlaceholder implements Encryption BinData (subtype 6)
* sub-subtype 0, the intent-to-encrypt mapping. Contains a value to encrypt and
@@ -116,11 +141,16 @@ typedef struct {
bson_iter_t v_iter;
_mongocrypt_buffer_t index_key_id;
_mongocrypt_buffer_t user_key_id;
- int64_t maxContentionCounter;
+ int64_t maxContentionFactor;
// sparsity is the Queryable Encryption range hypergraph sparsity factor
int64_t sparsity;
} mc_FLE2EncryptionPlaceholder_t;
+// `mc_FLE2EncryptionPlaceholder_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof_mc_FLE2EncryptionPlaceholder_t,
+ BSON_ALIGNOF(mc_FLE2EncryptionPlaceholder_t) >= BSON_ALIGNOF(bson_iter_t));
+
void mc_FLE2EncryptionPlaceholder_init(mc_FLE2EncryptionPlaceholder_t *placeholder);
bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out,
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-encryption-placeholder.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-encryption-placeholder.c
index 4a63b6b235c..fc839b88752 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-encryption-placeholder.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-encryption-placeholder.c
@@ -22,11 +22,14 @@
#include "mongocrypt-buffer-private.h"
#include "mongocrypt.h"
+#define CLIENT_ERR_PREFIXED_HELPER(Prefix, ErrorString, ...) CLIENT_ERR(Prefix ": " ErrorString, ##__VA_ARGS__)
+#define CLIENT_ERR_PREFIXED(ErrorString, ...) CLIENT_ERR_PREFIXED_HELPER(ERROR_PREFIX, ErrorString, ##__VA_ARGS__)
+
// Common logic for testing field name, tracking duplication, and presence.
#define IF_FIELD(Name) \
if (0 == strcmp(field, #Name)) { \
if (has_##Name) { \
- CLIENT_ERR("Duplicate field '" #Name "' in placeholder bson"); \
+ CLIENT_ERR_PREFIXED("Duplicate field '" #Name "' in placeholder bson"); \
goto fail; \
} \
has_##Name = true;
@@ -37,7 +40,7 @@
#define CHECK_HAS(Name) \
if (!has_##Name) { \
- CLIENT_ERR("Missing field '" #Name "' in placeholder"); \
+ CLIENT_ERR_PREFIXED("Missing field '" #Name "' in placeholder"); \
goto fail; \
}
@@ -45,10 +48,12 @@ void mc_FLE2EncryptionPlaceholder_init(mc_FLE2EncryptionPlaceholder_t *placehold
memset(placeholder, 0, sizeof(mc_FLE2EncryptionPlaceholder_t));
}
+#define ERROR_PREFIX "Error parsing FLE2EncryptionPlaceholder"
+
bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out,
const bson_t *in,
mongocrypt_status_t *status) {
- bson_iter_t iter;
+ bson_iter_t iter = {0};
bool has_t = false, has_a = false, has_v = false, has_cm = false;
bool has_ki = false, has_ku = false;
bool has_s = false;
@@ -58,7 +63,7 @@ bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out,
mc_FLE2EncryptionPlaceholder_init(out);
if (!bson_validate(in, BSON_VALIDATE_NONE, NULL) || !bson_iter_init(&iter, in)) {
- CLIENT_ERR("invalid BSON");
+ CLIENT_ERR_PREFIXED("invalid BSON");
return false;
}
@@ -69,12 +74,12 @@ bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out,
IF_FIELD(t) {
int32_t type;
if (!BSON_ITER_HOLDS_INT32(&iter)) {
- CLIENT_ERR("invalid marking, 't' must be an int32");
+ CLIENT_ERR_PREFIXED("invalid marking, 't' must be an int32");
goto fail;
}
type = bson_iter_int32(&iter);
if ((type != MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT) && (type != MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND)) {
- CLIENT_ERR("invalid placeholder type value: %d", type);
+ CLIENT_ERR_PREFIXED("invalid placeholder type value: %d", type);
goto fail;
}
out->type = (mongocrypt_fle2_placeholder_type_t)type;
@@ -84,13 +89,13 @@ bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out,
IF_FIELD(a) {
int32_t algorithm;
if (!BSON_ITER_HOLDS_INT32(&iter)) {
- CLIENT_ERR("invalid marking, 'a' must be an int32");
+ CLIENT_ERR_PREFIXED("invalid marking, 'a' must be an int32");
goto fail;
}
algorithm = bson_iter_int32(&iter);
if (algorithm != MONGOCRYPT_FLE2_ALGORITHM_UNINDEXED && algorithm != MONGOCRYPT_FLE2_ALGORITHM_EQUALITY
&& algorithm != MONGOCRYPT_FLE2_ALGORITHM_RANGE) {
- CLIENT_ERR("invalid algorithm value: %d", algorithm);
+ CLIENT_ERR_PREFIXED("invalid algorithm value: %d", algorithm);
goto fail;
}
out->algorithm = (mongocrypt_fle2_encryption_algorithm_t)algorithm;
@@ -99,7 +104,7 @@ bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out,
IF_FIELD(ki) {
if (!_mongocrypt_buffer_from_uuid_iter(&out->index_key_id, &iter)) {
- CLIENT_ERR("index key id must be a UUID");
+ CLIENT_ERR_PREFIXED("index key id must be a UUID");
goto fail;
}
}
@@ -107,7 +112,7 @@ bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out,
IF_FIELD(ku) {
if (!_mongocrypt_buffer_from_uuid_iter(&out->user_key_id, &iter)) {
- CLIENT_ERR("user key id must be a UUID");
+ CLIENT_ERR_PREFIXED("user key id must be a UUID");
goto fail;
}
}
@@ -120,11 +125,11 @@ bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out,
IF_FIELD(cm) {
if (!BSON_ITER_HOLDS_INT64(&iter)) {
- CLIENT_ERR("invalid marking, 'cm' must be an int64");
+ CLIENT_ERR_PREFIXED("invalid marking, 'cm' must be an int64");
goto fail;
}
- out->maxContentionCounter = bson_iter_int64(&iter);
- if (!mc_validate_contention(out->maxContentionCounter, status)) {
+ out->maxContentionFactor = bson_iter_int64(&iter);
+ if (!mc_validate_contention(out->maxContentionFactor, status)) {
goto fail;
}
}
@@ -132,7 +137,7 @@ bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out,
IF_FIELD(s) {
if (!BSON_ITER_HOLDS_INT64(&iter)) {
- CLIENT_ERR("invalid marking, 's' must be an int64");
+ CLIENT_ERR_PREFIXED("invalid marking, 's' must be an int64");
goto fail;
}
out->sparsity = bson_iter_int64(&iter);
@@ -168,37 +173,47 @@ void mc_FLE2EncryptionPlaceholder_cleanup(mc_FLE2EncryptionPlaceholder_t *placeh
mc_FLE2EncryptionPlaceholder_init(placeholder);
}
+#undef ERROR_PREFIX
+#define ERROR_PREFIX "Error validating contention"
+
bool mc_validate_contention(int64_t contention, mongocrypt_status_t *status) {
if (contention < 0) {
- CLIENT_ERR("contention must be non-negative, got: %" PRId64, contention);
+ CLIENT_ERR_PREFIXED("contention must be non-negative, got: %" PRId64, contention);
return false;
}
if (contention == INT64_MAX) {
- CLIENT_ERR("contention must be < INT64_MAX, got: %" PRId64, contention);
+ CLIENT_ERR_PREFIXED("contention must be < INT64_MAX, got: %" PRId64, contention);
return false;
}
return true;
}
+#undef ERROR_PREFIX
+#define ERROR_PREFIX "Error validating sparsity"
+
bool mc_validate_sparsity(int64_t sparsity, mongocrypt_status_t *status) {
if (sparsity < 0) {
- CLIENT_ERR("sparsity must be non-negative, got: %" PRId64, sparsity);
+ CLIENT_ERR_PREFIXED("sparsity must be non-negative, got: %" PRId64, sparsity);
return false;
}
// mc_getEdgesInt expects a size_t sparsity.
- if (sparsity >= SIZE_MAX) {
- CLIENT_ERR("sparsity must be < %zu, got: %" PRId64, SIZE_MAX, sparsity);
+ if ((uint64_t)sparsity >= SIZE_MAX) {
+ CLIENT_ERR_PREFIXED("sparsity must be < %zu, got: %" PRId64, SIZE_MAX, sparsity);
return false;
}
return true;
}
+#undef ERROR_PREFIX
+#define ERROR_PREFIX "Error parsing FLE2RangeFindSpecEdgesInfo"
+
static bool mc_FLE2RangeFindSpecEdgesInfo_parse(mc_FLE2RangeFindSpecEdgesInfo_t *out,
const bson_iter_t *in,
+ bool use_range_v2,
mongocrypt_status_t *status) {
bson_iter_t iter;
bool has_lowerBound = false, has_lbIncluded = false, has_upperBound = false, has_ubIncluded = false,
- has_indexMin = false, has_indexMax = false, has_precision = false;
+ has_indexMin = false, has_indexMax = false, has_precision = false, has_trimFactor = false;
BSON_ASSERT_PARAM(out);
BSON_ASSERT_PARAM(in);
@@ -206,8 +221,7 @@ static bool mc_FLE2RangeFindSpecEdgesInfo_parse(mc_FLE2RangeFindSpecEdgesInfo_t
iter = *in;
if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) {
- CLIENT_ERR("invalid FLE2RangeFindSpecEdgesInfo: must be an iterator to "
- "a document");
+ CLIENT_ERR_PREFIXED("must be an iterator to a document");
return false;
}
bson_iter_recurse(&iter, &iter);
@@ -223,8 +237,7 @@ static bool mc_FLE2RangeFindSpecEdgesInfo_parse(mc_FLE2RangeFindSpecEdgesInfo_t
IF_FIELD(lbIncluded) {
if (!BSON_ITER_HOLDS_BOOL(&iter)) {
- CLIENT_ERR("invalid FLE2RangeFindSpecEdgesInfo: 'lbIncluded' must "
- "be a bool");
+ CLIENT_ERR_PREFIXED("'lbIncluded' must be a bool");
goto fail;
}
out->lbIncluded = bson_iter_bool(&iter);
@@ -238,8 +251,7 @@ static bool mc_FLE2RangeFindSpecEdgesInfo_parse(mc_FLE2RangeFindSpecEdgesInfo_t
IF_FIELD(ubIncluded) {
if (!BSON_ITER_HOLDS_BOOL(&iter)) {
- CLIENT_ERR("invalid FLE2RangeFindSpecEdgesInfo: 'ubIncluded' must "
- "be a bool");
+ CLIENT_ERR_PREFIXED("'ubIncluded' must be a bool");
goto fail;
}
out->ubIncluded = bson_iter_bool(&iter);
@@ -258,18 +270,31 @@ static bool mc_FLE2RangeFindSpecEdgesInfo_parse(mc_FLE2RangeFindSpecEdgesInfo_t
IF_FIELD(precision) {
if (!BSON_ITER_HOLDS_INT32(&iter)) {
- CLIENT_ERR("invalid FLE2RangeFindSpecEdgesInfo: 'precision' must "
- "be an int32");
+ CLIENT_ERR_PREFIXED("'precision' must be an int32");
goto fail;
}
int32_t val = bson_iter_int32(&iter);
if (val < 0) {
- CLIENT_ERR("invalid FLE2RangeFindSpecEdgesInfo: 'precision' must be"
- "non-negative");
+ CLIENT_ERR_PREFIXED("'precision' must be non-negative");
goto fail;
}
- out->precision = OPT_U32((uint32_t)val);
+ out->precision = OPT_I32(val);
+ }
+ END_IF_FIELD
+
+ IF_FIELD(trimFactor) {
+ if (!BSON_ITER_HOLDS_INT32(&iter)) {
+ CLIENT_ERR_PREFIXED("'trimFactor' must be an int32");
+ goto fail;
+ }
+ int32_t val = bson_iter_int32(&iter);
+ if (val < 0) {
+ CLIENT_ERR_PREFIXED("'trimFactor' must be non-negative");
+ goto fail;
+ }
+
+ out->trimFactor = OPT_I32(val);
}
END_IF_FIELD
}
@@ -282,13 +307,25 @@ static bool mc_FLE2RangeFindSpecEdgesInfo_parse(mc_FLE2RangeFindSpecEdgesInfo_t
CHECK_HAS(indexMax)
// Do not error if precision is not present. Precision optional and only
// applies to double/decimal128.
+
+ if (!use_range_v2 && out->trimFactor.set) {
+ CLIENT_ERR_PREFIXED("'trimFactor' is not supported for QE range v1");
+ return false;
+ }
+
return true;
fail:
return false;
}
-bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status) {
+#undef ERROR_PREFIX
+#define ERROR_PREFIX "Error parsing FLE2RangeFindSpec"
+
+bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out,
+ const bson_iter_t *in,
+ bool use_range_v2,
+ mongocrypt_status_t *status) {
BSON_ASSERT_PARAM(out);
BSON_ASSERT_PARAM(in);
@@ -298,7 +335,7 @@ bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out, const bson_iter_t *
*out = (mc_FLE2RangeFindSpec_t){{{{0}}}};
if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) {
- CLIENT_ERR("invalid FLE2RangeFindSpec: must be an iterator to a document");
+ CLIENT_ERR_PREFIXED("must be an iterator to a document");
return false;
}
bson_iter_recurse(&iter, &iter);
@@ -308,7 +345,7 @@ bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out, const bson_iter_t *
BSON_ASSERT(field);
IF_FIELD(edgesInfo) {
- if (!mc_FLE2RangeFindSpecEdgesInfo_parse(&out->edgesInfo.value, &iter, status)) {
+ if (!mc_FLE2RangeFindSpecEdgesInfo_parse(&out->edgesInfo.value, &iter, use_range_v2, status)) {
goto fail;
}
out->edgesInfo.set = true;
@@ -317,7 +354,7 @@ bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out, const bson_iter_t *
IF_FIELD(payloadId) {
if (!BSON_ITER_HOLDS_INT32(&iter)) {
- CLIENT_ERR("invalid FLE2RangeFindSpec: 'payloadId' must be an int32");
+ CLIENT_ERR_PREFIXED("'payloadId' must be an int32");
goto fail;
}
out->payloadId = bson_iter_int32(&iter);
@@ -326,15 +363,14 @@ bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out, const bson_iter_t *
IF_FIELD(firstOperator) {
if (!BSON_ITER_HOLDS_INT32(&iter)) {
- CLIENT_ERR("invalid FLE2RangeFindSpec: 'firstOperator' must be an int32");
+ CLIENT_ERR_PREFIXED("'firstOperator' must be an int32");
goto fail;
}
const int32_t first_op = bson_iter_int32(&iter);
if (first_op < FLE2RangeOperator_min_val || first_op > FLE2RangeOperator_max_val) {
- CLIENT_ERR("invalid FLE2RangeFindSpec: 'firstOperator' must be "
- "between %d and %d",
- FLE2RangeOperator_min_val,
- FLE2RangeOperator_max_val);
+ CLIENT_ERR_PREFIXED("'firstOperator' must be between %d and %d",
+ FLE2RangeOperator_min_val,
+ FLE2RangeOperator_max_val);
goto fail;
}
out->firstOperator = (mc_FLE2RangeOperator_t)first_op;
@@ -343,15 +379,14 @@ bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out, const bson_iter_t *
IF_FIELD(secondOperator) {
if (!BSON_ITER_HOLDS_INT32(&iter)) {
- CLIENT_ERR("invalid FLE2RangeFindSpec: 'secondOperator' must be an int32");
+ CLIENT_ERR_PREFIXED("'secondOperator' must be an int32");
goto fail;
}
const int32_t second_op = bson_iter_int32(&iter);
if (second_op < FLE2RangeOperator_min_val || second_op > FLE2RangeOperator_max_val) {
- CLIENT_ERR("invalid FLE2RangeFindSpec: 'secondOperator' must be "
- "between %d and %d",
- FLE2RangeOperator_min_val,
- FLE2RangeOperator_max_val);
+ CLIENT_ERR_PREFIXED("'secondOperator' must be between %d and %d",
+ FLE2RangeOperator_min_val,
+ FLE2RangeOperator_max_val);
goto fail;
}
out->secondOperator = (mc_FLE2RangeOperator_t)second_op;
@@ -369,17 +404,23 @@ fail:
return false;
}
-bool mc_FLE2RangeInsertSpec_parse(mc_FLE2RangeInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status) {
+#undef ERROR_PREFIX
+#define ERROR_PREFIX "Error parsing FLE2RangeInsertSpec"
+
+bool mc_FLE2RangeInsertSpec_parse(mc_FLE2RangeInsertSpec_t *out,
+ const bson_iter_t *in,
+ bool use_range_v2,
+ mongocrypt_status_t *status) {
BSON_ASSERT_PARAM(out);
BSON_ASSERT_PARAM(in);
*out = (mc_FLE2RangeInsertSpec_t){{0}};
bson_iter_t iter = *in;
- bool has_v = false, has_min = false, has_max = false, has_precision = false;
+ bool has_v = false, has_min = false, has_max = false, has_precision = false, has_trimFactor = false;
if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) {
- CLIENT_ERR("invalid FLE2RangeInsertSpec: must be an iterator to a document");
+ CLIENT_ERR_PREFIXED("must be an iterator to a document");
return false;
}
bson_iter_recurse(&iter, &iter);
@@ -405,17 +446,29 @@ bool mc_FLE2RangeInsertSpec_parse(mc_FLE2RangeInsertSpec_t *out, const bson_iter
IF_FIELD(precision) {
if (!BSON_ITER_HOLDS_INT32(&iter)) {
- CLIENT_ERR("invalid FLE2RangeFindSpecEdgesInfo: 'precision' must "
- "be an int32");
+ CLIENT_ERR_PREFIXED("'precision' must be an int32");
goto fail;
}
int32_t val = bson_iter_int32(&iter);
if (val < 0) {
- CLIENT_ERR("invalid FLE2RangeFindSpecEdgesInfo: 'precision' must be"
- "non-negative");
+ CLIENT_ERR_PREFIXED("'precision' must be non-negative");
goto fail;
}
- out->precision = OPT_U32((uint32_t)val);
+ out->precision = OPT_I32(val);
+ }
+ END_IF_FIELD
+
+ IF_FIELD(trimFactor) {
+ if (!BSON_ITER_HOLDS_INT32(&iter)) {
+ CLIENT_ERR_PREFIXED("'trimFactor' must be an int32");
+ goto fail;
+ }
+ int32_t val = bson_iter_int32(&iter);
+ if (val < 0) {
+ CLIENT_ERR_PREFIXED("'trimFactor' must be non-negative");
+ goto fail;
+ }
+ out->trimFactor = OPT_I32(val);
}
END_IF_FIELD
}
@@ -425,8 +478,16 @@ bool mc_FLE2RangeInsertSpec_parse(mc_FLE2RangeInsertSpec_t *out, const bson_iter
CHECK_HAS(max)
// Do not error if precision is not present. Precision optional and only
// applies to double/decimal128.
+
+ if (!use_range_v2 && out->trimFactor.set) {
+ CLIENT_ERR_PREFIXED("'trimFactor' is not supported for QE range v1");
+ return false;
+ }
+
return true;
fail:
return false;
}
+
+#undef ERROR_PREFIX
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-private-v2.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-private-v2.h
index 452f2a9a7ca..02fa0e5bc79 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-private-v2.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-private-v2.h
@@ -25,7 +25,7 @@ typedef struct {
_mongocrypt_buffer_t edcDerivedToken; // d
_mongocrypt_buffer_t escDerivedToken; // s
_mongocrypt_buffer_t serverDerivedFromDataToken; // l
- int64_t maxContentionCounter; // cm
+ int64_t maxContentionFactor; // cm
} mc_FLE2FindEqualityPayloadV2_t;
void mc_FLE2FindEqualityPayloadV2_init(mc_FLE2FindEqualityPayloadV2_t *payload);
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-private.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-private.h
index 8eb399eabf3..6dcc0e38fe4 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-private.h
@@ -26,7 +26,7 @@ typedef struct {
_mongocrypt_buffer_t escDerivedToken; // s
_mongocrypt_buffer_t eccDerivedToken; // c
_mongocrypt_buffer_t serverEncryptionToken; // e
- int64_t maxContentionCounter; // cm
+ int64_t maxContentionFactor; // cm
} mc_FLE2FindEqualityPayload_t;
void mc_FLE2FindEqualityPayload_init(mc_FLE2FindEqualityPayload_t *payload);
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-v2.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-v2.c
index 02d2dbfde65..afd1654fb81 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-v2.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload-v2.c
@@ -99,7 +99,7 @@ bool mc_FLE2FindEqualityPayloadV2_parse(mc_FLE2FindEqualityPayloadV2_t *out,
CLIENT_ERR("Field 'cm' expected to hold an int64");
goto fail;
}
- out->maxContentionCounter = bson_iter_int64(&iter);
+ out->maxContentionFactor = bson_iter_int64(&iter);
}
END_IF_FIELD
}
@@ -126,7 +126,7 @@ bool mc_FLE2FindEqualityPayloadV2_serialize(const mc_FLE2FindEqualityPayloadV2_t
APPEND_BINDATA("d", payload->edcDerivedToken);
APPEND_BINDATA("s", payload->escDerivedToken);
APPEND_BINDATA("l", payload->serverDerivedFromDataToken);
- if (!BSON_APPEND_INT64(out, "cm", payload->maxContentionCounter)) {
+ if (!BSON_APPEND_INT64(out, "cm", payload->maxContentionFactor)) {
return false;
}
return true;
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload.c
index 24d551a03de..e60dfa90e88 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-equality-payload.c
@@ -101,7 +101,7 @@ bool mc_FLE2FindEqualityPayload_parse(mc_FLE2FindEqualityPayload_t *out,
CLIENT_ERR("Field 'cm' expected to hold an int64");
goto fail;
}
- out->maxContentionCounter = bson_iter_int64(&iter);
+ out->maxContentionFactor = bson_iter_int64(&iter);
}
END_IF_FIELD
}
@@ -130,7 +130,7 @@ bool mc_FLE2FindEqualityPayload_serialize(const mc_FLE2FindEqualityPayload_t *pa
PAYLOAD_APPEND_BINDATA("s", payload->escDerivedToken);
PAYLOAD_APPEND_BINDATA("c", payload->eccDerivedToken);
PAYLOAD_APPEND_BINDATA("e", payload->serverEncryptionToken);
- if (!BSON_APPEND_INT64(out, "cm", payload->maxContentionCounter)) {
+ if (!BSON_APPEND_INT64(out, "cm", payload->maxContentionFactor)) {
return false;
}
return true;
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-private-v2.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-private-v2.h
index f35331aa200..79bbd8ca991 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-private-v2.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-private-v2.h
@@ -23,13 +23,14 @@
#include "mc-array-private.h"
#include "mc-fle2-range-operator-private.h"
+#include "mc-optional-private.h"
/** FLE2FindRangePayloadEdgesInfoV2 represents the token information for a range
* find query. It is encoded inside an FLE2FindRangePayloadV2.
*/
typedef struct {
mc_array_t edgeFindTokenSetArray; // g
- int64_t maxContentionCounter; // cm
+ int64_t maxContentionFactor; // cm
} mc_FLE2FindRangePayloadEdgesInfoV2_t;
/**
@@ -44,8 +45,17 @@ typedef struct {
* } FLE2FindRangePayloadV2;
*
* bson is a BSON document of this form:
- * g: array // Array of Edges
- * cm: // Queryable Encryption max counter
+ * payload:
+ * g: array // Array of Edges
+ * cm: // Queryable Encryption max counter
+ * payloadId: // Payload ID.
+ * firstOperator:
+ * secondOperator:
+ * sp: optional // Sparsity.
+ * pn: optional // Precision.
+ * tf: optional // Trim Factor.
+ * mn: optional // Index Min.
+ * mx: optional // Index Max.
*/
typedef struct {
struct {
@@ -61,12 +71,22 @@ typedef struct {
// secondOperator represents the second query operator for which this payload
// was generated. Only populated for two-sided ranges. It is 0 if unset.
mc_FLE2RangeOperator_t secondOperator;
+ mc_optional_int64_t sparsity; // sp
+ mc_optional_int32_t precision; // pn
+ mc_optional_int32_t trimFactor; // tf
+ bson_value_t indexMin; // mn
+ bson_value_t indexMax; // mx
} mc_FLE2FindRangePayloadV2_t;
+// `mc_FLE2FindRangePayloadV2_t` inherits extended alignment from libbson. To dynamically allocate, use aligned
+// allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof_mc_FLE2FindRangePayloadV2_t,
+ BSON_ALIGNOF(mc_FLE2FindRangePayloadV2_t) >= BSON_ALIGNOF(bson_value_t));
+
/**
* EdgeFindTokenSetV2 is the following BSON document:
- * d: // EDCDerivedFromDataTokenAndCounter
- * s: // ESCDerivedFromDataTokenAndCounter
+ * d: // EDCDerivedFromDataTokenAndContentionFactor
+ * s: // ESCDerivedFromDataTokenAndContentionFactor
* l: // ServerDerivedFromDataToken
*
* Instances of mc_EdgeFindTokenSetV2_t are expected to be owned by
@@ -81,7 +101,7 @@ typedef struct {
void mc_FLE2FindRangePayloadV2_init(mc_FLE2FindRangePayloadV2_t *payload);
-bool mc_FLE2FindRangePayloadV2_serialize(const mc_FLE2FindRangePayloadV2_t *payload, bson_t *out);
+bool mc_FLE2FindRangePayloadV2_serialize(const mc_FLE2FindRangePayloadV2_t *payload, bson_t *out, bool use_range_v2);
void mc_FLE2FindRangePayloadV2_cleanup(mc_FLE2FindRangePayloadV2_t *payload);
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-private.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-private.h
index 0796c30e319..d2e4ee4457e 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-private.h
@@ -32,7 +32,7 @@
typedef struct {
mc_array_t edgeFindTokenSetArray; // g
_mongocrypt_buffer_t serverEncryptionToken; // e
- int64_t maxContentionCounter; // cm
+ int64_t maxContentionFactor; // cm
} mc_FLE2FindRangePayloadEdgesInfo_t;
/**
@@ -49,7 +49,7 @@ typedef struct {
* bson is a BSON document of this form:
* g: array // Array of Edges
* e: // ServerDataEncryptionLevel1Token
- * cm: // Queryable Encryption max counter
+ * cm: // Queryable Encryption max contentionFactor
*/
typedef struct {
struct {
@@ -69,9 +69,9 @@ typedef struct {
/**
* EdgeFindTokenSet is the following BSON document:
- * d: // EDCDerivedFromDataTokenAndCounter
- * s: // ESCDerivedFromDataTokenAndCounter
- * c: // ECCDerivedFromDataTokenAndCounter
+ * d: // EDCDerivedFromDataTokenAndContentionFactor
+ * s: // ESCDerivedFromDataTokenAndContentionFactor
+ * c: // ECCDerivedFromDataTokenAndContentionFactor
*
* Instances of mc_EdgeFindTokenSet_t are expected to be owned by
* mc_FLE2FindRangePayload_t and are freed in
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-v2.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-v2.c
index dfb5421a663..e07cd76781c 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-v2.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload-v2.c
@@ -53,7 +53,7 @@ void mc_FLE2FindRangePayloadV2_cleanup(mc_FLE2FindRangePayloadV2_t *payload) {
return false; \
}
-bool mc_FLE2FindRangePayloadV2_serialize(const mc_FLE2FindRangePayloadV2_t *payload, bson_t *out) {
+bool mc_FLE2FindRangePayloadV2_serialize(const mc_FLE2FindRangePayloadV2_t *payload, bson_t *out, bool use_range_v2) {
BSON_ASSERT_PARAM(out);
BSON_ASSERT_PARAM(payload);
@@ -106,7 +106,7 @@ bool mc_FLE2FindRangePayloadV2_serialize(const mc_FLE2FindRangePayloadV2_t *payl
}
// Append "payload.cm".
- if (!BSON_APPEND_INT64(&payload_bson, "cm", payload->payload.value.maxContentionCounter)) {
+ if (!BSON_APPEND_INT64(&payload_bson, "cm", payload->payload.value.maxContentionFactor)) {
return false;
}
@@ -131,6 +131,42 @@ bool mc_FLE2FindRangePayloadV2_serialize(const mc_FLE2FindRangePayloadV2_t *payl
return false;
}
+ if (use_range_v2) {
+ // Encode parameters that were used to generate the mincover.
+ // The crypto parameters are all optionally set. Find payloads may come in pairs (a lower and upper bound).
+ // One of the pair includes the mincover. The other payload was not generated with crypto parameters.
+
+ if (payload->sparsity.set) {
+ if (!BSON_APPEND_INT64(out, "sp", payload->sparsity.value)) {
+ return false;
+ }
+ }
+
+ if (payload->precision.set) {
+ if (!BSON_APPEND_INT32(out, "pn", payload->precision.value)) {
+ return false;
+ }
+ }
+
+ if (payload->trimFactor.set) {
+ if (!BSON_APPEND_INT32(out, "tf", payload->trimFactor.value)) {
+ return false;
+ }
+ }
+
+ if (payload->indexMin.value_type != BSON_TYPE_EOD) {
+ if (!BSON_APPEND_VALUE(out, "mn", &payload->indexMin)) {
+ return false;
+ }
+ }
+
+ if (payload->indexMax.value_type != BSON_TYPE_EOD) {
+ if (!BSON_APPEND_VALUE(out, "mx", &payload->indexMax)) {
+ return false;
+ }
+ }
+ }
+
return true;
}
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload.c
index 4eb1de4b9f1..1a3610e0d71 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-find-range-payload.c
@@ -108,7 +108,7 @@ bool mc_FLE2FindRangePayload_serialize(const mc_FLE2FindRangePayload_t *payload,
// Append "payload.e" and "payload.cm".
APPEND_BINDATA(&payload_bson, "e", payload->payload.value.serverEncryptionToken);
- if (!BSON_APPEND_INT64(&payload_bson, "cm", payload->payload.value.maxContentionCounter)) {
+ if (!BSON_APPEND_INT64(&payload_bson, "cm", payload->payload.value.maxContentionFactor)) {
return false;
}
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-private-v2.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-private-v2.h
index e3556263f46..911ca746bd2 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-private-v2.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-private-v2.h
@@ -20,6 +20,7 @@
#include
#include "mc-array-private.h"
+#include "mc-optional-private.h"
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt.h"
@@ -36,8 +37,8 @@
* } FLE2InsertUpdatePayloadV2;
*
* bson is a BSON document of this form:
- * d: // EDCDerivedFromDataTokenAndCounter
- * s: // ESCDerivedFromDataTokenAndCounter
+ * d: // EDCDerivedFromDataTokenAndContentionFactor
+ * s: // ESCDerivedFromDataTokenAndContentionFactor
* p: // Encrypted Tokens
* u: // Index KeyId
* t: // Encrypted type
@@ -45,13 +46,18 @@
* e: // ServerDataEncryptionLevel1Token
* l: // ServerDerivedFromDataToken
* k: // Randomly sampled contention factor value
- * g: array // Array of Edges
+ * g: array // Array of Edges. Only included for range payloads.
+ * sp: optional // Sparsity. Only included for range payloads.
+ * pn: optional // Precision. Only included for range payloads.
+ * tf: optional // Trim Factor. Only included for range payloads.
+ * mn: optional // Index Min. Only included for range payloads.
+ * mx: optional // Index Max. Only included for range payloads.
*
* p is the result of:
* Encrypt(
* key=ECOCToken,
* plaintext=(
- * ESCDerivedFromDataTokenAndCounter)
+ * ESCDerivedFromDataTokenAndContentionFactor)
* )
*
* v is the result of:
@@ -72,14 +78,24 @@ typedef struct {
_mongocrypt_buffer_t serverDerivedFromDataToken; // l
int64_t contentionFactor; // k
mc_array_t edgeTokenSetArray; // g
+ mc_optional_int64_t sparsity; // sp
+ mc_optional_int32_t precision; // pn
+ mc_optional_int32_t trimFactor; // tf
+ bson_value_t indexMin; // mn
+ bson_value_t indexMax; // mx
_mongocrypt_buffer_t plaintext;
_mongocrypt_buffer_t userKeyId;
} mc_FLE2InsertUpdatePayloadV2_t;
+// `mc_FLE2InsertUpdatePayloadV2_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof_mc_FLE2InsertUpdatePayloadV2_t,
+ BSON_ALIGNOF(mc_FLE2InsertUpdatePayloadV2_t) >= BSON_ALIGNOF(bson_value_t));
+
/**
* EdgeTokenSetV2 is the following BSON document:
- * d: // EDCDerivedFromDataTokenAndCounter
- * s: // ESCDerivedFromDataTokenAndCounter
+ * d: // EDCDerivedFromDataTokenAndContentionFactor
+ * s: // ESCDerivedFromDataTokenAndContentionFactor
* l: // ServerDerivedFromDataToken
* p: // Encrypted Tokens
*
@@ -110,7 +126,9 @@ const _mongocrypt_buffer_t *mc_FLE2InsertUpdatePayloadV2_decrypt(_mongocrypt_cry
bool mc_FLE2InsertUpdatePayloadV2_serialize(const mc_FLE2InsertUpdatePayloadV2_t *payload, bson_t *out);
-bool mc_FLE2InsertUpdatePayloadV2_serializeForRange(const mc_FLE2InsertUpdatePayloadV2_t *payload, bson_t *out);
+bool mc_FLE2InsertUpdatePayloadV2_serializeForRange(const mc_FLE2InsertUpdatePayloadV2_t *payload,
+ bson_t *out,
+ bool use_range_v2);
void mc_FLE2InsertUpdatePayloadV2_cleanup(mc_FLE2InsertUpdatePayloadV2_t *payload);
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-private.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-private.h
index 76bdf4af1c4..a7d44958a93 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-private.h
@@ -36,9 +36,9 @@
* } FLE2InsertUpdatePayload;
*
* bson is a BSON document of this form:
- * d: // EDCDerivedFromDataTokenAndCounter
- * s: // ESCDerivedFromDataTokenAndCounter
- * c: // ECCDerivedFromDataTokenAndCounter
+ * d: // EDCDerivedFromDataTokenAndContentionFactor
+ * s: // ESCDerivedFromDataTokenAndContentionFactor
+ * c: // ECCDerivedFromDataTokenAndContentionFactor
* p: // Encrypted Tokens
* u: // Index KeyId
* t: // Encrypted type
@@ -50,8 +50,8 @@
* Encrypt(
* key=ECOCToken,
* plaintext=(
- * ESCDerivedFromDataTokenAndCounter ||
- * ECCDerivedFromDataTokenAndCounter)
+ * ESCDerivedFromDataTokenAndContentionFactor ||
+ * ECCDerivedFromDataTokenAndContentionFactor)
* )
*
* v is the result of:
@@ -77,9 +77,9 @@ typedef struct {
/**
* EdgeTokenSet is the following BSON document:
- * d: // EDCDerivedFromDataTokenAndCounter
- * s: // ESCDerivedFromDataTokenAndCounter
- * c: // ECCDerivedFromDataTokenAndCounter
+ * d: // EDCDerivedFromDataTokenAndContentionFactor
+ * s: // ESCDerivedFromDataTokenAndContentionFactor
+ * c: // ECCDerivedFromDataTokenAndContentionFactor
* p: // Encrypted Tokens
*
* Instances of mc_EdgeTokenSet_t are expected to be owned by
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-v2.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-v2.c
index f4e9bf309e0..b86e41239a0 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-v2.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-insert-update-payload-v2.c
@@ -18,6 +18,7 @@
#include "mc-fle2-insert-update-payload-private-v2.h"
#include "mongocrypt-buffer-private.h"
+#include "mongocrypt-util-private.h" // mc_bson_type_to_string
#include "mongocrypt.h"
void mc_FLE2InsertUpdatePayloadV2_init(mc_FLE2InsertUpdatePayloadV2_t *payload) {
@@ -53,6 +54,8 @@ void mc_FLE2InsertUpdatePayloadV2_cleanup(mc_FLE2InsertUpdatePayloadV2_t *payloa
mc_EdgeTokenSetV2_cleanup(&entry);
}
_mc_array_destroy(&payload->edgeTokenSetArray);
+ bson_value_destroy(&payload->indexMin);
+ bson_value_destroy(&payload->indexMax);
}
#define IF_FIELD(Name) \
@@ -104,6 +107,7 @@ bool mc_FLE2InsertUpdatePayloadV2_parse(mc_FLE2InsertUpdatePayloadV2_t *out,
bool has_d = false, has_s = false, has_p = false;
bool has_u = false, has_t = false, has_v = false;
bool has_e = false, has_l = false, has_k = false;
+ bool has_sp = false, has_pn = false, has_tf = false, has_mn = false, has_mx = false;
bson_t in_bson;
BSON_ASSERT_PARAM(out);
@@ -163,6 +167,57 @@ bool mc_FLE2InsertUpdatePayloadV2_parse(mc_FLE2InsertUpdatePayloadV2_t *out,
PARSE_BINARY(v, value)
PARSE_BINARY(e, serverEncryptionToken)
PARSE_BINARY(l, serverDerivedFromDataToken)
+
+ IF_FIELD(sp) {
+ if (!BSON_ITER_HOLDS_INT64(&iter)) {
+ CLIENT_ERR("Field 'sp' expected to hold an int64, got: %s",
+ mc_bson_type_to_string(bson_iter_type(&iter)));
+ goto fail;
+ }
+ int64_t sparsity = bson_iter_int64(&iter);
+ out->sparsity = OPT_I64(sparsity);
+ }
+ END_IF_FIELD
+
+ IF_FIELD(pn) {
+ if (!BSON_ITER_HOLDS_INT32(&iter)) {
+ CLIENT_ERR("Field 'pn' expected to hold an int32, got: %s",
+ mc_bson_type_to_string(bson_iter_type(&iter)));
+ goto fail;
+ }
+ int32_t precision = bson_iter_int32(&iter);
+ if (precision < 0) {
+ CLIENT_ERR("Field 'pn' must be non-negative, got: %" PRId32, precision);
+ goto fail;
+ }
+ out->precision = OPT_I32(precision);
+ }
+ END_IF_FIELD
+
+ IF_FIELD(tf) {
+ if (!BSON_ITER_HOLDS_INT32(&iter)) {
+ CLIENT_ERR("Field 'tf' expected to hold an int32, got: %s",
+ mc_bson_type_to_string(bson_iter_type(&iter)));
+ goto fail;
+ }
+ int32_t trimFactor = bson_iter_int32(&iter);
+ if (trimFactor < 0) {
+ CLIENT_ERR("Field 'tf' must be non-negative, got: %" PRId32, trimFactor);
+ goto fail;
+ }
+ out->trimFactor = OPT_I32(trimFactor);
+ }
+ END_IF_FIELD
+
+ IF_FIELD(mn) {
+ bson_value_copy(bson_iter_value(&iter), &out->indexMin);
+ }
+ END_IF_FIELD
+
+ IF_FIELD(mx) {
+ bson_value_copy(bson_iter_value(&iter), &out->indexMax);
+ }
+ END_IF_FIELD
}
CHECK_HAS(d);
@@ -174,6 +229,7 @@ bool mc_FLE2InsertUpdatePayloadV2_parse(mc_FLE2InsertUpdatePayloadV2_t *out,
CHECK_HAS(e);
CHECK_HAS(l);
CHECK_HAS(k);
+ // The fields `sp`, `pn`, `tf`, `mn`, and `mx` are only set for "range" payloads.
if (!_mongocrypt_buffer_from_subrange(&out->userKeyId, &out->value, 0, UUID_LEN)) {
CLIENT_ERR("failed to create userKeyId buffer");
@@ -213,7 +269,9 @@ bool mc_FLE2InsertUpdatePayloadV2_serialize(const mc_FLE2InsertUpdatePayloadV2_t
return true;
}
-bool mc_FLE2InsertUpdatePayloadV2_serializeForRange(const mc_FLE2InsertUpdatePayloadV2_t *payload, bson_t *out) {
+bool mc_FLE2InsertUpdatePayloadV2_serializeForRange(const mc_FLE2InsertUpdatePayloadV2_t *payload,
+ bson_t *out,
+ bool use_range_v2) {
BSON_ASSERT_PARAM(out);
BSON_ASSERT_PARAM(payload);
@@ -257,6 +315,36 @@ bool mc_FLE2InsertUpdatePayloadV2_serializeForRange(const mc_FLE2InsertUpdatePay
return false;
}
+ if (use_range_v2) {
+ // Encode parameters that were used to generate the payload.
+ BSON_ASSERT(payload->sparsity.set);
+ if (!BSON_APPEND_INT64(out, "sp", payload->sparsity.value)) {
+ return false;
+ }
+
+ // Precision may be unset.
+ if (payload->precision.set) {
+ if (!BSON_APPEND_INT32(out, "pn", payload->precision.value)) {
+ return false;
+ }
+ }
+
+ BSON_ASSERT(payload->trimFactor.set);
+ if (!BSON_APPEND_INT32(out, "tf", payload->trimFactor.value)) {
+ return false;
+ }
+
+ BSON_ASSERT(payload->indexMin.value_type != BSON_TYPE_EOD);
+ if (!BSON_APPEND_VALUE(out, "mn", &payload->indexMin)) {
+ return false;
+ }
+
+ BSON_ASSERT(payload->indexMax.value_type != BSON_TYPE_EOD);
+ if (!BSON_APPEND_VALUE(out, "mx", &payload->indexMax)) {
+ return false;
+ }
+ }
+
return true;
}
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-private-v2.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-private-v2.h
index 5a585cd4ce0..4ead1756e93 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-private-v2.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-private-v2.h
@@ -17,6 +17,7 @@
#ifndef MONGOCRYPT_INDEXED_ENCRYPTED_VALUE_PRIVATE_V2_H
#define MONGOCRYPT_INDEXED_ENCRYPTED_VALUE_PRIVATE_V2_H
+#include "mc-fle2-tag-and-encrypted-metadata-block-private.h"
#include "mc-tokens-private.h"
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-crypto-private.h"
@@ -24,31 +25,98 @@
/*
* FLE2IndexedEqualityEncryptedValueV2 and FLE2IndexedRangeEncryptedValueV2
- * share a common internal implementation. Accessors such as add/get_[SK]_Key
- * may be called for either type and produce appropriate results,
- * however the _parse() method is unique per type.
+ * share a common internal implementation.
*
* Lifecycle:
* 1. mc_FLE2IndexedEncryptedValueV2_init
- * 2. mc_FLE2Indexed(Equality|Range)EncryptedValueV2_parse
+ * 2. mc_FLE2IndexedEncryptedValueV2_parse
* 3. mc_FLE2IndexedEncryptedValueV2_get_S_KeyId
* 4. mc_FLE2IndexedEncryptedValueV2_add_S_Key
* 5. mc_FLE2IndexedEncryptedValueV2_get_K_KeyId
* 6. mc_FLE2IndexedEncryptedValueV2_add_K_Key
* 7. mc_FLE2IndexedEncryptedValueV2_get_ClientValue
- * 8. mc_FLE2IndexedEncryptedValueV2_destroy
+ * 8. mc_FLE2IndexedEncryptedValueV2_serialize
+ * 9. mc_FLE2IndexedEncryptedValueV2_destroy
+ *
+ *
+ * FLE2IndexedEqualityEncryptedValueV2 has the following data layout:
+ *
+ * struct FLE2IndexedEqualityEncryptedValueV2 {
+ * uint8_t fle_blob_subtype = 14;
+ * uint8_t S_KeyId[16];
+ * uint8_t original_bson_type;
+ * uint8_t ServerEncryptedValue[ServerEncryptedValue.length];
+ * FLE2TagAndEncryptedMetadataBlock metadata;
+ * }
+ *
+ * ServerEncryptedValue :=
+ * EncryptCTR(ServerEncryptionToken, K_KeyId || ClientEncryptedValue)
+ * ClientEncryptedValue := EncryptCBCAEAD(K_Key, clientValue, AD=K_KeyId)
+ *
+ *
+ * struct FLE2TagAndEncryptedMetadataBlock {
+ * uint8_t encryptedCount[32]; // EncryptCTR(countEncryptionToken,
+ * // count || contentionFactor)
+ * uint8_t tag[32]; // HMAC-SHA256(count, edcTwiceDerived)
+ * uint8_t encryptedZeros[32]; // EncryptCTR(zerosEncryptionToken, 0*)
+ * }
+ *
+ *
+ * FLE2IndexedRangeEncryptedValueV2 has the following data layout:
+ *
+ * struct FLE2IndexedRangeEncryptedValueV2 {
+ * uint8_t fle_blob_subtype = 15;
+ * uint8_t S_KeyId[16];
+ * uint8_t original_bson_type;
+ * uint8_t edge_count;
+ * uint8_t ServerEncryptedValue[ServerEncryptedValue.length];
+ * FLE2TagAndEncryptedMetadataBlock metadata[edge_count];
+ * }
+ *
+ * Note that this format differs from FLE2IndexedEqualityEncryptedValueV2
+ * in only two ways:
+ * 1/ `edge_count` is introduced as an octet following `original_bson_type`.
+ * 2/ Rather than a single metadata block, we have {edge_count} blocks.
+ *
*/
typedef struct _mc_FLE2IndexedEncryptedValueV2_t mc_FLE2IndexedEncryptedValueV2_t;
mc_FLE2IndexedEncryptedValueV2_t *mc_FLE2IndexedEncryptedValueV2_new(void);
+bson_type_t mc_FLE2IndexedEncryptedValueV2_get_bson_value_type(const mc_FLE2IndexedEncryptedValueV2_t *iev,
+ mongocrypt_status_t *status);
+/*
+ * Populates an mc_FLE2IndexedEncryptedValueV2_t from a buffer.
+ *
+ * Input buffer must take the form of:
+ * fle_blob_subtype (8u)
+ * S_KeyId (8u * 16u)
+ * original_bson_type (8u)
+ * if (range)
+ * edge_count(8u)
+ * ServerEncryptedValue (8u * SEV_len)
+ * metadata (96u * {range ? edge_count : 1u})
+ *
+ * Returns an error if the input buffer is not valid.
+ */
bool mc_FLE2IndexedEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
const _mongocrypt_buffer_t *buf,
mongocrypt_status_t *status);
-bson_type_t mc_FLE2IndexedEncryptedValueV2_get_bson_value_type(const mc_FLE2IndexedEncryptedValueV2_t *iev,
- mongocrypt_status_t *status);
+/*
+ * Serializes an mc_FLE2IndexedEncryptedValueV2_t into a buffer.
+ *
+ * The serialized output follows the same layout as the input `buf` to
+ * mc_FLE2IndexedEncryptedValueV2_parse, allowing for round-trip
+ * conversions between the serialized and parsed forms.
+ *
+ * Returns an error if the input structure is not valid, or if the buffer
+ * provided is insufficient to hold the serialized data.
+ */
+bool mc_FLE2IndexedEncryptedValueV2_serialize(const mc_FLE2IndexedEncryptedValueV2_t *iev,
+ _mongocrypt_buffer_t *buf,
+ mongocrypt_status_t *status);
const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValueV2_get_S_KeyId(const mc_FLE2IndexedEncryptedValueV2_t *iev,
mongocrypt_status_t *status);
@@ -73,61 +141,18 @@ bool mc_FLE2IndexedEncryptedValueV2_add_K_Key(_mongocrypt_crypto_t *crypto,
const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValueV2_get_ClientValue(const mc_FLE2IndexedEncryptedValueV2_t *iev,
mongocrypt_status_t *status);
+uint8_t mc_FLE2IndexedEncryptedValueV2_get_edge_count(const mc_FLE2IndexedEncryptedValueV2_t *iev,
+ mongocrypt_status_t *status);
+
+bool mc_FLE2IndexedEncryptedValueV2_get_edge(const mc_FLE2IndexedEncryptedValueV2_t *iev,
+ mc_FLE2TagAndEncryptedMetadataBlock_t *out,
+ const uint8_t edge_index,
+ mongocrypt_status_t *status);
+
+bool mc_FLE2IndexedEncryptedValueV2_get_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev,
+ mc_FLE2TagAndEncryptedMetadataBlock_t *out,
+ mongocrypt_status_t *status);
+
void mc_FLE2IndexedEncryptedValueV2_destroy(mc_FLE2IndexedEncryptedValueV2_t *iev);
-/*
- * FLE2IndexedEqualityEncryptedValueV2 has the following data layout:
- *
- * struct FLE2IndexedEqualityEncryptedValueV2 {
- * uint8_t fle_blob_subtype = 14;
- * uint8_t S_KeyId[16];
- * uint8_t original_bson_type;
- * uint8_t ServerEncryptedValue[ServerEncryptedValue.length];
- * FLE2TagAndEncryptedMetadataBlock metadata;
- * }
- *
- * ServerEncryptedValue :=
- * EncryptCTR(ServerEncryptionToken, K_KeyId || ClientEncryptedValue)
- * ClientEncryptedValue := EncryptCBCAEAD(K_Key, clientValue, AD=K_KeyId)
- *
- * The MetadataBlock is ignored by libmongocrypt,
- * but has the following structure and a fixed size of 96 octets:
- *
- * struct FLE2TagAndEncryptedMetadataBlock {
- * uint8_t encryptedCount[32]; // EncryptCTR(countEncryptionToken,
- * // count || contentionFactor)
- * uint8_t tag[32]; // HMAC-SHA256(count, edcTwiceDerived)
- * uint8_t encryptedZeros[32]; // EncryptCTR(zerosEncryptionToken, 0*)
- * }
- */
-
-bool mc_FLE2IndexedEqualityEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
- const _mongocrypt_buffer_t *buf,
- mongocrypt_status_t *status);
-
-/*
- * FLE2IndexedRangeEncryptedValueV2 has the following data layout:
- *
- * struct FLE2IndexedRangeEncryptedValueV2 {
- * uint8_t fle_blob_subtype = 15;
- * uint8_t S_KeyId[16];
- * uint8_t original_bson_type;
- * uint8_t edge_count;
- * uint8_t ServerEncryptedValue[ServerEncryptedValue.length];
- * FLE2TagAndEncryptedMetadataBlock metadata[edge_count];
- * }
- *
- * Note that this format differs from FLE2IndexedEqualityEncryptedValueV2
- * in only two ways:
- * 1/ `edge_count` is introduced as an octet following `original_bson_type`.
- * 2/ Rather than a single metadata block, we have {edge_count} blocks.
- *
- * Since libmongocrypt ignores metadata blocks, we can ignore most all
- * differences between Equality and Range types for IndexedEncrypted data.
- */
-
-bool mc_FLE2IndexedRangeEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
- const _mongocrypt_buffer_t *buf,
- mongocrypt_status_t *status);
-
#endif /* MONGOCRYPT_INDEXED_ENCRYPTED_VALUE_PRIVATE_V2_H */
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-v2.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-v2.c
index de11a223247..211d335781e 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-v2.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-v2.c
@@ -19,6 +19,7 @@
#include "mc-fle-blob-subtype-private.h"
#include "mc-fle2-payload-iev-private-v2.h"
+#include "mc-fle2-tag-and-encrypted-metadata-block-private.h"
#include "mc-reader-private.h"
#include "mc-tokens-private.h"
#include "mc-writer-private.h"
@@ -54,6 +55,8 @@ struct _mc_FLE2IndexedEncryptedValueV2_t {
// Populated during _add_K_Key
// ClientValue := DecryptCBCAEAD(K_Key, ClientEncryptedValue, AD=K_KeyId)
_mongocrypt_buffer_t ClientValue;
+
+ mc_FLE2TagAndEncryptedMetadataBlock_t *metadata;
};
#define kMetadataLen 96U // encCount(32) + tag(32) + encZeros(32)
@@ -69,31 +72,6 @@ mc_FLE2IndexedEncryptedValueV2_t *mc_FLE2IndexedEncryptedValueV2_new(void) {
return bson_malloc0(sizeof(mc_FLE2IndexedEncryptedValueV2_t));
}
-bool mc_FLE2IndexedEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
- const _mongocrypt_buffer_t *buf,
- mongocrypt_status_t *status) {
- BSON_ASSERT_PARAM(iev);
- BSON_ASSERT_PARAM(buf);
-
- if ((buf->data == NULL) || (buf->len == 0)) {
- CLIENT_ERR("Empty buffer passed to mc_FLE2IndexedEncryptedValueV2_parse");
- return false;
- }
-
- if (buf->data[0] == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2) {
- return mc_FLE2IndexedEqualityEncryptedValueV2_parse(iev, buf, status);
- } else if (buf->data[0] == MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2) {
- return mc_FLE2IndexedRangeEncryptedValueV2_parse(iev, buf, status);
- } else {
- CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_parse expected "
- "fle_blob_subtype %d or %d got: %" PRIu8,
- MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2,
- MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2,
- iev->fle_blob_subtype);
- return false;
- }
-}
-
bson_type_t mc_FLE2IndexedEncryptedValueV2_get_bson_value_type(const mc_FLE2IndexedEncryptedValueV2_t *iev,
mongocrypt_status_t *status) {
BSON_ASSERT_PARAM(iev);
@@ -196,7 +174,7 @@ bool mc_FLE2IndexedEncryptedValueV2_add_S_Key(_mongocrypt_crypto_t *crypto,
BSON_ASSERT(bytes_written == DecryptedServerEncryptedValueLen);
if (!_mongocrypt_buffer_from_subrange(&iev->K_KeyId, &iev->DecryptedServerEncryptedValue, 0, UUID_LEN)) {
CLIENT_ERR("Error creating K_KeyId subrange from DecryptedServerEncryptedValue");
- return false;
+ goto fail;
}
iev->K_KeyId.subtype = BSON_SUBTYPE_UUID;
@@ -207,7 +185,7 @@ bool mc_FLE2IndexedEncryptedValueV2_add_S_Key(_mongocrypt_crypto_t *crypto,
iev->DecryptedServerEncryptedValue.len - UUID_LEN)) {
CLIENT_ERR("Error creating ClientEncryptedValue subrange from "
"DecryptedServerEncryptedValue");
- return false;
+ goto fail;
}
iev->ClientEncryptedValueDecoded = true;
@@ -322,70 +300,99 @@ void mc_FLE2IndexedEncryptedValueV2_destroy(mc_FLE2IndexedEncryptedValueV2_t *ie
_mongocrypt_buffer_cleanup(&iev->DecryptedServerEncryptedValue);
_mongocrypt_buffer_cleanup(&iev->ServerEncryptedValue);
_mongocrypt_buffer_cleanup(&iev->S_KeyId);
+
+ for (int i = 0; i < iev->edge_count; i++) {
+ mc_FLE2TagAndEncryptedMetadataBlock_cleanup(&iev->metadata[i]);
+ }
+
+ // Metadata array is dynamically allocated
+ bson_free(iev->metadata);
+
bson_free(iev);
}
-// -----------------------------------------------------------------------
-// Equality
-
-bool mc_FLE2IndexedEqualityEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
- const _mongocrypt_buffer_t *buf,
- mongocrypt_status_t *status) {
+uint8_t mc_FLE2IndexedEncryptedValueV2_get_edge_count(const mc_FLE2IndexedEncryptedValueV2_t *iev,
+ mongocrypt_status_t *status) {
BSON_ASSERT_PARAM(iev);
- BSON_ASSERT_PARAM(buf);
- if (iev->type != kTypeInit) {
- CLIENT_ERR("mc_FLE2IndexedEqualityEncryptedValueV2_parse must not be "
- "called twice");
+ if (iev->type == kTypeInit) {
+ CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge_count "
+ "must be called after "
+ "mc_FLE2IndexedEncryptedValueV2_parse");
+ return 0;
+ }
+
+ if (iev->type != kTypeRange) {
+ CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge_count must be called with type range");
+ return 0;
+ }
+
+ return iev->edge_count;
+}
+
+bool mc_FLE2IndexedEncryptedValueV2_get_edge(const mc_FLE2IndexedEncryptedValueV2_t *iev,
+ mc_FLE2TagAndEncryptedMetadataBlock_t *out,
+ const uint8_t edge_index,
+ mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(iev);
+ BSON_ASSERT_PARAM(out);
+
+ if (iev->type == kTypeInit) {
+ CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge "
+ "must be called after "
+ "mc_FLE2IndexedEncryptedValueV2_parse");
return false;
}
- mc_reader_t reader;
- mc_reader_init_from_buffer(&reader, buf, __FUNCTION__);
-
- CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->fle_blob_subtype, status));
-
- if (iev->fle_blob_subtype != MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2) {
- CLIENT_ERR("mc_FLE2IndexedEqualityEncryptedValueV2_parse expected "
- "fle_blob_subtype %d got: %" PRIu8,
- MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2,
- iev->fle_blob_subtype);
+ if (iev->type != kTypeRange) {
+ CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge must be called with type range");
return false;
}
- /* Read S_KeyId. */
- CHECK_AND_RETURN(mc_reader_read_uuid_buffer(&reader, &iev->S_KeyId, status));
-
- /* Read original_bson_type. */
- CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->bson_value_type, status));
-
- /* Read ServerEncryptedValue. */
- const uint64_t SEV_and_metadata_len = mc_reader_get_remaining_length(&reader);
- if (SEV_and_metadata_len < kMinSEVAndMetadataLen) {
- CLIENT_ERR("Invalid payload size %" PRIu64 ", smaller than minimum length %d",
- SEV_and_metadata_len,
- kMinSEVAndMetadataLen);
+ if (edge_index >= iev->edge_count) {
+ CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge must be called with index edge_index less than edge count");
return false;
}
- const uint64_t SEV_len = SEV_and_metadata_len - kMetadataLen;
- CHECK_AND_RETURN(mc_reader_read_buffer(&reader, &iev->ServerEncryptedValue, SEV_len, status));
- // Ignore Metadata block.
- BSON_ASSERT(mc_reader_get_remaining_length(&reader) == kMetadataLen);
-
- iev->type = kTypeEquality;
+ // Write edge into out struct
+ *out = iev->metadata[edge_index];
return true;
}
-// -----------------------------------------------------------------------
-// Range
+bool mc_FLE2IndexedEncryptedValueV2_get_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev,
+ mc_FLE2TagAndEncryptedMetadataBlock_t *out,
+ mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(iev);
+ BSON_ASSERT_PARAM(out);
-bool mc_FLE2IndexedRangeEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
- const _mongocrypt_buffer_t *buf,
- mongocrypt_status_t *status) {
+ if (iev->type == kTypeInit) {
+ CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_metadata "
+ "must be called after "
+ "mc_FLE2IndexedEncryptedValueV2_parse");
+ return false;
+ }
+
+ if (iev->type != kTypeEquality) {
+ CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_metadata must be called with type equality");
+ return false;
+ }
+
+ // Write edge into out struct
+ *out = *iev->metadata;
+ return true;
+}
+
+bool mc_FLE2IndexedEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev,
+ const _mongocrypt_buffer_t *buf,
+ mongocrypt_status_t *status) {
BSON_ASSERT_PARAM(iev);
BSON_ASSERT_PARAM(buf);
+ if ((buf->data == NULL) || (buf->len == 0)) {
+ CLIENT_ERR("Empty buffer passed to mc_FLE2IndexedEncryptedValueV2_parse");
+ return false;
+ }
+
if (iev->type != kTypeInit) {
CLIENT_ERR("mc_FLE2IndexedRangeEncryptedValueV2_parse must not be "
"called twice");
@@ -397,10 +404,13 @@ bool mc_FLE2IndexedRangeEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t
CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->fle_blob_subtype, status));
- if (iev->fle_blob_subtype != MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2) {
- CLIENT_ERR("mc_FLE2IndexedRangeEncryptedValueV2_parse expected "
- "fle_blob_subtype %d got: %" PRIu8,
- MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2,
+ if (iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2) {
+ iev->type = kTypeEquality;
+ } else if (iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2) {
+ iev->type = kTypeRange;
+ } else {
+ CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_parse expected "
+ "fle_blob_subtype MC_SUBTYPE_FLE2Indexed(Equality|Range)EncryptedValueV2 got: %" PRIu8,
iev->fle_blob_subtype);
return false;
}
@@ -412,26 +422,88 @@ bool mc_FLE2IndexedRangeEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t
CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->bson_value_type, status));
/* Read edge_count */
- CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->edge_count, status));
+ // Set equality edge_count to 1 as it doesn't technically exist but
+ // there will be a singular metadata block
+ if (iev->type == kTypeEquality) {
+ iev->edge_count = 1;
+ } else {
+ CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->edge_count, status));
+ }
+
// Maximum edge_count(255) times kMetadataLen(96) fits easily without
// overflow.
- const uint64_t edges_len = iev->edge_count * kMetadataLen;
+ const uint64_t metadata_len = iev->edge_count * kMetadataLen;
/* Read ServerEncryptedValue. */
- const uint64_t min_required_len = kMinServerEncryptedValueLen + edges_len;
- const uint64_t SEV_and_edges_len = mc_reader_get_remaining_length(&reader);
- if (SEV_and_edges_len < min_required_len) {
+ const uint64_t min_required_len = kMinServerEncryptedValueLen + metadata_len;
+ const uint64_t SEV_and_metadata_len = mc_reader_get_remaining_length(&reader);
+ if (SEV_and_metadata_len < min_required_len) {
CLIENT_ERR("Invalid payload size %" PRIu64 ", smaller than minimum length %" PRIu64,
- SEV_and_edges_len,
+ SEV_and_metadata_len,
min_required_len);
return false;
}
- const uint64_t SEV_len = SEV_and_edges_len - edges_len;
+ const uint64_t SEV_len = SEV_and_metadata_len - metadata_len;
CHECK_AND_RETURN(mc_reader_read_buffer(&reader, &iev->ServerEncryptedValue, SEV_len, status));
- // Ignore Metadata block.
- BSON_ASSERT(mc_reader_get_remaining_length(&reader) == edges_len);
+ iev->metadata = (mc_FLE2TagAndEncryptedMetadataBlock_t *)bson_malloc0(
+ iev->edge_count * sizeof(mc_FLE2TagAndEncryptedMetadataBlock_t));
+
+ // Read each metadata element
+ for (uint8_t i = 0; i < iev->edge_count; i++) {
+ _mongocrypt_buffer_t tmp_buf;
+
+ CHECK_AND_RETURN(mc_reader_read_buffer(&reader, &tmp_buf, kMetadataLen, status));
+ CHECK_AND_RETURN(mc_FLE2TagAndEncryptedMetadataBlock_parse(&iev->metadata[i], &tmp_buf, status));
+
+ _mongocrypt_buffer_cleanup(&tmp_buf);
+ }
- iev->type = kTypeRange;
return true;
}
+
+bool mc_FLE2IndexedEncryptedValueV2_serialize(const mc_FLE2IndexedEncryptedValueV2_t *iev,
+ _mongocrypt_buffer_t *buf,
+ mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(iev);
+ BSON_ASSERT_PARAM(buf);
+
+ if (iev->type != kTypeRange && iev->type != kTypeEquality) {
+ CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_serialize must be called with type equality or range");
+ return false;
+ }
+
+ mc_writer_t writer;
+ mc_writer_init_from_buffer(&writer, buf, __FUNCTION__);
+
+ // Serialize fle_blob_subtype
+ CHECK_AND_RETURN(mc_writer_write_u8(&writer, iev->fle_blob_subtype, status));
+
+ // Serialize S_KeyId
+ CHECK_AND_RETURN(mc_writer_write_uuid_buffer(&writer, &iev->S_KeyId, status));
+
+ // Serialize bson_value_type
+ CHECK_AND_RETURN(mc_writer_write_u8(&writer, iev->bson_value_type, status));
+
+ // Serialize edge_count (only serialized for type range)
+ if (iev->type == kTypeRange) {
+ CHECK_AND_RETURN(mc_writer_write_u8(&writer, iev->edge_count, status));
+ }
+
+ // Serialize encrypted value
+ CHECK_AND_RETURN(
+ mc_writer_write_buffer(&writer, &iev->ServerEncryptedValue, iev->ServerEncryptedValue.len, status));
+
+ // Serialize metadata
+ for (int i = 0; i < iev->edge_count; ++i) {
+ _mongocrypt_buffer_t tmp_buf;
+ _mongocrypt_buffer_init_size(&tmp_buf, kMetadataLen);
+
+ CHECK_AND_RETURN(mc_FLE2TagAndEncryptedMetadataBlock_serialize(&iev->metadata[i], &tmp_buf, status));
+ CHECK_AND_RETURN(mc_writer_write_buffer(&writer, &tmp_buf, kMetadataLen, status));
+
+ _mongocrypt_buffer_cleanup(&tmp_buf);
+ }
+
+ return true;
+}
\ No newline at end of file
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev.c
index 48b2eef0a45..0d2654db18c 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev.c
@@ -100,7 +100,6 @@ bool mc_FLE2IndexedEncryptedValue_write(_mongocrypt_crypto_t *crypto,
goto cleanup; \
}
- const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm();
bool ok = false;
BSON_ASSERT_PARAM(crypto);
@@ -130,27 +129,6 @@ bool mc_FLE2IndexedEncryptedValue_write(_mongocrypt_crypto_t *crypto,
index_tokens,
&encryption_out,
status));
- uint32_t expected_plaintext_size = 0;
- CHECK_AND_GOTO(safe_uint32_t_sum(ClientEncryptedValue->len,
- (uint32_t)(sizeof(uint64_t) * 2 + sizeof(uint32_t) * 3),
- &expected_plaintext_size,
- status));
-
- uint32_t expected_cipher_size = fle2alg->get_ciphertext_len(expected_plaintext_size, status);
-
- if (expected_cipher_size == 0) {
- CHECK_AND_GOTO(false);
- }
-
- uint32_t expected_buf_size = 0;
- CHECK_AND_RETURN(
- safe_uint32_t_sum(expected_cipher_size, (uint32_t)(1 + sizeof(S_KeyId)), &expected_buf_size, status));
-
- if (buf->len < expected_buf_size) {
- CLIENT_ERR("mc_FLE2IndexedEncryptedValue_write buf is not large enough for iev");
- CHECK_AND_GOTO(false);
- }
-
mc_writer_t writer;
mc_writer_init_from_buffer(&writer, buf, __FUNCTION__);
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-rfds-private.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-rfds-private.h
index 3d99c6b4d00..cbdde696f4b 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-rfds-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-rfds-private.h
@@ -41,6 +41,11 @@ typedef struct {
mc_FLE2RangeOperator_t secondOp;
} mc_FLE2RangeFindDriverSpec_t;
+// `mc_FLE2RangeFindDriverSpec_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof_mc_FLE2RangeFindDriverSpec_t,
+ BSON_ALIGNOF(mc_FLE2RangeFindDriverSpec_t) >= BSON_ALIGNOF(bson_iter_t));
+
// mc_FLE2RangeFindDriverSpec_parse parses a FLE2RangeFindDriverSpec document.
bool mc_FLE2RangeFindDriverSpec_parse(mc_FLE2RangeFindDriverSpec_t *spec,
const bson_t *in,
@@ -52,7 +57,7 @@ bool mc_FLE2RangeFindDriverSpec_parse(mc_FLE2RangeFindDriverSpec_t *spec,
// `out` must be initialized by caller.
bool mc_FLE2RangeFindDriverSpec_to_placeholders(mc_FLE2RangeFindDriverSpec_t *spec,
const mc_RangeOpts_t *range_opts,
- int64_t maxContentionCounter,
+ int64_t maxContentionFactor,
const _mongocrypt_buffer_t *user_key_id,
const _mongocrypt_buffer_t *index_key_id,
int32_t payloadId,
@@ -73,11 +78,17 @@ typedef struct {
mc_FLE2RangeOperator_t secondOp;
bson_iter_t indexMin;
bson_iter_t indexMax;
- int64_t maxContentionCounter;
+ int64_t maxContentionFactor;
int64_t sparsity;
- mc_optional_uint32_t precision;
+ mc_optional_int32_t precision;
+ mc_optional_int32_t trimFactor;
} mc_makeRangeFindPlaceholder_args_t;
+// `mc_makeRangeFindPlaceholder_args_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof_mc_makeRangeFindPlaceholder_args_t,
+ BSON_ALIGNOF(mc_makeRangeFindPlaceholder_args_t) >= BSON_ALIGNOF(bson_iter_t));
+
// mc_makeRangeFindPlaceholder creates a placeholder to be consumed by
// libmongocrypt to encrypt a range find query. It is included in the header to
// be used by tests.
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-rfds.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-rfds.c
index 02dac30439b..0622bfd9d54 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-fle2-rfds.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-rfds.c
@@ -70,7 +70,7 @@ static bool parse_and(const bson_t *in, bson_iter_t *out, mongocrypt_status_t *s
BSON_ASSERT_PARAM(out);
BSON_ASSERT(status || true);
- bson_iter_t and;
+ bson_iter_t and = {0};
if (!bson_iter_init(&and, in) || !bson_iter_next(&and) || 0 != strcmp(bson_iter_key(&and), "$and")) {
ERR_WITH_BSON(in, "%s", "error unable to find '$and'");
return false;
@@ -105,7 +105,7 @@ parse_aggregate_expression(const bson_t *orig, bson_iter_t *in, operator_value_t
BSON_ASSERT_PARAM(out);
BSON_ASSERT(status || true);
- bson_iter_t array, value;
+ bson_iter_t array = {0}, value;
const char *op_type_str = bson_iter_key(in);
bool ok = false;
const char *field;
@@ -162,7 +162,7 @@ parse_match_expression(const bson_t *orig, bson_iter_t *in, operator_value_t *ou
BSON_ASSERT_PARAM(out);
BSON_ASSERT(status || true);
- bson_iter_t document, value;
+ bson_iter_t document = {0}, value;
const char *op_type_str;
bool ok = false;
const char *field = bson_iter_key(in);
@@ -217,7 +217,7 @@ bool mc_FLE2RangeFindDriverSpec_parse(mc_FLE2RangeFindDriverSpec_t *spec,
// {$and: [{$gt: ["$age", 5]}, {$lt:["$age", 50]}]}
// Or `in` may be a Match Expression with this form:
// {$and: [{age: {$gt: 5}}, {age: {$lt: 50}} ]}
- bson_iter_t and, array;
+ bson_iter_t and = {0}, array = {0};
bool ok = false;
if (!parse_and(in, &and, status)) {
@@ -264,7 +264,7 @@ bool mc_FLE2RangeFindDriverSpec_parse(mc_FLE2RangeFindDriverSpec_t *spec,
}
}
- operator_value_t op;
+ operator_value_t op = {0};
switch (arg_type) {
case AGGREGATE_EXPRESSION:
if (!parse_aggregate_expression(in, &doc, &op, status)) {
@@ -364,8 +364,10 @@ bool mc_makeRangeFindPlaceholder(mc_makeRangeFindPlaceholder_args_t *args,
TRY(bson_append_iter(edgesInfo, "indexMin", -1, &args->indexMin));
TRY(bson_append_iter(edgesInfo, "indexMax", -1, &args->indexMax));
if (args->precision.set) {
- BSON_ASSERT(args->precision.value <= INT32_MAX);
- TRY(BSON_APPEND_INT32(edgesInfo, "precision", (int32_t)args->precision.value));
+ TRY(BSON_APPEND_INT32(edgesInfo, "precision", args->precision.value));
+ }
+ if (args->trimFactor.set) {
+ TRY(BSON_APPEND_INT32(edgesInfo, "trimFactor", args->trimFactor.value));
}
TRY(BSON_APPEND_DOCUMENT(v, "edgesInfo", edgesInfo));
}
@@ -383,7 +385,7 @@ bool mc_makeRangeFindPlaceholder(mc_makeRangeFindPlaceholder_args_t *args,
TRY(_mongocrypt_buffer_append(args->index_key_id, p, "ki", 2));
TRY(_mongocrypt_buffer_append(args->user_key_id, p, "ku", 2));
TRY(BSON_APPEND_DOCUMENT(p, "v", v));
- TRY(BSON_APPEND_INT64(p, "cm", args->maxContentionCounter));
+ TRY(BSON_APPEND_INT64(p, "cm", args->maxContentionFactor));
TRY(BSON_APPEND_INT64(p, "s", args->sparsity));
#undef TRY
@@ -403,7 +405,7 @@ fail:
bool mc_FLE2RangeFindDriverSpec_to_placeholders(mc_FLE2RangeFindDriverSpec_t *spec,
const mc_RangeOpts_t *range_opts,
- int64_t maxContentionCounter,
+ int64_t maxContentionFactor,
const _mongocrypt_buffer_t *user_key_id,
const _mongocrypt_buffer_t *index_key_id,
int32_t payloadId,
@@ -469,8 +471,9 @@ bool mc_FLE2RangeFindDriverSpec_to_placeholders(mc_FLE2RangeFindDriverSpec_t *sp
.indexMin = indexMin,
.indexMax = indexMax,
.precision = range_opts->precision,
- .maxContentionCounter = maxContentionCounter,
- .sparsity = range_opts->sparsity};
+ .maxContentionFactor = maxContentionFactor,
+ .sparsity = range_opts->sparsity,
+ .trimFactor = range_opts->trimFactor};
// First operator is the non-stub.
if (!mc_makeRangeFindPlaceholder(&args, &p1, status)) {
@@ -485,7 +488,7 @@ bool mc_FLE2RangeFindDriverSpec_to_placeholders(mc_FLE2RangeFindDriverSpec_t *sp
.payloadId = payloadId,
.firstOp = spec->firstOp,
.secondOp = spec->secondOp,
- .maxContentionCounter = maxContentionCounter,
+ .maxContentionFactor = maxContentionFactor,
.sparsity = range_opts->sparsity};
// First operator is the non-stub.
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-tag-and-encrypted-metadata-block-private.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-tag-and-encrypted-metadata-block-private.h
new file mode 100644
index 00000000000..e287c43e501
--- /dev/null
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-tag-and-encrypted-metadata-block-private.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MC_FLE2_TAG_AND_ENCRYPTED_METADATA_BLOCK_H
+#define MC_FLE2_TAG_AND_ENCRYPTED_METADATA_BLOCK_H
+
+#include "mc-reader-private.h"
+#include "mc-writer-private.h"
+#include "mongocrypt-private.h"
+
+typedef struct {
+ _mongocrypt_buffer_t encryptedCount;
+ _mongocrypt_buffer_t tag;
+ _mongocrypt_buffer_t encryptedZeros;
+} mc_FLE2TagAndEncryptedMetadataBlock_t;
+
+#define kFieldLen 32U
+
+void mc_FLE2TagAndEncryptedMetadataBlock_init(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata);
+
+void mc_FLE2TagAndEncryptedMetadataBlock_cleanup(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata);
+
+bool mc_FLE2TagAndEncryptedMetadataBlock_parse(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata,
+ const _mongocrypt_buffer_t *buf,
+ mongocrypt_status_t *status);
+
+bool mc_FLE2TagAndEncryptedMetadataBlock_serialize(const mc_FLE2TagAndEncryptedMetadataBlock_t *metadata,
+ _mongocrypt_buffer_t *buf,
+ mongocrypt_status_t *status);
+
+#endif /* MC_FLE2_TAG_AND_ENCRYPTED_METADATA_BLOCK_H */
\ No newline at end of file
diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-tag-and-encrypted-metadata-block.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-tag-and-encrypted-metadata-block.c
new file mode 100644
index 00000000000..404e9840367
--- /dev/null
+++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-tag-and-encrypted-metadata-block.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2024-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mc-fle2-tag-and-encrypted-metadata-block-private.h"
+#include "mc-reader-private.h"
+#include "mc-writer-private.h"
+#include "mongocrypt-private.h"
+
+#define CHECK_AND_RETURN(x) \
+ if (!(x)) { \
+ return false; \
+ }
+
+void mc_FLE2TagAndEncryptedMetadataBlock_init(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata) {
+ BSON_ASSERT_PARAM(metadata);
+ memset(metadata, 0, sizeof(mc_FLE2TagAndEncryptedMetadataBlock_t));
+}
+
+void mc_FLE2TagAndEncryptedMetadataBlock_cleanup(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata) {
+ BSON_ASSERT_PARAM(metadata);
+
+ _mongocrypt_buffer_cleanup(&metadata->encryptedCount);
+ _mongocrypt_buffer_cleanup(&metadata->tag);
+ _mongocrypt_buffer_cleanup(&metadata->encryptedZeros);
+}
+
+bool mc_FLE2TagAndEncryptedMetadataBlock_parse(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata,
+ const _mongocrypt_buffer_t *buf,
+ mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(metadata);
+ BSON_ASSERT_PARAM(buf);
+
+ if ((buf->data == NULL) || (buf->len == 0)) {
+ CLIENT_ERR("Empty buffer passed to mc_FLE2IndexedEncryptedValueV2_parse");
+ return false;
+ }
+
+ mc_reader_t reader;
+ mc_reader_init_from_buffer(&reader, buf, __FUNCTION__);
+
+ mc_FLE2TagAndEncryptedMetadataBlock_init(metadata);
+
+ CHECK_AND_RETURN(mc_reader_read_buffer(&reader, &metadata->encryptedCount, kFieldLen, status));
+
+ CHECK_AND_RETURN(mc_reader_read_buffer(&reader, &metadata->tag, kFieldLen, status));
+
+ CHECK_AND_RETURN(mc_reader_read_buffer(&reader, &metadata->encryptedZeros, kFieldLen, status));
+
+ return true;
+}
+
+bool mc_FLE2TagAndEncryptedMetadataBlock_serialize(const mc_FLE2TagAndEncryptedMetadataBlock_t *metadata,
+ _mongocrypt_buffer_t *buf,
+ mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(metadata);
+ BSON_ASSERT_PARAM(buf);
+
+ mc_writer_t writer;
+ mc_writer_init_from_buffer(&writer, buf, __FUNCTION__);
+
+ CHECK_AND_RETURN(mc_writer_write_buffer(&writer, &metadata->encryptedCount, kFieldLen, status));
+
+ CHECK_AND_RETURN(mc_writer_write_buffer(&writer, &metadata->tag, kFieldLen, status));
+
+ CHECK_AND_RETURN(mc_writer_write_buffer(&writer, &metadata->encryptedZeros, kFieldLen, status));
+
+ return true;
+}
diff --git a/src/third_party/libmongocrypt/dist/src/mc-optional-private.h b/src/third_party/libmongocrypt/dist/src/mc-optional-private.h
index d27a53d605b..42a92eb398b 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-optional-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-optional-private.h
@@ -23,6 +23,17 @@
#include "./mc-dec128.h"
#include "./mlib/int128.h"
+typedef struct {
+ bool set;
+ bool value;
+} mc_optional_bool_t;
+
+#define OPT_BOOL(val) \
+ (mc_optional_bool_t) { .set = true, .value = val }
+
+#define OPT_BOOL_C(val) \
+ { .set = true, .value = val }
+
typedef struct {
bool set;
int32_t value;
diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation-private.h b/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation-private.h
index e9d2a5f86f6..268eef335e6 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation-private.h
@@ -37,49 +37,59 @@ size_t mc_edges_len(mc_edges_t *edges);
// mc_edges_destroys frees `edges`.
void mc_edges_destroy(mc_edges_t *edges);
+// mc_edges_is_leaf returns whether the given edge is the leaf node of the edge set.
+bool mc_edges_is_leaf(const mc_edges_t *edges, const char *edge);
+
+// Return the trimFactor that was used to generate these edges.
+int32_t mc_edges_get_used_trimFactor(const mc_edges_t *edges);
+
typedef struct {
int32_t value;
mc_optional_int32_t min;
mc_optional_int32_t max;
size_t sparsity;
+ mc_optional_int32_t trimFactor;
} mc_getEdgesInt32_args_t;
// mc_getEdgesInt32 implements the Edge Generation algorithm described in
// SERVER-67751 for int32_t.
-mc_edges_t *mc_getEdgesInt32(mc_getEdgesInt32_args_t args, mongocrypt_status_t *status);
+mc_edges_t *mc_getEdgesInt32(mc_getEdgesInt32_args_t args, mongocrypt_status_t *status, bool use_range_v2);
typedef struct {
int64_t value;
mc_optional_int64_t min;
mc_optional_int64_t max;
size_t sparsity;
+ mc_optional_int32_t trimFactor;
} mc_getEdgesInt64_args_t;
// mc_getEdgesInt64 implements the Edge Generation algorithm described in
// SERVER-67751 for int64_t.
-mc_edges_t *mc_getEdgesInt64(mc_getEdgesInt64_args_t args, mongocrypt_status_t *status);
+mc_edges_t *mc_getEdgesInt64(mc_getEdgesInt64_args_t args, mongocrypt_status_t *status, bool use_range_v2);
typedef struct {
double value;
size_t sparsity;
mc_optional_double_t min;
mc_optional_double_t max;
- mc_optional_uint32_t precision;
+ mc_optional_int32_t precision;
+ mc_optional_int32_t trimFactor;
} mc_getEdgesDouble_args_t;
// mc_getEdgesDouble implements the Edge Generation algorithm described in
// SERVER-67751 for double.
-mc_edges_t *mc_getEdgesDouble(mc_getEdgesDouble_args_t args, mongocrypt_status_t *status);
+mc_edges_t *mc_getEdgesDouble(mc_getEdgesDouble_args_t args, mongocrypt_status_t *status, bool use_range_v2);
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
typedef struct {
mc_dec128 value;
size_t sparsity;
mc_optional_dec128_t min, max;
- mc_optional_uint32_t precision;
+ mc_optional_int32_t precision;
+ mc_optional_int32_t trimFactor;
} mc_getEdgesDecimal128_args_t;
-mc_edges_t *mc_getEdgesDecimal128(mc_getEdgesDecimal128_args_t args, mongocrypt_status_t *status);
+mc_edges_t *mc_getEdgesDecimal128(mc_getEdgesDecimal128_args_t args, mongocrypt_status_t *status, bool use_range_v2);
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
BSON_STATIC_ASSERT2(ull_is_u64, sizeof(uint64_t) == sizeof(unsigned long long));
diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation.c b/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation.c
index 3e7c5d0a00a..83b1900c042 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation.c
@@ -14,10 +14,12 @@
* limitations under the License.
*/
+#include "mc-optional-private.h"
#include "mc-range-edge-generation-private.h"
#include "mc-array-private.h"
#include "mc-check-conversions-private.h"
+#include "mc-cmp-private.h"
#include "mc-range-encoding-private.h"
#include "mongocrypt-private.h"
@@ -25,27 +27,60 @@ struct _mc_edges_t {
size_t sparsity;
/* edges is an array of `char*` edge strings. */
mc_array_t edges;
+ char *leaf;
+ int32_t usedTrimFactor; // The `trimFactor` that was used to produce these edges.
};
-static mc_edges_t *mc_edges_new(const char *leaf, size_t sparsity, mongocrypt_status_t *status) {
+int32_t mc_edges_get_used_trimFactor(const mc_edges_t *edges) {
+ return edges->usedTrimFactor;
+}
+
+static mc_edges_t *mc_edges_new(const char *leaf,
+ size_t sparsity,
+ mc_optional_int32_t opt_trimFactor,
+ mongocrypt_status_t *status,
+ bool use_range_v2) {
BSON_ASSERT_PARAM(leaf);
if (sparsity < 1) {
CLIENT_ERR("sparsity must be 1 or larger");
return NULL;
}
+
+ const size_t leaf_len = strlen(leaf);
+ const int32_t trimFactor = trimFactorDefault(leaf_len, opt_trimFactor, use_range_v2);
+ if (trimFactor != 0 && mc_cmp_greater_equal_su(trimFactor, leaf_len)) {
+ // We append a total of leaf_len + 1 (for the root) - trimFactor edges. When this number is equal to 1, we
+ // degenerate into equality, which is not desired, so trimFactor must be less than leaf_len.
+ CLIENT_ERR("trimFactor must be less than the number of bits (%ld) used to represent an element of the domain, "
+ "but got %" PRId32,
+ leaf_len,
+ trimFactor);
+ return NULL;
+ }
+ if (trimFactor < 0) {
+ CLIENT_ERR("trimFactor must be >= 0, but got %" PRId32, trimFactor);
+ return NULL;
+ }
+
mc_edges_t *edges = bson_malloc0(sizeof(mc_edges_t));
+ edges->usedTrimFactor = trimFactor;
edges->sparsity = sparsity;
_mc_array_init(&edges->edges, sizeof(char *));
+ edges->leaf = bson_strdup(leaf);
- char *root = bson_strdup("root");
- _mc_array_append_val(&edges->edges, root);
+ if (trimFactor == 0) {
+ char *root = bson_strdup("root");
+ _mc_array_append_val(&edges->edges, root);
+ }
char *leaf_copy = bson_strdup(leaf);
_mc_array_append_val(&edges->edges, leaf_copy);
- const size_t leaf_len = strlen(leaf);
- // Start loop at 1. The full leaf is unconditionally appended after loop.
- for (size_t i = 1; i < leaf_len; i++) {
+ // Start loop at max(trimFactor, 1). The full leaf is unconditionally appended after loop.
+ BSON_ASSERT(mc_in_range_size_t_signed(trimFactor));
+ size_t trimFactor_sz = (size_t)trimFactor;
+ size_t startLevel = trimFactor > 0 ? trimFactor_sz : 1;
+ for (size_t i = startLevel; i < leaf_len; i++) {
if (i % sparsity == 0) {
char *edge = bson_malloc(i + 1);
bson_strncpy(edge, leaf, i + 1);
@@ -78,20 +113,26 @@ void mc_edges_destroy(mc_edges_t *edges) {
bson_free(val);
}
_mc_array_destroy(&edges->edges);
+ bson_free(edges->leaf);
bson_free(edges);
}
+bool mc_edges_is_leaf(const mc_edges_t *edges, const char *edge) {
+ BSON_ASSERT_PARAM(edges);
+ BSON_ASSERT_PARAM(edge);
+
+ return strcmp(edge, edges->leaf) == 0;
+}
+
mc_bitstring mc_convert_to_bitstring_u64(uint64_t in) {
mc_bitstring ret = {{0}};
char *out = ret.str;
uint64_t bit = UINT64_C(1) << 63;
+ int loops = 0; // used to determine a bit shift
while (bit > 0) {
- if (bit & in) {
- *out++ = '1';
- } else {
- *out++ = '0';
- }
+ *out++ = (char)('0' + ((bit & in) >> (63 - loops)));
bit >>= 1;
+ loops++;
}
return ret;
}
@@ -122,7 +163,7 @@ mc_bitstring mc_convert_to_bitstring_u128(mlib_int128 i) {
return ret;
}
-mc_edges_t *mc_getEdgesInt32(mc_getEdgesInt32_args_t args, mongocrypt_status_t *status) {
+mc_edges_t *mc_getEdgesInt32(mc_getEdgesInt32_args_t args, mongocrypt_status_t *status, bool use_range_v2) {
mc_OSTType_Int32 got;
if (!mc_getTypeInfo32((mc_getTypeInfo32_args_t){.value = args.value, .min = args.min, .max = args.max},
&got,
@@ -138,11 +179,11 @@ mc_edges_t *mc_getEdgesInt32(mc_getEdgesInt32_args_t args, mongocrypt_status_t *
mc_bitstring valueBin = mc_convert_to_bitstring_u32(got.value);
size_t offset = mc_count_leading_zeros_u32(got.max);
const char *leaf = valueBin.str + offset;
- mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, status);
+ mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, args.trimFactor, status, use_range_v2);
return ret;
}
-mc_edges_t *mc_getEdgesInt64(mc_getEdgesInt64_args_t args, mongocrypt_status_t *status) {
+mc_edges_t *mc_getEdgesInt64(mc_getEdgesInt64_args_t args, mongocrypt_status_t *status, bool use_range_v2) {
mc_OSTType_Int64 got;
if (!mc_getTypeInfo64((mc_getTypeInfo64_args_t){.value = args.value, .min = args.min, .max = args.max},
&got,
@@ -158,18 +199,19 @@ mc_edges_t *mc_getEdgesInt64(mc_getEdgesInt64_args_t args, mongocrypt_status_t *
mc_bitstring valueBin = mc_convert_to_bitstring_u64(got.value);
size_t offset = mc_count_leading_zeros_u64(got.max);
const char *leaf = valueBin.str + offset;
- mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, status);
+ mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, args.trimFactor, status, use_range_v2);
return ret;
}
-mc_edges_t *mc_getEdgesDouble(mc_getEdgesDouble_args_t args, mongocrypt_status_t *status) {
+mc_edges_t *mc_getEdgesDouble(mc_getEdgesDouble_args_t args, mongocrypt_status_t *status, bool use_range_v2) {
mc_OSTType_Double got;
if (!mc_getTypeInfoDouble((mc_getTypeInfoDouble_args_t){.value = args.value,
.min = args.min,
.max = args.max,
.precision = args.precision},
&got,
- status)) {
+ status,
+ use_range_v2)) {
return NULL;
}
@@ -181,12 +223,12 @@ mc_edges_t *mc_getEdgesDouble(mc_getEdgesDouble_args_t args, mongocrypt_status_t
mc_bitstring valueBin = mc_convert_to_bitstring_u64(got.value);
size_t offset = mc_count_leading_zeros_u64(got.max);
const char *leaf = valueBin.str + offset;
- mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, status);
+ mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, args.trimFactor, status, use_range_v2);
return ret;
}
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
-mc_edges_t *mc_getEdgesDecimal128(mc_getEdgesDecimal128_args_t args, mongocrypt_status_t *status) {
+mc_edges_t *mc_getEdgesDecimal128(mc_getEdgesDecimal128_args_t args, mongocrypt_status_t *status, bool use_range_v2) {
mc_OSTType_Decimal128 got;
if (!mc_getTypeInfoDecimal128(
(mc_getTypeInfoDecimal128_args_t){
@@ -196,7 +238,8 @@ mc_edges_t *mc_getEdgesDecimal128(mc_getEdgesDecimal128_args_t args, mongocrypt_
.precision = args.precision,
},
&got,
- status)) {
+ status,
+ use_range_v2)) {
return NULL;
}
@@ -205,7 +248,7 @@ mc_edges_t *mc_getEdgesDecimal128(mc_getEdgesDecimal128_args_t args, mongocrypt_
mc_bitstring bits = mc_convert_to_bitstring_u128(got.value);
size_t offset = mc_count_leading_zeros_u128(got.max);
const char *leaf = bits.str + offset;
- mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, status);
+ mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, args.trimFactor, status, use_range_v2);
return ret;
}
-#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
\ No newline at end of file
+#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-encoding-private.h b/src/third_party/libmongocrypt/dist/src/mc-range-encoding-private.h
index a79e97a47ad..2250b1fb476 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-range-encoding-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-range-encoding-private.h
@@ -83,14 +83,22 @@ typedef struct {
double value;
mc_optional_double_t min;
mc_optional_double_t max;
- mc_optional_uint32_t precision;
+ mc_optional_int32_t precision;
} mc_getTypeInfoDouble_args_t;
+// `mc_canUsePrecisionModeDouble` returns true if the domain can be represented in fewer than 64 bits.
+bool mc_canUsePrecisionModeDouble(double min,
+ double max,
+ int32_t precision,
+ uint32_t *maxBitsOut,
+ mongocrypt_status_t *status);
+
/* mc_getTypeInfoDouble encodes the double `args.value` into an OSTType_Double
* `out`. Returns false and sets `status` on error. */
bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args,
mc_OSTType_Double *out,
- mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;
+ mongocrypt_status_t *status,
+ bool use_range_v2) MONGOCRYPT_WARN_UNUSED_RESULT;
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
/**
@@ -103,9 +111,16 @@ typedef struct {
typedef struct {
mc_dec128 value;
mc_optional_dec128_t min, max;
- mc_optional_uint32_t precision;
+ mc_optional_int32_t precision;
} mc_getTypeInfoDecimal128_args_t;
+// `mc_canUsePrecisionModeDecimal` returns true if the domain can be represented in fewer than 128 bits.
+bool mc_canUsePrecisionModeDecimal(mc_dec128 min,
+ mc_dec128 max,
+ int32_t precision,
+ uint32_t *maxBitsOut,
+ mongocrypt_status_t *status);
+
/**
* @brief Obtain the OST encoding of a finite Decimal128 value.
*
@@ -116,7 +131,11 @@ typedef struct {
*/
bool mc_getTypeInfoDecimal128(mc_getTypeInfoDecimal128_args_t args,
mc_OSTType_Decimal128 *out,
- mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;
+ mongocrypt_status_t *status,
+ bool use_range_v2) MONGOCRYPT_WARN_UNUSED_RESULT;
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
+extern const int64_t mc_FLERangeSparsityDefault;
+
+int32_t trimFactorDefault(size_t maxlen, mc_optional_int32_t trimFactor, bool use_range_v2);
#endif /* MC_RANGE_ENCODING_PRIVATE_H */
diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-encoding.c b/src/third_party/libmongocrypt/dist/src/mc-range-encoding.c
index 65f34915b99..e7d423b4be4 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-range-encoding.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-range-encoding.c
@@ -15,6 +15,7 @@
*/
#include "mc-check-conversions-private.h"
+#include "mc-cmp-private.h"
#include "mc-range-encoding-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt-util-private.h" // mc_isinf
@@ -162,8 +163,131 @@ bool mc_getTypeInfo64(mc_getTypeInfo64_args_t args, mc_OSTType_Int64 *out, mongo
}
#define exp10Double(x) pow(10, x)
+#define SCALED_DOUBLE_BOUNDS 9007199254740992.0 // 2^53
-bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, mc_OSTType_Double *out, mongocrypt_status_t *status) {
+uint64_t subtract_int64_t(int64_t max, int64_t min) {
+ BSON_ASSERT(max > min);
+ // If the values have the same sign, then simple subtraction
+ // will work because we know max > min.
+ if ((max > 0 && min > 0) || (max < 0 && min < 0)) {
+ return (uint64_t)(max - min);
+ }
+
+ // If they are opposite signs, then we can just invert
+ // min to be positive and return the sum.
+ uint64_t u_return = (uint64_t)max;
+ u_return += (uint64_t)(~min + 1);
+ return u_return;
+}
+
+bool ceil_log2_double(uint64_t i, uint32_t *maxBitsOut, mongocrypt_status_t *status) {
+ if (i == 0) {
+ CLIENT_ERR("Invalid input to ceil_log2_double function. Input cannot be 0.");
+ return false;
+ }
+
+ uint32_t clz = (uint32_t)_mlibCountLeadingZeros_u64(i);
+ uint32_t bits;
+ if ((i & (i - 1)) == 0) {
+ bits = 64 - clz - 1;
+ } else {
+ bits = 64 - clz;
+ }
+ *maxBitsOut = bits;
+ return true;
+}
+
+bool mc_canUsePrecisionModeDouble(double min,
+ double max,
+ int32_t precision,
+ uint32_t *maxBitsOut,
+ mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(maxBitsOut);
+ BSON_ASSERT(precision >= 0);
+
+ if (min >= max) {
+ CLIENT_ERR("Invalid bounds for double range precision, min must be less than max. min: %g, max: %g", min, max);
+ return false;
+ }
+
+ const double scaled_prc = exp10Double(precision);
+
+ const double scaled_max = max * scaled_prc;
+ const double scaled_min = min * scaled_prc;
+
+ if (scaled_max != trunc(scaled_max)) {
+ CLIENT_ERR("Invalid upper bound for double precision. Fractional digits must be less than the specified "
+ "precision value. max: %g",
+ max);
+ return false;
+ }
+
+ if (scaled_min != trunc(scaled_min)) {
+ CLIENT_ERR("Invalid lower bound for double precision. Fractional digits must be less than the specified "
+ "precision value. min: %g",
+ min);
+ return false;
+ }
+
+ if (fabs(scaled_max) >= SCALED_DOUBLE_BOUNDS) {
+ CLIENT_ERR(
+ "Invalid upper bound for double precision. Absolute scaled value of max must be less than %g. max: %g",
+ SCALED_DOUBLE_BOUNDS,
+ max);
+ return false;
+ }
+
+ if (fabs(scaled_min) >= SCALED_DOUBLE_BOUNDS) {
+ CLIENT_ERR(
+ "Invalid lower bound for double precision. Absolute scaled value of min must be less than %g. min: %g",
+ SCALED_DOUBLE_BOUNDS,
+ min);
+ return false;
+ }
+
+ const double t_1 = scaled_max - scaled_min;
+ const double t_4 = (double)UINT64_MAX - t_1;
+ const double t_5 = floor(log10(t_4)) - 1;
+
+ if ((double)precision > t_5) {
+ CLIENT_ERR("Invalid value for precision. precision: %" PRId32, precision);
+ return false;
+ }
+
+ const int64_t i_1 = (int64_t)(scaled_max);
+ const int64_t i_2 = (int64_t)(scaled_min);
+
+ const uint64_t range = subtract_int64_t(i_1, i_2);
+
+ if (((uint64_t)scaled_prc) > UINT64_MAX - range) {
+ CLIENT_ERR("Invalid value for min, max, and precision. The calculated domain size is too large. min: %g, max: "
+ "%g, precision: %" PRId32,
+ min,
+ max,
+ precision);
+ return false;
+ }
+
+ const uint64_t i_3 = range + (uint64_t)(scaled_prc);
+
+ if (!ceil_log2_double(i_3, maxBitsOut, status)) {
+ return false;
+ }
+
+ // Integers between -2^53 and 2^53 can be exactly represented. Outside this range, doubles lose precision by a
+ // multiple of 2^(n-52) where n = #bits. We disallow users from using precision mode when the bounds exceed 2^53 to
+ // prevent the users from being surprised by how floating point math works.
+ if (*maxBitsOut >= 53) {
+ return false;
+ }
+
+ return true;
+}
+
+bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args,
+ mc_OSTType_Double *out,
+ mongocrypt_status_t *status,
+ bool use_range_v2) {
if (args.min.set != args.max.set || args.min.set != args.precision.set) {
CLIENT_ERR("min, max, and precision must all be set or must all be unset");
return false;
@@ -194,6 +318,19 @@ bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, mc_OSTType_Double *o
}
}
+ if (args.precision.set) {
+ if (args.precision.value < 0) {
+ CLIENT_ERR("Precision must be non-negative, but got %" PRId32, args.precision.value);
+ return false;
+ }
+
+ double scaled = exp10Double(args.precision.value);
+ if (!mc_isfinite(scaled)) {
+ CLIENT_ERR("Precision is too large and cannot be used to calculate the scaled range bounds");
+ return false;
+ }
+ }
+
const bool is_neg = args.value < 0.0;
// Map negative 0 to zero so sign bit is 0.
@@ -210,39 +347,33 @@ bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, mc_OSTType_Double *o
bool use_precision_mode = false;
uint32_t bits_range;
if (args.precision.set) {
- // Subnormal representations can support up to 5x10^-324 as a number
- if (args.precision.value > 324) {
- CLIENT_ERR("Precision must be between 0 and 324 inclusive, got: %" PRIu32, args.precision.value);
+ use_precision_mode =
+ mc_canUsePrecisionModeDouble(args.min.value, args.max.value, args.precision.value, &bits_range, status);
+
+ if (!use_precision_mode && use_range_v2) {
+ if (!mongocrypt_status_ok(status)) {
+ return false;
+ }
+
+ CLIENT_ERR("The domain of double values specified by the min, max, and precision cannot be represented in "
+ "fewer than 53 bits. min: %g, max: %g, precision: %" PRId32,
+ args.min.value,
+ args.max.value,
+ args.precision.value);
return false;
}
- double range = args.max.value - args.min.value;
-
- // We can overflow if max = max double and min = min double so make sure
- // we have finite number after we do subtraction
- // Ignore conversion warnings to fix error with glibc.
- if (mc_isfinite(range)) {
- // This creates a range which is wider then we permit by our min/max
- // bounds check with the +1 but it is as the algorithm is written in
- // WRITING-11907.
- double rangeAndPrecision = (range + 1) * exp10Double(args.precision.value);
-
- if (mc_isfinite(rangeAndPrecision)) {
- double bits_range_double = log2(rangeAndPrecision);
- bits_range = (uint32_t)ceil(bits_range_double);
-
- if (bits_range < 64) {
- use_precision_mode = true;
- }
- }
- }
+ // If we are not in range_v2, then we don't care about the error returned
+ // from canUsePrecisionMode so we can reset the status.
+ _mongocrypt_status_reset(status);
}
if (use_precision_mode) {
// Take a number of xxxx.ppppp and truncate it xxxx.ppp if precision = 3.
// We do not change the digits before the decimal place.
- double v_prime = trunc(args.value * exp10Double(args.precision.value)) / exp10Double(args.precision.value);
- int64_t v_prime2 = (int64_t)((v_prime - args.min.value) * exp10Double(args.precision.value));
+ int64_t v_prime = (int64_t)(trunc(args.value * exp10Double(args.precision.value)));
+ int64_t scaled_min = (int64_t)(args.min.value * exp10Double(args.precision.value));
+ int64_t v_prime2 = v_prime - scaled_min;
BSON_ASSERT(v_prime2 < INT64_MAX && v_prime2 >= 0);
@@ -298,7 +429,7 @@ bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, mc_OSTType_Double *o
* @param dec
* @return mlib_int128
*/
-static mlib_int128 dec128_to_int128(mc_dec128 dec) {
+static mlib_int128 dec128_to_uint128(mc_dec128 dec) {
// Only normal numbers
BSON_ASSERT(mc_dec128_is_finite(dec));
BSON_ASSERT(!mc_dec128_is_nan(dec));
@@ -313,6 +444,7 @@ static mlib_int128 dec128_to_int128(mc_dec128 dec) {
// Decimal128:
int32_t exp = ((int32_t)mc_dec128_get_biased_exp(dec)) - MC_DEC128_EXPONENT_BIAS;
// We will scale up/down based on whether it is negative:
+ BSON_ASSERT(abs(exp) <= UINT8_MAX);
mlib_int128 e1 = mlib_int128_pow10((uint8_t)abs(exp));
if (exp < 0) {
ret = mlib_int128_div(ret, e1);
@@ -323,15 +455,176 @@ static mlib_int128 dec128_to_int128(mc_dec128 dec) {
return ret;
}
+// (2^127 - 1) = the maximum signed 128-bit integer value, as a decimal128
+#define INT_128_MAX_AS_DECIMAL mc_dec128_from_string("170141183460469231731687303715884105727")
+// (2^128 - 1) = the max unsigned 128-bit integer value, as a decimal128
+#define UINT_128_MAX_AS_DECIMAL mc_dec128_from_string("340282366920938463463374607431768211455")
+
+static mlib_int128 dec128_to_int128(mc_dec128 dec) {
+ BSON_ASSERT(mc_dec128_less(dec, INT_128_MAX_AS_DECIMAL));
+
+ bool negative = false;
+
+ if (mc_dec128_is_negative(dec)) {
+ negative = true;
+ dec = mc_dec128_mul(MC_DEC128(-1), dec);
+ }
+
+ mlib_int128 ret_val = dec128_to_uint128(dec);
+
+ if (negative) {
+ ret_val = mlib_int128_mul(MLIB_INT128(-1), ret_val);
+ }
+
+ return ret_val;
+}
+
+bool ceil_log2_int128(mlib_int128 i, uint32_t *maxBitsOut, mongocrypt_status_t *status) {
+ if (mlib_int128_eq(i, MLIB_INT128(0))) {
+ CLIENT_ERR("Invalid input to ceil_log2_int128 function. Input cannot be 0.");
+ return false;
+ }
+
+ uint32_t clz = (uint32_t)_mlibCountLeadingZeros_u128(i);
+ uint32_t bits;
+
+ // if i & (i - 1) == 0
+ if (mlib_int128_eq((mlib_int128_bitand(i, (mlib_int128_sub(i, MLIB_INT128(1))))), MLIB_INT128(0))) {
+ bits = 128 - clz - 1;
+ } else {
+ bits = 128 - clz;
+ }
+ *maxBitsOut = bits;
+ return true;
+}
+
+bool mc_canUsePrecisionModeDecimal(mc_dec128 min,
+ mc_dec128 max,
+ int32_t precision,
+ uint32_t *maxBitsOut,
+ mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(maxBitsOut);
+ BSON_ASSERT(precision >= 0);
+
+ if (!mc_dec128_is_finite(max)) {
+ CLIENT_ERR("Invalid upper bound for Decimal128 precision. Max is infinite.");
+ return false;
+ }
+
+ if (!mc_dec128_is_finite(min)) {
+ CLIENT_ERR("Invalid lower bound for Decimal128 precision. Min is infinite.");
+ return false;
+ }
+
+ if (mc_dec128_greater_equal(min, max)) {
+ CLIENT_ERR("Invalid upper and lower bounds for Decimal128 precision. Min must be strictly less than max. min: "
+ "%s, max: %s",
+ mc_dec128_to_string(min).str,
+ mc_dec128_to_string(max).str);
+ return false;
+ }
+
+ mc_dec128 scaled_max = mc_dec128_scale(max, precision);
+ mc_dec128 scaled_min = mc_dec128_scale(min, precision);
+
+ mc_dec128 scaled_max_trunc = mc_dec128_round_integral_ex(scaled_max, MC_DEC128_ROUND_TOWARD_ZERO, NULL);
+ mc_dec128 scaled_min_trunc = mc_dec128_round_integral_ex(scaled_min, MC_DEC128_ROUND_TOWARD_ZERO, NULL);
+
+ if (mc_dec128_not_equal(scaled_max, scaled_max_trunc)) {
+ CLIENT_ERR("Invalid upper bound for Decimal128 precision. Fractional digits must be less than "
+ "the specified precision value. max: %s, precision: %" PRId32,
+ mc_dec128_to_string(max).str,
+ precision);
+ return false;
+ }
+
+ if (mc_dec128_not_equal(scaled_min, scaled_min_trunc)) {
+ CLIENT_ERR("Invalid lower bound for Decimal128 precision. Fractional digits must be less than "
+ "the specified precision value. min: %s, precision: %" PRId32,
+ mc_dec128_to_string(min).str,
+ precision);
+ return false;
+ }
+
+ if (mc_dec128_greater(mc_dec128_abs(scaled_max), INT_128_MAX_AS_DECIMAL)) {
+ CLIENT_ERR("Invalid upper bound for Decimal128 precision. Absolute scaled value must be less than "
+ "or equal to %s. max: %s",
+ mc_dec128_to_string(INT_128_MAX_AS_DECIMAL).str,
+ mc_dec128_to_string(max).str);
+ return false;
+ }
+
+ if (mc_dec128_greater(mc_dec128_abs(scaled_min), INT_128_MAX_AS_DECIMAL)) {
+ CLIENT_ERR("Invalid lower bound for Decimal128 precision. Absolute scaled value must be less than "
+ "or equal to %s. min: %s",
+ mc_dec128_to_string(INT_128_MAX_AS_DECIMAL).str,
+ mc_dec128_to_string(min).str);
+ return false;
+ }
+
+ mc_dec128 t_1 = mc_dec128_sub(scaled_max, scaled_min);
+ mc_dec128 t_4 = mc_dec128_sub(UINT_128_MAX_AS_DECIMAL, t_1);
+
+ // t_5 = floor(log10(t_4)) - 1;
+ mc_dec128 t_5 = mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_TOWARD_ZERO, NULL),
+ MC_DEC128(1));
+
+ // We convert precision to a double so we can avoid warning C4146 on Windows.
+ mc_dec128 prc_dec = mc_dec128_from_double((double)precision);
+
+ if (mc_dec128_less(t_5, prc_dec)) {
+ CLIENT_ERR("Invalid value for precision. precision: %" PRId32, precision);
+ return false;
+ }
+
+ mlib_int128 i_1 = dec128_to_int128(scaled_max);
+ mlib_int128 i_2 = dec128_to_int128(scaled_min);
+
+ // Because we have guaranteed earlier that max is greater than min, we can
+ // subtract these values and guarantee that taking their unsigned
+ // representation will yield the actual range result.
+ mlib_int128 range128 = mlib_int128_sub(i_1, i_2);
+
+ if (precision > UINT8_MAX) {
+ CLIENT_ERR("Invalid value for precision. Must be less than 255. precision: %" PRId32, precision);
+ return false;
+ }
+
+ mlib_int128 i_3 = mlib_int128_add(range128, mlib_int128_pow10((uint8_t)precision));
+ if (!ceil_log2_int128(i_3, maxBitsOut, status)) {
+ return false;
+ }
+
+ if (*maxBitsOut >= 128) {
+ return false;
+ }
+
+ return true;
+}
+
bool mc_getTypeInfoDecimal128(mc_getTypeInfoDecimal128_args_t args,
mc_OSTType_Decimal128 *out,
- mongocrypt_status_t *status) {
+ mongocrypt_status_t *status,
+ bool use_range_v2) {
/// Basic param checks
if (args.min.set != args.max.set || args.min.set != args.precision.set) {
CLIENT_ERR("min, max, and precision must all be set or must all be unset");
return false;
}
+ if (args.precision.set) {
+ if (args.precision.value < 0) {
+ CLIENT_ERR("Precision must be non-negative, but got %" PRId32, args.precision.value);
+ return false;
+ }
+
+ mc_dec128 scaled = mc_dec128_scale(MC_DEC128(1), args.precision.value);
+ if (!mc_dec128_is_finite(scaled)) {
+ CLIENT_ERR("Precision is too large and cannot be used to calculate the scaled range bounds");
+ return false;
+ }
+ }
+
// We only accept normal numbers
if (mc_dec128_is_inf(args.value) || mc_dec128_is_nan(args.value)) {
CLIENT_ERR("Infinity and Nan Decimal128 values are not supported.");
@@ -375,45 +668,27 @@ bool mc_getTypeInfoDecimal128(mc_getTypeInfoDecimal128_args_t args,
// full range.
bool use_precision_mode = false;
// The number of bits required to hold the result (used for precision mode)
- uint8_t bits_range = 0;
+ uint32_t bits_range = 0;
if (args.precision.set) {
- // Subnormal representations can support up to 5x10^-6182 as a number
- if (args.precision.value > 6182) {
- CLIENT_ERR("Precision must be between 0 and 6182 inclusive, got: %" PRIu32, args.precision.value);
+ use_precision_mode =
+ mc_canUsePrecisionModeDecimal(args.min.value, args.max.value, args.precision.value, &bits_range, status);
+
+ if (use_range_v2 && !use_precision_mode) {
+ if (!mongocrypt_status_ok(status)) {
+ return false;
+ }
+
+ CLIENT_ERR("The domain of decimal values specified by the min, max, and precision cannot be represented in "
+ "fewer than 128 bits. min: %s, max: %s, precision: %" PRIu32,
+ mc_dec128_to_string(args.min.value).str,
+ mc_dec128_to_string(args.max.value).str,
+ args.precision.value);
return false;
}
- // max - min
- mc_dec128 bounds_n1 = mc_dec128_sub(args.max.value, args.min.value);
- // The size of [min, max]: (max - min) + 1
- mc_dec128 bounds = mc_dec128_add(bounds_n1, MC_DEC128_ONE);
-
- // We can overflow if max = max_dec128 and min = min_dec128 so make sure
- // we have finite number after we do subtraction
- if (mc_dec128_is_finite(bounds)) {
- // This creates a range which is wider then we permit by our min/max
- // bounds check with the +1 but it is as the algorithm is written in
- // WRITING-11907.
- mc_dec128 precision_scaled_bounds = mc_dec128_scale(bounds, args.precision.value);
- /// The number of bits required to hold the result for the given
- /// precision (as decimal)
- mc_dec128 bits_range_dec = mc_dec128_log2(precision_scaled_bounds);
-
- if (mc_dec128_is_finite(bits_range_dec) && mc_dec128_less(bits_range_dec, MC_DEC128(128))) {
- // We need fewer than 128 bits to hold the result. But round up,
- // just to be sure:
- int64_t r =
- mc_dec128_to_int64(mc_dec128_round_integral_ex(bits_range_dec, MC_DEC128_ROUND_UPWARD, NULL));
- BSON_ASSERT(r >= 0);
- BSON_ASSERT(r <= UINT8_MAX);
- // We've computed the proper 'bits_range'
- bits_range = (uint8_t)r;
-
- if (bits_range < 128) {
- use_precision_mode = true;
- }
- }
- }
+ // If we are not in range_v2, then we don't care about the error returned
+ // from canUsePrecisionMode so we can reset the status.
+ _mongocrypt_status_reset(status);
}
// Constant zero
@@ -456,9 +731,9 @@ bool mc_getTypeInfoDecimal128(mc_getTypeInfoDecimal128_args_t args,
v_prime2 = mc_dec128_round_integral_ex(v_prime2, MC_DEC128_ROUND_TOWARD_ZERO, NULL);
BSON_ASSERT(mc_dec128_less(mc_dec128_log2(v_prime2), MC_DEC128(128)));
-
+ BSON_ASSERT(bits_range < 128);
// Resulting OST maximum
- mlib_int128 ost_max = mlib_int128_sub(mlib_int128_pow2(bits_range), i128_one);
+ mlib_int128 ost_max = mlib_int128_sub(mlib_int128_pow2((uint8_t)bits_range), i128_one);
// Now we need to get the Decimal128 out as a 128-bit integer
// But Decimal128 does not support conversion to Int128.
@@ -575,3 +850,23 @@ bool mc_getTypeInfoDecimal128(mc_getTypeInfoDecimal128_args_t args,
}
#endif // defined MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
+
+const int64_t mc_FLERangeSparsityDefault = 2;
+const int32_t mc_FLERangeTrimFactorDefault = 6;
+
+int32_t trimFactorDefault(size_t maxlen, mc_optional_int32_t trimFactor, bool use_range_v2) {
+ if (trimFactor.set) {
+ return trimFactor.value;
+ }
+
+ if (!use_range_v2) {
+ // Preserve old default.
+ return 0;
+ }
+
+ if (mc_cmp_greater_su(mc_FLERangeTrimFactorDefault, maxlen - 1)) {
+ return (int32_t)(maxlen - 1);
+ } else {
+ return mc_FLERangeTrimFactorDefault;
+ }
+}
diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-mincover-generator.template.h b/src/third_party/libmongocrypt/dist/src/mc-range-mincover-generator.template.h
index 30809ee2651..9da1bff7f00 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-range-mincover-generator.template.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-range-mincover-generator.template.h
@@ -17,18 +17,6 @@
// mc-range-mincover-generator.template.h is meant to be included in another
// source file.
-// TODO: replace `CONCAT` with `BSON_CONCAT` after libbson dependency is
-// upgraded to 1.20.0 or higher.
-#ifndef CONCAT
-#define CONCAT_1(a, b) a##b
-#define CONCAT(a, b) CONCAT_1(a, b)
-#endif
-// TODO: replace `CONCAT3` with `BSON_CONCAT3` after libbson dependency is
-// upgraded to 1.20.0 or higher.
-#ifndef CONCAT3
-#define CONCAT3(a, b, c) CONCAT(a, CONCAT(b, c))
-#endif
-
#if !(defined(UINT_T) && defined(UINT_C) && defined(UINT_FMT_S) && defined(DECORATE_NAME))
#ifdef __INTELLISENSE__
#define UINT_T uint32_t
@@ -101,6 +89,7 @@ typedef struct {
UINT_T _rangeMin;
UINT_T _rangeMax;
size_t _sparsity;
+ int32_t _trimFactor;
// _maxlen is the maximum bit length of edges in the mincover.
size_t _maxlen;
} DECORATE_NAME(MinCoverGenerator);
@@ -110,7 +99,9 @@ static inline DECORATE_NAME(MinCoverGenerator)
UINT_T rangeMax,
UINT_T max,
size_t sparsity,
- mongocrypt_status_t *status) {
+ mc_optional_int32_t opt_trimFactor,
+ mongocrypt_status_t *status,
+ bool use_range_v2) {
BSON_ASSERT_PARAM(status);
if (UINT_COMPARE(rangeMin, rangeMax) > 0) {
@@ -131,11 +122,25 @@ static inline DECORATE_NAME(MinCoverGenerator)
CLIENT_ERR("Sparsity must be > 0");
return NULL;
}
+ size_t maxlen = (size_t)BITS - DECORATE_NAME(mc_count_leading_zeros)(max);
+ int32_t trimFactor = trimFactorDefault(maxlen, opt_trimFactor, use_range_v2);
+ if (trimFactor != 0 && mc_cmp_greater_equal_su(trimFactor, maxlen)) {
+ CLIENT_ERR("Trim factor must be less than the number of bits (%ld) used to represent an element of the domain, "
+ "but got %" PRId32,
+ maxlen,
+ trimFactor);
+ return NULL;
+ }
+ if (trimFactor < 0) {
+ CLIENT_ERR("Trim factor must be >= 0, but got (%" PRId32 ")", trimFactor);
+ return NULL;
+ }
DECORATE_NAME(MinCoverGenerator) *mcg = bson_malloc0(sizeof(DECORATE_NAME(MinCoverGenerator)));
mcg->_rangeMin = rangeMin;
mcg->_rangeMax = rangeMax;
mcg->_maxlen = (size_t)BITS - DECORATE_NAME(mc_count_leading_zeros)(max);
mcg->_sparsity = sparsity;
+ mcg->_trimFactor = trimFactor;
return mcg;
}
@@ -164,7 +169,9 @@ static inline bool DECORATE_NAME(MinCoverGenerator_isLevelStored)(DECORATE_NAME(
size_t maskedBits) {
BSON_ASSERT_PARAM(mcg);
size_t level = mcg->_maxlen - maskedBits;
- return 0 == maskedBits || 0 == (level % mcg->_sparsity);
+ BSON_ASSERT(mc_in_range_size_t_signed(mcg->_trimFactor));
+ size_t trimFactor_sz = (size_t)mcg->_trimFactor;
+ return 0 == maskedBits || (level >= trimFactor_sz && 0 == (level % mcg->_sparsity));
}
char *
@@ -219,6 +226,11 @@ static inline mc_mincover_t *DECORATE_NAME(MinCoverGenerator_minCover)(DECORATE_
return mc;
}
+static inline int32_t DECORATE_NAME(MinCoverGenerator_usedTrimFactor)(DECORATE_NAME(MinCoverGenerator) * mcg) {
+ BSON_ASSERT_PARAM(mcg);
+ return mcg->_trimFactor;
+}
+
// adjustBounds increments *lowerBound if includeLowerBound is false and
// decrements *upperBound if includeUpperBound is false.
// lowerBound, min, upperBound, and max are expected to come from the result
diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-mincover-private.h b/src/third_party/libmongocrypt/dist/src/mc-range-mincover-private.h
index 3e073b0d726..2129dff0895 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-range-mincover-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-range-mincover-private.h
@@ -33,6 +33,9 @@ const char *mc_mincover_get(mc_mincover_t *mincover, size_t index);
// mc_mincover_len returns the number of represented mincover.
size_t mc_mincover_len(mc_mincover_t *mincover);
+// Return the trimFactor that was used to generate this mincover.
+int32_t mc_mincover_get_used_trimFactor(const mc_mincover_t *mincover);
+
// mc_mincover_destroys frees `mincover`.
void mc_mincover_destroy(mc_mincover_t *mincover);
@@ -44,12 +47,14 @@ typedef struct {
mc_optional_int32_t min;
mc_optional_int32_t max;
size_t sparsity;
+ mc_optional_int32_t trimFactor;
} mc_getMincoverInt32_args_t;
// mc_getMincoverInt32 implements the Mincover Generation algorithm described in
// SERVER-68600 for int32_t.
mc_mincover_t *mc_getMincoverInt32(mc_getMincoverInt32_args_t args,
- mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;
+ mongocrypt_status_t *status,
+ bool use_range_v2) MONGOCRYPT_WARN_UNUSED_RESULT;
typedef struct {
int64_t lowerBound;
@@ -59,12 +64,14 @@ typedef struct {
mc_optional_int64_t min;
mc_optional_int64_t max;
size_t sparsity;
+ mc_optional_int32_t trimFactor;
} mc_getMincoverInt64_args_t;
// mc_getMincoverInt64 implements the Mincover Generation algorithm described in
// SERVER-68600 for int64_t.
mc_mincover_t *mc_getMincoverInt64(mc_getMincoverInt64_args_t args,
- mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;
+ mongocrypt_status_t *status,
+ bool use_range_v2) MONGOCRYPT_WARN_UNUSED_RESULT;
typedef struct {
double lowerBound;
@@ -74,13 +81,15 @@ typedef struct {
size_t sparsity;
mc_optional_double_t min;
mc_optional_double_t max;
- mc_optional_uint32_t precision;
+ mc_optional_int32_t precision;
+ mc_optional_int32_t trimFactor;
} mc_getMincoverDouble_args_t;
// mc_getMincoverDouble implements the Mincover Generation algorithm described
// in SERVER-68600 for double.
mc_mincover_t *mc_getMincoverDouble(mc_getMincoverDouble_args_t args,
- mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;
+ mongocrypt_status_t *status,
+ bool use_range_v2) MONGOCRYPT_WARN_UNUSED_RESULT;
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
typedef struct {
@@ -90,13 +99,15 @@ typedef struct {
bool includeUpperBound;
size_t sparsity;
mc_optional_dec128_t min, max;
- mc_optional_uint32_t precision;
+ mc_optional_int32_t precision;
+ mc_optional_int32_t trimFactor;
} mc_getMincoverDecimal128_args_t;
// mc_getMincoverDecimal128 implements the Mincover Generation algorithm
// described in SERVER-68600 for Decimal128 (as mc_dec128).
mc_mincover_t *mc_getMincoverDecimal128(mc_getMincoverDecimal128_args_t args,
- mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;
+ mongocrypt_status_t *status,
+ bool use_range_v2) MONGOCRYPT_WARN_UNUSED_RESULT;
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
#endif /* MC_RANGE_MINCOVER_PRIVATE_H */
diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-mincover.c b/src/third_party/libmongocrypt/dist/src/mc-range-mincover.c
index 514d24cfdbb..3e8d8d471bd 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-range-mincover.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-range-mincover.c
@@ -19,14 +19,16 @@
#include "mc-check-conversions-private.h"
#include "mc-array-private.h"
+#include "mc-cmp-private.h"
#include "mc-range-edge-generation-private.h" // mc_count_leading_zeros_u32
-#include "mc-range-encoding-private.h" // mc_getTypeInfo32
+#include "mc-range-encoding-private.h" // mc_getTypeInfo32, trimFactorDefault
#include "mc-range-mincover-private.h"
#include "mongocrypt-private.h"
struct _mc_mincover_t {
/* mincover is an array of `char*` edge strings. */
mc_array_t mincover;
+ int32_t usedTrimFactor; // The `trimFactor` that was used to produce this mincover.
};
static mc_mincover_t *mc_mincover_new(void) {
@@ -48,6 +50,10 @@ size_t mc_mincover_len(mc_mincover_t *mincover) {
return mincover->mincover.len;
}
+int32_t mc_mincover_get_used_trimFactor(const mc_mincover_t *mincover) {
+ return mincover->usedTrimFactor;
+}
+
void mc_mincover_destroy(mc_mincover_t *mincover) {
if (NULL == mincover) {
return;
@@ -127,7 +133,7 @@ void mc_mincover_destroy(mc_mincover_t *mincover) {
} else \
(void)0
-mc_mincover_t *mc_getMincoverInt32(mc_getMincoverInt32_args_t args, mongocrypt_status_t *status) {
+mc_mincover_t *mc_getMincoverInt32(mc_getMincoverInt32_args_t args, mongocrypt_status_t *status, bool use_range_v2) {
BSON_ASSERT_PARAM(status);
CHECK_BOUNDS(args, PRId32, IDENTITY, LESSTHAN);
mc_OSTType_Int32 a, b;
@@ -149,16 +155,18 @@ mc_mincover_t *mc_getMincoverInt32(mc_getMincoverInt32_args_t args, mongocrypt_s
return NULL;
}
- MinCoverGenerator_u32 *mcg = MinCoverGenerator_new_u32(a.value, b.value, a.max, args.sparsity, status);
+ MinCoverGenerator_u32 *mcg =
+ MinCoverGenerator_new_u32(a.value, b.value, a.max, args.sparsity, args.trimFactor, status, use_range_v2);
if (!mcg) {
return NULL;
}
mc_mincover_t *mc = MinCoverGenerator_minCover_u32(mcg);
+ mc->usedTrimFactor = MinCoverGenerator_usedTrimFactor_u32(mcg);
MinCoverGenerator_destroy_u32(mcg);
return mc;
}
-mc_mincover_t *mc_getMincoverInt64(mc_getMincoverInt64_args_t args, mongocrypt_status_t *status) {
+mc_mincover_t *mc_getMincoverInt64(mc_getMincoverInt64_args_t args, mongocrypt_status_t *status, bool use_range_v2) {
BSON_ASSERT_PARAM(status);
CHECK_BOUNDS(args, PRId64, IDENTITY, LESSTHAN);
mc_OSTType_Int64 a, b;
@@ -180,18 +188,20 @@ mc_mincover_t *mc_getMincoverInt64(mc_getMincoverInt64_args_t args, mongocrypt_s
return NULL;
}
- MinCoverGenerator_u64 *mcg = MinCoverGenerator_new_u64(a.value, b.value, a.max, args.sparsity, status);
+ MinCoverGenerator_u64 *mcg =
+ MinCoverGenerator_new_u64(a.value, b.value, a.max, args.sparsity, args.trimFactor, status, use_range_v2);
if (!mcg) {
return NULL;
}
mc_mincover_t *mc = MinCoverGenerator_minCover_u64(mcg);
+ mc->usedTrimFactor = MinCoverGenerator_usedTrimFactor_u64(mcg);
MinCoverGenerator_destroy_u64(mcg);
return mc;
}
// mc_getMincoverDouble implements the Mincover Generation algorithm described
// in SERVER-68600 for double.
-mc_mincover_t *mc_getMincoverDouble(mc_getMincoverDouble_args_t args, mongocrypt_status_t *status) {
+mc_mincover_t *mc_getMincoverDouble(mc_getMincoverDouble_args_t args, mongocrypt_status_t *status, bool use_range_v2) {
BSON_ASSERT_PARAM(status);
CHECK_BOUNDS(args, "g", IDENTITY, LESSTHAN);
@@ -201,7 +211,8 @@ mc_mincover_t *mc_getMincoverDouble(mc_getMincoverDouble_args_t args, mongocrypt
.max = args.max,
.precision = args.precision},
&a,
- status)) {
+ status,
+ use_range_v2)) {
return NULL;
}
if (!mc_getTypeInfoDouble((mc_getTypeInfoDouble_args_t){.value = args.upperBound,
@@ -209,7 +220,8 @@ mc_mincover_t *mc_getMincoverDouble(mc_getMincoverDouble_args_t args, mongocrypt
.max = args.max,
.precision = args.precision},
&b,
- status)) {
+ status,
+ use_range_v2)) {
return NULL;
}
@@ -220,17 +232,20 @@ mc_mincover_t *mc_getMincoverDouble(mc_getMincoverDouble_args_t args, mongocrypt
return NULL;
}
- MinCoverGenerator_u64 *mcg = MinCoverGenerator_new_u64(a.value, b.value, a.max, args.sparsity, status);
+ MinCoverGenerator_u64 *mcg =
+ MinCoverGenerator_new_u64(a.value, b.value, a.max, args.sparsity, args.trimFactor, status, use_range_v2);
if (!mcg) {
return NULL;
}
mc_mincover_t *mc = MinCoverGenerator_minCover_u64(mcg);
+ mc->usedTrimFactor = MinCoverGenerator_usedTrimFactor_u64(mcg);
MinCoverGenerator_destroy_u64(mcg);
return mc;
}
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
-mc_mincover_t *mc_getMincoverDecimal128(mc_getMincoverDecimal128_args_t args, mongocrypt_status_t *status) {
+mc_mincover_t *
+mc_getMincoverDecimal128(mc_getMincoverDecimal128_args_t args, mongocrypt_status_t *status, bool use_range_v2) {
BSON_ASSERT_PARAM(status);
#define ToString(Dec) (mc_dec128_to_string(Dec).str)
CHECK_BOUNDS(args, "s", ToString, mc_dec128_less);
@@ -241,7 +256,8 @@ mc_mincover_t *mc_getMincoverDecimal128(mc_getMincoverDecimal128_args_t args, mo
.max = args.max,
.precision = args.precision},
&a,
- status)) {
+ status,
+ use_range_v2)) {
return NULL;
}
if (!mc_getTypeInfoDecimal128((mc_getTypeInfoDecimal128_args_t){.value = args.upperBound,
@@ -249,7 +265,8 @@ mc_mincover_t *mc_getMincoverDecimal128(mc_getMincoverDecimal128_args_t args, mo
.max = args.max,
.precision = args.precision},
&b,
- status)) {
+ status,
+ use_range_v2)) {
return NULL;
}
@@ -260,11 +277,13 @@ mc_mincover_t *mc_getMincoverDecimal128(mc_getMincoverDecimal128_args_t args, mo
return NULL;
}
- MinCoverGenerator_u128 *mcg = MinCoverGenerator_new_u128(a.value, b.value, a.max, args.sparsity, status);
+ MinCoverGenerator_u128 *mcg =
+ MinCoverGenerator_new_u128(a.value, b.value, a.max, args.sparsity, args.trimFactor, status, use_range_v2);
if (!mcg) {
return NULL;
}
mc_mincover_t *mc = MinCoverGenerator_minCover_u128(mcg);
+ mc->usedTrimFactor = MinCoverGenerator_usedTrimFactor_u128(mcg);
MinCoverGenerator_destroy_u128(mcg);
return mc;
}
diff --git a/src/third_party/libmongocrypt/dist/src/mc-rangeopts-private.h b/src/third_party/libmongocrypt/dist/src/mc-rangeopts-private.h
index 0df6725c870..80f4c807c71 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-rangeopts-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-rangeopts-private.h
@@ -36,19 +36,26 @@ typedef struct {
} max;
int64_t sparsity;
- mc_optional_uint32_t precision;
+ mc_optional_int32_t precision;
+ mc_optional_int32_t trimFactor;
} mc_RangeOpts_t;
+// `mc_RangeOpts_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof_mc_RangeOpts_t,
+ BSON_ALIGNOF(mc_RangeOpts_t) >= BSON_MAX(BSON_ALIGNOF(bson_t), BSON_ALIGNOF(bson_iter_t)));
+
/* mc_RangeOpts_parse parses a BSON document into mc_RangeOpts_t.
* The document is expected to have the form:
* {
* "min": BSON value,
* "max": BSON value,
- * "sparsity": Int64,
- * "precision": Optional
+ * "sparsity": Optional,
+ * "precision": Optional,
+ * "trimFactor": Optional,
* }
*/
-bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, mongocrypt_status_t *status);
+bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, bool use_range_v2, mongocrypt_status_t *status);
/*
* mc_RangeOpts_to_FLE2RangeInsertSpec creates a placeholder value to be
@@ -69,6 +76,7 @@ bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, mongocrypt_status_
bool mc_RangeOpts_to_FLE2RangeInsertSpec(const mc_RangeOpts_t *ro,
const bson_t *v,
bson_t *out,
+ bool use_range_v2,
mongocrypt_status_t *status);
/* mc_RangeOpts_appendMin appends the minimum value of the range for a given
@@ -89,6 +97,16 @@ bool mc_RangeOpts_appendMax(const mc_RangeOpts_t *ro,
bson_t *out,
mongocrypt_status_t *status);
+/* mc_RangeOpts_appendTrimFactor appends the trim factor of the field. If `ro->trimFactor` is unset,
+ * defaults to 0. Errors if `ro->trimFactor` is out of bounds based on the size of the domain
+ * computed from `valueType`, `ro->min` and `ro->max`. */
+bool mc_RangeOpts_appendTrimFactor(const mc_RangeOpts_t *ro,
+ bson_type_t valueType,
+ const char *fieldName,
+ bson_t *out,
+ mongocrypt_status_t *status,
+ bool use_range_v2);
+
void mc_RangeOpts_cleanup(mc_RangeOpts_t *ro);
#endif // MC_RANGEOPTS_PRIVATE_H
diff --git a/src/third_party/libmongocrypt/dist/src/mc-rangeopts.c b/src/third_party/libmongocrypt/dist/src/mc-rangeopts.c
index 5de8df50785..729ef49384e 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-rangeopts.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-rangeopts.c
@@ -17,16 +17,22 @@
#include "mc-rangeopts-private.h"
#include "mc-check-conversions-private.h"
+#include "mc-cmp-private.h"
+#include "mc-range-edge-generation-private.h" // mc_count_leading_zeros_XX
+#include "mc-range-encoding-private.h" // mc_getTypeInfoXX
#include "mongocrypt-private.h"
#include "mongocrypt-util-private.h" // mc_bson_type_to_string
#include // DBL_MAX
+#define CLIENT_ERR_PREFIXED_HELPER(Prefix, ErrorString, ...) CLIENT_ERR(Prefix ": " ErrorString, ##__VA_ARGS__)
+#define CLIENT_ERR_PREFIXED(ErrorString, ...) CLIENT_ERR_PREFIXED_HELPER(ERROR_PREFIX, ErrorString, ##__VA_ARGS__)
+
// Common logic for testing field name, tracking duplication, and presence.
-#define IF_FIELD(Name, ErrorPrefix) \
+#define IF_FIELD(Name) \
if (0 == strcmp(field, #Name)) { \
if (has_##Name) { \
- CLIENT_ERR("%sUnexpected duplicate field '" #Name "'", ErrorPrefix); \
+ CLIENT_ERR_PREFIXED("Unexpected duplicate field '" #Name "'"); \
return false; \
} \
has_##Name = true;
@@ -35,17 +41,17 @@
continue; \
}
-#define CHECK_HAS(Name, ErrorPrefix) \
+#define CHECK_HAS(Name) \
if (!has_##Name) { \
- CLIENT_ERR("%sMissing field '" #Name "'", ErrorPrefix); \
+ CLIENT_ERR_PREFIXED("Missing field '" #Name "'"); \
return false; \
}
-bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, mongocrypt_status_t *status) {
- bson_iter_t iter;
- bool has_min = false, has_max = false, has_sparsity = false, has_precision = false;
- const char *const error_prefix = "Error parsing RangeOpts: ";
+#define ERROR_PREFIX "Error parsing RangeOpts"
+bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, bool use_range_v2, mongocrypt_status_t *status) {
+ bson_iter_t iter = {0};
+ bool has_min = false, has_max = false, has_sparsity = false, has_precision = false, has_trimFactor = false;
BSON_ASSERT_PARAM(ro);
BSON_ASSERT_PARAM(in);
BSON_ASSERT(status || true);
@@ -54,7 +60,7 @@ bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, mongocrypt_status_
ro->bson = bson_copy(in);
if (!bson_iter_init(&iter, ro->bson)) {
- CLIENT_ERR("%sInvalid BSON", error_prefix);
+ CLIENT_ERR_PREFIXED("Invalid BSON");
return false;
}
@@ -62,74 +68,91 @@ bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, mongocrypt_status_
const char *field = bson_iter_key(&iter);
BSON_ASSERT(field);
- IF_FIELD(min, error_prefix)
+ IF_FIELD(min)
ro->min.set = true;
ro->min.value = iter;
END_IF_FIELD
- IF_FIELD(max, error_prefix)
+ IF_FIELD(max)
ro->max.set = true;
ro->max.value = iter;
END_IF_FIELD
- IF_FIELD(sparsity, error_prefix)
+ IF_FIELD(sparsity)
if (!BSON_ITER_HOLDS_INT64(&iter)) {
- CLIENT_ERR("%sExpected int64 for sparsity, got: %s",
- error_prefix,
- mc_bson_type_to_string(bson_iter_type(&iter)));
+ CLIENT_ERR_PREFIXED("Expected int64 for sparsity, got: %s", mc_bson_type_to_string(bson_iter_type(&iter)));
return false;
};
ro->sparsity = bson_iter_int64(&iter);
END_IF_FIELD
- IF_FIELD(precision, error_prefix) {
+ IF_FIELD(precision) {
if (!BSON_ITER_HOLDS_INT32(&iter)) {
- CLIENT_ERR("%s'precision' must be an int32", error_prefix);
+ CLIENT_ERR_PREFIXED("'precision' must be an int32");
return false;
}
int32_t val = bson_iter_int32(&iter);
if (val < 0) {
- CLIENT_ERR("%s'precision' must be non-negative", error_prefix);
+ CLIENT_ERR_PREFIXED("'precision' must be non-negative");
return false;
}
- ro->precision = OPT_U32((uint32_t)val);
+ ro->precision = OPT_I32(val);
}
END_IF_FIELD
- CLIENT_ERR("%sUnrecognized field: '%s'", error_prefix, field);
+ IF_FIELD(trimFactor) {
+ if (!BSON_ITER_HOLDS_INT32(&iter)) {
+ CLIENT_ERR_PREFIXED("Expected int32 for trimFactor, got: %s",
+ mc_bson_type_to_string(bson_iter_type(&iter)));
+ return false;
+ };
+ int32_t val = bson_iter_int32(&iter);
+ if (val < 0) {
+ CLIENT_ERR_PREFIXED("'trimFactor' must be non-negative");
+ return false;
+ }
+ ro->trimFactor = OPT_I32(val);
+ }
+ END_IF_FIELD
+
+ CLIENT_ERR_PREFIXED("Unrecognized field: '%s'", field);
return false;
}
// Do not error if min/max are not present. min/max are optional.
- CHECK_HAS(sparsity, error_prefix);
// Do not error if precision is not present. Precision is optional and only
// applies to double/decimal128.
+ // Do not error if trimFactor is not present. It is optional.
+
+ if (!has_sparsity && use_range_v2) {
+ ro->sparsity = mc_FLERangeSparsityDefault;
+ }
// Expect precision only to be set for double or decimal128.
if (has_precision) {
if (!ro->min.set) {
- CLIENT_ERR("setting precision requires min");
+ CLIENT_ERR_PREFIXED("setting precision requires min");
return false;
}
bson_type_t minType = bson_iter_type(&ro->min.value);
if (minType != BSON_TYPE_DOUBLE && minType != BSON_TYPE_DECIMAL128) {
- CLIENT_ERR("expected 'precision' to be set with double or decimal128 "
- "index, but got: %s min",
- mc_bson_type_to_string(minType));
+ CLIENT_ERR_PREFIXED("expected 'precision' to be set with double or decimal128 "
+ "index, but got: %s min",
+ mc_bson_type_to_string(minType));
return false;
}
if (!ro->max.set) {
- CLIENT_ERR("setting precision requires max");
+ CLIENT_ERR_PREFIXED("setting precision requires max");
return false;
}
bson_type_t maxType = bson_iter_type(&ro->max.value);
if (maxType != BSON_TYPE_DOUBLE && maxType != BSON_TYPE_DECIMAL128) {
- CLIENT_ERR("expected 'precision' to be set with double or decimal128 "
- "index, but got: %s max",
- mc_bson_type_to_string(maxType));
+ CLIENT_ERR_PREFIXED("expected 'precision' to be set with double or decimal128 "
+ "index, but got: %s max",
+ mc_bson_type_to_string(maxType));
return false;
}
}
@@ -138,10 +161,10 @@ bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, mongocrypt_status_
if (ro->min.set && ro->max.set) {
bson_type_t minType = bson_iter_type(&ro->min.value), maxType = bson_iter_type(&ro->max.value);
if (minType != maxType) {
- CLIENT_ERR("expected 'min' and 'max' to be same type, but got: %s "
- "min and %s max",
- mc_bson_type_to_string(minType),
- mc_bson_type_to_string(maxType));
+ CLIENT_ERR_PREFIXED("expected 'min' and 'max' to be same type, but got: %s "
+ "min and %s max",
+ mc_bson_type_to_string(minType),
+ mc_bson_type_to_string(maxType));
return false;
}
}
@@ -152,7 +175,8 @@ bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, mongocrypt_status_
bson_type_t minType = bson_iter_type(&ro->min.value);
if (minType == BSON_TYPE_DOUBLE || minType == BSON_TYPE_DECIMAL128) {
if (!has_precision) {
- CLIENT_ERR("expected 'precision' to be set with 'min' for %s", mc_bson_type_to_string(minType));
+ CLIENT_ERR_PREFIXED("expected 'precision' to be set with 'min' for %s",
+ mc_bson_type_to_string(minType));
return false;
}
}
@@ -162,39 +186,53 @@ bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, mongocrypt_status_
bson_type_t maxType = bson_iter_type(&ro->max.value);
if (maxType == BSON_TYPE_DOUBLE || maxType == BSON_TYPE_DECIMAL128) {
if (!has_precision) {
- CLIENT_ERR("expected 'precision' to be set with 'max' for %s", mc_bson_type_to_string(maxType));
+ CLIENT_ERR_PREFIXED("expected 'precision' to be set with 'max' for %s",
+ mc_bson_type_to_string(maxType));
return false;
}
}
}
}
+ if (ro->trimFactor.set) {
+ if (!use_range_v2) {
+ // Once `use_range_v2` is default true, this block may be removed.
+ CLIENT_ERR_PREFIXED("'trimFactor' is not supported for QE range v1");
+ return false;
+ }
+ // At this point, we do not know the type of the field if min and max are unspecified. Wait to
+ // validate the value of trimFactor.
+ }
+
return true;
}
+#undef ERROR_PREFIX
+#define ERROR_PREFIX "Error making FLE2RangeInsertSpec"
+
bool mc_RangeOpts_to_FLE2RangeInsertSpec(const mc_RangeOpts_t *ro,
const bson_t *v,
bson_t *out,
+ bool use_range_v2,
mongocrypt_status_t *status) {
BSON_ASSERT_PARAM(ro);
BSON_ASSERT_PARAM(v);
BSON_ASSERT_PARAM(out);
BSON_ASSERT(status || true);
- const char *const error_prefix = "Error making FLE2RangeInsertSpec: ";
bson_iter_t v_iter;
if (!bson_iter_init_find(&v_iter, v, "v")) {
- CLIENT_ERR("Unable to find 'v' in input");
+ CLIENT_ERR_PREFIXED("Unable to find 'v' in input");
return false;
}
bson_t child;
if (!BSON_APPEND_DOCUMENT_BEGIN(out, "v", &child)) {
- CLIENT_ERR("%sError appending to BSON", error_prefix);
+ CLIENT_ERR_PREFIXED("Error appending to BSON");
return false;
}
if (!bson_append_iter(&child, "v", 1, &v_iter)) {
- CLIENT_ERR("%sError appending to BSON", error_prefix);
+ CLIENT_ERR_PREFIXED("Error appending to BSON");
return false;
}
@@ -209,17 +247,26 @@ bool mc_RangeOpts_to_FLE2RangeInsertSpec(const mc_RangeOpts_t *ro,
if (ro->precision.set) {
BSON_ASSERT(ro->precision.value <= INT32_MAX);
if (!BSON_APPEND_INT32(&child, "precision", (int32_t)ro->precision.value)) {
- CLIENT_ERR("%sError appending to BSON", error_prefix);
+ CLIENT_ERR_PREFIXED("Error appending to BSON");
+ return false;
+ }
+ }
+
+ if (use_range_v2) {
+ if (!mc_RangeOpts_appendTrimFactor(ro, bson_iter_type(&v_iter), "trimFactor", &child, status, use_range_v2)) {
return false;
}
}
if (!bson_append_document_end(out, &child)) {
- CLIENT_ERR("%sError appending to BSON", error_prefix);
+ CLIENT_ERR_PREFIXED("Error appending to BSON");
return false;
}
return true;
}
+#undef ERROR_PREFIX
+#define ERROR_PREFIX "Error appending min to FLE2RangeInsertSpec"
+
bool mc_RangeOpts_appendMin(const mc_RangeOpts_t *ro,
bson_type_t valueType,
const char *fieldName,
@@ -232,46 +279,49 @@ bool mc_RangeOpts_appendMin(const mc_RangeOpts_t *ro,
if (ro->min.set) {
if (bson_iter_type(&ro->min.value) != valueType) {
- CLIENT_ERR("expected matching 'min' and value type. Got range option "
- "'min' of type %s and value of type %s",
- mc_bson_type_to_string(bson_iter_type(&ro->min.value)),
- mc_bson_type_to_string(valueType));
+ CLIENT_ERR_PREFIXED("expected matching 'min' and value type. Got range option "
+ "'min' of type %s and value of type %s",
+ mc_bson_type_to_string(bson_iter_type(&ro->min.value)),
+ mc_bson_type_to_string(valueType));
return false;
}
if (!bson_append_iter(out, fieldName, -1, &ro->min.value)) {
- CLIENT_ERR("failed to append BSON");
+ CLIENT_ERR_PREFIXED("failed to append BSON");
return false;
}
return true;
}
if (valueType == BSON_TYPE_INT32 || valueType == BSON_TYPE_INT64 || valueType == BSON_TYPE_DATE_TIME) {
- CLIENT_ERR("Range option 'min' is required for type: %s", mc_bson_type_to_string(valueType));
+ CLIENT_ERR_PREFIXED("Range option 'min' is required for type: %s", mc_bson_type_to_string(valueType));
return false;
} else if (valueType == BSON_TYPE_DOUBLE) {
if (!BSON_APPEND_DOUBLE(out, fieldName, -DBL_MAX)) {
- CLIENT_ERR("failed to append BSON");
+ CLIENT_ERR_PREFIXED("failed to append BSON");
return false;
}
} else if (valueType == BSON_TYPE_DECIMAL128) {
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
const bson_decimal128_t min = mc_dec128_to_bson_decimal128(MC_DEC128_LARGEST_NEGATIVE);
if (!BSON_APPEND_DECIMAL128(out, fieldName, &min)) {
- CLIENT_ERR("failed to append BSON");
+ CLIENT_ERR_PREFIXED("failed to append BSON");
return false;
}
#else // ↑↑↑↑↑↑↑↑ With Decimal128 / Without ↓↓↓↓↓↓↓↓↓↓
- CLIENT_ERR("unsupported BSON type (Decimal128) for range: libmongocrypt "
- "was built without extended Decimal128 support");
+ CLIENT_ERR_PREFIXED("unsupported BSON type (Decimal128) for range: libmongocrypt "
+ "was built without extended Decimal128 support");
return false;
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
} else {
- CLIENT_ERR("unsupported BSON type: %s for range", mc_bson_type_to_string(valueType));
+ CLIENT_ERR_PREFIXED("unsupported BSON type: %s for range", mc_bson_type_to_string(valueType));
return false;
}
return true;
}
+#undef ERROR_PREFIX
+#define ERROR_PREFIX "Error appending max to FLE2RangeInsertSpec"
+
bool mc_RangeOpts_appendMax(const mc_RangeOpts_t *ro,
bson_type_t valueType,
const char *fieldName,
@@ -284,46 +334,193 @@ bool mc_RangeOpts_appendMax(const mc_RangeOpts_t *ro,
if (ro->max.set) {
if (bson_iter_type(&ro->max.value) != valueType) {
- CLIENT_ERR("expected matching 'max' and value type. Got range option "
- "'max' of type %s and value of type %s",
- mc_bson_type_to_string(bson_iter_type(&ro->max.value)),
- mc_bson_type_to_string(valueType));
+ CLIENT_ERR_PREFIXED("expected matching 'max' and value type. Got range option "
+ "'max' of type %s and value of type %s",
+ mc_bson_type_to_string(bson_iter_type(&ro->max.value)),
+ mc_bson_type_to_string(valueType));
return false;
}
if (!bson_append_iter(out, fieldName, -1, &ro->max.value)) {
- CLIENT_ERR("failed to append BSON");
+ CLIENT_ERR_PREFIXED("failed to append BSON");
return false;
}
return true;
}
if (valueType == BSON_TYPE_INT32 || valueType == BSON_TYPE_INT64 || valueType == BSON_TYPE_DATE_TIME) {
- CLIENT_ERR("Range option 'max' is required for type: %s", mc_bson_type_to_string(valueType));
+ CLIENT_ERR_PREFIXED("Range option 'max' is required for type: %s", mc_bson_type_to_string(valueType));
return false;
} else if (valueType == BSON_TYPE_DOUBLE) {
if (!BSON_APPEND_DOUBLE(out, fieldName, DBL_MAX)) {
- CLIENT_ERR("failed to append BSON");
+ CLIENT_ERR_PREFIXED("failed to append BSON");
return false;
}
} else if (valueType == BSON_TYPE_DECIMAL128) {
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
const bson_decimal128_t max = mc_dec128_to_bson_decimal128(MC_DEC128_LARGEST_POSITIVE);
if (!BSON_APPEND_DECIMAL128(out, fieldName, &max)) {
- CLIENT_ERR("failed to append BSON");
+ CLIENT_ERR_PREFIXED("failed to append BSON");
return false;
}
#else // ↑↑↑↑↑↑↑↑ With Decimal128 / Without ↓↓↓↓↓↓↓↓↓↓
- CLIENT_ERR("unsupported BSON type (Decimal128) for range: libmongocrypt "
- "was built without extended Decimal128 support");
+ CLIENT_ERR_PREFIXED("unsupported BSON type (Decimal128) for range: libmongocrypt "
+ "was built without extended Decimal128 support");
return false;
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
} else {
- CLIENT_ERR("unsupported BSON type: %s for range", mc_bson_type_to_string(valueType));
+ CLIENT_ERR_PREFIXED("unsupported BSON type: %s for range", mc_bson_type_to_string(valueType));
return false;
}
return true;
}
+#undef ERROR_PREFIX
+#define ERROR_PREFIX "Error in getNumberOfBits"
+
+// Used to calculate max trim factor. Returns the number of bits required to represent any number in
+// the domain.
+bool mc_getNumberOfBits(const mc_RangeOpts_t *ro,
+ bson_type_t valueType,
+ uint32_t *bitsOut,
+ mongocrypt_status_t *status,
+ bool use_range_v2) {
+ BSON_ASSERT_PARAM(ro);
+ BSON_ASSERT_PARAM(bitsOut);
+
+ // For each type, we use getTypeInfo to get the total number of values in the domain (-1)
+ // which tells us how many bits are needed to represent the whole domain.
+ // note - can't use a switch statement because of -Werror=switch-enum
+ if (valueType == BSON_TYPE_INT32) {
+ int32_t value = 0;
+ mc_optional_int32_t rmin = {false, 0}, rmax = {false, 0};
+ if (ro->min.set) {
+ BSON_ASSERT(ro->max.set);
+ value = bson_iter_int32(&ro->min.value);
+ rmin = OPT_I32(value);
+ rmax = OPT_I32(bson_iter_int32(&ro->max.value));
+ }
+ mc_getTypeInfo32_args_t args = {value, rmin, rmax};
+ mc_OSTType_Int32 out;
+ if (!mc_getTypeInfo32(args, &out, status)) {
+ return false;
+ }
+ *bitsOut = 32 - (uint32_t)mc_count_leading_zeros_u32(out.max);
+ return true;
+ } else if (valueType == BSON_TYPE_INT64) {
+ int64_t value = 0;
+ mc_optional_int64_t rmin = {false, 0}, rmax = {false, 0};
+ if (ro->min.set) {
+ BSON_ASSERT(ro->max.set);
+ value = bson_iter_int64(&ro->min.value);
+ rmin = OPT_I64(value);
+ rmax = OPT_I64(bson_iter_int64(&ro->max.value));
+ }
+ mc_getTypeInfo64_args_t args = {value, rmin, rmax};
+ mc_OSTType_Int64 out;
+ if (!mc_getTypeInfo64(args, &out, status)) {
+ return false;
+ }
+ *bitsOut = 64 - (uint32_t)mc_count_leading_zeros_u64(out.max);
+ return true;
+ } else if (valueType == BSON_TYPE_DATE_TIME) {
+ int64_t value = 0;
+ mc_optional_int64_t rmin = {false, 0}, rmax = {false, 0};
+ if (ro->min.set) {
+ BSON_ASSERT(ro->max.set);
+ value = bson_iter_date_time(&ro->min.value);
+ rmin = OPT_I64(value);
+ rmax = OPT_I64(bson_iter_date_time(&ro->max.value));
+ }
+ mc_getTypeInfo64_args_t args = {value, rmin, rmax};
+ mc_OSTType_Int64 out;
+ if (!mc_getTypeInfo64(args, &out, status)) {
+ return false;
+ }
+ *bitsOut = 64 - (uint32_t)mc_count_leading_zeros_u64(out.max);
+ return true;
+ } else if (valueType == BSON_TYPE_DOUBLE) {
+ double value = 0;
+ mc_optional_double_t rmin = {false, 0}, rmax = {false, 0};
+ mc_optional_int32_t prec = ro->precision;
+ if (ro->min.set) {
+ BSON_ASSERT(ro->max.set);
+ value = bson_iter_double(&ro->min.value);
+ rmin = OPT_DOUBLE(value);
+ rmax = OPT_DOUBLE(bson_iter_double(&ro->max.value));
+ }
+ mc_getTypeInfoDouble_args_t args = {value, rmin, rmax, prec};
+ mc_OSTType_Double out;
+ if (!mc_getTypeInfoDouble(args, &out, status, use_range_v2)) {
+ return false;
+ }
+ *bitsOut = 64 - (uint32_t)mc_count_leading_zeros_u64(out.max);
+ return true;
+ }
+#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
+ else if (valueType == BSON_TYPE_DECIMAL128) {
+ mc_dec128 value = MC_DEC128_ZERO;
+ mc_optional_dec128_t rmin = {false, MC_DEC128_ZERO}, rmax = {false, MC_DEC128_ZERO};
+ mc_optional_int32_t prec = ro->precision;
+ if (ro->min.set) {
+ BSON_ASSERT(ro->max.set);
+ value = mc_dec128_from_bson_iter(&ro->min.value);
+ rmin = OPT_MC_DEC128(value);
+ rmax = OPT_MC_DEC128(mc_dec128_from_bson_iter(&ro->max.value));
+ }
+ mc_getTypeInfoDecimal128_args_t args = {value, rmin, rmax, prec};
+ mc_OSTType_Decimal128 out;
+ if (!mc_getTypeInfoDecimal128(args, &out, status, use_range_v2)) {
+ return false;
+ }
+ *bitsOut = 128 - (uint32_t)mc_count_leading_zeros_u128(out.max);
+ return true;
+ }
+#endif
+ CLIENT_ERR_PREFIXED("unsupported BSON type: %s for range", mc_bson_type_to_string(valueType));
+ return false;
+}
+
+#undef ERROR_PREFIX
+#define ERROR_PREFIX "Error appending trim factor to FLE2RangeInsertSpec"
+
+bool mc_RangeOpts_appendTrimFactor(const mc_RangeOpts_t *ro,
+ bson_type_t valueType,
+ const char *fieldName,
+ bson_t *out,
+ mongocrypt_status_t *status,
+ bool use_range_v2) {
+ BSON_ASSERT_PARAM(ro);
+ BSON_ASSERT_PARAM(fieldName);
+ BSON_ASSERT_PARAM(out);
+ BSON_ASSERT(status || true);
+
+ if (!ro->trimFactor.set) {
+ // A default `trimFactor` will be selected later with `trimFactorDefault`
+ return true;
+ }
+
+ uint32_t nbits;
+ if (!mc_getNumberOfBits(ro, valueType, &nbits, status, use_range_v2)) {
+ return false;
+ }
+ // if nbits = 0, we want to allow trim factor = 0.
+ uint32_t test = nbits ? nbits : 1;
+ if (mc_cmp_greater_equal_su(ro->trimFactor.value, test)) {
+ CLIENT_ERR_PREFIXED("Trim factor (%d) must be less than the total number of bits (%d) used to represent "
+ "any element in the domain.",
+ ro->trimFactor.value,
+ nbits);
+ return false;
+ }
+ if (!BSON_APPEND_INT32(out, fieldName, ro->trimFactor.value)) {
+ CLIENT_ERR_PREFIXED("failed to append BSON");
+ return false;
+ }
+ return true;
+}
+
+#undef ERROR_PREFIX
+
void mc_RangeOpts_cleanup(mc_RangeOpts_t *ro) {
if (!ro) {
return;
diff --git a/src/third_party/libmongocrypt/dist/src/mc-tokens-private.h b/src/third_party/libmongocrypt/dist/src/mc-tokens-private.h
index 2812a9cf2ca..e2aad7c5039 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-tokens-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mc-tokens-private.h
@@ -24,7 +24,7 @@
*
* v is a BSON value. It is the bytes after "e_name" in "element" in
* https://bsonspec.org/spec.html.
- * u is a "contention factor" counter. It is a uint64_t.
+ * u is a "contention factor". It is a uint64_t.
* HMAC is the HMAC-SHA-256 function.
* Integers are represented as uint64_t in little-endian.
*
@@ -40,11 +40,21 @@
* ESCDerivedFromDataToken = HMAC(ESCToken, v)
* ECCDerivedFromDataToken = HMAC(ECCToken, v)
*
- * EDCDerivedFromDataTokenAndCounter = HMAC(EDCDerivedFromDataToken, u)
- * ESCDerivedFromDataTokenAndCounter = HMAC(ESCDerivedFromDataToken, u)
- * ECCDerivedFromDataTokenAndCounter = HMAC(ECCDerivedFromDataToken, u)
+ * EDCDerivedFromDataTokenAndContentionFactor = HMAC(EDCDerivedFromDataToken, u)
+ * ESCDerivedFromDataTokenAndContentionFactor = HMAC(ESCDerivedFromDataToken, u)
+ * ECCDerivedFromDataTokenAndContentionFactor = HMAC(ECCDerivedFromDataToken, u)
+ *
+ * EDCTwiceDerivedToken = HMAC(EDCDerivedFromDataTokenAndContentionFactor, 1)
+
+ * ESCTwiceDerivedTagToken = HMAC(ESCDerivedFromDataTokenAndContentionFactor, 1)
+ * ESCTwiceDerivedValueToken = HMAC(ESCDerivedFromDataTokenAndContentionFactor, 2)
+
+ * ECCTwiceDerivedTagToken = HMAC(ECCDerivedFromDataTokenAndContentionFactor, 1)
+ * ECCTwiceDerivedValueToken = HMAC(ECCDerivedFromDataTokenAndContentionFactor, 2)
*
* Note: ECC related tokens are used in FLE2v1 only.
+ * Further, ECCTwiceDerivedValue(Tag|Token) have been omitted entirely.
+ * The above comment describing derivation is for doc purposes only.
* ----------------------------------------------------------------------------
* Added in FLE2v2:
*
@@ -54,38 +64,35 @@
* ServerCountAndContentionFactorEncryptionToken =
* HMAC(ServerDerivedFromDataToken, 1)
* ServerZerosEncryptionToken = HMAC(ServerDerivedFromDataToken, 2)
+ * ----------------------------------------------------------------------------
+ * Added in Range V2:
+ *
+ * d is a 17-byte blob of zeros.
+ *
+ * AnchorPaddingTokenRoot = HMAC(ESCToken, d)
+ * Server-side:
+ * AnchorPaddingTokenKey = HMAC(AnchorPaddingTokenRoot, 1)
+ * AnchorPaddingTokenValue = HMAC(AnchorPaddingTokenRoot, 2)
* ======================== End: FLE 2 Token Reference ========================
*/
-// TODO: replace `CONCAT` with `BSON_CONCAT` after libbson dependency is
-// upgraded to 1.20.0 or higher.
-#ifndef CONCAT
-#define CONCAT_1(a, b) a##b
-#define CONCAT(a, b) CONCAT_1(a, b)
-#endif
-// TODO: replace `CONCAT3` with `BSON_CONCAT3` after libbson dependency is
-// upgraded to 1.20.0 or higher.
-#ifndef CONCAT3
-#define CONCAT3(a, b, c) CONCAT(a, CONCAT(b, c))
-#endif
-
/// Declare a token type named 'Name', with constructor parameters given by the
/// remaining arguments. Each constructor will also have the implicit first
/// argument '_mongocrypt_crypto_t* crypto' and a final argument
/// 'mongocrypt_status_t* status'
-#define DECL_TOKEN_TYPE(Name, ...) DECL_TOKEN_TYPE_1(Name, CONCAT(Name, _t), __VA_ARGS__)
+#define DECL_TOKEN_TYPE(Name, ...) DECL_TOKEN_TYPE_1(Name, BSON_CONCAT(Name, _t), __VA_ARGS__)
#define DECL_TOKEN_TYPE_1(Prefix, T, ...) \
/* Opaque typedef the struct */ \
typedef struct T T; \
/* Data-getter */ \
- extern const _mongocrypt_buffer_t *CONCAT(Prefix, _get)(const T *t); \
+ extern const _mongocrypt_buffer_t *BSON_CONCAT(Prefix, _get)(const T *t); \
/* Destructor */ \
- extern void CONCAT(Prefix, _destroy)(T * t); \
+ extern void BSON_CONCAT(Prefix, _destroy)(T * t); \
/* Constructor for server to create tokens from raw buffer */ \
- extern T *CONCAT(Prefix, _new_from_buffer)(_mongocrypt_buffer_t * buf); \
+ extern T *BSON_CONCAT(Prefix, _new_from_buffer)(_mongocrypt_buffer_t * buf); \
/* Constructor. Parameter list given as variadic args */ \
- extern T *CONCAT(Prefix, _new)(_mongocrypt_crypto_t * crypto, __VA_ARGS__, mongocrypt_status_t * status)
+ extern T *BSON_CONCAT(Prefix, _new)(_mongocrypt_crypto_t * crypto, __VA_ARGS__, mongocrypt_status_t * status)
DECL_TOKEN_TYPE(mc_CollectionsLevel1Token, const _mongocrypt_buffer_t *);
DECL_TOKEN_TYPE(mc_ServerTokenDerivationLevel1Token, const _mongocrypt_buffer_t *);
@@ -100,16 +107,23 @@ DECL_TOKEN_TYPE(mc_EDCDerivedFromDataToken, const mc_EDCToken_t *EDCToken, const
DECL_TOKEN_TYPE(mc_ECCDerivedFromDataToken, const mc_ECCToken_t *ECCToken, const _mongocrypt_buffer_t *v);
DECL_TOKEN_TYPE(mc_ESCDerivedFromDataToken, const mc_ESCToken_t *ESCToken, const _mongocrypt_buffer_t *v);
-DECL_TOKEN_TYPE(mc_EDCDerivedFromDataTokenAndCounter,
+DECL_TOKEN_TYPE(mc_EDCDerivedFromDataTokenAndContentionFactor,
const mc_EDCDerivedFromDataToken_t *EDCDerivedFromDataToken,
uint64_t u);
-DECL_TOKEN_TYPE(mc_ESCDerivedFromDataTokenAndCounter,
+DECL_TOKEN_TYPE(mc_ESCDerivedFromDataTokenAndContentionFactor,
const mc_ESCDerivedFromDataToken_t *ESCDerivedFromDataToken,
uint64_t u);
-DECL_TOKEN_TYPE(mc_ECCDerivedFromDataTokenAndCounter,
+DECL_TOKEN_TYPE(mc_ECCDerivedFromDataTokenAndContentionFactor,
const mc_ECCDerivedFromDataToken_t *ECCDerivedFromDataToken,
uint64_t u);
+DECL_TOKEN_TYPE(mc_EDCTwiceDerivedToken,
+ const mc_EDCDerivedFromDataTokenAndContentionFactor_t *EDCDerivedFromDataTokenAndContentionFactor);
+DECL_TOKEN_TYPE(mc_ESCTwiceDerivedTagToken,
+ const mc_ESCDerivedFromDataTokenAndContentionFactor_t *ESCDerivedFromDataTokenAndContentionFactor);
+DECL_TOKEN_TYPE(mc_ESCTwiceDerivedValueToken,
+ const mc_ESCDerivedFromDataTokenAndContentionFactor_t *ESCDerivedFromDataTokenAndContentionFactor);
+
DECL_TOKEN_TYPE(mc_ServerDerivedFromDataToken,
const mc_ServerTokenDerivationLevel1Token_t *ServerTokenDerivationToken,
const _mongocrypt_buffer_t *v);
@@ -118,6 +132,8 @@ DECL_TOKEN_TYPE(mc_ServerCountAndContentionFactorEncryptionToken,
const mc_ServerDerivedFromDataToken_t *serverDerivedFromDataToken);
DECL_TOKEN_TYPE(mc_ServerZerosEncryptionToken, const mc_ServerDerivedFromDataToken_t *serverDerivedFromDataToken);
+DECL_TOKEN_TYPE(mc_AnchorPaddingTokenRoot, const mc_ESCToken_t *ESCToken);
+
#undef DECL_TOKEN_TYPE
#undef DECL_TOKEN_TYPE_1
diff --git a/src/third_party/libmongocrypt/dist/src/mc-tokens.c b/src/third_party/libmongocrypt/dist/src/mc-tokens.c
index 2b7afa3589a..34592dca035 100644
--- a/src/third_party/libmongocrypt/dist/src/mc-tokens.c
+++ b/src/third_party/libmongocrypt/dist/src/mc-tokens.c
@@ -15,12 +15,13 @@
*/
#include "mc-tokens-private.h"
+#include "mongocrypt-buffer-private.h"
/// Define a token type of the given name, with constructor parameters given as
/// the remaining arguments. This macro usage should be followed by the
/// constructor body, with the implicit first argument '_mongocrypt_crypto_t*
/// crypto' and final argument 'mongocrypt_status_t* status'
-#define DEF_TOKEN_TYPE(Name, ...) DEF_TOKEN_TYPE_1(Name, CONCAT(Name, _t), __VA_ARGS__)
+#define DEF_TOKEN_TYPE(Name, ...) DEF_TOKEN_TYPE_1(Name, BSON_CONCAT(Name, _t), __VA_ARGS__)
#define DEF_TOKEN_TYPE_1(Prefix, T, ...) \
/* Define the struct for the token */ \
@@ -28,9 +29,9 @@
_mongocrypt_buffer_t data; \
}; \
/* Data-getter */ \
- const _mongocrypt_buffer_t *CONCAT(Prefix, _get)(const T *self) { return &self->data; } \
+ const _mongocrypt_buffer_t *BSON_CONCAT(Prefix, _get)(const T *self) { return &self->data; } \
/* Destructor */ \
- void CONCAT(Prefix, _destroy)(T * self) { \
+ void BSON_CONCAT(Prefix, _destroy)(T * self) { \
if (!self) { \
return; \
} \
@@ -38,23 +39,23 @@
bson_free(self); \
} \
/* Constructor. From raw buffer */ \
- T *CONCAT(Prefix, _new_from_buffer)(_mongocrypt_buffer_t * buf) { \
+ T *BSON_CONCAT(Prefix, _new_from_buffer)(_mongocrypt_buffer_t * buf) { \
BSON_ASSERT(buf->len == MONGOCRYPT_HMAC_SHA256_LEN); \
T *t = bson_malloc(sizeof(T)); \
_mongocrypt_buffer_set_to(buf, &t->data); \
return t; \
} \
/* Constructor. Parameter list given as variadic args. */ \
- T *CONCAT(Prefix, _new)(_mongocrypt_crypto_t * crypto, __VA_ARGS__, mongocrypt_status_t * status)
+ T *BSON_CONCAT(Prefix, _new)(_mongocrypt_crypto_t * crypto, __VA_ARGS__, mongocrypt_status_t * status)
#define IMPL_TOKEN_NEW_1(Name, Key, Arg, Clean) \
{ \
- CONCAT(Name, _t) *t = bson_malloc(sizeof(CONCAT(Name, _t))); \
+ BSON_CONCAT(Name, _t) *t = bson_malloc(sizeof(BSON_CONCAT(Name, _t))); \
_mongocrypt_buffer_init(&t->data); \
_mongocrypt_buffer_resize(&t->data, MONGOCRYPT_HMAC_SHA256_LEN); \
\
if (!_mongocrypt_hmac_sha_256(crypto, Key, Arg, &t->data, status)) { \
- CONCAT(Name, _destroy)(t); \
+ BSON_CONCAT(Name, _destroy)(t); \
Clean; \
return NULL; \
} \
@@ -97,23 +98,46 @@ IMPL_TOKEN_NEW(mc_ESCDerivedFromDataToken, mc_ESCToken_get(ESCToken), v)
DEF_TOKEN_TYPE(mc_ECCDerivedFromDataToken, const mc_ECCToken_t *ECCToken, const _mongocrypt_buffer_t *v)
IMPL_TOKEN_NEW(mc_ECCDerivedFromDataToken, mc_ECCToken_get(ECCToken), v)
+DEF_TOKEN_TYPE(mc_EDCTwiceDerivedToken,
+ const mc_EDCDerivedFromDataTokenAndContentionFactor_t *EDCDerivedFromDataTokenAndContentionFactor)
+IMPL_TOKEN_NEW_CONST(mc_EDCTwiceDerivedToken,
+ mc_EDCDerivedFromDataTokenAndContentionFactor_get(EDCDerivedFromDataTokenAndContentionFactor),
+ 1)
+
+DEF_TOKEN_TYPE(mc_ESCTwiceDerivedTagToken,
+ const mc_ESCDerivedFromDataTokenAndContentionFactor_t *ESCDerivedFromDataTokenAndContentionFactor)
+IMPL_TOKEN_NEW_CONST(mc_ESCTwiceDerivedTagToken,
+ mc_ESCDerivedFromDataTokenAndContentionFactor_get(ESCDerivedFromDataTokenAndContentionFactor),
+ 1)
+DEF_TOKEN_TYPE(mc_ESCTwiceDerivedValueToken,
+ const mc_ESCDerivedFromDataTokenAndContentionFactor_t *ESCDerivedFromDataTokenAndContentionFactor)
+IMPL_TOKEN_NEW_CONST(mc_ESCTwiceDerivedValueToken,
+ mc_ESCDerivedFromDataTokenAndContentionFactor_get(ESCDerivedFromDataTokenAndContentionFactor),
+ 2)
+
DEF_TOKEN_TYPE(mc_ServerDataEncryptionLevel1Token, const _mongocrypt_buffer_t *RootKey)
IMPL_TOKEN_NEW_CONST(mc_ServerDataEncryptionLevel1Token, RootKey, 3)
-DEF_TOKEN_TYPE(mc_EDCDerivedFromDataTokenAndCounter,
+DEF_TOKEN_TYPE(mc_EDCDerivedFromDataTokenAndContentionFactor,
const mc_EDCDerivedFromDataToken_t *EDCDerivedFromDataToken,
uint64_t u)
-IMPL_TOKEN_NEW_CONST(mc_EDCDerivedFromDataTokenAndCounter, mc_EDCDerivedFromDataToken_get(EDCDerivedFromDataToken), u)
+IMPL_TOKEN_NEW_CONST(mc_EDCDerivedFromDataTokenAndContentionFactor,
+ mc_EDCDerivedFromDataToken_get(EDCDerivedFromDataToken),
+ u)
-DEF_TOKEN_TYPE(mc_ESCDerivedFromDataTokenAndCounter,
+DEF_TOKEN_TYPE(mc_ESCDerivedFromDataTokenAndContentionFactor,
const mc_ESCDerivedFromDataToken_t *ESCDerivedFromDataToken,
uint64_t u)
-IMPL_TOKEN_NEW_CONST(mc_ESCDerivedFromDataTokenAndCounter, mc_ESCDerivedFromDataToken_get(ESCDerivedFromDataToken), u)
+IMPL_TOKEN_NEW_CONST(mc_ESCDerivedFromDataTokenAndContentionFactor,
+ mc_ESCDerivedFromDataToken_get(ESCDerivedFromDataToken),
+ u)
-DEF_TOKEN_TYPE(mc_ECCDerivedFromDataTokenAndCounter,
+DEF_TOKEN_TYPE(mc_ECCDerivedFromDataTokenAndContentionFactor,
const mc_ECCDerivedFromDataToken_t *ECCDerivedFromDataToken,
uint64_t u)
-IMPL_TOKEN_NEW_CONST(mc_ECCDerivedFromDataTokenAndCounter, mc_ECCDerivedFromDataToken_get(ECCDerivedFromDataToken), u)
+IMPL_TOKEN_NEW_CONST(mc_ECCDerivedFromDataTokenAndContentionFactor,
+ mc_ECCDerivedFromDataToken_get(ECCDerivedFromDataToken),
+ u)
/* FLE2v2 */
@@ -133,3 +157,23 @@ IMPL_TOKEN_NEW_CONST(mc_ServerCountAndContentionFactorEncryptionToken,
DEF_TOKEN_TYPE(mc_ServerZerosEncryptionToken, const mc_ServerDerivedFromDataToken_t *serverDerivedFromDataToken)
IMPL_TOKEN_NEW_CONST(mc_ServerZerosEncryptionToken, mc_ServerDerivedFromDataToken_get(serverDerivedFromDataToken), 2)
+
+// d = 17 bytes of 0, AnchorPaddingTokenRoot = HMAC(ESCToken, d)
+#define ANCHOR_PADDING_TOKEN_D_LENGTH 17
+const uint8_t mc_AnchorPaddingTokenDValue[ANCHOR_PADDING_TOKEN_D_LENGTH] =
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+DEF_TOKEN_TYPE(mc_AnchorPaddingTokenRoot, const mc_ESCToken_t *ESCToken) {
+ _mongocrypt_buffer_t to_hash;
+ if (!_mongocrypt_buffer_copy_from_data_and_size(&to_hash,
+ mc_AnchorPaddingTokenDValue,
+ ANCHOR_PADDING_TOKEN_D_LENGTH)) {
+ return NULL;
+ }
+ IMPL_TOKEN_NEW_1(mc_AnchorPaddingTokenRoot,
+ mc_ESCToken_get(ESCToken),
+ &to_hash,
+ _mongocrypt_buffer_cleanup(&to_hash))
+}
+
+#undef ANCHOR_PADDING_TOKEN_D_LENGTH
diff --git a/src/third_party/libmongocrypt/dist/src/mlib/int128.h b/src/third_party/libmongocrypt/dist/src/mlib/int128.h
index 1619d200f92..d43338392f7 100644
--- a/src/third_party/libmongocrypt/dist/src/mlib/int128.h
+++ b/src/third_party/libmongocrypt/dist/src/mlib/int128.h
@@ -200,6 +200,13 @@ static mlib_constexpr_fn mlib_int128 mlib_int128_bitor(mlib_int128 l, mlib_int12
return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(l.r.lo | r.r.lo, l.r.hi | r.r.hi);
}
+/**
+ * @brief Bitwise-and two 128-bit integers
+ */
+static mlib_constexpr_fn mlib_int128 mlib_int128_bitand(mlib_int128 l, mlib_int128 r) {
+ return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(l.r.lo & r.r.lo, l.r.hi & r.r.hi);
+}
+
// Multiply two 64bit integers to get a 128-bit result without overflow
static mlib_constexpr_fn mlib_int128 _mlibUnsignedMult128(uint64_t left, uint64_t right) {
// Perform a Knuth 4.3.1M multiplication
@@ -245,6 +252,16 @@ static mlib_constexpr_fn int _mlibCountLeadingZeros_u64(uint64_t bits) {
return n;
}
+static mlib_constexpr_fn int _mlibCountLeadingZeros_u128(mlib_int128 r) {
+ int clz_l = _mlibCountLeadingZeros_u64(r.r.hi);
+ if (clz_l != 64) {
+ return clz_l;
+ }
+
+ int clz_r = _mlibCountLeadingZeros_u64(r.r.lo);
+ return clz_l + clz_r;
+}
+
/// Implementation of Knuth's algorithm 4.3.1 D for unsigned integer division
static mlib_constexpr_fn void
_mlibKnuth431D(uint32_t *const u, const int ulen, const uint32_t *const v, const int vlen, uint32_t *quotient) {
diff --git a/src/third_party/libmongocrypt/dist/src/mlib/int128.test.cpp b/src/third_party/libmongocrypt/dist/src/mlib/int128.test.cpp
index 99e66d21f0c..3a0854a3bb6 100644
--- a/src/third_party/libmongocrypt/dist/src/mlib/int128.test.cpp
+++ b/src/third_party/libmongocrypt/dist/src/mlib/int128.test.cpp
@@ -14,6 +14,11 @@
// Old GCC and old MSVC have partially-broken constexpr that prevents us from
// properly using static_assert with from_string()
#define BROKEN_CONSTEXPR
+#elif (defined(_MSC_VER) && _MSC_VER >= 1930)
+// Avoid internal compiler error on VS 2022 versions 17.0 and newer when
+// evaluating mlib_int128_from_string via operator""_i128. Assumed to be related
+// to: https://developercommunity.visualstudio.com/t/User-defined-literals-cause-ICEs/10259122
+#define BROKEN_CONSTEXPR
#endif
#ifndef BROKEN_CONSTEXPR
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-binary-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-binary-private.h
index 08da7877943..235caf2e288 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-binary-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-binary-private.h
@@ -21,11 +21,6 @@
#include "mongocrypt.h"
-struct _mongocrypt_binary_t {
- uint8_t *data;
- uint32_t len;
-};
-
bool _mongocrypt_binary_to_bson(mongocrypt_binary_t *binary, bson_t *out) MONGOCRYPT_WARN_UNUSED_RESULT;
#endif /* MONGOCRYPT_BINARY_PRIVATE_H */
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-buffer.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-buffer.c
index f9579157caf..cf7b1ccfce3 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-buffer.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-buffer.c
@@ -19,6 +19,11 @@
#include "mongocrypt-util-private.h"
#include
+// Require libbson 1.16.0 or newer. Fix for CDRIVER-3360 is needed.
+#if !BSON_CHECK_VERSION(1, 16, 0)
+#error "libbson 1.16.0 or newer is required."
+#endif
+
#define INT32_LEN 4
#define TYPE_LEN 1
#define NULL_BYTE_LEN 1
@@ -306,13 +311,6 @@ bool _mongocrypt_buffer_to_bson_value(_mongocrypt_buffer_t *plaintext, uint8_t t
}
bson_value_copy(bson_iter_value(&iter), out);
- /* Due to an open libbson bug (CDRIVER-3340), give an empty
- * binary payload a real address. TODO: remove this after
- * CDRIVER-3340 is fixed. */
- if (out->value_type == BSON_TYPE_BINARY && 0 == out->value.v_binary.data_len) {
- out->value.v_binary.data = bson_malloc(1); /* Freed in bson_value_destroy */
- }
-
ret = true;
fail:
bson_free(data);
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-collinfo.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-collinfo.c
index 87dc4afca8a..a25a5ae241b 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-collinfo.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-collinfo.c
@@ -61,5 +61,5 @@ void _mongocrypt_cache_collinfo_init(_mongocrypt_cache_t *cache) {
cache->destroy_value = _destroy_value;
_mongocrypt_mutex_init(&cache->mutex);
cache->pair = NULL;
- cache->expiration = CACHE_EXPIRATION_MS;
+ cache->expiration = CACHE_EXPIRATION_MS_DEFAULT;
}
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-key.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-key.c
index 601ebdf4637..24ac81f97b7 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-key.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-key.c
@@ -85,6 +85,7 @@ static void _dump_attr(void *attr_in) {
for (altname = attr->alt_names; NULL != altname; altname = altname->next) {
printf("%s\n", _mongocrypt_key_alt_name_get_string(altname));
}
+ bson_free(hex);
}
_mongocrypt_cache_key_value_t *_mongocrypt_cache_key_value_new(_mongocrypt_key_doc_t *key_doc,
@@ -128,7 +129,7 @@ void _mongocrypt_cache_key_init(_mongocrypt_cache_t *cache) {
cache->dump_attr = _dump_attr;
_mongocrypt_mutex_init(&cache->mutex);
cache->pair = NULL;
- cache->expiration = CACHE_EXPIRATION_MS;
+ cache->expiration = CACHE_EXPIRATION_MS_DEFAULT;
}
/* Since key cache may be looked up by either _id or keyAltName,
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-oauth-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-oauth-private.h
index b15e2c8ef97..7a52ea5e313 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-oauth-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-oauth-private.h
@@ -20,21 +20,19 @@
#include "mongocrypt-mutex-private.h"
#include "mongocrypt-status-private.h"
-typedef struct {
- bson_t *entry;
- char *access_token;
- int64_t expiration_time_us;
- mongocrypt_mutex_t mutex; /* global lock of cache. */
-} _mongocrypt_cache_oauth_t;
+// `mc_mapof_kmsid_to_token_t` maps a KMS ID (e.g. `azure` or `azure:myname`) to an OAuth token.
+typedef struct _mc_mapof_kmsid_to_token_t mc_mapof_kmsid_to_token_t;
-_mongocrypt_cache_oauth_t *_mongocrypt_cache_oauth_new(void);
+mc_mapof_kmsid_to_token_t *mc_mapof_kmsid_to_token_new(void);
+void mc_mapof_kmsid_to_token_destroy(mc_mapof_kmsid_to_token_t *k2t);
+// `mc_mapof_kmsid_to_token_get_token` returns a copy of the base64 encoded oauth token, or NULL.
+// Thread-safe.
+char *mc_mapof_kmsid_to_token_get_token(mc_mapof_kmsid_to_token_t *k2t, const char *kmsid);
+// `mc_mapof_kmsid_to_token_add_response` overwrites an entry if `kms_id` exists.
+// Thread-safe.
+bool mc_mapof_kmsid_to_token_add_response(mc_mapof_kmsid_to_token_t *k2t,
+ const char *kmsid,
+ bson_t *response,
+ mongocrypt_status_t *status);
-void _mongocrypt_cache_oauth_destroy(_mongocrypt_cache_oauth_t *cache);
-
-bool _mongocrypt_cache_oauth_add(_mongocrypt_cache_oauth_t *cache, bson_t *oauth_response, mongocrypt_status_t *status);
-
-/* Returns a copy of the base64 encoded oauth token, or NULL if nothing is
- * cached. */
-char *_mongocrypt_cache_oauth_get(_mongocrypt_cache_oauth_t *cache);
-
-#endif /* MONGOCRYPT_CACHE_OAUTH_PRIVATE_H */
\ No newline at end of file
+#endif /* MONGOCRYPT_CACHE_OAUTH_PRIVATE_H */
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-oauth.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-oauth.c
index e11d22293d6..9626e37e4ac 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-oauth.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-oauth.c
@@ -16,6 +16,7 @@
#include "mongocrypt-cache-oauth-private.h"
+#include "mc-array-private.h"
#include "mongocrypt-private.h"
/* How long before the reported "expires_in" time cache entries get evicted.
@@ -24,91 +25,119 @@
*/
#define MONGOCRYPT_OAUTH_CACHE_EVICTION_PERIOD_US 5000 * 1000
-_mongocrypt_cache_oauth_t *_mongocrypt_cache_oauth_new(void) {
- _mongocrypt_cache_oauth_t *cache;
-
- cache = bson_malloc0(sizeof(_mongocrypt_cache_oauth_t));
- _mongocrypt_mutex_init(&cache->mutex);
- return cache;
-}
-
-void _mongocrypt_cache_oauth_destroy(_mongocrypt_cache_oauth_t *cache) {
- BSON_ASSERT_PARAM(cache);
-
- _mongocrypt_mutex_cleanup(&cache->mutex);
- bson_destroy(cache->entry);
- bson_free(cache->access_token);
- bson_free(cache);
-}
-
-bool _mongocrypt_cache_oauth_add(_mongocrypt_cache_oauth_t *cache,
- bson_t *oauth_response,
- mongocrypt_status_t *status) {
- bson_iter_t iter;
+typedef struct {
+ char *kmsid;
+ char *access_token;
int64_t expiration_time_us;
- int64_t cache_time_us;
- int64_t expires_in_s;
- int64_t expires_in_us;
+} mc_mapof_kmsid_to_token_entry_t;
+
+struct _mc_mapof_kmsid_to_token_t {
+ mc_array_t entries;
+ mongocrypt_mutex_t mutex; // Guards `entries`.
+};
+
+mc_mapof_kmsid_to_token_t *mc_mapof_kmsid_to_token_new(void) {
+ mc_mapof_kmsid_to_token_t *k2t = bson_malloc0(sizeof(mc_mapof_kmsid_to_token_t));
+ _mc_array_init(&k2t->entries, sizeof(mc_mapof_kmsid_to_token_entry_t));
+ _mongocrypt_mutex_init(&k2t->mutex);
+ return k2t;
+}
+
+void mc_mapof_kmsid_to_token_destroy(mc_mapof_kmsid_to_token_t *k2t) {
+ if (!k2t) {
+ return;
+ }
+ _mongocrypt_mutex_cleanup(&k2t->mutex);
+ for (size_t i = 0; i < k2t->entries.len; i++) {
+ mc_mapof_kmsid_to_token_entry_t k2te = _mc_array_index(&k2t->entries, mc_mapof_kmsid_to_token_entry_t, i);
+ bson_free(k2te.kmsid);
+ bson_free(k2te.access_token);
+ }
+ _mc_array_destroy(&k2t->entries);
+ bson_free(k2t);
+}
+
+char *mc_mapof_kmsid_to_token_get_token(mc_mapof_kmsid_to_token_t *k2t, const char *kmsid) {
+ BSON_ASSERT_PARAM(k2t);
+ BSON_ASSERT_PARAM(kmsid);
+
+ _mongocrypt_mutex_lock(&k2t->mutex);
+
+ for (size_t i = 0; i < k2t->entries.len; i++) {
+ mc_mapof_kmsid_to_token_entry_t k2te = _mc_array_index(&k2t->entries, mc_mapof_kmsid_to_token_entry_t, i);
+ if (0 == strcmp(k2te.kmsid, kmsid)) {
+ if (bson_get_monotonic_time() >= k2te.expiration_time_us) {
+ // Expired.
+ _mongocrypt_mutex_unlock(&k2t->mutex);
+ return NULL;
+ }
+ char *access_token = bson_strdup(k2te.access_token);
+ _mongocrypt_mutex_unlock(&k2t->mutex);
+ return access_token;
+ }
+ }
+
+ _mongocrypt_mutex_unlock(&k2t->mutex);
+ return NULL;
+}
+
+bool mc_mapof_kmsid_to_token_add_response(mc_mapof_kmsid_to_token_t *k2t,
+ const char *kmsid,
+ bson_t *response,
+ mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(k2t);
+ BSON_ASSERT_PARAM(kmsid);
+ BSON_ASSERT_PARAM(response);
+
+ // Parse access token before locking.
const char *access_token;
+ int64_t expiration_time_us;
+ {
+ bson_iter_t iter;
+ int64_t cache_time_us;
+ int64_t expires_in_s;
+ int64_t expires_in_us;
- BSON_ASSERT_PARAM(cache);
- BSON_ASSERT_PARAM(oauth_response);
+ /* The OAuth spec strongly implies that the value of expires_in is positive,
+ * so the overflow checks in this function don't consider negative values. */
+ if (!bson_iter_init_find(&iter, response, "expires_in") || !BSON_ITER_HOLDS_INT(&iter)) {
+ CLIENT_ERR("OAuth response invalid, no 'expires_in' field.");
+ return false;
+ }
+ cache_time_us = bson_get_monotonic_time();
+ expires_in_s = bson_iter_as_int64(&iter);
+ BSON_ASSERT(expires_in_s <= INT64_MAX / 1000 / 1000);
+ expires_in_us = expires_in_s * 1000 * 1000;
+ BSON_ASSERT(expires_in_us <= INT64_MAX - cache_time_us
+ && expires_in_us + cache_time_us > MONGOCRYPT_OAUTH_CACHE_EVICTION_PERIOD_US);
+ expiration_time_us = expires_in_us + cache_time_us - MONGOCRYPT_OAUTH_CACHE_EVICTION_PERIOD_US;
- /* The OAuth spec strongly implies that the value of expires_in is positive,
- * so the overflow checks in this function don't consider negative values. */
- if (!bson_iter_init_find(&iter, oauth_response, "expires_in") || !BSON_ITER_HOLDS_INT(&iter)) {
- CLIENT_ERR("OAuth response invalid, no 'expires_in' field.");
- return false;
+ if (!bson_iter_init_find(&iter, response, "access_token") || !BSON_ITER_HOLDS_UTF8(&iter)) {
+ CLIENT_ERR("OAuth response invalid, no 'access_token' field.");
+ return false;
+ }
+ access_token = bson_iter_utf8(&iter, NULL);
}
- cache_time_us = bson_get_monotonic_time();
- expires_in_s = bson_iter_as_int64(&iter);
- BSON_ASSERT(expires_in_s <= INT64_MAX / 1000 / 1000);
- expires_in_us = expires_in_s * 1000 * 1000;
- BSON_ASSERT(expires_in_us <= INT64_MAX - cache_time_us
- && expires_in_us + cache_time_us > MONGOCRYPT_OAUTH_CACHE_EVICTION_PERIOD_US);
- expiration_time_us = expires_in_us + cache_time_us - MONGOCRYPT_OAUTH_CACHE_EVICTION_PERIOD_US;
- if (!bson_iter_init_find(&iter, oauth_response, "access_token") || !BSON_ITER_HOLDS_UTF8(&iter)) {
- CLIENT_ERR("OAuth response invalid, no 'access_token' field.");
- return false;
- }
- access_token = bson_iter_utf8(&iter, NULL);
+ _mongocrypt_mutex_lock(&k2t->mutex);
- _mongocrypt_mutex_lock(&cache->mutex);
- if (expiration_time_us > cache->expiration_time_us) {
- bson_destroy(cache->entry);
- cache->entry = bson_copy(oauth_response);
- cache->expiration_time_us = expiration_time_us;
- bson_free(cache->access_token);
- cache->access_token = bson_strdup(access_token);
+ // Check if there is an existing entry.
+ for (size_t i = 0; i < k2t->entries.len; i++) {
+ mc_mapof_kmsid_to_token_entry_t *k2te = &_mc_array_index(&k2t->entries, mc_mapof_kmsid_to_token_entry_t, i);
+ if (0 == strcmp(k2te->kmsid, kmsid)) {
+ // Update entry.
+ bson_free(k2te->access_token);
+ k2te->access_token = bson_strdup(access_token);
+ k2te->expiration_time_us = expiration_time_us;
+ _mongocrypt_mutex_unlock(&k2t->mutex);
+ return true;
+ }
}
- _mongocrypt_mutex_unlock(&cache->mutex);
+ // Create an entry.
+ mc_mapof_kmsid_to_token_entry_t to_put = {.kmsid = bson_strdup(kmsid),
+ .access_token = bson_strdup(access_token),
+ .expiration_time_us = expiration_time_us};
+ _mc_array_append_val(&k2t->entries, to_put);
+ _mongocrypt_mutex_unlock(&k2t->mutex);
return true;
}
-
-/* Returns a copy of the base64 encoded oauth token, or NULL if nothing is
- * cached. */
-char *_mongocrypt_cache_oauth_get(_mongocrypt_cache_oauth_t *cache) {
- char *access_token;
-
- BSON_ASSERT_PARAM(cache);
-
- _mongocrypt_mutex_lock(&cache->mutex);
- if (!cache->entry) {
- _mongocrypt_mutex_unlock(&cache->mutex);
- return NULL;
- }
-
- if (bson_get_monotonic_time() >= cache->expiration_time_us) {
- bson_destroy(cache->entry);
- cache->entry = NULL;
- cache->expiration_time_us = 0;
- _mongocrypt_mutex_unlock(&cache->mutex);
- return NULL;
- }
-
- access_token = bson_strdup(cache->access_token);
- _mongocrypt_mutex_unlock(&cache->mutex);
-
- return access_token;
-}
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-private.h
index 2c6160bc6a2..841a494d2e9 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache-private.h
@@ -20,7 +20,7 @@
#include "mongocrypt-mutex-private.h"
#include "mongocrypt-status-private.h"
-#define CACHE_EXPIRATION_MS 60000
+#define CACHE_EXPIRATION_MS_DEFAULT 60000
/* A generic simple cache.
* To avoid overusing the names "key" or "id", the cache contains
@@ -74,4 +74,4 @@ void _mongocrypt_cache_set_expiration(_mongocrypt_cache_t *cache, uint64_t milli
uint32_t _mongocrypt_cache_num_entries(_mongocrypt_cache_t *cache);
-#endif /* MONGOCRYPT_CACHE_PRIVATE */
\ No newline at end of file
+#endif /* MONGOCRYPT_CACHE_PRIVATE */
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache.c
index 489a7b9a4c2..aad4b25e9b4 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-cache.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-cache.c
@@ -28,7 +28,7 @@ static bool _pair_expired(_mongocrypt_cache_t *cache, _mongocrypt_cache_pair_t *
current = bson_get_monotonic_time() / 1000;
BSON_ASSERT(current >= INT64_MIN + pair->last_updated);
BSON_ASSERT(cache->expiration <= INT64_MAX);
- return (current - pair->last_updated) > (int64_t)cache->expiration;
+ return cache->expiration > 0 && (current - pair->last_updated) > (int64_t)cache->expiration;
}
/* Return the pair after the one being destroyed. */
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-ciphertext.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-ciphertext.c
index e7dac7c4253..3b029c9ed2c 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-ciphertext.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-ciphertext.c
@@ -92,7 +92,7 @@ bool _mongocrypt_ciphertext_parse_unowned(_mongocrypt_buffer_t *in,
ciphertext->original_bson_type = in->data[offset];
offset += 1;
- memset(&ciphertext->data, 0, sizeof(ciphertext->data));
+ _mongocrypt_buffer_init(&ciphertext->data);
ciphertext->data.data = in->data + offset;
ciphertext->data.len = in->len - offset;
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-crypto.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-crypto.c
index 5682ad54003..2d2a4c4db67 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-crypto.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-crypto.c
@@ -78,7 +78,10 @@ static bool _crypto_aes_256_ctr_encrypt_decrypt_via_ecb(void *ctx,
/* XOR resulting stream with original data */
for (uint32_t i = 0; i < bytes_written && ptr < args.in->len; i++, ptr++) {
- out_bin.data[ptr] = in_bin.data[ptr] ^ tmp_bin.data[i];
+ uint8_t *in_bin_u8 = in_bin.data;
+ uint8_t *out_bin_u8 = out_bin.data;
+ uint8_t *tmp_bin_u8 = tmp_bin.data;
+ out_bin_u8[ptr] = in_bin_u8[ptr] ^ tmp_bin_u8[i];
}
/* Increment value in CTR buffer */
@@ -86,9 +89,10 @@ static bool _crypto_aes_256_ctr_encrypt_decrypt_via_ecb(void *ctx,
/* assert rather than return since this should never happen */
BSON_ASSERT(ctr_bin.len == 0u || ctr_bin.len - 1u <= INT_MAX);
for (int i = (int)ctr_bin.len - 1; i >= 0 && carry != 0; --i) {
- uint32_t bpp = carry + ctr_bin.data[i];
+ uint8_t *ctr_bin_u8 = ctr_bin.data;
+ uint32_t bpp = carry + ctr_bin_u8[i];
carry = bpp >> 8;
- ctr_bin.data[i] = bpp & 0xFF;
+ ctr_bin_u8[i] = bpp & 0xFF;
}
}
@@ -1384,6 +1388,8 @@ bool _mongocrypt_random_uint64(_mongocrypt_crypto_t *crypto,
}
memcpy(&rand_u64, rand_u64_buf.data, rand_u64_buf.len);
+ // Use little-endian to enable deterministic tests on big-endian machines.
+ rand_u64 = BSON_UINT64_FROM_LE(rand_u64);
if (rand_u64 >= min) {
break;
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-datakey.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-datakey.c
index 2b98957fbe7..09f62cf1a67 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-datakey.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-datakey.c
@@ -16,6 +16,7 @@
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-ctx-private.h"
+#include "mongocrypt-kms-ctx-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt.h"
@@ -39,114 +40,169 @@ static mongocrypt_kms_ctx_t *_next_kms_ctx(mongocrypt_ctx_t *ctx) {
BSON_ASSERT_PARAM(ctx);
dkctx = (_mongocrypt_ctx_datakey_t *)ctx;
- if (dkctx->kms_returned) {
+ if (!dkctx->kms.should_retry && dkctx->kms_returned) {
return NULL;
}
+ dkctx->kms.should_retry = false; // Reset retry state.
dkctx->kms_returned = true;
return &dkctx->kms;
}
-static bool _kms_kmip_start(mongocrypt_ctx_t *ctx) {
+static bool _kms_kmip_start(mongocrypt_ctx_t *ctx, const mc_kms_creds_t *kc) {
bool ret = false;
_mongocrypt_ctx_datakey_t *dkctx = (_mongocrypt_ctx_datakey_t *)ctx;
char *user_supplied_keyid = NULL;
- _mongocrypt_endpoint_t *endpoint = NULL;
+ const _mongocrypt_endpoint_t *endpoint = NULL;
mongocrypt_status_t *status = ctx->status;
_mongocrypt_buffer_t secretdata = {0};
BSON_ASSERT_PARAM(ctx);
+ BSON_ASSERT_PARAM(kc);
+ BSON_ASSERT(kc->type == MONGOCRYPT_KMS_PROVIDER_KMIP);
if (ctx->opts.kek.kms_provider != MONGOCRYPT_KMS_PROVIDER_KMIP) {
CLIENT_ERR("KMS provider is not KMIP");
goto fail;
}
+ const bool delegated = ctx->opts.kek.provider.kmip.delegated;
user_supplied_keyid = ctx->opts.kek.provider.kmip.key_id;
if (ctx->opts.kek.provider.kmip.endpoint) {
endpoint = ctx->opts.kek.provider.kmip.endpoint;
- } else if (_mongocrypt_ctx_kms_providers(ctx)->kmip.endpoint) {
- endpoint = _mongocrypt_ctx_kms_providers(ctx)->kmip.endpoint;
+ } else if (kc->value.kmip.endpoint) {
+ endpoint = kc->value.kmip.endpoint;
} else {
CLIENT_ERR("endpoint not set for KMIP request");
goto fail;
}
- /* The KMIP createDataKey flow is the following:
- *
- * 1. Send a KMIP Register request with a new 96 byte key as a SecretData
- * managed object. This returns a Unique Identifier.
- * 2. Send a KMIP Activate request with the Unique Identifier.
- * This returns the same Unique Identifier.
- * 3. Send a KMIP Get request with the Unique Identifier.
- * This returns the 96 byte SecretData.
- * 4. Use the 96 byte SecretData to encrypt a new DEK.
- *
- * If the user set a 'keyId' to use, the flow begins at step 3.
- */
-
if (user_supplied_keyid && !dkctx->kmip_unique_identifier) {
/* User set a 'keyId'. */
dkctx->kmip_unique_identifier = bson_strdup(user_supplied_keyid);
dkctx->kmip_activated = true;
- /* Fall through to Step 3. */
}
- if (!dkctx->kmip_unique_identifier) {
- /* User did not set a 'keyId'. */
- /* Step 1. Send a KMIP Register request with a new 96 byte SecretData. */
- _mongocrypt_buffer_init(&secretdata);
- _mongocrypt_buffer_resize(&secretdata, MONGOCRYPT_KEY_LEN);
- if (!_mongocrypt_random(ctx->crypt->crypto, &secretdata, MONGOCRYPT_KEY_LEN, ctx->status)) {
- goto fail;
+ if (delegated) {
+ /*
+ * The KMIP delegated createDataKey flow is the following:
+ * 1. Send a KMIP Create request (symmetric key) (returns keyId)
+ * 2. Send a KMIP Activate request with that keyId
+ * 3. Send a KMIP Encrypt request to encrypt the DEK
+ *
+ * Steps 1 and 2 are skipped if the user provided a keyId
+ */
+
+ if (!dkctx->kmip_unique_identifier) {
+ /* User did not set a 'keyId'. */
+ /* Step 1. Send a KMIP Create request for a new AES-256 symmetric key. */
+ if (!_mongocrypt_kms_ctx_init_kmip_create(&dkctx->kms, endpoint, ctx->opts.kek.kmsid, &ctx->crypt->log)) {
+ mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
+ goto fail;
+ }
+ ctx->state = MONGOCRYPT_CTX_NEED_KMS;
+ goto success;
}
- if (!_mongocrypt_kms_ctx_init_kmip_register(&dkctx->kms,
- endpoint,
- secretdata.data,
- secretdata.len,
- &ctx->crypt->log)) {
- mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
+ if (!dkctx->kmip_activated) {
+ /* Step 2. Send a KMIP Activate request. */
+ if (!_mongocrypt_kms_ctx_init_kmip_activate(&dkctx->kms,
+ endpoint,
+ dkctx->kmip_unique_identifier,
+ ctx->opts.kek.kmsid,
+ &ctx->crypt->log)) {
+ mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
+ goto fail;
+ }
+ ctx->state = MONGOCRYPT_CTX_NEED_KMS;
+ goto success;
+ }
+
+ if (!dkctx->encrypted_key_material.data) {
+ /* Step 3. Have the KMS encrypt a new DEK. */
+ if (!_mongocrypt_kms_ctx_init_kmip_encrypt(&dkctx->kms,
+ endpoint,
+ dkctx->kmip_unique_identifier,
+ ctx->opts.kek.kmsid,
+ &dkctx->plaintext_key_material,
+ &ctx->crypt->log)) {
+ mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
+ goto fail;
+ }
+ ctx->state = MONGOCRYPT_CTX_NEED_KMS;
+ goto success;
+ }
+ } else {
+ /* The KMIP createDataKey flow is the following:
+ *
+ * 1. Send a KMIP Register request with a new 96 byte key as a SecretData
+ * managed object. This returns a Unique Identifier.
+ * 2. Send a KMIP Activate request with the Unique Identifier.
+ * This returns the same Unique Identifier.
+ * 3. Send a KMIP Get request with the Unique Identifier.
+ * This returns the 96 byte SecretData.
+ * 4. Use the 96 byte SecretData to encrypt a new DEK.
+ *
+ * If the user set a 'keyId' to use, the flow begins at step 3.
+ */
+ if (!dkctx->kmip_unique_identifier) {
+ /* User did not set a 'keyId'. */
+ /* Step 1. Send a KMIP Register request with a new 96 byte SecretData. */
+ _mongocrypt_buffer_init(&secretdata);
+ _mongocrypt_buffer_resize(&secretdata, MONGOCRYPT_KEY_LEN);
+ if (!_mongocrypt_random(ctx->crypt->crypto, &secretdata, MONGOCRYPT_KEY_LEN, ctx->status)) {
+ goto fail;
+ }
+
+ if (!_mongocrypt_kms_ctx_init_kmip_register(&dkctx->kms,
+ endpoint,
+ secretdata.data,
+ secretdata.len,
+ ctx->opts.kek.kmsid,
+ &ctx->crypt->log)) {
+ mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
+ goto fail;
+ }
+ ctx->state = MONGOCRYPT_CTX_NEED_KMS;
+ goto success;
+ }
+
+ if (!dkctx->kmip_activated) {
+ /* Step 2. Send a KMIP Activate request. */
+ if (!_mongocrypt_kms_ctx_init_kmip_activate(&dkctx->kms,
+ endpoint,
+ dkctx->kmip_unique_identifier,
+ ctx->opts.kek.kmsid,
+ &ctx->crypt->log)) {
+ mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
+ goto fail;
+ }
+ ctx->state = MONGOCRYPT_CTX_NEED_KMS;
+ goto success;
+ }
+
+ if (!dkctx->kmip_secretdata.data) {
+ /* Step 3. Send a KMIP Get request with the Unique Identifier. */
+ if (!_mongocrypt_kms_ctx_init_kmip_get(&dkctx->kms,
+ endpoint,
+ dkctx->kmip_unique_identifier,
+ ctx->opts.kek.kmsid,
+ &ctx->crypt->log)) {
+ mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
+ goto fail;
+ }
+ ctx->state = MONGOCRYPT_CTX_NEED_KMS;
+ goto success;
+ }
+
+ /* Step 4. Use the 96 byte SecretData to encrypt a new DEK. */
+ if (!_mongocrypt_wrap_key(ctx->crypt->crypto,
+ &dkctx->kmip_secretdata,
+ &dkctx->plaintext_key_material,
+ &dkctx->encrypted_key_material,
+ ctx->status)) {
goto fail;
}
- ctx->state = MONGOCRYPT_CTX_NEED_KMS;
-
- goto success;
- }
-
- if (!dkctx->kmip_activated) {
- /* Step 2. Send a KMIP Activate request. */
- if (!_mongocrypt_kms_ctx_init_kmip_activate(&dkctx->kms,
- endpoint,
- dkctx->kmip_unique_identifier,
- &ctx->crypt->log)) {
- mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
- goto fail;
- }
- ctx->state = MONGOCRYPT_CTX_NEED_KMS;
- goto success;
- }
-
- if (!dkctx->kmip_secretdata.data) {
- /* Step 3. Send a KMIP Get request with the Unique Identifier. */
- if (!_mongocrypt_kms_ctx_init_kmip_get(&dkctx->kms,
- endpoint,
- dkctx->kmip_unique_identifier,
- &ctx->crypt->log)) {
- mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
- goto fail;
- }
- ctx->state = MONGOCRYPT_CTX_NEED_KMS;
- goto success;
- }
-
- /* Step 4. Use the 96 byte SecretData to encrypt a new DEK. */
- if (!_mongocrypt_wrap_key(ctx->crypt->crypto,
- &dkctx->kmip_secretdata,
- &dkctx->plaintext_key_material,
- &dkctx->encrypted_key_material,
- ctx->status)) {
- goto fail;
}
if (!ctx->opts.kek.provider.kmip.key_id) {
@@ -180,14 +236,23 @@ static bool _kms_start(mongocrypt_ctx_t *ctx) {
dkctx = (_mongocrypt_ctx_datakey_t *)ctx;
+ mc_kms_creds_t kc;
+ if (!_mongocrypt_opts_kms_providers_lookup(kms_providers, ctx->opts.kek.kmsid, &kc)) {
+ mongocrypt_status_t *status = ctx->status;
+ CLIENT_ERR("KMS provider `%s` is not configured", ctx->opts.kek.kmsid);
+ _mongocrypt_ctx_fail(ctx);
+ goto done;
+ }
+
/* Clear out any pre-existing initialized KMS context, and zero it (so it is
* safe to call cleanup again). */
_mongocrypt_kms_ctx_cleanup(&dkctx->kms);
memset(&dkctx->kms, 0, sizeof(dkctx->kms));
dkctx->kms_returned = false;
if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_LOCAL) {
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_LOCAL);
if (!_mongocrypt_wrap_key(ctx->crypt->crypto,
- &kms_providers->local.key,
+ &kc.value.local.key,
&dkctx->plaintext_key_material,
&dkctx->encrypted_key_material,
ctx->status)) {
@@ -203,8 +268,9 @@ static bool _kms_start(mongocrypt_ctx_t *ctx) {
kms_providers,
&ctx->opts,
&dkctx->plaintext_key_material,
- &ctx->crypt->log,
- ctx->crypt->crypto)) {
+ ctx->crypt->crypto,
+ ctx->opts.kek.kmsid,
+ &ctx->crypt->log)) {
mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
_mongocrypt_ctx_fail(ctx);
goto done;
@@ -212,27 +278,30 @@ static bool _kms_start(mongocrypt_ctx_t *ctx) {
ctx->state = MONGOCRYPT_CTX_NEED_KMS;
} else if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_AZURE) {
- if (ctx->kms_providers.azure.access_token) {
- access_token = bson_strdup(ctx->kms_providers.azure.access_token);
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_AZURE);
+ if (kc.value.azure.access_token) {
+ access_token = bson_strdup(kc.value.azure.access_token);
} else {
- access_token = _mongocrypt_cache_oauth_get(ctx->crypt->cache_oauth_azure);
+ access_token = mc_mapof_kmsid_to_token_get_token(ctx->crypt->cache_oauth, ctx->opts.kek.kmsid);
}
if (access_token) {
if (!_mongocrypt_kms_ctx_init_azure_wrapkey(&dkctx->kms,
- &ctx->crypt->log,
kms_providers,
&ctx->opts,
access_token,
- &dkctx->plaintext_key_material)) {
+ &dkctx->plaintext_key_material,
+ ctx->opts.kek.kmsid,
+ &ctx->crypt->log)) {
mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
_mongocrypt_ctx_fail(ctx);
goto done;
}
} else {
if (!_mongocrypt_kms_ctx_init_azure_auth(&dkctx->kms,
- &ctx->crypt->log,
- kms_providers,
- ctx->opts.kek.provider.azure.key_vault_endpoint)) {
+ &kc,
+ ctx->opts.kek.provider.azure.key_vault_endpoint,
+ ctx->opts.kek.kmsid,
+ &ctx->crypt->log)) {
mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
_mongocrypt_ctx_fail(ctx);
goto done;
@@ -240,28 +309,31 @@ static bool _kms_start(mongocrypt_ctx_t *ctx) {
}
ctx->state = MONGOCRYPT_CTX_NEED_KMS;
} else if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
- if (NULL != ctx->kms_providers.gcp.access_token) {
- access_token = bson_strdup((const char *)ctx->kms_providers.gcp.access_token);
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_GCP);
+ if (NULL != kc.value.gcp.access_token) {
+ access_token = bson_strdup(kc.value.gcp.access_token);
} else {
- access_token = _mongocrypt_cache_oauth_get(ctx->crypt->cache_oauth_gcp);
+ access_token = mc_mapof_kmsid_to_token_get_token(ctx->crypt->cache_oauth, ctx->opts.kek.kmsid);
}
if (access_token) {
if (!_mongocrypt_kms_ctx_init_gcp_encrypt(&dkctx->kms,
- &ctx->crypt->log,
kms_providers,
&ctx->opts,
access_token,
- &dkctx->plaintext_key_material)) {
+ &dkctx->plaintext_key_material,
+ ctx->opts.kek.kmsid,
+ &ctx->crypt->log)) {
mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
_mongocrypt_ctx_fail(ctx);
goto done;
}
} else {
if (!_mongocrypt_kms_ctx_init_gcp_auth(&dkctx->kms,
- &ctx->crypt->log,
&ctx->crypt->opts,
- kms_providers,
- ctx->opts.kek.provider.gcp.endpoint)) {
+ &kc,
+ ctx->opts.kek.provider.gcp.endpoint,
+ ctx->opts.kek.kmsid,
+ &ctx->crypt->log)) {
mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
_mongocrypt_ctx_fail(ctx);
goto done;
@@ -269,7 +341,7 @@ static bool _kms_start(mongocrypt_ctx_t *ctx) {
}
ctx->state = MONGOCRYPT_CTX_NEED_KMS;
} else if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_KMIP) {
- if (!_kms_kmip_start(ctx)) {
+ if (!_kms_kmip_start(ctx, &kc)) {
goto done;
}
} else {
@@ -305,7 +377,10 @@ static bool _kms_done(mongocrypt_ctx_t *ctx) {
bson_t oauth_response;
BSON_ASSERT(_mongocrypt_buffer_to_bson(&dkctx->kms.result, &oauth_response));
- if (!_mongocrypt_cache_oauth_add(ctx->crypt->cache_oauth_azure, &oauth_response, status)) {
+ if (!mc_mapof_kmsid_to_token_add_response(ctx->crypt->cache_oauth,
+ ctx->opts.kek.kmsid,
+ &oauth_response,
+ status)) {
return _mongocrypt_ctx_fail(ctx);
}
return _kms_start(ctx);
@@ -313,11 +388,15 @@ static bool _kms_done(mongocrypt_ctx_t *ctx) {
bson_t oauth_response;
BSON_ASSERT(_mongocrypt_buffer_to_bson(&dkctx->kms.result, &oauth_response));
- if (!_mongocrypt_cache_oauth_add(ctx->crypt->cache_oauth_gcp, &oauth_response, status)) {
+ if (!mc_mapof_kmsid_to_token_add_response(ctx->crypt->cache_oauth,
+ ctx->opts.kek.kmsid,
+ &oauth_response,
+ status)) {
return _mongocrypt_ctx_fail(ctx);
}
return _kms_start(ctx);
- } else if (dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_REGISTER) {
+ } else if (dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_REGISTER
+ || dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_CREATE) {
dkctx->kmip_unique_identifier = bson_strdup((const char *)dkctx->kms.result.data);
return _kms_start(ctx);
} else if (dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_ACTIVATE) {
@@ -326,6 +405,9 @@ static bool _kms_done(mongocrypt_ctx_t *ctx) {
} else if (dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_GET) {
_mongocrypt_buffer_copy_to(&dkctx->kms.result, &dkctx->kmip_secretdata);
return _kms_start(ctx);
+ } else if (dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_ENCRYPT) {
+ _mongocrypt_buffer_copy_to(&dkctx->kms.result, &dkctx->encrypted_key_material);
+ return _kms_start(ctx);
}
/* Store the result. */
@@ -475,7 +557,7 @@ bool mongocrypt_ctx_datakey_init(mongocrypt_ctx_t *ctx) {
}
}
- if (_mongocrypt_needs_credentials_for_provider(ctx->crypt, ctx->opts.kek.kms_provider)) {
+ if (_mongocrypt_needs_credentials_for_provider(ctx->crypt, ctx->opts.kek.kms_provider, ctx->opts.kek.kmsid_name)) {
ctx->state = MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS;
} else if (!_kms_start(ctx)) {
goto done;
@@ -484,4 +566,4 @@ bool mongocrypt_ctx_datakey_init(mongocrypt_ctx_t *ctx) {
ret = true;
done:
return ret;
-}
+}
\ No newline at end of file
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-decrypt.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-decrypt.c
index c37e51e5b32..cd696b3e900 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-decrypt.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-decrypt.c
@@ -406,7 +406,7 @@ static bool _replace_ciphertext_with_plaintext(void *ctx,
static bool _finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
bson_t as_bson, final_bson;
- bson_iter_t iter;
+ bson_iter_t iter = {0};
_mongocrypt_ctx_decrypt_t *dctx;
bool res;
@@ -504,6 +504,7 @@ static bool _collect_K_KeyID_from_FLE2IndexedEncryptedValueV2(void *ctx,
|| (in->data[0] == MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2));
mc_FLE2IndexedEncryptedValueV2_t *iev = mc_FLE2IndexedEncryptedValueV2_new();
+ _mongocrypt_buffer_t S_Key = {0};
CHECK_AND_RETURN(iev);
CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValueV2_parse(iev, in, status));
@@ -511,7 +512,6 @@ static bool _collect_K_KeyID_from_FLE2IndexedEncryptedValueV2(void *ctx,
CHECK_AND_RETURN(S_KeyId);
_mongocrypt_key_broker_t *kb = ctx;
- _mongocrypt_buffer_t S_Key = {0};
CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, S_KeyId, &S_Key));
/* Decrypt InnerEncrypted to get K_KeyId. */
@@ -537,6 +537,7 @@ static bool _collect_K_KeyID_from_FLE2IndexedEncryptedValue(void *ctx,
BSON_ASSERT_PARAM(in);
BSON_ASSERT(in->data);
bool ret = false;
+ _mongocrypt_buffer_t S_Key = {0};
BSON_ASSERT((in->data[0] == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue)
|| (in->data[0] == MC_SUBTYPE_FLE2IndexedRangeEncryptedValue));
@@ -549,7 +550,6 @@ static bool _collect_K_KeyID_from_FLE2IndexedEncryptedValue(void *ctx,
CHECK_AND_RETURN(S_KeyId);
_mongocrypt_key_broker_t *kb = ctx;
- _mongocrypt_buffer_t S_Key = {0};
CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, S_KeyId, &S_Key));
/* Decrypt InnerEncrypted to get K_KeyId. */
@@ -604,7 +604,7 @@ static bool _check_for_K_KeyId(mongocrypt_ctx_t *ctx) {
}
bson_t as_bson;
- bson_iter_t iter;
+ bson_iter_t iter = {0};
_mongocrypt_ctx_decrypt_t *dctx = (_mongocrypt_ctx_decrypt_t *)ctx;
if (!_mongocrypt_buffer_to_bson(&dctx->original_doc, &as_bson)) {
return _mongocrypt_ctx_fail_w_msg(ctx, "error converting original_doc to bson");
@@ -843,7 +843,7 @@ static bool _kms_done(mongocrypt_ctx_t *ctx) {
bool mongocrypt_ctx_decrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *doc) {
_mongocrypt_ctx_decrypt_t *dctx;
bson_t as_bson;
- bson_iter_t iter;
+ bson_iter_t iter = {0};
_mongocrypt_ctx_opts_spec_t opts_spec;
memset(&opts_spec, 0, sizeof(opts_spec));
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-encrypt.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-encrypt.c
index b6043996d3c..a8e67402947 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-encrypt.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-encrypt.c
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "mc-efc-private.h"
#include "mc-fle2-rfds-private.h"
#include "mc-tokens-private.h"
#include "mongocrypt-ciphertext-private.h"
@@ -23,6 +24,7 @@
#include "mongocrypt-marking-private.h"
#include "mongocrypt-traverse-util-private.h"
#include "mongocrypt-util-private.h" // mc_iter_document_as_bson
+#include "mongocrypt.h"
/* _fle2_append_encryptedFieldConfig copies encryptedFieldConfig and applies
* default state collection names for escCollection, eccCollection, and
@@ -30,7 +32,7 @@
static bool _fle2_append_encryptedFieldConfig(const mongocrypt_ctx_t *ctx,
bson_t *dst,
bson_t *encryptedFieldConfig,
- const char *coll_name,
+ const char *target_coll,
mongocrypt_status_t *status) {
bson_iter_t iter;
bool has_escCollection = false;
@@ -39,7 +41,7 @@ static bool _fle2_append_encryptedFieldConfig(const mongocrypt_ctx_t *ctx,
BSON_ASSERT_PARAM(dst);
BSON_ASSERT_PARAM(encryptedFieldConfig);
- BSON_ASSERT_PARAM(coll_name);
+ BSON_ASSERT_PARAM(target_coll);
if (!bson_iter_init(&iter, encryptedFieldConfig)) {
CLIENT_ERR("unable to iterate encryptedFieldConfig");
@@ -63,7 +65,7 @@ static bool _fle2_append_encryptedFieldConfig(const mongocrypt_ctx_t *ctx,
}
if (!has_escCollection) {
- char *default_escCollection = bson_strdup_printf("enxcol_.%s.esc", coll_name);
+ char *default_escCollection = bson_strdup_printf("enxcol_.%s.esc", target_coll);
if (!BSON_APPEND_UTF8(dst, "escCollection", default_escCollection)) {
CLIENT_ERR("unable to append escCollection");
bson_free(default_escCollection);
@@ -72,7 +74,7 @@ static bool _fle2_append_encryptedFieldConfig(const mongocrypt_ctx_t *ctx,
bson_free(default_escCollection);
}
if (!has_eccCollection && !ctx->crypt->opts.use_fle2_v2) {
- char *default_eccCollection = bson_strdup_printf("enxcol_.%s.ecc", coll_name);
+ char *default_eccCollection = bson_strdup_printf("enxcol_.%s.ecc", target_coll);
if (!BSON_APPEND_UTF8(dst, "eccCollection", default_eccCollection)) {
CLIENT_ERR("unable to append eccCollection");
bson_free(default_eccCollection);
@@ -81,7 +83,7 @@ static bool _fle2_append_encryptedFieldConfig(const mongocrypt_ctx_t *ctx,
bson_free(default_eccCollection);
}
if (!has_ecocCollection) {
- char *default_ecocCollection = bson_strdup_printf("enxcol_.%s.ecoc", coll_name);
+ char *default_ecocCollection = bson_strdup_printf("enxcol_.%s.ecoc", target_coll);
if (!BSON_APPEND_UTF8(dst, "ecocCollection", default_ecocCollection)) {
CLIENT_ERR("unable to append ecocCollection");
bson_free(default_ecocCollection);
@@ -94,20 +96,20 @@ static bool _fle2_append_encryptedFieldConfig(const mongocrypt_ctx_t *ctx,
static bool _fle2_append_encryptionInformation(const mongocrypt_ctx_t *ctx,
bson_t *dst,
- const char *ns,
+ const char *target_ns,
bson_t *encryptedFieldConfig,
bson_t *deleteTokens,
- const char *coll_name,
+ const char *target_coll,
mongocrypt_status_t *status) {
bson_t encryption_information_bson;
bson_t schema_bson;
bson_t encrypted_field_config_bson;
BSON_ASSERT_PARAM(dst);
- BSON_ASSERT_PARAM(ns);
+ BSON_ASSERT_PARAM(target_ns);
BSON_ASSERT_PARAM(encryptedFieldConfig);
/* deleteTokens may be NULL */
- BSON_ASSERT_PARAM(coll_name);
+ BSON_ASSERT_PARAM(target_coll);
if (!BSON_APPEND_DOCUMENT_BEGIN(dst, "encryptionInformation", &encryption_information_bson)) {
CLIENT_ERR("unable to begin appending 'encryptionInformation'");
@@ -122,7 +124,7 @@ static bool _fle2_append_encryptionInformation(const mongocrypt_ctx_t *ctx,
return false;
}
- if (!BSON_APPEND_DOCUMENT_BEGIN(&schema_bson, ns, &encrypted_field_config_bson)) {
+ if (!BSON_APPEND_DOCUMENT_BEGIN(&schema_bson, target_ns, &encrypted_field_config_bson)) {
CLIENT_ERR("unable to begin appending 'encryptedFieldConfig' to "
"'encryptionInformation'.'schema'");
return false;
@@ -131,7 +133,7 @@ static bool _fle2_append_encryptionInformation(const mongocrypt_ctx_t *ctx,
if (!_fle2_append_encryptedFieldConfig(ctx,
&encrypted_field_config_bson,
encryptedFieldConfig,
- coll_name,
+ target_coll,
status)) {
return false;
}
@@ -153,8 +155,8 @@ static bool _fle2_append_encryptionInformation(const mongocrypt_ctx_t *ctx,
"'encryptionInformation'");
return false;
}
- if (!BSON_APPEND_DOCUMENT(&delete_tokens_bson, ns, deleteTokens)) {
- CLIENT_ERR("unable to append '%s' to 'deleteTokens'", ns);
+ if (!BSON_APPEND_DOCUMENT(&delete_tokens_bson, target_ns, deleteTokens)) {
+ CLIENT_ERR("unable to append '%s' to 'deleteTokens'", target_ns);
return false;
}
if (!bson_append_document_end(&encryption_information_bson, &delete_tokens_bson)) {
@@ -178,12 +180,12 @@ typedef enum { MC_TO_CSFLE, MC_TO_MONGOCRYPTD, MC_TO_MONGOD } mc_cmd_target_t;
*
* @param cmd_name The name of the command.
* @param cmd The command being rewritten. It is an input and output.
- * @param ns The . namespace for the command.
+ * @param target_ns The . namespace for the command.
* @param encryptedFieldConfig The "encryptedFields" document for the
* collection.
* @param deleteTokens Delete tokens to append to "encryptionInformation". May
* be NULL.
- * @param coll_name The collection name.
+ * @param target_coll The collection name.
* @param cmd_target The intended destination of the command. csfle,
* mongocryptd, and mongod have different requirements for the location of
* "encryptionInformation".
@@ -194,10 +196,10 @@ typedef enum { MC_TO_CSFLE, MC_TO_MONGOCRYPTD, MC_TO_MONGOD } mc_cmd_target_t;
static bool _fle2_insert_encryptionInformation(const mongocrypt_ctx_t *ctx,
const char *cmd_name,
bson_t *cmd /* in and out */,
- const char *ns,
+ const char *target_ns,
bson_t *encryptedFieldConfig,
bson_t *deleteTokens,
- const char *coll_name,
+ const char *target_coll,
mc_cmd_target_t cmd_target,
mongocrypt_status_t *status) {
bson_t out = BSON_INITIALIZER;
@@ -207,16 +209,96 @@ static bool _fle2_insert_encryptionInformation(const mongocrypt_ctx_t *ctx,
BSON_ASSERT_PARAM(cmd_name);
BSON_ASSERT_PARAM(cmd);
- BSON_ASSERT_PARAM(ns);
+ BSON_ASSERT_PARAM(target_ns);
BSON_ASSERT_PARAM(encryptedFieldConfig);
/* deleteTokens may be NULL */
- BSON_ASSERT_PARAM(coll_name);
+ BSON_ASSERT_PARAM(target_coll);
+
+ // For `bulkWrite`, append `encryptionInformation` inside the `nsInfo.0` document.
+ if (0 == strcmp(cmd_name, "bulkWrite")) {
+ // Get the single `nsInfo` document from the input command.
+ bson_t nsInfo; // Non-owning.
+ {
+ bson_iter_t nsInfo_iter;
+ if (!bson_iter_init(&nsInfo_iter, cmd)) {
+ CLIENT_ERR("failed to iterate command");
+ goto fail;
+ }
+ if (!bson_iter_find_descendant(&nsInfo_iter, "nsInfo.0", &nsInfo_iter)) {
+ CLIENT_ERR("expected one namespace in `bulkWrite`, but found zero.");
+ goto fail;
+ }
+ if (bson_has_field(cmd, "nsInfo.1")) {
+ CLIENT_ERR(
+ "expected one namespace in `bulkWrite`, but found more than one. Only one namespace is supported.");
+ goto fail;
+ }
+ if (!mc_iter_document_as_bson(&nsInfo_iter, &nsInfo, status)) {
+ goto fail;
+ }
+ // Ensure `nsInfo` does not already have an `encryptionInformation` field.
+ if (bson_has_field(&nsInfo, "encryptionInformation")) {
+ CLIENT_ERR("unexpected `encryptionInformation` present in input `nsInfo`.");
+ goto fail;
+ }
+ }
+
+ // Copy input and append `encryptionInformation` to `nsInfo`.
+ {
+ // Append everything from input except `nsInfo`.
+ bson_copy_to_excluding_noinit(cmd, &out, "nsInfo", NULL);
+ // Append `nsInfo` array.
+ bson_t nsInfo_array;
+ if (!BSON_APPEND_ARRAY_BEGIN(&out, "nsInfo", &nsInfo_array)) {
+ CLIENT_ERR("unable to begin appending 'nsInfo' array");
+ goto fail;
+ }
+ bson_t nsInfo_array_0;
+ if (!BSON_APPEND_DOCUMENT_BEGIN(&nsInfo_array, "0", &nsInfo_array_0)) {
+ CLIENT_ERR("unable to append 'nsInfo.0' document");
+ goto fail;
+ }
+ // Copy everything from input `nsInfo`.
+ bson_concat(&nsInfo_array_0, &nsInfo);
+ // And append `encryptionInformation`.
+ if (!_fle2_append_encryptionInformation(ctx,
+ &nsInfo_array_0,
+ target_ns,
+ encryptedFieldConfig,
+ deleteTokens,
+ target_coll,
+ status)) {
+ goto fail;
+ }
+ if (!bson_append_document_end(&nsInfo_array, &nsInfo_array_0)) {
+ CLIENT_ERR("unable to end appending 'nsInfo' document in array");
+ }
+ if (!bson_append_array_end(&out, &nsInfo_array)) {
+ CLIENT_ERR("unable to end appending 'nsInfo' array");
+ goto fail;
+ }
+ // Overwrite `cmd`.
+ bson_destroy(cmd);
+ if (!bson_steal(cmd, &out)) {
+ CLIENT_ERR("failed to steal BSON with encryptionInformation");
+ goto fail;
+ }
+ }
+
+ goto success;
+ }
if (0 != strcmp(cmd_name, "explain") || cmd_target == MC_TO_MONGOCRYPTD) {
- // All commands except "explain" expect "encryptionInformation"
+ // All commands except "explain" and "bulkWrite" expect "encryptionInformation"
// at top-level. "explain" sent to mongocryptd expects
// "encryptionInformation" at top-level.
- if (!_fle2_append_encryptionInformation(ctx, cmd, ns, encryptedFieldConfig, deleteTokens, coll_name, status)) {
+ if (!_fle2_append_encryptionInformation(ctx,
+ cmd,
+ target_ns,
+ encryptedFieldConfig,
+ deleteTokens,
+ target_coll,
+ status)) {
goto fail;
}
bson_destroy(&out);
@@ -253,7 +335,13 @@ static bool _fle2_insert_encryptionInformation(const mongocrypt_ctx_t *ctx,
bson_copy_to(&tmp, &explain);
}
- if (!_fle2_append_encryptionInformation(ctx, &explain, ns, encryptedFieldConfig, deleteTokens, coll_name, status)) {
+ if (!_fle2_append_encryptionInformation(ctx,
+ &explain,
+ target_ns,
+ encryptedFieldConfig,
+ deleteTokens,
+ target_coll,
+ status)) {
goto fail;
}
@@ -265,7 +353,7 @@ static bool _fle2_insert_encryptionInformation(const mongocrypt_ctx_t *ctx,
bson_copy_to_excluding_noinit(cmd, &out, "explain", NULL);
bson_destroy(cmd);
if (!bson_steal(cmd, &out)) {
- CLIENT_ERR("failed to steal BSON without encryptionInformation");
+ CLIENT_ERR("failed to steal BSON with encryptionInformation");
goto fail;
}
@@ -288,7 +376,7 @@ static bool _mongo_op_collinfo(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out)
BSON_ASSERT_PARAM(out);
ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
- cmd = BCON_NEW("name", BCON_UTF8(ectx->coll_name));
+ cmd = BCON_NEW("name", BCON_UTF8(ectx->target_coll));
CRYPT_TRACEF(&ectx->parent.crypt->log, "constructed: %s\n", tmp_json(cmd));
_mongocrypt_buffer_steal_from_bson(&ectx->list_collections_filter, cmd);
out->data = ectx->list_collections_filter.data;
@@ -328,10 +416,46 @@ static bool _set_schema_from_collinfo(mongocrypt_ctx_t *ctx, bson_t *collinfo) {
if (!_mongocrypt_buffer_to_bson(&ectx->encrypted_field_config, &efc_bson)) {
return _mongocrypt_ctx_fail_w_msg(ctx, "unable to create BSON from encrypted_field_config");
}
- if (!mc_EncryptedFieldConfig_parse(&ectx->efc, &efc_bson, ctx->status)) {
+ if (!mc_EncryptedFieldConfig_parse(&ectx->efc, &efc_bson, ctx->status, ctx->crypt->opts.use_range_v2)) {
_mongocrypt_ctx_fail(ctx);
return false;
}
+ } else if (0 == strcmp(ectx->cmd_name, "bulkWrite")) {
+ ectx->used_empty_encryptedFields = true;
+ // `bulkWrite` is a special case. Sending `bulkWrite` with `jsonSchema` to query analysis results in an error:
+ // `The bulkWrite command only supports Queryable Encryption`
+ //
+ // Add an empty encryptedFields (rather than an empty JSON schema) to ensure `bulkWrite` can be sent to query
+ // analysis.
+ bson_t empty_encryptedFields = BSON_INITIALIZER;
+ {
+ char *escCollection = bson_strdup_printf("enxcol_.%s.esc", ectx->target_coll);
+ char *ecocCollection = bson_strdup_printf("enxcol_.%s.ecoc", ectx->target_coll);
+ bson_t empty_array = BSON_INITIALIZER;
+ if (!BSON_APPEND_UTF8(&empty_encryptedFields, "escCollection", escCollection)) {
+ return _mongocrypt_ctx_fail_w_msg(ctx, "failed to append `escCollection`");
+ }
+ if (!BSON_APPEND_UTF8(&empty_encryptedFields, "ecocCollection", ecocCollection)) {
+ return _mongocrypt_ctx_fail_w_msg(ctx, "failed to append `ecocCollection`");
+ }
+ if (!BSON_APPEND_ARRAY(&empty_encryptedFields, "fields", &empty_array)) {
+ return _mongocrypt_ctx_fail_w_msg(ctx, "failed to append `fields`");
+ }
+
+ bson_destroy(&empty_array);
+ bson_free(escCollection);
+ bson_free(ecocCollection);
+ }
+
+ if (!mc_EncryptedFieldConfig_parse(&ectx->efc,
+ &empty_encryptedFields,
+ ctx->status,
+ ctx->crypt->opts.use_range_v2)) {
+ bson_destroy(&empty_encryptedFields);
+ _mongocrypt_ctx_fail(ctx);
+ return false;
+ }
+ _mongocrypt_buffer_steal_from_bson(&ectx->encrypted_field_config, &empty_encryptedFields);
}
BSON_ASSERT(bson_iter_init(&iter, collinfo));
@@ -458,7 +582,7 @@ static bool _fle2_collect_keys_for_deleteTokens(mongocrypt_ctx_t *ctx) {
mc_EncryptedField_t *field;
for (field = ectx->efc.fields; field != NULL; field = field->next) {
- if (field->has_queries) {
+ if (field->supported_queries) {
if (!_mongocrypt_key_broker_request_id(&ctx->kb, &field->keyId)) {
_mongocrypt_key_broker_status(&ctx->kb, ctx->status);
_mongocrypt_ctx_fail(ctx);
@@ -469,9 +593,10 @@ static bool _fle2_collect_keys_for_deleteTokens(mongocrypt_ctx_t *ctx) {
return true;
}
-/* _fle2_collect_keys_for_compact requests keys required to produce
- * compactionTokens. compactionTokens is only applicable to FLE 2. */
-static bool _fle2_collect_keys_for_compact(mongocrypt_ctx_t *ctx) {
+/* _fle2_collect_keys_for_compaction requests keys required to produce
+ * compactionTokens or cleanupTokens.
+ * compactionTokens and cleanupTokens are only applicable to FLE 2. */
+static bool _fle2_collect_keys_for_compaction(mongocrypt_ctx_t *ctx) {
_mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
BSON_ASSERT_PARAM(ctx);
@@ -483,11 +608,12 @@ static bool _fle2_collect_keys_for_compact(mongocrypt_ctx_t *ctx) {
const char *cmd_name = ectx->cmd_name;
- if (0 != strcmp(cmd_name, "compactStructuredEncryptionData")) {
+ if (0 != strcmp(cmd_name, "compactStructuredEncryptionData")
+ && 0 != strcmp(cmd_name, "cleanupStructuredEncryptionData")) {
return true;
}
- /* compactStructuredEncryptionData must not be sent to mongocryptd. */
+ /* (compact/cleanup)StructuredEncryptionData must not be sent to mongocryptd. */
ectx->bypass_query_analysis = true;
mc_EncryptedField_t *field;
@@ -516,7 +642,7 @@ static bool _mongo_feed_collinfo(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in)
}
/* Cache the received collinfo. */
- if (!_mongocrypt_cache_add_copy(&ctx->crypt->cache_collinfo, ectx->ns, &as_bson, ctx->status)) {
+ if (!_mongocrypt_cache_add_copy(&ctx->crypt->cache_collinfo, ectx->target_ns, &as_bson, ctx->status)) {
return _mongocrypt_ctx_fail(ctx);
}
@@ -538,8 +664,12 @@ static bool _mongo_done_collinfo(mongocrypt_ctx_t *ctx) {
if (_mongocrypt_buffer_empty(&ectx->schema)) {
bson_t empty_collinfo = BSON_INITIALIZER;
- /* If no collinfo was fed, cache an empty collinfo. */
- if (!_mongocrypt_cache_add_copy(&ctx->crypt->cache_collinfo, ectx->ns, &empty_collinfo, ctx->status)) {
+ /* If no collinfo was fed, apply and cache an empty collinfo. */
+ if (!_set_schema_from_collinfo(ctx, &empty_collinfo)) {
+ bson_destroy(&empty_collinfo);
+ return false;
+ }
+ if (!_mongocrypt_cache_add_copy(&ctx->crypt->cache_collinfo, ectx->target_ns, &empty_collinfo, ctx->status)) {
bson_destroy(&empty_collinfo);
return _mongocrypt_ctx_fail(ctx);
}
@@ -550,7 +680,7 @@ static bool _mongo_done_collinfo(mongocrypt_ctx_t *ctx) {
return false;
}
- if (!_fle2_collect_keys_for_compact(ctx)) {
+ if (!_fle2_collect_keys_for_compaction(ctx)) {
return false;
}
@@ -564,6 +694,19 @@ static bool _mongo_done_collinfo(mongocrypt_ctx_t *ctx) {
return _try_run_csfle_marking(ctx);
}
+static const char *_mongo_db_collinfo(mongocrypt_ctx_t *ctx) {
+ _mongocrypt_ctx_encrypt_t *ectx;
+
+ BSON_ASSERT_PARAM(ctx);
+
+ ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
+ if (!ectx->target_db) {
+ _mongocrypt_ctx_fail_w_msg(ctx, "Expected target database for `listCollections`, but none exists.");
+ return NULL;
+ }
+ return ectx->target_db;
+}
+
static bool _fle2_mongo_op_markings(mongocrypt_ctx_t *ctx, bson_t *out) {
_mongocrypt_ctx_encrypt_t *ectx;
bson_t cmd_bson = BSON_INITIALIZER, encrypted_field_config_bson = BSON_INITIALIZER;
@@ -594,10 +737,10 @@ static bool _fle2_mongo_op_markings(mongocrypt_ctx_t *ctx, bson_t *out) {
if (!_fle2_insert_encryptionInformation(ctx,
cmd_name,
out,
- ectx->ns,
+ ectx->target_ns,
&encrypted_field_config_bson,
NULL /* deleteTokens */,
- ectx->coll_name,
+ ectx->target_coll,
ctx->crypt->csfle.okay ? MC_TO_CSFLE : MC_TO_MONGOCRYPTD,
ctx->status)) {
return _mongocrypt_ctx_fail(ctx);
@@ -734,7 +877,7 @@ static bool _collect_key_from_marking(void *ctx, _mongocrypt_buffer_t *in, mongo
static bool _mongo_feed_markings(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in) {
/* Find keys. */
bson_t as_bson;
- bson_iter_t iter;
+ bson_iter_t iter = {0};
_mongocrypt_ctx_encrypt_t *ectx;
BSON_ASSERT_PARAM(ctx);
@@ -823,7 +966,7 @@ static bool _mongo_done_markings(mongocrypt_ctx_t *ctx) {
/**
* @brief Append $db to a command being passed to csfle.
*/
-static bool _add_dollar_db(const char *cmd_name, bson_t *cmd, const char *db_name, mongocrypt_status_t *status) {
+static bool _add_dollar_db(const char *cmd_name, bson_t *cmd, const char *cmd_db, mongocrypt_status_t *status) {
bson_t out = BSON_INITIALIZER;
bson_t explain = BSON_INITIALIZER;
bson_iter_t iter;
@@ -831,10 +974,10 @@ static bool _add_dollar_db(const char *cmd_name, bson_t *cmd, const char *db_nam
BSON_ASSERT_PARAM(cmd_name);
BSON_ASSERT_PARAM(cmd);
- BSON_ASSERT_PARAM(db_name);
+ BSON_ASSERT_PARAM(cmd_db);
if (!bson_iter_init_find(&iter, cmd, "$db")) {
- if (!BSON_APPEND_UTF8(cmd, "$db", db_name)) {
+ if (!BSON_APPEND_UTF8(cmd, "$db", cmd_db)) {
CLIENT_ERR("failed to append '$db'");
goto fail;
}
@@ -868,7 +1011,7 @@ static bool _add_dollar_db(const char *cmd_name, bson_t *cmd, const char *db_nam
bson_copy_to(&tmp, &explain);
}
- if (!BSON_APPEND_UTF8(&explain, "$db", db_name)) {
+ if (!BSON_APPEND_UTF8(&explain, "$db", cmd_db)) {
CLIENT_ERR("failed to append '$db'");
goto fail;
}
@@ -947,7 +1090,7 @@ static bool _try_run_csfle_marking(mongocrypt_ctx_t *ctx) {
const char *cmd_name = ectx->cmd_name;
- if (!_add_dollar_db(cmd_name, &cmd, ectx->db_name, ctx->status)) {
+ if (!_add_dollar_db(cmd_name, &cmd, ectx->cmd_db, ctx->status)) {
_mongocrypt_ctx_fail(ctx);
goto fail_create_cmd;
}
@@ -975,8 +1118,12 @@ static bool _try_run_csfle_marking(mongocrypt_ctx_t *ctx) {
CHECK_CSFLE_ERROR("query_analyzer_create", fail_qa_create);
uint32_t marked_bson_len = 0;
- uint8_t *marked_bson =
- csfle.analyze_query(qa, bson_get_data(&cmd), ectx->ns, (uint32_t)strlen(ectx->ns), &marked_bson_len, status);
+ uint8_t *marked_bson = csfle.analyze_query(qa,
+ bson_get_data(&cmd),
+ ectx->target_ns,
+ (uint32_t)strlen(ectx->target_ns),
+ &marked_bson_len,
+ status);
CHECK_CSFLE_ERROR("analyze_query", fail_analyze_query);
// Copy out the marked document.
@@ -1067,14 +1214,12 @@ fail:
static bool
_replace_marking_with_ciphertext(void *ctx, _mongocrypt_buffer_t *in, bson_value_t *out, mongocrypt_status_t *status) {
- _mongocrypt_marking_t marking;
+ _mongocrypt_marking_t marking = {0};
bool ret;
BSON_ASSERT_PARAM(ctx);
BSON_ASSERT_PARAM(in);
- memset(&marking, 0, sizeof(marking));
-
if (!_mongocrypt_marking_parse_unowned(in, &marking, status)) {
_mongocrypt_marking_cleanup(&marking);
return false;
@@ -1107,7 +1252,7 @@ static bson_t *generate_delete_tokens(_mongocrypt_crypto_t *crypto,
mc_ECOCToken_t *ecoc = NULL;
bool loop_ok = false;
/* deleteTokens are only necessary for indexed fields. */
- if (!ef->has_queries) {
+ if (!ef->supported_queries) {
goto loop_continue;
}
@@ -1223,8 +1368,11 @@ typedef struct {
// must_omit_encryptionInformation returns true if the command
// must omit the "encryptionInformation" field when sent to mongod / mongos.
-static moe_result
-must_omit_encryptionInformation(const char *command_name, const bson_t *command, mongocrypt_status_t *status) {
+static moe_result must_omit_encryptionInformation(const char *command_name,
+ const bson_t *command,
+ bool use_range_v2,
+ const mc_EncryptedFieldConfig_t *efc,
+ mongocrypt_status_t *status) {
// eligible_commands may omit encryptionInformation if the command does not
// contain payloads requiring encryption.
const char *eligible_commands[] = {"find", "aggregate", "distinct", "count", "insert"};
@@ -1232,10 +1380,28 @@ must_omit_encryptionInformation(const char *command_name, const bson_t *command,
bool found = false;
// prohibited_commands prohibit encryptionInformation on mongod / mongos.
- const char *prohibited_commands[] = {"compactStructuredEncryptionData", "create", "collMod", "createIndexes"};
+ const char *prohibited_commands[] = {"cleanupStructuredEncryptionData", "create", "collMod", "createIndexes"};
BSON_ASSERT_PARAM(command_name);
BSON_ASSERT_PARAM(command);
+ BSON_ASSERT_PARAM(efc);
+
+ if (0 == strcmp("compactStructuredEncryptionData", command_name)) {
+ // `compactStructuredEncryptionData` is a special case:
+ // - Server 7.0 prohibits `encryptionInformation`.
+ // - Server 8.0 requires `encryptionInformation` if "range" fields are referenced. Otherwise ignores.
+ // Only send `encryptionInformation` if "range" fields are present to support both server versions.
+ bool uses_range_fields = false;
+ if (use_range_v2) {
+ for (const mc_EncryptedField_t *ef = efc->fields; ef != NULL; ef = ef->next) {
+ if (ef->supported_queries & SUPPORTS_RANGE_QUERIES) {
+ uses_range_fields = true;
+ break;
+ }
+ }
+ }
+ return (moe_result){.ok = true, .must_omit = !uses_range_fields};
+ }
for (i = 0; i < sizeof(prohibited_commands) / sizeof(prohibited_commands[0]); i++) {
if (0 == strcmp(prohibited_commands[i], command_name)) {
@@ -1254,7 +1420,7 @@ must_omit_encryptionInformation(const char *command_name, const bson_t *command,
}
bool has_payload_requiring_encryptionInformation = false;
- bson_iter_t iter;
+ bson_iter_t iter = {0};
if (!bson_iter_init(&iter, command)) {
CLIENT_ERR("unable to iterate command");
return (moe_result){.ok = false};
@@ -1274,35 +1440,46 @@ must_omit_encryptionInformation(const char *command_name, const bson_t *command,
}
/* _fle2_append_compactionTokens appends compactionTokens if command_name is
- * "compactStructuredEncryptionData" */
-static bool _fle2_append_compactionTokens(_mongocrypt_crypto_t *crypto,
+ * "compactStructuredEncryptionData" or cleanupTokens if command_name is
+ * "cleanupStructuredEncryptionData"
+ */
+static bool _fle2_append_compactionTokens(mongocrypt_t *crypt,
_mongocrypt_key_broker_t *kb,
mc_EncryptedFieldConfig_t *efc,
const char *command_name,
bson_t *out,
mongocrypt_status_t *status) {
- bson_t result_compactionTokens;
+ bson_t result_compactionTokens = BSON_INITIALIZER;
bool ret = false;
- BSON_ASSERT_PARAM(crypto);
+ BSON_ASSERT_PARAM(crypt);
BSON_ASSERT_PARAM(kb);
BSON_ASSERT_PARAM(efc);
BSON_ASSERT_PARAM(command_name);
BSON_ASSERT_PARAM(out);
+ _mongocrypt_crypto_t *crypto = crypt->crypto;
- if (0 != strcmp(command_name, "compactStructuredEncryptionData")) {
+ bool cleanup = (0 == strcmp(command_name, "cleanupStructuredEncryptionData"));
+
+ if (0 != strcmp(command_name, "compactStructuredEncryptionData") && !cleanup) {
return true;
}
- BSON_APPEND_DOCUMENT_BEGIN(out, "compactionTokens", &result_compactionTokens);
+ if (cleanup) {
+ BSON_APPEND_DOCUMENT_BEGIN(out, "cleanupTokens", &result_compactionTokens);
+ } else {
+ BSON_APPEND_DOCUMENT_BEGIN(out, "compactionTokens", &result_compactionTokens);
+ }
mc_EncryptedField_t *ptr;
for (ptr = efc->fields; ptr != NULL; ptr = ptr->next) {
- /* Append ECOC token. */
+ /* Append tokens. */
_mongocrypt_buffer_t key = {0};
_mongocrypt_buffer_t tokenkey = {0};
mc_CollectionsLevel1Token_t *cl1t = NULL;
mc_ECOCToken_t *ecoct = NULL;
+ mc_ESCToken_t *esct = NULL;
+ mc_AnchorPaddingTokenRoot_t *padt = NULL;
bool ecoc_ok = false;
if (!_mongocrypt_key_broker_decrypted_key_by_id(kb, &ptr->keyId, &key)) {
@@ -1333,10 +1510,35 @@ static bool _fle2_append_compactionTokens(_mongocrypt_crypto_t *crypto,
const _mongocrypt_buffer_t *ecoct_buf = mc_ECOCToken_get(ecoct);
- BSON_APPEND_BINARY(&result_compactionTokens, ptr->path, BSON_SUBTYPE_BINARY, ecoct_buf->data, ecoct_buf->len);
+ if (crypt->opts.use_range_v2 && (ptr->supported_queries & SUPPORTS_RANGE_QUERIES)) {
+ // Append the document {ecoc: , anchorPaddingToken: }
+ esct = mc_ESCToken_new(crypto, cl1t, status);
+ if (!esct) {
+ goto ecoc_fail;
+ }
+ padt = mc_AnchorPaddingTokenRoot_new(crypto, esct, status);
+ if (!padt) {
+ goto ecoc_fail;
+ }
+ const _mongocrypt_buffer_t *padt_buf = mc_AnchorPaddingTokenRoot_get(padt);
+ bson_t tokenDoc;
+ BSON_APPEND_DOCUMENT_BEGIN(&result_compactionTokens, ptr->path, &tokenDoc);
+ BSON_APPEND_BINARY(&tokenDoc, "ecoc", BSON_SUBTYPE_BINARY, ecoct_buf->data, ecoct_buf->len);
+ BSON_APPEND_BINARY(&tokenDoc, "anchorPaddingToken", BSON_SUBTYPE_BINARY, padt_buf->data, padt_buf->len);
+ bson_append_document_end(&result_compactionTokens, &tokenDoc);
+ } else {
+ // Append just
+ BSON_APPEND_BINARY(&result_compactionTokens,
+ ptr->path,
+ BSON_SUBTYPE_BINARY,
+ ecoct_buf->data,
+ ecoct_buf->len);
+ }
ecoc_ok = true;
ecoc_fail:
+ mc_AnchorPaddingTokenRoot_destroy(padt);
+ mc_ESCToken_destroy(esct);
mc_ECOCToken_destroy(ecoct);
mc_CollectionsLevel1Token_destroy(cl1t);
_mongocrypt_buffer_cleanup(&key);
@@ -1363,11 +1565,63 @@ _fle2_strip_encryptionInformation(const char *cmd_name, bson_t *cmd /* in and ou
BSON_ASSERT_PARAM(cmd_name);
BSON_ASSERT_PARAM(cmd);
- if (0 != strcmp(cmd_name, "explain")) {
+ if (0 != strcmp(cmd_name, "explain") && 0 != strcmp(cmd_name, "bulkWrite")) {
bson_copy_to_excluding_noinit(cmd, &stripped, "encryptionInformation", NULL);
goto success;
}
+ if (0 == strcmp(cmd_name, "bulkWrite")) {
+ // Get the single `nsInfo` document from the input command.
+ bson_t nsInfo; // Non-owning.
+ {
+ bson_iter_t nsInfo_iter;
+ if (!bson_iter_init(&nsInfo_iter, cmd)) {
+ CLIENT_ERR("failed to iterate command");
+ goto fail;
+ }
+ if (!bson_iter_find_descendant(&nsInfo_iter, "nsInfo.0", &nsInfo_iter)) {
+ CLIENT_ERR("expected one namespace in `bulkWrite`, but found zero.");
+ goto fail;
+ }
+ if (bson_has_field(cmd, "nsInfo.1")) {
+ CLIENT_ERR(
+ "expected one namespace in `bulkWrite`, but found more than one. Only one namespace is supported.");
+ goto fail;
+ }
+ if (!mc_iter_document_as_bson(&nsInfo_iter, &nsInfo, status)) {
+ goto fail;
+ }
+ }
+
+ // Copy input and exclude `encryptionInformation` from `nsInfo`.
+ {
+ // Append everything from input except `nsInfo`.
+ bson_copy_to_excluding_noinit(cmd, &stripped, "nsInfo", NULL);
+ // Append `nsInfo` array.
+ bson_t nsInfo_array;
+ if (!BSON_APPEND_ARRAY_BEGIN(&stripped, "nsInfo", &nsInfo_array)) {
+ CLIENT_ERR("unable to begin appending 'nsInfo' array");
+ goto fail;
+ }
+ bson_t nsInfo_array_0;
+ if (!BSON_APPEND_DOCUMENT_BEGIN(&nsInfo_array, "0", &nsInfo_array_0)) {
+ CLIENT_ERR("unable to append 'nsInfo.0' document");
+ goto fail;
+ }
+ // Copy everything from input `nsInfo` and exclude `encryptionInformation`.
+ bson_copy_to_excluding_noinit(&nsInfo, &nsInfo_array_0, "encryptionInformation", NULL);
+ if (!bson_append_document_end(&nsInfo_array, &nsInfo_array_0)) {
+ CLIENT_ERR("unable to end appending 'nsInfo' document in array");
+ }
+ if (!bson_append_array_end(&stripped, &nsInfo_array)) {
+ CLIENT_ERR("unable to end appending 'nsInfo' array");
+ goto fail;
+ }
+ }
+
+ goto success;
+ }
+
// The 'explain' command is a special case.
// 'encryptionInformation' is returned from mongocryptd and csfle nested
// inside 'explain'. Example:
@@ -1451,7 +1705,7 @@ static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
bson_copy_to(&original_cmd_bson, &converted);
} else {
bson_t as_bson;
- bson_iter_t iter;
+ bson_iter_t iter = {0};
if (!_mongocrypt_buffer_to_bson(&ectx->marked_cmd, &as_bson)) {
return _mongocrypt_ctx_fail_w_msg(ctx, "malformed bson");
@@ -1488,7 +1742,11 @@ static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
}
}
- moe_result result = must_omit_encryptionInformation(command_name, &converted, ctx->status);
+ moe_result result = must_omit_encryptionInformation(command_name,
+ &converted,
+ ctx->crypt->opts.use_range_v2,
+ &ectx->efc,
+ ctx->status);
if (!result.ok) {
bson_destroy(&converted);
bson_destroy(deleteTokens);
@@ -1496,14 +1754,14 @@ static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
}
/* Append a new 'encryptionInformation'. */
- if (!result.must_omit) {
+ if (!result.must_omit && !ectx->used_empty_encryptedFields) {
if (!_fle2_insert_encryptionInformation(ctx,
command_name,
&converted,
- ectx->ns,
+ ectx->target_ns,
&encrypted_field_config_bson,
deleteTokens,
- ectx->coll_name,
+ ectx->target_coll,
MC_TO_MONGOD,
ctx->status)) {
bson_destroy(&converted);
@@ -1513,12 +1771,7 @@ static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
}
bson_destroy(deleteTokens);
- if (!_fle2_append_compactionTokens(ctx->crypt->crypto,
- &ctx->kb,
- &ectx->efc,
- command_name,
- &converted,
- ctx->status)) {
+ if (!_fle2_append_compactionTokens(ctx->crypt, &ctx->kb, &ectx->efc, command_name, &converted, ctx->status)) {
bson_destroy(&converted);
return _mongocrypt_ctx_fail(ctx);
}
@@ -1527,7 +1780,7 @@ static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
bson_iter_t iter;
if (bson_iter_init_find(&iter, &original_cmd_bson, "$db")) {
if (!bson_iter_init_find(&iter, &converted, "$db")) {
- BSON_APPEND_UTF8(&converted, "$db", ectx->db_name);
+ BSON_APPEND_UTF8(&converted, "$db", ectx->cmd_db);
}
}
@@ -1545,6 +1798,9 @@ static bool FLE2RangeFindDriverSpec_to_ciphertexts(mongocrypt_ctx_t *ctx, mongoc
BSON_ASSERT_PARAM(ctx);
BSON_ASSERT_PARAM(out);
+ bson_t with_placholders = BSON_INITIALIZER;
+ bson_t with_ciphertexts = BSON_INITIALIZER;
+
if (!ctx->opts.rangeopts.set) {
_mongocrypt_ctx_fail_w_msg(ctx, "Expected RangeOpts to be set for Range Find");
goto fail;
@@ -1554,8 +1810,6 @@ static bool FLE2RangeFindDriverSpec_to_ciphertexts(mongocrypt_ctx_t *ctx, mongoc
goto fail;
}
- bson_t with_placholders = BSON_INITIALIZER;
- bson_t with_ciphertexts = BSON_INITIALIZER;
bson_t in_bson;
if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &in_bson)) {
_mongocrypt_ctx_fail_w_msg(ctx, "unable to convert input to BSON");
@@ -1663,7 +1917,13 @@ static bool _fle2_finalize_explicit(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *
marking.type = MONGOCRYPT_MARKING_FLE2_ENCRYPTION;
if (ctx->opts.query_type.set) {
switch (ctx->opts.query_type.value) {
- case MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW:
+ case MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED:
+ if (ctx->crypt->opts.use_range_v2) {
+ _mongocrypt_ctx_fail_w_msg(ctx, "Cannot use rangePreview query type with Range V2");
+ goto fail;
+ }
+ // fallthrough
+ case MONGOCRYPT_QUERY_TYPE_RANGE:
case MONGOCRYPT_QUERY_TYPE_EQUALITY: marking.fle2.type = MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND; break;
default: _mongocrypt_ctx_fail_w_msg(ctx, "Invalid value for EncryptOpts.queryType"); goto fail;
}
@@ -1674,7 +1934,13 @@ static bool _fle2_finalize_explicit(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *
switch (ctx->opts.index_type.value) {
case MONGOCRYPT_INDEX_TYPE_EQUALITY: marking.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_EQUALITY; break;
case MONGOCRYPT_INDEX_TYPE_NONE: marking.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_UNINDEXED; break;
- case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW: marking.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_RANGE; break;
+ case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED:
+ if (ctx->crypt->opts.use_range_v2) {
+ _mongocrypt_ctx_fail_w_msg(ctx, "Cannot use rangePreview index type with Range V2");
+ goto fail;
+ }
+ // fallthrough
+ case MONGOCRYPT_INDEX_TYPE_RANGE: marking.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_RANGE; break;
default:
// This might be unreachable because of other validation. Better safe than
// sorry.
@@ -1695,7 +1961,11 @@ static bool _fle2_finalize_explicit(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *
// RangeOpts with query_type is handled above.
BSON_ASSERT(!ctx->opts.query_type.set);
- if (!mc_RangeOpts_to_FLE2RangeInsertSpec(&ctx->opts.rangeopts.value, &old_v, &new_v, ctx->status)) {
+ if (!mc_RangeOpts_to_FLE2RangeInsertSpec(&ctx->opts.rangeopts.value,
+ &old_v,
+ &new_v,
+ ctx->crypt->opts.use_range_v2,
+ ctx->status)) {
_mongocrypt_ctx_fail(ctx);
goto fail;
}
@@ -1730,7 +2000,7 @@ static bool _fle2_finalize_explicit(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *
}
if (ctx->opts.contention_factor.set) {
- marking.fle2.maxContentionCounter = ctx->opts.contention_factor.value;
+ marking.fle2.maxContentionFactor = ctx->opts.contention_factor.value;
} else if (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_EQUALITY) {
_mongocrypt_ctx_fail_w_msg(ctx, "contention factor required for indexed algorithm");
goto fail;
@@ -1764,7 +2034,7 @@ fail:
static bool _finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
bson_t as_bson, converted;
- bson_iter_t iter;
+ bson_iter_t iter = {0};
_mongocrypt_ctx_encrypt_t *ectx;
bool res;
@@ -1811,7 +2081,7 @@ static bool _finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
bson_iter_t iter;
if (bson_iter_init_find(&iter, &original_cmd_bson, "$db")) {
if (!bson_iter_init_find(&iter, &converted, "$db")) {
- BSON_APPEND_UTF8(&converted, "$db", ectx->db_name);
+ BSON_APPEND_UTF8(&converted, "$db", ectx->cmd_db);
}
}
} else {
@@ -1869,9 +2139,10 @@ static void _cleanup(mongocrypt_ctx_t *ctx) {
}
ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
- bson_free(ectx->ns);
- bson_free(ectx->db_name);
- bson_free(ectx->coll_name);
+ bson_free(ectx->target_ns);
+ bson_free(ectx->cmd_db);
+ bson_free(ectx->target_db);
+ bson_free(ectx->target_coll);
_mongocrypt_buffer_cleanup(&ectx->list_collections_filter);
_mongocrypt_buffer_cleanup(&ectx->schema);
_mongocrypt_buffer_cleanup(&ectx->encrypted_field_config);
@@ -1903,7 +2174,7 @@ static bool _try_schema_from_schema_map(mongocrypt_ctx_t *ctx) {
return _mongocrypt_ctx_fail_w_msg(ctx, "malformed schema map");
}
- if (bson_iter_init_find(&iter, &schema_map, ectx->ns)) {
+ if (bson_iter_init_find(&iter, &schema_map, ectx->target_ns)) {
if (!_mongocrypt_buffer_copy_from_document_iter(&ectx->schema, &iter)) {
return _mongocrypt_ctx_fail_w_msg(ctx, "malformed schema map");
}
@@ -1939,7 +2210,7 @@ static bool _fle2_try_encrypted_field_config_from_map(mongocrypt_ctx_t *ctx) {
return _mongocrypt_ctx_fail_w_msg(ctx, "unable to convert encrypted_field_config_map to BSON");
}
- if (bson_iter_init_find(&iter, &encrypted_field_config_map, ectx->ns)) {
+ if (bson_iter_init_find(&iter, &encrypted_field_config_map, ectx->target_ns)) {
if (!_mongocrypt_buffer_copy_from_document_iter(&ectx->encrypted_field_config, &iter)) {
return _mongocrypt_ctx_fail_w_msg(ctx,
"unable to copy encrypted_field_config from "
@@ -1949,7 +2220,7 @@ static bool _fle2_try_encrypted_field_config_from_map(mongocrypt_ctx_t *ctx) {
if (!_mongocrypt_buffer_to_bson(&ectx->encrypted_field_config, &efc_bson)) {
return _mongocrypt_ctx_fail_w_msg(ctx, "unable to create BSON from encrypted_field_config");
}
- if (!mc_EncryptedFieldConfig_parse(&ectx->efc, &efc_bson, ctx->status)) {
+ if (!mc_EncryptedFieldConfig_parse(&ectx->efc, &efc_bson, ctx->status, ctx->crypt->opts.use_range_v2)) {
_mongocrypt_ctx_fail(ctx);
return false;
}
@@ -1970,18 +2241,32 @@ static bool _try_schema_from_cache(mongocrypt_ctx_t *ctx) {
/* Otherwise, we need a remote schema. Check if we have a response to
* listCollections cached. */
- if (!_mongocrypt_cache_get(&ctx->crypt->cache_collinfo, ectx->ns /* null terminated */, (void **)&collinfo)) {
+ if (!_mongocrypt_cache_get(&ctx->crypt->cache_collinfo,
+ ectx->target_ns /* null terminated */,
+ (void **)&collinfo)) {
return _mongocrypt_ctx_fail_w_msg(ctx, "failed to retrieve from cache");
}
if (collinfo) {
if (!_set_schema_from_collinfo(ctx, collinfo)) {
+ bson_destroy(collinfo);
return _mongocrypt_ctx_fail(ctx);
}
ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
} else {
/* we need to get it. */
ctx->state = MONGOCRYPT_CTX_NEED_MONGO_COLLINFO;
+ if (ectx->target_db) {
+ if (!ctx->crypt->opts.use_need_mongo_collinfo_with_db_state) {
+ _mongocrypt_ctx_fail_w_msg(
+ ctx,
+ "Fetching remote collection information on separate databases is not supported. Try "
+ "upgrading driver, or specify a local schemaMap or encryptedFieldsMap.");
+ return false;
+ }
+ // Target database may differ from command database. Request collection info from target database.
+ ctx->state = MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB;
+ }
}
bson_destroy(collinfo);
@@ -2138,7 +2423,7 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms
_mongocrypt_ctx_encrypt_t *ectx;
bson_t as_bson;
bson_iter_t iter;
- _mongocrypt_ctx_opts_spec_t opts_spec;
+ _mongocrypt_ctx_opts_spec_t opts_spec = {0};
if (!ctx) {
return false;
@@ -2222,7 +2507,9 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms
return _mongocrypt_ctx_fail_w_msg(ctx, "contention factor is required for indexed algorithm");
}
- if (ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW) {
+ if (ctx->opts.index_type.set
+ && (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGE
+ || ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED)) {
if (!ctx->opts.contention_factor.set) {
return _mongocrypt_ctx_fail_w_msg(ctx, "contention factor is required for range indexed algorithm");
}
@@ -2242,8 +2529,14 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms
bool matches = false;
switch (ctx->opts.query_type.value) {
- case MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW:
- matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW);
+ case MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED:
+ // Don't allow deprecated query type if we are using new index type.
+ matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED);
+ break;
+ case MONGOCRYPT_QUERY_TYPE_RANGE:
+ // New query type is compatible with both new and old index types.
+ matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED
+ || ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGE);
break;
case MONGOCRYPT_QUERY_TYPE_EQUALITY:
matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_EQUALITY);
@@ -2317,7 +2610,9 @@ bool mongocrypt_ctx_explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_bina
if (!explicit_encrypt_init(ctx, msg)) {
return false;
}
- if (ctx->opts.query_type.set && ctx->opts.query_type.value == MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW) {
+ if (ctx->opts.query_type.set
+ && (ctx->opts.query_type.value == MONGOCRYPT_QUERY_TYPE_RANGE
+ || ctx->opts.query_type.value == MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED)) {
return _mongocrypt_ctx_fail_w_msg(ctx, "Encrypt may not be used for range queries. Use EncryptExpression.");
}
return true;
@@ -2327,22 +2622,77 @@ bool mongocrypt_ctx_explicit_encrypt_expression_init(mongocrypt_ctx_t *ctx, mong
if (!explicit_encrypt_init(ctx, msg)) {
return false;
}
- if (!ctx->opts.query_type.set || ctx->opts.query_type.value != MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW) {
+ if (!ctx->opts.query_type.set
+ || !(ctx->opts.query_type.value == MONGOCRYPT_QUERY_TYPE_RANGE
+ || ctx->opts.query_type.value == MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED)) {
return _mongocrypt_ctx_fail_w_msg(ctx, "EncryptExpression may only be used for range queries.");
}
return true;
}
-static bool
-_check_cmd_for_auto_encrypt(mongocrypt_binary_t *cmd, bool *bypass, char **collname, mongocrypt_status_t *status) {
+static bool _check_cmd_for_auto_encrypt_bulkWrite(mongocrypt_binary_t *cmd,
+ char **target_db,
+ char **target_coll,
+ mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(cmd);
+ BSON_ASSERT_PARAM(target_db);
+ BSON_ASSERT_PARAM(target_coll);
+
bson_t as_bson;
- bson_iter_t iter, ns_iter;
+ bson_iter_t cmd_iter = {0};
+
+ if (!_mongocrypt_binary_to_bson(cmd, &as_bson) || !bson_iter_init(&cmd_iter, &as_bson)) {
+ CLIENT_ERR("invalid command BSON");
+ return false;
+ }
+
+ bson_iter_t ns_iter = cmd_iter;
+ if (!bson_iter_find_descendant(&ns_iter, "nsInfo.0.ns", &ns_iter)) {
+ CLIENT_ERR("failed to find namespace in `bulkWrite` command");
+ return false;
+ }
+
+ if (!BSON_ITER_HOLDS_UTF8(&ns_iter)) {
+ CLIENT_ERR("expected namespace to be UTF8, got: %s", mc_bson_type_to_string(bson_iter_type(&ns_iter)));
+ return false;
+ }
+
+ const char *target_ns = bson_iter_utf8(&ns_iter, NULL /* length */);
+ // Parse `target_ns` into "."
+ const char *dot = strstr(target_ns, ".");
+ if (!dot) {
+ CLIENT_ERR("expected namespace to contain dot, got: %s", target_ns);
+ return false;
+ }
+ *target_coll = bson_strdup(dot + 1);
+ // Get the database from the `ns` field (which may differ from `cmd_db`).
+ ptrdiff_t db_len = dot - target_ns;
+ if ((uint64_t)db_len > SIZE_MAX) {
+ CLIENT_ERR("unexpected database length exceeds %zu", SIZE_MAX);
+ return false;
+ }
+ *target_db = bson_strndup(target_ns, (size_t)db_len);
+
+ // Ensure only one `nsInfo` element is present.
+ // Query analysis (mongocryptd/crypt_shared) currently only supports one namespace.
+ if (bson_has_field(&as_bson, "nsInfo.1")) {
+ CLIENT_ERR("expected one namespace in `bulkWrite`, but found more than one. Only one namespace is supported.");
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+_check_cmd_for_auto_encrypt(mongocrypt_binary_t *cmd, bool *bypass, char **target_coll, mongocrypt_status_t *status) {
+ bson_t as_bson;
+ bson_iter_t iter = {0}, target_coll_iter;
const char *cmd_name;
bool eligible = false;
BSON_ASSERT_PARAM(cmd);
BSON_ASSERT_PARAM(bypass);
- BSON_ASSERT_PARAM(collname);
+ BSON_ASSERT_PARAM(target_coll);
*bypass = false;
@@ -2366,22 +2716,22 @@ _check_cmd_for_auto_encrypt(mongocrypt_binary_t *cmd, bool *bypass, char **colln
CLIENT_ERR("explain value is not a document");
return false;
}
- if (!bson_iter_recurse(&iter, &ns_iter)) {
+ if (!bson_iter_recurse(&iter, &target_coll_iter)) {
CLIENT_ERR("malformed BSON for encrypt command");
return false;
}
- if (!bson_iter_next(&ns_iter)) {
+ if (!bson_iter_next(&target_coll_iter)) {
CLIENT_ERR("invalid empty BSON");
return false;
}
} else {
- memcpy(&ns_iter, &iter, sizeof(iter));
+ memcpy(&target_coll_iter, &iter, sizeof(iter));
}
- if (BSON_ITER_HOLDS_UTF8(&ns_iter)) {
- *collname = bson_strdup(bson_iter_utf8(&ns_iter, NULL));
+ if (BSON_ITER_HOLDS_UTF8(&target_coll_iter)) {
+ *target_coll = bson_strdup(bson_iter_utf8(&target_coll_iter, NULL));
} else {
- *collname = NULL;
+ *target_coll = NULL;
}
/* check if command is eligible for auto encryption, bypassed, or ineligible.
@@ -2461,6 +2811,8 @@ _check_cmd_for_auto_encrypt(mongocrypt_binary_t *cmd, bool *bypass, char **colln
*bypass = true;
} else if (0 == strcmp(cmd_name, "compactStructuredEncryptionData")) {
eligible = true;
+ } else if (0 == strcmp(cmd_name, "cleanupStructuredEncryptionData")) {
+ eligible = true;
} else if (0 == strcmp(cmd_name, "collMod")) {
eligible = true;
} else if (0 == strcmp(cmd_name, "hello")) {
@@ -2483,11 +2835,11 @@ _check_cmd_for_auto_encrypt(mongocrypt_binary_t *cmd, bool *bypass, char **colln
/* database/client commands are ineligible. */
if (eligible) {
- if (!*collname) {
+ if (!*target_coll) {
CLIENT_ERR("non-collection command not supported for auto encryption: %s", cmd_name);
return false;
}
- if (0 == strlen(*collname)) {
+ if (0 == strlen(*target_coll)) {
CLIENT_ERR("empty collection name on command: %s", cmd_name);
return false;
}
@@ -2515,7 +2867,6 @@ static bool needs_ismaster_check(mongocrypt_ctx_t *ctx) {
bool mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t *ctx, const char *db, int32_t db_len, mongocrypt_binary_t *cmd) {
_mongocrypt_ctx_encrypt_t *ectx;
_mongocrypt_ctx_opts_spec_t opts_spec;
- bool bypass;
if (!ctx) {
return false;
@@ -2537,15 +2888,13 @@ bool mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t *ctx, const char *db, int32_t
ctx->vtable.mongo_op_collinfo = _mongo_op_collinfo;
ctx->vtable.mongo_feed_collinfo = _mongo_feed_collinfo;
ctx->vtable.mongo_done_collinfo = _mongo_done_collinfo;
+ ctx->vtable.mongo_db_collinfo = _mongo_db_collinfo;
ctx->vtable.mongo_op_collinfo = _mongo_op_collinfo;
ctx->vtable.mongo_op_markings = _mongo_op_markings;
ctx->vtable.mongo_feed_markings = _mongo_feed_markings;
ctx->vtable.mongo_done_markings = _mongo_done_markings;
ctx->vtable.finalize = _finalize;
ctx->vtable.cleanup = _cleanup;
- ctx->vtable.mongo_op_collinfo = _mongo_op_collinfo;
- ctx->vtable.mongo_feed_collinfo = _mongo_feed_collinfo;
- ctx->vtable.mongo_done_collinfo = _mongo_done_collinfo;
ectx->bypass_query_analysis = ctx->crypt->opts.bypass_query_analysis;
if (!cmd || !cmd->data) {
@@ -2559,27 +2908,38 @@ bool mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t *ctx, const char *db, int32_t
return _mongocrypt_ctx_fail(ctx);
}
- if (!_check_cmd_for_auto_encrypt(cmd, &bypass, &ectx->coll_name, ctx->status)) {
- return _mongocrypt_ctx_fail(ctx);
- }
-
- if (bypass) {
- ctx->nothing_to_do = true;
- ctx->state = MONGOCRYPT_CTX_READY;
- return true;
- }
-
- /* if _check_cmd_for_auto_encrypt did not bypass or error, a collection name
- * must have been set. */
- if (!ectx->coll_name) {
- return _mongocrypt_ctx_fail_w_msg(ctx, "unexpected error: did not bypass or error but no collection name");
- }
-
- if (!_mongocrypt_validate_and_copy_string(db, db_len, &ectx->db_name) || 0 == strlen(ectx->db_name)) {
+ if (!_mongocrypt_validate_and_copy_string(db, db_len, &ectx->cmd_db) || 0 == strlen(ectx->cmd_db)) {
return _mongocrypt_ctx_fail_w_msg(ctx, "invalid db");
}
- ectx->ns = bson_strdup_printf("%s.%s", ectx->db_name, ectx->coll_name);
+ if (0 == strcmp(ectx->cmd_name, "bulkWrite")) {
+ // Handle `bulkWrite` as a special case.
+ // `bulkWrite` includes the target namespaces in an `nsInfo` field.
+ // Only one target namespace is supported.
+ if (!_check_cmd_for_auto_encrypt_bulkWrite(cmd, &ectx->target_db, &ectx->target_coll, ctx->status)) {
+ return _mongocrypt_ctx_fail(ctx);
+ }
+
+ ectx->target_ns = bson_strdup_printf("%s.%s", ectx->target_db, ectx->target_coll);
+ } else {
+ bool bypass;
+ if (!_check_cmd_for_auto_encrypt(cmd, &bypass, &ectx->target_coll, ctx->status)) {
+ return _mongocrypt_ctx_fail(ctx);
+ }
+
+ if (bypass) {
+ ctx->nothing_to_do = true;
+ ctx->state = MONGOCRYPT_CTX_READY;
+ return true;
+ }
+
+ /* if _check_cmd_for_auto_encrypt did not bypass or error, a collection name
+ * must have been set. */
+ if (!ectx->target_coll) {
+ return _mongocrypt_ctx_fail_w_msg(ctx, "unexpected error: did not bypass or error but no collection name");
+ }
+ ectx->target_ns = bson_strdup_printf("%s.%s", ectx->cmd_db, ectx->target_coll);
+ }
if (ctx->opts.kek.provider.aws.region || ctx->opts.kek.provider.aws.cmk) {
return _mongocrypt_ctx_fail_w_msg(ctx, "aws masterkey options must not be set");
@@ -2601,7 +2961,7 @@ bool mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t *ctx, const char *db, int32_t
"%s (%s=\"%s\", %s=%d, %s=\"%s\")",
BSON_FUNC,
"db",
- ectx->db_name,
+ ectx->cmd_db,
"db_len",
db_len,
"cmd",
@@ -2680,6 +3040,17 @@ static bool mongocrypt_ctx_encrypt_ismaster_done(mongocrypt_ctx_t *ctx) {
/* Otherwise, we need the the driver to fetch the schema. */
if (_mongocrypt_buffer_empty(&ectx->schema)) {
ctx->state = MONGOCRYPT_CTX_NEED_MONGO_COLLINFO;
+ if (ectx->target_db) {
+ if (!ctx->crypt->opts.use_need_mongo_collinfo_with_db_state) {
+ _mongocrypt_ctx_fail_w_msg(
+ ctx,
+ "Fetching remote collection information on separate databases is not supported. Try "
+ "upgrading driver, or specify a local schemaMap or encryptedFieldsMap.");
+ return false;
+ }
+ // Target database may differ from command database. Request collection info from target database.
+ ctx->state = MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB;
+ }
}
}
@@ -2689,7 +3060,7 @@ static bool mongocrypt_ctx_encrypt_ismaster_done(mongocrypt_ctx_t *ctx) {
return false;
}
- if (!_fle2_collect_keys_for_compact(ctx)) {
+ if (!_fle2_collect_keys_for_compaction(ctx)) {
return false;
}
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-private.h
index 1500686f901..688db865d2c 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-private.h
@@ -39,12 +39,17 @@ typedef enum {
typedef enum {
MONGOCRYPT_INDEX_TYPE_NONE = 1,
MONGOCRYPT_INDEX_TYPE_EQUALITY = 2,
- MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW = 3
+ MONGOCRYPT_INDEX_TYPE_RANGE = 3,
+ MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED = 4
} mongocrypt_index_type_t;
const char *_mongocrypt_index_type_to_string(mongocrypt_index_type_t val);
-typedef enum { MONGOCRYPT_QUERY_TYPE_EQUALITY = 1, MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW = 2 } mongocrypt_query_type_t;
+typedef enum {
+ MONGOCRYPT_QUERY_TYPE_EQUALITY = 1,
+ MONGOCRYPT_QUERY_TYPE_RANGE = 2,
+ MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED = 3
+} mongocrypt_query_type_t;
const char *_mongocrypt_query_type_to_string(mongocrypt_query_type_t val);
@@ -58,6 +63,7 @@ typedef struct __mongocrypt_ctx_opts_t {
_mongocrypt_buffer_t key_material;
mongocrypt_encryption_algorithm_t algorithm;
_mongocrypt_kek_t kek;
+ bool retry_enabled;
struct {
mongocrypt_index_type_t value;
@@ -82,8 +88,15 @@ typedef struct __mongocrypt_ctx_opts_t {
} rangeopts;
} _mongocrypt_ctx_opts_t;
+// `_mongocrypt_ctx_opts_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_opts_t,
+ BSON_ALIGNOF(_mongocrypt_ctx_opts_t)
+ >= BSON_MAX(BSON_ALIGNOF(_mongocrypt_key_alt_name_t), BSON_ALIGNOF(mc_RangeOpts_t)));
+
/* All derived contexts may override these methods. */
typedef struct {
+ const char *(*mongo_db_collinfo)(mongocrypt_ctx_t *ctx);
bool (*mongo_op_collinfo)(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out);
bool (*mongo_feed_collinfo)(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in);
bool (*mongo_done_collinfo)(mongocrypt_ctx_t *ctx);
@@ -129,9 +142,22 @@ bool _mongocrypt_ctx_fail_w_msg(mongocrypt_ctx_t *ctx, const char *msg);
typedef struct {
mongocrypt_ctx_t parent;
bool explicit;
- char *coll_name;
- char *db_name;
- char *ns;
+
+ // `cmd_db` is the command database (appended as `$db`).
+ char *cmd_db;
+
+ // `target_ns` is the target namespace "." for the operation. May be associated with
+ // jsonSchema (CSFLE) or encryptedFields (QE). For `bulkWrite`, the target namespace database may differ from
+ // `cmd_db`.
+ char *target_ns;
+
+ // `target_db` is the target database for the operation. For `bulkWrite`, the target namespace database may differ
+ // from `cmd_db`. If `target_db` is NULL, the target namespace database is the same as `cmd_db`.
+ char *target_db;
+
+ // `target_coll` is the target namespace collection name.
+ char *target_coll;
+
_mongocrypt_buffer_t list_collections_filter;
_mongocrypt_buffer_t schema;
/* TODO CDRIVER-3150: audit + rename these buffers.
@@ -156,13 +182,19 @@ typedef struct {
* schema, and there were siblings. */
bool collinfo_has_siblings;
/* encrypted_field_config is set when:
- * 1. . is present in an encrypted_field_config_map.
+ * 1. `target_ns` is present in an encrypted_field_config_map.
* 2. (TODO MONGOCRYPT-414) The collection has encryptedFields in the
* response to listCollections. encrypted_field_config is true if and only if
* encryption is using FLE 2.0.
+ * 3. The `bulkWrite` command is processed and needs an empty encryptedFields to be processed by query analysis.
+ * (`bulkWrite` does not support empty JSON schema).
*/
_mongocrypt_buffer_t encrypted_field_config;
mc_EncryptedFieldConfig_t efc;
+ // `used_empty_encryptedFields` is true if the collection has no JSON schema or encryptedFields,
+ // yet an empty encryptedFields was constructed to support query analysis.
+ // When true, an empty encryptedFields is sent to query analysis, but not appended to the final command.
+ bool used_empty_encryptedFields;
/* bypass_query_analysis is set to true to skip the
* MONGOCRYPT_CTX_NEED_MONGO_MARKINGS state. */
bool bypass_query_analysis;
@@ -177,6 +209,11 @@ typedef struct {
const char *cmd_name;
} _mongocrypt_ctx_encrypt_t;
+// `_mongocrypt_ctx_encrypt_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_encrypt_t,
+ BSON_ALIGNOF(_mongocrypt_ctx_encrypt_t) >= BSON_ALIGNOF(mongocrypt_ctx_t));
+
typedef struct {
mongocrypt_ctx_t parent;
/* TODO CDRIVER-3150: audit + rename these buffers.
@@ -187,6 +224,11 @@ typedef struct {
_mongocrypt_buffer_t decrypted_doc;
} _mongocrypt_ctx_decrypt_t;
+// `_mongocrypt_ctx_datakey_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_decrypt_t,
+ BSON_ALIGNOF(_mongocrypt_ctx_decrypt_t) >= BSON_ALIGNOF(mongocrypt_ctx_t));
+
typedef struct {
mongocrypt_ctx_t parent;
mongocrypt_kms_ctx_t kms;
@@ -200,6 +242,11 @@ typedef struct {
_mongocrypt_buffer_t kmip_secretdata;
} _mongocrypt_ctx_datakey_t;
+// `_mongocrypt_ctx_datakey_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_datakey_t,
+ BSON_ALIGNOF(_mongocrypt_ctx_datakey_t) >= BSON_ALIGNOF(mongocrypt_ctx_t));
+
typedef struct _mongocrypt_ctx_rmd_datakey_t _mongocrypt_ctx_rmd_datakey_t;
struct _mongocrypt_ctx_rmd_datakey_t {
@@ -217,12 +264,40 @@ typedef struct {
_mongocrypt_buffer_t results;
} _mongocrypt_ctx_rewrap_many_datakey_t;
+// `_mongocrypt_ctx_rewrap_many_datakey_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_rewrap_many_datakey_t,
+ BSON_ALIGNOF(_mongocrypt_ctx_rewrap_many_datakey_t) >= BSON_ALIGNOF(mongocrypt_ctx_t));
+
typedef struct {
mongocrypt_ctx_t parent;
_mongocrypt_buffer_t result;
mc_EncryptedFieldConfig_t efc;
} _mongocrypt_ctx_compact_t;
+// `_mongocrypt_ctx_compact_t` inherits extended alignment from libbson. To dynamically allocate, use aligned
+// allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_compact_t,
+ BSON_ALIGNOF(_mongocrypt_ctx_compact_t) >= BSON_ALIGNOF(mongocrypt_ctx_t));
+
+#define MONGOCRYPT_CTX_ALLOC_SIZE \
+ BSON_MAX(sizeof(_mongocrypt_ctx_encrypt_t), \
+ BSON_MAX(sizeof(_mongocrypt_ctx_decrypt_t), \
+ BSON_MAX(sizeof(_mongocrypt_ctx_datakey_t), \
+ BSON_MAX(sizeof(_mongocrypt_ctx_rewrap_many_datakey_t), \
+ sizeof(_mongocrypt_ctx_compact_t)))))
+
+#define MONGOCRYPT_CTX_ALLOC_ALIGNMENT \
+ BSON_MAX(BSON_ALIGNOF(_mongocrypt_ctx_encrypt_t), \
+ BSON_MAX(BSON_ALIGNOF(_mongocrypt_ctx_decrypt_t), \
+ BSON_MAX(BSON_ALIGNOF(_mongocrypt_ctx_datakey_t), \
+ BSON_MAX(BSON_ALIGNOF(_mongocrypt_ctx_rewrap_many_datakey_t), \
+ BSON_ALIGNOF(_mongocrypt_ctx_compact_t)))))
+
+// `_mongocrypt_ctx_t` inherits extended alignment from libbson. To dynamically allocate, use
+// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof_mongocrypt_ctx_t, BSON_ALIGNOF(mongocrypt_ctx_t) >= MONGOCRYPT_CTX_ALLOC_ALIGNMENT);
+
/* Used for option validation. True means required. False means prohibited. */
typedef enum { OPT_PROHIBITED = 0, OPT_REQUIRED, OPT_OPTIONAL } _mongocrypt_ctx_opt_spec_t;
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx.c
index e464771269f..f0384d9ec79 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx.c
@@ -258,8 +258,15 @@ bool mongocrypt_ctx_setopt_algorithm(mongocrypt_ctx_t *ctx, const char *algorith
} else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_UNINDEXED_STR))) {
ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_NONE;
ctx->opts.index_type.set = true;
- } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_RANGEPREVIEW_STR))) {
- ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW;
+ } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_RANGE_STR))) {
+ ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_RANGE;
+ ctx->opts.index_type.set = true;
+ } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_RANGEPREVIEW_DEPRECATED_STR))) {
+ if (ctx->crypt->opts.use_range_v2) {
+ _mongocrypt_ctx_fail_w_msg(ctx, "Algorithm 'rangePreview' is deprecated, please use 'range'");
+ return false;
+ }
+ ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED;
ctx->opts.index_type.set = true;
} else {
char *error = bson_strdup_printf("unsupported algorithm string \"%.*s\"",
@@ -275,7 +282,6 @@ bool mongocrypt_ctx_setopt_algorithm(mongocrypt_ctx_t *ctx, const char *algorith
mongocrypt_ctx_t *mongocrypt_ctx_new(mongocrypt_t *crypt) {
mongocrypt_ctx_t *ctx;
- size_t ctx_size;
if (!crypt) {
return NULL;
@@ -287,19 +293,17 @@ mongocrypt_ctx_t *mongocrypt_ctx_new(mongocrypt_t *crypt) {
CLIENT_ERR("cannot create context from uninitialized crypt");
return NULL;
}
- ctx_size = sizeof(_mongocrypt_ctx_encrypt_t);
- if (sizeof(_mongocrypt_ctx_decrypt_t) > ctx_size) {
- ctx_size = sizeof(_mongocrypt_ctx_decrypt_t);
- }
- if (sizeof(_mongocrypt_ctx_datakey_t) > ctx_size) {
- ctx_size = sizeof(_mongocrypt_ctx_datakey_t);
- }
- ctx = bson_malloc0(ctx_size);
+
+ // Allocate with memory and alignment large enough for any possible context type.
+ static const size_t ctx_alignment = MONGOCRYPT_CTX_ALLOC_ALIGNMENT;
+ static const size_t ctx_size = MONGOCRYPT_CTX_ALLOC_SIZE;
+ ctx = bson_aligned_alloc0(ctx_alignment, ctx_size);
BSON_ASSERT(ctx);
ctx->crypt = crypt;
ctx->status = mongocrypt_status_new();
ctx->opts.algorithm = MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE;
+ ctx->opts.retry_enabled = crypt->retry_enabled;
ctx->state = MONGOCRYPT_CTX_DONE;
return ctx;
}
@@ -379,6 +383,7 @@ bool mongocrypt_ctx_mongo_op(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
}
switch (ctx->state) {
+ case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: CHECK_AND_CALL(mongo_op_collinfo, ctx, out);
case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: CHECK_AND_CALL(mongo_op_markings, ctx, out);
case MONGOCRYPT_CTX_NEED_MONGO_KEYS: CHECK_AND_CALL(mongo_op_keys, ctx, out);
@@ -391,6 +396,38 @@ bool mongocrypt_ctx_mongo_op(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
}
}
+const char *mongocrypt_ctx_mongo_db(mongocrypt_ctx_t *ctx) {
+ if (!ctx) {
+ return NULL;
+ }
+ if (!ctx->initialized) {
+ _mongocrypt_ctx_fail_w_msg(ctx, "ctx NULL or uninitialized");
+ return NULL;
+ }
+
+ switch (ctx->state) {
+ case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB: {
+ if (!ctx->vtable.mongo_db_collinfo) {
+ _mongocrypt_ctx_fail_w_msg(ctx, "not applicable to context");
+ return NULL;
+ }
+ return ctx->vtable.mongo_db_collinfo(ctx);
+ }
+ case MONGOCRYPT_CTX_ERROR: return false;
+ case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
+ case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
+ case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
+ case MONGOCRYPT_CTX_DONE:
+ case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
+ case MONGOCRYPT_CTX_NEED_KMS:
+ case MONGOCRYPT_CTX_READY:
+ default: {
+ _mongocrypt_ctx_fail_w_msg(ctx, "wrong state");
+ return NULL;
+ }
+ }
+}
+
bool mongocrypt_ctx_mongo_feed(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in) {
if (!ctx) {
return false;
@@ -412,6 +449,7 @@ bool mongocrypt_ctx_mongo_feed(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in) {
}
switch (ctx->state) {
+ case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: CHECK_AND_CALL(mongo_feed_collinfo, ctx, in);
case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: CHECK_AND_CALL(mongo_feed_markings, ctx, in);
case MONGOCRYPT_CTX_NEED_MONGO_KEYS: CHECK_AND_CALL(mongo_feed_keys, ctx, in);
@@ -433,6 +471,7 @@ bool mongocrypt_ctx_mongo_done(mongocrypt_ctx_t *ctx) {
}
switch (ctx->state) {
+ case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: CHECK_AND_CALL(mongo_done_collinfo, ctx);
case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: CHECK_AND_CALL(mongo_done_markings, ctx);
case MONGOCRYPT_CTX_NEED_MONGO_KEYS: CHECK_AND_CALL(mongo_done_keys, ctx);
@@ -471,17 +510,24 @@ mongocrypt_kms_ctx_t *mongocrypt_ctx_next_kms_ctx(mongocrypt_ctx_t *ctx) {
return NULL;
}
+ mongocrypt_kms_ctx_t *ret;
switch (ctx->state) {
- case MONGOCRYPT_CTX_NEED_KMS: return ctx->vtable.next_kms_ctx(ctx);
+ case MONGOCRYPT_CTX_NEED_KMS: ret = ctx->vtable.next_kms_ctx(ctx); break;
case MONGOCRYPT_CTX_ERROR: return NULL;
case MONGOCRYPT_CTX_DONE:
case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
+ case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
case MONGOCRYPT_CTX_READY:
default: _mongocrypt_ctx_fail_w_msg(ctx, "wrong state"); return NULL;
}
+
+ if (ret) {
+ ret->retry_enabled = ctx->opts.retry_enabled;
+ }
+ return ret;
}
bool mongocrypt_ctx_provide_kms_providers(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *kms_providers_definition) {
@@ -504,6 +550,8 @@ bool mongocrypt_ctx_provide_kms_providers(mongocrypt_ctx_t *ctx, mongocrypt_bina
return false;
}
+ _mongocrypt_opts_kms_providers_init(&ctx->per_ctx_kms_providers);
+
if (!_mongocrypt_parse_kms_providers(kms_providers_definition,
&ctx->per_ctx_kms_providers,
ctx->status,
@@ -545,6 +593,7 @@ bool mongocrypt_ctx_kms_done(mongocrypt_ctx_t *ctx) {
case MONGOCRYPT_CTX_ERROR: return false;
case MONGOCRYPT_CTX_DONE:
case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
+ case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
@@ -575,6 +624,7 @@ bool mongocrypt_ctx_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
case MONGOCRYPT_CTX_DONE:
case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
case MONGOCRYPT_CTX_NEED_KMS:
+ case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
@@ -711,6 +761,7 @@ bool mongocrypt_ctx_setopt_masterkey_local(mongocrypt_ctx_t *ctx) {
}
ctx->opts.kek.kms_provider = MONGOCRYPT_KMS_PROVIDER_LOCAL;
+ ctx->opts.kek.kmsid = bson_strdup("local");
return true;
}
@@ -747,9 +798,13 @@ bool _mongocrypt_ctx_init(mongocrypt_ctx_t *ctx, _mongocrypt_ctx_opts_spec_t *op
if (!ctx->opts.kek.kms_provider) {
return _mongocrypt_ctx_fail_w_msg(ctx, "master key required");
}
- if (!ctx->crypt->opts.use_need_kms_credentials_state
- && !((int)ctx->opts.kek.kms_provider & _mongocrypt_ctx_kms_providers(ctx)->configured_providers)) {
- return _mongocrypt_ctx_fail_w_msg(ctx, "requested kms provider not configured");
+ mc_kms_creds_t unused;
+ bool is_configured =
+ _mongocrypt_opts_kms_providers_lookup(_mongocrypt_ctx_kms_providers(ctx), ctx->opts.kek.kmsid, &unused);
+ if (!ctx->crypt->opts.use_need_kms_credentials_state && !is_configured) {
+ mongocrypt_status_t *status = ctx->status;
+ CLIENT_ERR("requested kms provider not configured: `%s`", ctx->opts.kek.kmsid);
+ return _mongocrypt_ctx_fail(ctx);
}
}
@@ -759,9 +814,16 @@ bool _mongocrypt_ctx_init(mongocrypt_ctx_t *ctx, _mongocrypt_ctx_opts_spec_t *op
/* Check that the kms provider required by the datakey is configured. */
if (ctx->opts.kek.kms_provider) {
- if (!((ctx->crypt->opts.kms_providers.need_credentials | ctx->crypt->opts.kms_providers.configured_providers)
- & (int)ctx->opts.kek.kms_provider)) {
- return _mongocrypt_ctx_fail_w_msg(ctx, "kms provider required by datakey is not configured");
+ mc_kms_creds_t unused;
+ bool is_configured =
+ _mongocrypt_opts_kms_providers_lookup(_mongocrypt_ctx_kms_providers(ctx), ctx->opts.kek.kmsid, &unused);
+ bool needs = _mongocrypt_needs_credentials_for_provider(ctx->crypt,
+ ctx->opts.kek.kms_provider,
+ ctx->opts.kek.kmsid_name);
+ if (!is_configured && !needs) {
+ mongocrypt_status_t *status = ctx->status;
+ CLIENT_ERR("requested kms provider required by datakey is not configured: `%s`", ctx->opts.kek.kmsid);
+ return _mongocrypt_ctx_fail(ctx);
}
}
@@ -1002,8 +1064,15 @@ bool mongocrypt_ctx_setopt_query_type(mongocrypt_ctx_t *ctx, const char *query_t
if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_EQUALITY_STR))) {
ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_EQUALITY;
ctx->opts.query_type.set = true;
- } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_STR))) {
- ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW;
+ } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_RANGE_STR))) {
+ ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_RANGE;
+ ctx->opts.query_type.set = true;
+ } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED_STR))) {
+ if (ctx->crypt->opts.use_range_v2) {
+ _mongocrypt_ctx_fail_w_msg(ctx, "Query type 'rangePreview' is deprecated, please use 'range'");
+ return false;
+ }
+ ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED;
ctx->opts.query_type.set = true;
} else {
/* don't check if qt_str.len fits in int; we want the diagnostic output */
@@ -1021,7 +1090,8 @@ const char *_mongocrypt_index_type_to_string(mongocrypt_index_type_t val) {
switch (val) {
case MONGOCRYPT_INDEX_TYPE_NONE: return "None";
case MONGOCRYPT_INDEX_TYPE_EQUALITY: return "Equality";
- case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW: return "RangePreview";
+ case MONGOCRYPT_INDEX_TYPE_RANGE: return "Range";
+ case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED: return "RangePreview";
default: return "Unknown";
}
}
@@ -1029,7 +1099,8 @@ const char *_mongocrypt_index_type_to_string(mongocrypt_index_type_t val) {
const char *_mongocrypt_query_type_to_string(mongocrypt_query_type_t val) {
switch (val) {
case MONGOCRYPT_QUERY_TYPE_EQUALITY: return "Equality";
- case MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW: return "RangePreview";
+ case MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED: return "RangePreview";
+ case MONGOCRYPT_QUERY_TYPE_RANGE: return "Range";
default: return "Unknown";
}
}
@@ -1057,7 +1128,7 @@ bool mongocrypt_ctx_setopt_algorithm_range(mongocrypt_ctx_t *ctx, mongocrypt_bin
return _mongocrypt_ctx_fail_w_msg(ctx, "invalid BSON");
}
- if (!mc_RangeOpts_parse(&ctx->opts.rangeopts.value, &as_bson, ctx->status)) {
+ if (!mc_RangeOpts_parse(&ctx->opts.rangeopts.value, &as_bson, ctx->crypt->opts.use_range_v2, ctx->status)) {
return _mongocrypt_ctx_fail(ctx);
}
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-dll-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-dll-private.h
index e52b8cbeb42..3debb7314ec 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-dll-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-dll-private.h
@@ -83,7 +83,14 @@ typedef struct mcr_dll_path_result {
* library, or an error string.
*
* @note Caller must free both `retval.path` and `retval.error_string`.
+ * @note Returns an error if not supported on this platform. Use
+ * `mcr_dll_path_supported` to check before calling.
*/
mcr_dll_path_result mcr_dll_path(mcr_dll dll);
+/**
+ * @brief Return true if `mcr_dll_path` is supported on this platform.
+ */
+bool mcr_dll_path_supported(void);
+
#endif // MONGOCRYPT_DLL_PRIVATE_H
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-kek-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-kek-private.h
index 8879b0f5c8f..a396b086f4d 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-kek-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-kek-private.h
@@ -69,6 +69,7 @@ typedef struct {
typedef struct {
char *key_id; /* optional on parsing, required on appending. */
_mongocrypt_endpoint_t *endpoint; /* optional. */
+ bool delegated;
} _mongocrypt_kmip_kek_t;
typedef struct {
@@ -80,6 +81,9 @@ typedef struct {
_mongocrypt_aws_kek_t aws;
_mongocrypt_kmip_kek_t kmip;
} provider;
+
+ char *kmsid;
+ const char *kmsid_name;
} _mongocrypt_kek_t;
/* Parse a document describing a key encryption key.
@@ -100,4 +104,4 @@ void _mongocrypt_kek_copy_to(const _mongocrypt_kek_t *src, _mongocrypt_kek_t *ds
void _mongocrypt_kek_cleanup(_mongocrypt_kek_t *kek);
-#endif /* MONGOCRYPT_KEK_PRIVATE_H */
\ No newline at end of file
+#endif /* MONGOCRYPT_KEK_PRIVATE_H */
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-kek.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-kek.c
index 11a0bd720f4..9329200ebb5 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-kek.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-kek.c
@@ -18,6 +18,126 @@
#include "mongocrypt-opts-private.h"
#include "mongocrypt-private.h"
+static bool _mongocrypt_azure_kek_parse(_mongocrypt_azure_kek_t *azure,
+ const char *kmsid,
+ const bson_t *def,
+ mongocrypt_status_t *status) {
+ if (!_mongocrypt_parse_required_endpoint(def,
+ "keyVaultEndpoint",
+ &azure->key_vault_endpoint,
+ NULL /* opts */,
+ status)) {
+ return false;
+ }
+
+ if (!_mongocrypt_parse_required_utf8(def, "keyName", &azure->key_name, status)) {
+ return false;
+ }
+
+ if (!_mongocrypt_parse_optional_utf8(def, "keyVersion", &azure->key_version, status)) {
+ return false;
+ }
+
+ if (!_mongocrypt_check_allowed_fields(def,
+ NULL /* root */,
+ status,
+ "provider",
+ "keyVaultEndpoint",
+ "keyName",
+ "keyVersion")) {
+ return false;
+ }
+ return true;
+}
+
+static bool _mongocrypt_gcp_kek_parse(_mongocrypt_gcp_kek_t *gcp,
+ const char *kmsid,
+ const bson_t *def,
+ mongocrypt_status_t *status) {
+ if (!_mongocrypt_parse_optional_endpoint(def, "endpoint", &gcp->endpoint, NULL /* opts */, status)) {
+ return false;
+ }
+
+ if (!_mongocrypt_parse_required_utf8(def, "projectId", &gcp->project_id, status)) {
+ return false;
+ }
+
+ if (!_mongocrypt_parse_required_utf8(def, "location", &gcp->location, status)) {
+ return false;
+ }
+
+ if (!_mongocrypt_parse_required_utf8(def, "keyRing", &gcp->key_ring, status)) {
+ return false;
+ }
+
+ if (!_mongocrypt_parse_required_utf8(def, "keyName", &gcp->key_name, status)) {
+ return false;
+ }
+
+ if (!_mongocrypt_parse_optional_utf8(def, "keyVersion", &gcp->key_version, status)) {
+ return false;
+ }
+ if (!_mongocrypt_check_allowed_fields(def,
+ NULL,
+ status,
+ "provider",
+ "endpoint",
+ "projectId",
+ "location",
+ "keyRing",
+ "keyName",
+ "keyVersion")) {
+ return false;
+ }
+ return true;
+}
+
+static bool _mongocrypt_aws_kek_parse(_mongocrypt_aws_kek_t *aws,
+ const char *kmsid,
+ const bson_t *def,
+ mongocrypt_status_t *status) {
+ if (!_mongocrypt_parse_required_utf8(def, "key", &aws->cmk, status)) {
+ return false;
+ }
+ if (!_mongocrypt_parse_required_utf8(def, "region", &aws->region, status)) {
+ return false;
+ }
+ if (!_mongocrypt_parse_optional_endpoint(def, "endpoint", &aws->endpoint, NULL /* opts */, status)) {
+ return false;
+ }
+ if (!_mongocrypt_check_allowed_fields(def, NULL, status, "provider", "key", "region", "endpoint")) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool _mongocrypt_kmip_kek_parse(_mongocrypt_kmip_kek_t *kmip,
+ const char *kmsid,
+ const bson_t *def,
+ mongocrypt_status_t *status) {
+ _mongocrypt_endpoint_parse_opts_t opts = {0};
+
+ opts.allow_empty_subdomain = true;
+ if (!_mongocrypt_parse_optional_endpoint(def, "endpoint", &kmip->endpoint, &opts, status)) {
+ return false;
+ }
+
+ if (!_mongocrypt_parse_optional_utf8(def, "keyId", &kmip->key_id, status)) {
+ return false;
+ }
+
+ kmip->delegated = false;
+ if (!_mongocrypt_parse_optional_bool(def, "delegated", &kmip->delegated, status)) {
+ return false;
+ }
+
+ if (!_mongocrypt_check_allowed_fields(def, NULL, status, "provider", "endpoint", "keyId", "delegated")) {
+ return false;
+ }
+ return true;
+}
+
/* Possible documents to parse:
* AWS
* provider: "aws"
@@ -55,116 +175,50 @@ bool _mongocrypt_kek_parse_owned(const bson_t *bson, _mongocrypt_kek_t *kek, mon
goto done;
}
- if (0 == strcmp(kms_provider, "aws")) {
- kek->kms_provider = MONGOCRYPT_KMS_PROVIDER_AWS;
- if (!_mongocrypt_parse_required_utf8(bson, "key", &kek->provider.aws.cmk, status)) {
+ kek->kmsid = bson_strdup(kms_provider);
+
+ _mongocrypt_kms_provider_t type;
+ if (!mc_kmsid_parse(kek->kmsid, &type, &kek->kmsid_name, status)) {
+ goto done;
+ }
+
+ kek->kms_provider = type;
+ switch (type) {
+ default:
+ case MONGOCRYPT_KMS_PROVIDER_NONE: {
+ CLIENT_ERR("Unexpected parsing KMS type: none");
+ goto done;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_AWS: {
+ if (!_mongocrypt_aws_kek_parse(&kek->provider.aws, kek->kmsid, bson, status)) {
goto done;
}
- if (!_mongocrypt_parse_required_utf8(bson, "region", &kek->provider.aws.region, status)) {
- goto done;
- }
- if (!_mongocrypt_parse_optional_endpoint(bson,
- "endpoint",
- &kek->provider.aws.endpoint,
- NULL /* opts */,
- status)) {
- goto done;
- }
- if (!_mongocrypt_check_allowed_fields(bson, NULL, status, "provider", "key", "region", "endpoint")) {
- goto done;
- }
- } else if (0 == strcmp(kms_provider, "local")) {
- kek->kms_provider = MONGOCRYPT_KMS_PROVIDER_LOCAL;
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_LOCAL: {
if (!_mongocrypt_check_allowed_fields(bson, NULL, status, "provider")) {
goto done;
}
- } else if (0 == strcmp(kms_provider, "azure")) {
- kek->kms_provider = MONGOCRYPT_KMS_PROVIDER_AZURE;
- if (!_mongocrypt_parse_required_endpoint(bson,
- "keyVaultEndpoint",
- &kek->provider.azure.key_vault_endpoint,
- NULL /* opts */,
- status)) {
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_AZURE: {
+ if (!_mongocrypt_azure_kek_parse(&kek->provider.azure, kek->kmsid, bson, status)) {
goto done;
}
-
- if (!_mongocrypt_parse_required_utf8(bson, "keyName", &kek->provider.azure.key_name, status)) {
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_GCP: {
+ if (!_mongocrypt_gcp_kek_parse(&kek->provider.gcp, kek->kmsid, bson, status)) {
goto done;
}
-
- if (!_mongocrypt_parse_optional_utf8(bson, "keyVersion", &kek->provider.azure.key_version, status)) {
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_KMIP: {
+ if (!_mongocrypt_kmip_kek_parse(&kek->provider.kmip, kek->kmsid, bson, status)) {
goto done;
}
-
- if (!_mongocrypt_check_allowed_fields(bson,
- NULL,
- status,
- "provider",
- "keyVaultEndpoint",
- "keyName",
- "keyVersion")) {
- goto done;
- }
- } else if (0 == strcmp(kms_provider, "gcp")) {
- kek->kms_provider = MONGOCRYPT_KMS_PROVIDER_GCP;
- if (!_mongocrypt_parse_optional_endpoint(bson,
- "endpoint",
- &kek->provider.gcp.endpoint,
- NULL /* opts */,
- status)) {
- goto done;
- }
-
- if (!_mongocrypt_parse_required_utf8(bson, "projectId", &kek->provider.gcp.project_id, status)) {
- goto done;
- }
-
- if (!_mongocrypt_parse_required_utf8(bson, "location", &kek->provider.gcp.location, status)) {
- goto done;
- }
-
- if (!_mongocrypt_parse_required_utf8(bson, "keyRing", &kek->provider.gcp.key_ring, status)) {
- goto done;
- }
-
- if (!_mongocrypt_parse_required_utf8(bson, "keyName", &kek->provider.gcp.key_name, status)) {
- goto done;
- }
-
- if (!_mongocrypt_parse_optional_utf8(bson, "keyVersion", &kek->provider.gcp.key_version, status)) {
- goto done;
- }
- if (!_mongocrypt_check_allowed_fields(bson,
- NULL,
- status,
- "provider",
- "endpoint",
- "projectId",
- "location",
- "keyRing",
- "keyName",
- "keyVersion")) {
- goto done;
- }
- } else if (0 == strcmp(kms_provider, "kmip")) {
- kek->kms_provider = MONGOCRYPT_KMS_PROVIDER_KMIP;
- _mongocrypt_endpoint_parse_opts_t opts = {0};
-
- opts.allow_empty_subdomain = true;
- if (!_mongocrypt_parse_optional_endpoint(bson, "endpoint", &kek->provider.kmip.endpoint, &opts, status)) {
- goto done;
- }
-
- if (!_mongocrypt_parse_optional_utf8(bson, "keyId", &kek->provider.kmip.key_id, status)) {
- goto done;
- }
-
- if (!_mongocrypt_check_allowed_fields(bson, NULL, status, "provider", "endpoint", "keyId")) {
- goto done;
- }
- } else {
- CLIENT_ERR("unrecognized KMS provider: %s", kms_provider);
- goto done;
+ break;
+ }
}
ret = true;
@@ -177,24 +231,22 @@ bool _mongocrypt_kek_append(const _mongocrypt_kek_t *kek, bson_t *bson, mongocry
BSON_ASSERT_PARAM(kek);
BSON_ASSERT_PARAM(bson);
+ BSON_APPEND_UTF8(bson, "provider", kek->kmsid);
if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_AWS) {
- BSON_APPEND_UTF8(bson, "provider", "aws");
BSON_APPEND_UTF8(bson, "region", kek->provider.aws.region);
BSON_APPEND_UTF8(bson, "key", kek->provider.aws.cmk);
if (kek->provider.aws.endpoint) {
BSON_APPEND_UTF8(bson, "endpoint", kek->provider.aws.endpoint->host_and_port);
}
} else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_LOCAL) {
- BSON_APPEND_UTF8(bson, "provider", "local");
+ // Only `provider` is needed.
} else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_AZURE) {
- BSON_APPEND_UTF8(bson, "provider", "azure");
BSON_APPEND_UTF8(bson, "keyVaultEndpoint", kek->provider.azure.key_vault_endpoint->host_and_port);
BSON_APPEND_UTF8(bson, "keyName", kek->provider.azure.key_name);
if (kek->provider.azure.key_version) {
BSON_APPEND_UTF8(bson, "keyVersion", kek->provider.azure.key_version);
}
} else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
- BSON_APPEND_UTF8(bson, "provider", "gcp");
BSON_APPEND_UTF8(bson, "projectId", kek->provider.gcp.project_id);
BSON_APPEND_UTF8(bson, "location", kek->provider.gcp.location);
BSON_APPEND_UTF8(bson, "keyRing", kek->provider.gcp.key_ring);
@@ -206,11 +258,14 @@ bool _mongocrypt_kek_append(const _mongocrypt_kek_t *kek, bson_t *bson, mongocry
BSON_APPEND_UTF8(bson, "endpoint", kek->provider.gcp.endpoint->host_and_port);
}
} else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_KMIP) {
- BSON_APPEND_UTF8(bson, "provider", "kmip");
if (kek->provider.kmip.endpoint) {
BSON_APPEND_UTF8(bson, "endpoint", kek->provider.kmip.endpoint->host_and_port);
}
+ if (kek->provider.kmip.delegated) {
+ BSON_APPEND_BOOL(bson, "delegated", kek->provider.kmip.delegated);
+ }
+
/* "keyId" is required in the final data key document for the "kmip" KMS
* provider. It may be set from the "kmip.keyId" in the BSON document set
* in mongocrypt_ctx_setopt_key_encryption_key, Otherwise, libmongocrypt
@@ -249,11 +304,13 @@ void _mongocrypt_kek_copy_to(const _mongocrypt_kek_t *src, _mongocrypt_kek_t *ds
} else if (src->kms_provider == MONGOCRYPT_KMS_PROVIDER_KMIP) {
dst->provider.kmip.endpoint = _mongocrypt_endpoint_copy(src->provider.kmip.endpoint);
dst->provider.kmip.key_id = bson_strdup(src->provider.kmip.key_id);
+ dst->provider.kmip.delegated = src->provider.kmip.delegated;
} else {
BSON_ASSERT(src->kms_provider == MONGOCRYPT_KMS_PROVIDER_NONE
|| src->kms_provider == MONGOCRYPT_KMS_PROVIDER_LOCAL);
}
dst->kms_provider = src->kms_provider;
+ dst->kmsid = bson_strdup(src->kmsid);
}
void _mongocrypt_kek_cleanup(_mongocrypt_kek_t *kek) {
@@ -283,5 +340,6 @@ void _mongocrypt_kek_cleanup(_mongocrypt_kek_t *kek) {
BSON_ASSERT(kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_NONE
|| kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_LOCAL);
}
+ bson_free(kek->kmsid);
return;
}
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker-private.h
index b4ca3b74dad..630dd9c0952 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker-private.h
@@ -87,11 +87,7 @@ typedef struct _key_returned_t {
struct _key_returned_t *next;
} key_returned_t;
-typedef struct _auth_request_t {
- mongocrypt_kms_ctx_t kms;
- bool returned;
- bool initialized;
-} auth_request_t;
+typedef struct _mc_mapof_kmsid_to_authrequest_t mc_mapof_kmsid_to_authrequest_t;
typedef struct {
key_broker_state_t state;
@@ -109,8 +105,7 @@ typedef struct {
mongocrypt_t *crypt;
key_returned_t *decryptor_iter;
- auth_request_t auth_request_azure;
- auth_request_t auth_request_gcp;
+ mc_mapof_kmsid_to_authrequest_t *auth_requests;
} _mongocrypt_key_broker_t;
void _mongocrypt_key_broker_init(_mongocrypt_key_broker_t *kb, mongocrypt_t *crypt);
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker.c
index 77ce882efb8..7af66c4e594 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker.c
@@ -14,9 +14,87 @@
* limitations under the License.
*/
+#include "mc-array-private.h"
#include "mongocrypt-key-broker-private.h"
#include "mongocrypt-private.h"
+typedef struct _auth_request_t {
+ mongocrypt_kms_ctx_t kms;
+ bool returned;
+ char *kmsid;
+} auth_request_t;
+
+auth_request_t *auth_request_new() {
+ return bson_malloc0(sizeof(auth_request_t));
+}
+
+void auth_request_destroy(auth_request_t *ar) {
+ if (!ar) {
+ return;
+ }
+ _mongocrypt_kms_ctx_cleanup(&ar->kms);
+ bson_free(ar->kmsid);
+ bson_free(ar);
+}
+
+struct _mc_mapof_kmsid_to_authrequest_t {
+ mc_array_t entries;
+};
+
+mc_mapof_kmsid_to_authrequest_t *mc_mapof_kmsid_to_authrequest_new(void) {
+ mc_mapof_kmsid_to_authrequest_t *k2a = bson_malloc0(sizeof(mc_mapof_kmsid_to_authrequest_t));
+ _mc_array_init(&k2a->entries, sizeof(auth_request_t *));
+ return k2a;
+}
+
+void mc_mapof_kmsid_to_authrequest_destroy(mc_mapof_kmsid_to_authrequest_t *k2a) {
+ if (!k2a) {
+ return;
+ }
+ for (size_t i = 0; i < k2a->entries.len; i++) {
+ auth_request_t *ar = _mc_array_index(&k2a->entries, auth_request_t *, i);
+ auth_request_destroy(ar);
+ }
+ _mc_array_destroy(&k2a->entries);
+ bson_free(k2a);
+}
+
+bool mc_mapof_kmsid_to_authrequest_has(const mc_mapof_kmsid_to_authrequest_t *k2a, const char *kmsid) {
+ BSON_ASSERT_PARAM(k2a);
+ BSON_ASSERT_PARAM(kmsid);
+ for (size_t i = 0; i < k2a->entries.len; i++) {
+ auth_request_t *ar = _mc_array_index(&k2a->entries, auth_request_t *, i);
+ if (0 == strcmp(ar->kmsid, kmsid)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t mc_mapof_kmsid_to_authrequest_len(const mc_mapof_kmsid_to_authrequest_t *k2a) {
+ BSON_ASSERT_PARAM(k2a);
+ return k2a->entries.len;
+}
+
+bool mc_mapof_kmsid_to_authrequest_empty(const mc_mapof_kmsid_to_authrequest_t *k2a) {
+ BSON_ASSERT_PARAM(k2a);
+ return k2a->entries.len == 0;
+}
+
+// `mc_mapof_kmsid_to_authrequest_put` moves `to_put` into the map and takes ownership of `to_put`.
+// No checking is done to prohibit duplicate entries.
+void mc_mapof_kmsid_to_authrequest_put(mc_mapof_kmsid_to_authrequest_t *k2a, auth_request_t *to_put) {
+ BSON_ASSERT_PARAM(k2a);
+
+ _mc_array_append_val(&k2a->entries, to_put);
+}
+
+auth_request_t *mc_mapof_kmsid_to_authrequest_at(mc_mapof_kmsid_to_authrequest_t *k2a, size_t i) {
+ BSON_ASSERT_PARAM(k2a);
+
+ return _mc_array_index(&k2a->entries, auth_request_t *, i);
+}
+
void _mongocrypt_key_broker_init(_mongocrypt_key_broker_t *kb, mongocrypt_t *crypt) {
BSON_ASSERT_PARAM(kb);
BSON_ASSERT_PARAM(crypt);
@@ -25,6 +103,7 @@ void _mongocrypt_key_broker_init(_mongocrypt_key_broker_t *kb, mongocrypt_t *cry
kb->crypt = crypt;
kb->state = KB_REQUESTING;
kb->status = mongocrypt_status_new();
+ kb->auth_requests = mc_mapof_kmsid_to_authrequest_new();
}
/*
@@ -481,8 +560,12 @@ bool _mongocrypt_key_broker_add_doc(_mongocrypt_key_broker_t *kb,
/* Check that the returned key doc's provider matches. */
kek_provider = key_doc->kek.kms_provider;
- if (0 == ((int)kek_provider & kms_providers->configured_providers)) {
- _key_broker_fail_w_msg(kb, "client not configured with KMS provider necessary to decrypt");
+
+ mc_kms_creds_t kc;
+ if (!_mongocrypt_opts_kms_providers_lookup(kms_providers, key_doc->kek.kmsid, &kc)) {
+ mongocrypt_status_t *status = kb->status;
+ CLIENT_ERR("KMS provider `%s` is not configured", key_doc->kek.kmsid);
+ _key_broker_fail(kb);
goto done;
}
@@ -490,8 +573,9 @@ bool _mongocrypt_key_broker_add_doc(_mongocrypt_key_broker_t *kb,
* HTTP KMS request. */
BSON_ASSERT(kb->crypt);
if (kek_provider == MONGOCRYPT_KMS_PROVIDER_LOCAL) {
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_LOCAL);
if (!_mongocrypt_unwrap_key(kb->crypt->crypto,
- &kms_providers->local.key,
+ &kc.value.local.key,
&key_returned->doc->key_material,
&key_returned->decrypted_key_material,
kb->status)) {
@@ -506,38 +590,45 @@ bool _mongocrypt_key_broker_add_doc(_mongocrypt_key_broker_t *kb,
if (!_mongocrypt_kms_ctx_init_aws_decrypt(&key_returned->kms,
kms_providers,
key_doc,
- &kb->crypt->log,
- kb->crypt->crypto)) {
+ kb->crypt->crypto,
+ key_doc->kek.kmsid,
+ &kb->crypt->log)) {
mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
_key_broker_fail(kb);
goto done;
}
} else if (kek_provider == MONGOCRYPT_KMS_PROVIDER_AZURE) {
- if (kms_providers->azure.access_token) {
- access_token = bson_strdup(kms_providers->azure.access_token);
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_AZURE);
+ if (kc.value.azure.access_token) {
+ access_token = bson_strdup(kc.value.azure.access_token);
} else {
- access_token = _mongocrypt_cache_oauth_get(kb->crypt->cache_oauth_azure);
+ access_token = mc_mapof_kmsid_to_token_get_token(kb->crypt->cache_oauth, key_doc->kek.kmsid);
}
if (!access_token) {
key_returned->needs_auth = true;
/* Create an oauth request if one does not exist. */
- if (!kb->auth_request_azure.initialized) {
- if (!_mongocrypt_kms_ctx_init_azure_auth(&kb->auth_request_azure.kms,
- &kb->crypt->log,
- kms_providers,
+ if (!mc_mapof_kmsid_to_authrequest_has(kb->auth_requests, key_doc->kek.kmsid)) {
+ auth_request_t *ar = auth_request_new();
+ if (!_mongocrypt_kms_ctx_init_azure_auth(&ar->kms,
+ &kc,
/* The key vault endpoint is used to determine the scope. */
- key_doc->kek.provider.azure.key_vault_endpoint)) {
- mongocrypt_kms_ctx_status(&kb->auth_request_azure.kms, kb->status);
+ key_doc->kek.provider.azure.key_vault_endpoint,
+ key_doc->kek.kmsid,
+ &kb->crypt->log)) {
+ mongocrypt_kms_ctx_status(&ar->kms, kb->status);
_key_broker_fail(kb);
+ auth_request_destroy(ar);
goto done;
}
- kb->auth_request_azure.initialized = true;
+ ar->kmsid = bson_strdup(key_doc->kek.kmsid);
+ mc_mapof_kmsid_to_authrequest_put(kb->auth_requests, ar);
}
} else {
if (!_mongocrypt_kms_ctx_init_azure_unwrapkey(&key_returned->kms,
kms_providers,
access_token,
key_doc,
+ key_returned->doc->kek.kmsid,
&kb->crypt->log)) {
mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
_key_broker_fail(kb);
@@ -545,31 +636,37 @@ bool _mongocrypt_key_broker_add_doc(_mongocrypt_key_broker_t *kb,
}
}
} else if (kek_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
- if (NULL != kms_providers->gcp.access_token) {
- access_token = bson_strdup(kms_providers->gcp.access_token);
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_GCP);
+ if (NULL != kc.value.gcp.access_token) {
+ access_token = bson_strdup(kc.value.gcp.access_token);
} else {
- access_token = _mongocrypt_cache_oauth_get(kb->crypt->cache_oauth_gcp);
+ access_token = mc_mapof_kmsid_to_token_get_token(kb->crypt->cache_oauth, key_doc->kek.kmsid);
}
if (!access_token) {
key_returned->needs_auth = true;
/* Create an oauth request if one does not exist. */
- if (!kb->auth_request_gcp.initialized) {
- if (!_mongocrypt_kms_ctx_init_gcp_auth(&kb->auth_request_gcp.kms,
- &kb->crypt->log,
+ if (!mc_mapof_kmsid_to_authrequest_has(kb->auth_requests, key_doc->kek.kmsid)) {
+ auth_request_t *ar = auth_request_new();
+ if (!_mongocrypt_kms_ctx_init_gcp_auth(&ar->kms,
&kb->crypt->opts,
- kms_providers,
- key_doc->kek.provider.gcp.endpoint)) {
- mongocrypt_kms_ctx_status(&kb->auth_request_gcp.kms, kb->status);
+ &kc,
+ key_doc->kek.provider.gcp.endpoint,
+ key_doc->kek.kmsid,
+ &kb->crypt->log)) {
+ mongocrypt_kms_ctx_status(&ar->kms, kb->status);
_key_broker_fail(kb);
+ auth_request_destroy(ar);
goto done;
}
- kb->auth_request_gcp.initialized = true;
+ ar->kmsid = bson_strdup(key_doc->kek.kmsid);
+ mc_mapof_kmsid_to_authrequest_put(kb->auth_requests, ar);
}
} else {
if (!_mongocrypt_kms_ctx_init_gcp_decrypt(&key_returned->kms,
kms_providers,
access_token,
key_doc,
+ key_returned->doc->kek.kmsid,
&kb->crypt->log)) {
mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
_key_broker_fail(kb);
@@ -577,6 +674,7 @@ bool _mongocrypt_key_broker_add_doc(_mongocrypt_key_broker_t *kb,
}
}
} else if (kek_provider == MONGOCRYPT_KMS_PROVIDER_KMIP) {
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_KMIP);
char *unique_identifier;
_mongocrypt_endpoint_t *endpoint;
@@ -589,17 +687,33 @@ bool _mongocrypt_key_broker_add_doc(_mongocrypt_key_broker_t *kb,
if (key_returned->doc->kek.provider.kmip.endpoint) {
endpoint = key_returned->doc->kek.provider.kmip.endpoint;
- } else if (kms_providers->kmip.endpoint) {
- endpoint = kms_providers->kmip.endpoint;
+ } else if (kc.value.kmip.endpoint) {
+ endpoint = kc.value.kmip.endpoint;
} else {
_key_broker_fail_w_msg(kb, "endpoint not set for KMIP request");
goto done;
}
- if (!_mongocrypt_kms_ctx_init_kmip_get(&key_returned->kms, endpoint, unique_identifier, &kb->crypt->log)) {
- mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
- _key_broker_fail(kb);
- goto done;
+ if (key_returned->doc->kek.provider.kmip.delegated) {
+ if (!_mongocrypt_kms_ctx_init_kmip_decrypt(&key_returned->kms,
+ endpoint,
+ key_doc->kek.kmsid,
+ key_doc,
+ &kb->crypt->log)) {
+ mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
+ _key_broker_fail(kb);
+ goto done;
+ }
+ } else {
+ if (!_mongocrypt_kms_ctx_init_kmip_get(&key_returned->kms,
+ endpoint,
+ unique_identifier,
+ key_doc->kek.kmsid,
+ &kb->crypt->log)) {
+ mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
+ _key_broker_fail(kb);
+ goto done;
+ }
}
} else {
_key_broker_fail_w_msg(kb, "unrecognized kms provider");
@@ -683,25 +797,40 @@ mongocrypt_kms_ctx_t *_mongocrypt_key_broker_next_kms(_mongocrypt_key_broker_t *
}
if (kb->state == KB_AUTHENTICATING) {
- if (!kb->auth_request_azure.initialized && !kb->auth_request_gcp.initialized) {
+ if (mc_mapof_kmsid_to_authrequest_empty(kb->auth_requests)) {
_key_broker_fail_w_msg(kb,
"unexpected, attempting to authenticate but "
"KMS request not initialized");
return NULL;
}
- if (kb->auth_request_azure.initialized && !kb->auth_request_azure.returned) {
- kb->auth_request_azure.returned = true;
- return &kb->auth_request_azure.kms;
- }
- if (kb->auth_request_gcp.initialized && !kb->auth_request_gcp.returned) {
- kb->auth_request_gcp.returned = true;
- return &kb->auth_request_gcp.kms;
+ // Return the first not-yet-returned auth request.
+ for (size_t i = 0; i < mc_mapof_kmsid_to_authrequest_len(kb->auth_requests); i++) {
+ auth_request_t *ar = mc_mapof_kmsid_to_authrequest_at(kb->auth_requests, i);
+
+ if (ar->kms.should_retry) {
+ ar->kms.should_retry = false;
+ ar->returned = true;
+ return &ar->kms;
+ }
+
+ if (ar->returned) {
+ continue;
+ }
+ ar->returned = true;
+ return &ar->kms;
}
return NULL;
}
+ // Check if any requests need retry
+ for (key_returned_t *ptr = kb->keys_returned; ptr != NULL; ptr = ptr->next) {
+ if (ptr->kms.should_retry) {
+ ptr->kms.should_retry = false;
+ return &ptr->kms;
+ }
+ }
while (kb->decryptor_iter) {
if (!kb->decryptor_iter->decrypted) {
key_returned_t *key_returned;
@@ -731,30 +860,20 @@ bool _mongocrypt_key_broker_kms_done(_mongocrypt_key_broker_t *kb, _mongocrypt_o
bson_t oauth_response;
_mongocrypt_buffer_t oauth_response_buf;
- if (kb->auth_request_azure.initialized) {
- if (!_mongocrypt_kms_ctx_result(&kb->auth_request_azure.kms, &oauth_response_buf)) {
- mongocrypt_kms_ctx_status(&kb->auth_request_azure.kms, kb->status);
+ // Apply tokens from oauth responses to oauth token cache.
+ for (size_t i = 0; i < mc_mapof_kmsid_to_authrequest_len(kb->auth_requests); i++) {
+ auth_request_t *ar = mc_mapof_kmsid_to_authrequest_at(kb->auth_requests, i);
+
+ if (!_mongocrypt_kms_ctx_result(&ar->kms, &oauth_response_buf)) {
+ mongocrypt_kms_ctx_status(&ar->kms, kb->status);
return _key_broker_fail(kb);
}
/* Cache returned tokens. */
BSON_ASSERT(_mongocrypt_buffer_to_bson(&oauth_response_buf, &oauth_response));
- if (!_mongocrypt_cache_oauth_add(kb->crypt->cache_oauth_azure, &oauth_response, kb->status)) {
- return false;
- }
- }
-
- if (kb->auth_request_gcp.initialized) {
- if (!_mongocrypt_kms_ctx_result(&kb->auth_request_gcp.kms, &oauth_response_buf)) {
- mongocrypt_kms_ctx_status(&kb->auth_request_gcp.kms, kb->status);
+ if (!mc_mapof_kmsid_to_token_add_response(kb->crypt->cache_oauth, ar->kmsid, &oauth_response, kb->status)) {
return _key_broker_fail(kb);
}
-
- /* Cache returned tokens. */
- BSON_ASSERT(_mongocrypt_buffer_to_bson(&oauth_response_buf, &oauth_response));
- if (!_mongocrypt_cache_oauth_add(kb->crypt->cache_oauth_gcp, &oauth_response, kb->status)) {
- return false;
- }
}
/* Auth should be finished, create any remaining KMS requests. */
@@ -765,11 +884,20 @@ bool _mongocrypt_key_broker_kms_done(_mongocrypt_key_broker_t *kb, _mongocrypt_o
continue;
}
+ mc_kms_creds_t kc;
+ if (!_mongocrypt_opts_kms_providers_lookup(kms_providers, key_returned->doc->kek.kmsid, &kc)) {
+ mongocrypt_status_t *status = kb->status;
+ CLIENT_ERR("KMS provider `%s` is not configured", key_returned->doc->kek.kmsid);
+ return _key_broker_fail(kb);
+ }
+
if (key_returned->doc->kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_AZURE) {
- if (kms_providers->azure.access_token) {
- access_token = bson_strdup(kms_providers->azure.access_token);
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_AZURE);
+ if (kc.value.azure.access_token) {
+ access_token = bson_strdup(kc.value.azure.access_token);
} else {
- access_token = _mongocrypt_cache_oauth_get(kb->crypt->cache_oauth_azure);
+ access_token =
+ mc_mapof_kmsid_to_token_get_token(kb->crypt->cache_oauth, key_returned->doc->kek.kmsid);
}
if (!access_token) {
@@ -780,6 +908,7 @@ bool _mongocrypt_key_broker_kms_done(_mongocrypt_key_broker_t *kb, _mongocrypt_o
kms_providers,
access_token,
key_returned->doc,
+ key_returned->doc->kek.kmsid,
&kb->crypt->log)) {
mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
bson_free(access_token);
@@ -789,10 +918,12 @@ bool _mongocrypt_key_broker_kms_done(_mongocrypt_key_broker_t *kb, _mongocrypt_o
key_returned->needs_auth = false;
bson_free(access_token);
} else if (key_returned->doc->kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
- if (NULL != kms_providers->gcp.access_token) {
- access_token = bson_strdup(kms_providers->gcp.access_token);
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_GCP);
+ if (kc.value.gcp.access_token) {
+ access_token = bson_strdup(kc.value.gcp.access_token);
} else {
- access_token = _mongocrypt_cache_oauth_get(kb->crypt->cache_oauth_gcp);
+ access_token =
+ mc_mapof_kmsid_to_token_get_token(kb->crypt->cache_oauth, key_returned->doc->kek.kmsid);
}
if (!access_token) {
@@ -803,6 +934,7 @@ bool _mongocrypt_key_broker_kms_done(_mongocrypt_key_broker_t *kb, _mongocrypt_o
kms_providers,
access_token,
key_returned->doc,
+ key_returned->doc->kek.kmsid,
&kb->crypt->log)) {
mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
bson_free(access_token);
@@ -850,11 +982,16 @@ bool _mongocrypt_key_broker_kms_done(_mongocrypt_key_broker_t *kb, _mongocrypt_o
return _key_broker_fail(kb);
}
- if (!_mongocrypt_unwrap_key(kb->crypt->crypto,
- &kek,
- &key_returned->doc->key_material,
- &key_returned->decrypted_key_material,
- kb->status)) {
+ if (key_returned->doc->kek.provider.kmip.delegated) {
+ if (!_mongocrypt_kms_ctx_result(&key_returned->kms, &key_returned->decrypted_key_material)) {
+ mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
+ return _key_broker_fail(kb);
+ }
+ } else if (!_mongocrypt_unwrap_key(kb->crypt->crypto,
+ &kek,
+ &key_returned->doc->key_material,
+ &key_returned->decrypted_key_material,
+ kb->status)) {
_key_broker_fail(kb);
_mongocrypt_buffer_cleanup(&kek);
return false;
@@ -1008,8 +1145,7 @@ void _mongocrypt_key_broker_cleanup(_mongocrypt_key_broker_t *kb) {
_destroy_keys_returned(kb->keys_returned);
_destroy_keys_returned(kb->keys_cached);
_destroy_key_requests(kb->key_requests);
- _mongocrypt_kms_ctx_cleanup(&kb->auth_request_azure.kms);
- _mongocrypt_kms_ctx_cleanup(&kb->auth_request_gcp.kms);
+ mc_mapof_kmsid_to_authrequest_destroy(kb->auth_requests);
}
void _mongocrypt_key_broker_add_test_key(_mongocrypt_key_broker_t *kb, const _mongocrypt_buffer_t *key_id) {
@@ -1026,6 +1162,7 @@ void _mongocrypt_key_broker_add_test_key(_mongocrypt_key_broker_t *kb, const _mo
key_returned->decrypted = true;
_mongocrypt_buffer_init(&key_returned->decrypted_key_material);
_mongocrypt_buffer_resize(&key_returned->decrypted_key_material, MONGOCRYPT_KEY_LEN);
+ // Initialize test key material with all zeros.
memset(key_returned->decrypted_key_material.data, 0, MONGOCRYPT_KEY_LEN);
_mongocrypt_key_destroy(key_doc);
/* Hijack state and move directly to DONE. */
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-key-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-key-private.h
index 3831d2f5c91..5b681a09154 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-key-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-key-private.h
@@ -27,6 +27,11 @@ typedef struct __mongocrypt_key_alt_name_t {
bson_value_t value;
} _mongocrypt_key_alt_name_t;
+// `_mongocrypt_key_alt_name_t` inherits extended alignment from libbson. To dynamically allocate, use aligned
+// allocation (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof__mongocrypt_key_alt_name_t,
+ BSON_ALIGNOF(_mongocrypt_key_alt_name_t) >= BSON_ALIGNOF(bson_value_t));
+
typedef struct {
bson_t bson; /* original BSON for this key. */
_mongocrypt_buffer_t id;
@@ -37,6 +42,10 @@ typedef struct {
_mongocrypt_kek_t kek;
} _mongocrypt_key_doc_t;
+// `_mongocrypt_key_doc_t` inherits extended alignment from libbson. To dynamically allocate, use aligned allocation
+// (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof__mongocrypt_key_doc_t, BSON_ALIGNOF(_mongocrypt_key_doc_t) >= BSON_ALIGNOF(bson_t));
+
_mongocrypt_key_alt_name_t *_mongocrypt_key_alt_name_new(const bson_value_t *value);
bool _mongocrypt_key_alt_name_from_iter(const bson_iter_t *iter,
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-key.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-key.c
index 25421d9eea9..003ee6d2bb4 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-key.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-key.c
@@ -127,7 +127,7 @@ bool _mongocrypt_key_alt_name_from_iter(const bson_iter_t *iter_in,
/* Takes ownership of all fields. */
bool _mongocrypt_key_parse_owned(const bson_t *bson, _mongocrypt_key_doc_t *out, mongocrypt_status_t *status) {
- bson_iter_t iter;
+ bson_iter_t iter = {0};
bool has_id = false, has_key_material = false, has_status = false, has_creation_date = false,
has_update_date = false, has_master_key = false;
@@ -271,7 +271,10 @@ bool _mongocrypt_key_parse_owned(const bson_t *bson, _mongocrypt_key_doc_t *out,
_mongocrypt_key_doc_t *_mongocrypt_key_new(void) {
_mongocrypt_key_doc_t *key_doc;
- key_doc = (_mongocrypt_key_doc_t *)bson_malloc0(sizeof *key_doc);
+ key_doc = BSON_ALIGNED_ALLOC(_mongocrypt_key_doc_t);
+ // Use two sets of braces to avoid erroneous missing-braces warning in GCC. Refer:
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119
+ *key_doc = (_mongocrypt_key_doc_t){{0}};
bson_init(&key_doc->bson);
return key_doc;
@@ -389,7 +392,8 @@ _mongocrypt_key_alt_name_t *_mongocrypt_key_alt_name_create(const char *name, ..
_mongocrypt_key_alt_name_t *_mongocrypt_key_alt_name_new(const bson_value_t *value) {
BSON_ASSERT_PARAM(value);
- _mongocrypt_key_alt_name_t *name = bson_malloc0(sizeof(*name));
+ _mongocrypt_key_alt_name_t *name = BSON_ALIGNED_ALLOC(_mongocrypt_key_alt_name_t);
+ *name = (_mongocrypt_key_alt_name_t){0};
BSON_ASSERT(name);
bson_value_copy(value, &name->value);
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-kms-ctx-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-kms-ctx-private.h
index e1b66f10427..2a14c795c99 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-kms-ctx-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-kms-ctx-private.h
@@ -23,6 +23,7 @@
#include "mongocrypt-compat.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-endpoint-private.h"
+#include "mongocrypt-key-private.h"
#include "mongocrypt-opts-private.h"
#include "mongocrypt.h"
@@ -39,7 +40,10 @@ typedef enum {
MONGOCRYPT_KMS_GCP_DECRYPT,
MONGOCRYPT_KMS_KMIP_REGISTER,
MONGOCRYPT_KMS_KMIP_ACTIVATE,
- MONGOCRYPT_KMS_KMIP_GET
+ MONGOCRYPT_KMS_KMIP_GET,
+ MONGOCRYPT_KMS_KMIP_CREATE,
+ MONGOCRYPT_KMS_KMIP_ENCRYPT,
+ MONGOCRYPT_KMS_KMIP_DECRYPT,
} _kms_request_type_t;
struct _mongocrypt_kms_ctx_t {
@@ -51,76 +55,113 @@ struct _mongocrypt_kms_ctx_t {
_mongocrypt_buffer_t result;
char *endpoint;
_mongocrypt_log_t *log;
+ char *kmsid;
+ int64_t sleep_usec;
+ int attempts;
+ bool retry_enabled;
+ bool should_retry;
};
+static const int kms_max_attempts = 3;
+
bool _mongocrypt_kms_ctx_init_aws_decrypt(mongocrypt_kms_ctx_t *kms,
_mongocrypt_opts_kms_providers_t *kms_providers,
_mongocrypt_key_doc_t *key,
- _mongocrypt_log_t *log,
- _mongocrypt_crypto_t *crypto) MONGOCRYPT_WARN_UNUSED_RESULT;
+ _mongocrypt_crypto_t *crypto,
+ const char *kmsid,
+ _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
bool _mongocrypt_kms_ctx_init_aws_encrypt(mongocrypt_kms_ctx_t *kms,
_mongocrypt_opts_kms_providers_t *kms_providers,
struct __mongocrypt_ctx_opts_t *ctx_opts,
_mongocrypt_buffer_t *decrypted_key_material,
- _mongocrypt_log_t *log,
- _mongocrypt_crypto_t *crypto) MONGOCRYPT_WARN_UNUSED_RESULT;
+ _mongocrypt_crypto_t *crypto,
+ const char *kmsid,
+ _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
bool _mongocrypt_kms_ctx_result(mongocrypt_kms_ctx_t *kms, _mongocrypt_buffer_t *out) MONGOCRYPT_WARN_UNUSED_RESULT;
void _mongocrypt_kms_ctx_cleanup(mongocrypt_kms_ctx_t *kms);
bool _mongocrypt_kms_ctx_init_azure_auth(mongocrypt_kms_ctx_t *kms,
- _mongocrypt_log_t *log,
- _mongocrypt_opts_kms_providers_t *kms_providers,
- _mongocrypt_endpoint_t *key_vault_endpoint) MONGOCRYPT_WARN_UNUSED_RESULT;
+ const mc_kms_creds_t *kc,
+ _mongocrypt_endpoint_t *key_vault_endpoint,
+ const char *kmsid,
+ _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
bool _mongocrypt_kms_ctx_init_azure_wrapkey(mongocrypt_kms_ctx_t *kms,
- _mongocrypt_log_t *log,
_mongocrypt_opts_kms_providers_t *kms_providers,
struct __mongocrypt_ctx_opts_t *ctx_opts,
const char *access_token,
- _mongocrypt_buffer_t *plaintext_key_material) MONGOCRYPT_WARN_UNUSED_RESULT;
+ _mongocrypt_buffer_t *plaintext_key_material,
+ const char *kmsid,
+ _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
bool _mongocrypt_kms_ctx_init_azure_unwrapkey(mongocrypt_kms_ctx_t *kms,
_mongocrypt_opts_kms_providers_t *kms_providers,
const char *access_token,
_mongocrypt_key_doc_t *key,
+ const char *kmsid,
_mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
bool _mongocrypt_kms_ctx_init_gcp_auth(mongocrypt_kms_ctx_t *kms,
- _mongocrypt_log_t *log,
_mongocrypt_opts_t *crypt_opts,
- _mongocrypt_opts_kms_providers_t *kms_providers,
- _mongocrypt_endpoint_t *kms_endpoint) MONGOCRYPT_WARN_UNUSED_RESULT;
+ const mc_kms_creds_t *kc,
+ _mongocrypt_endpoint_t *kms_endpoint,
+ const char *kmsid,
+ _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
bool _mongocrypt_kms_ctx_init_gcp_encrypt(mongocrypt_kms_ctx_t *kms,
- _mongocrypt_log_t *log,
_mongocrypt_opts_kms_providers_t *kms_providers,
struct __mongocrypt_ctx_opts_t *ctx_opts,
const char *access_token,
- _mongocrypt_buffer_t *plaintext_key_material) MONGOCRYPT_WARN_UNUSED_RESULT;
+ _mongocrypt_buffer_t *plaintext_key_material,
+ const char *kmsid,
+ _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
bool _mongocrypt_kms_ctx_init_gcp_decrypt(mongocrypt_kms_ctx_t *kms,
_mongocrypt_opts_kms_providers_t *kms_providers,
const char *access_token,
_mongocrypt_key_doc_t *key,
+ const char *kmsid,
_mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
bool _mongocrypt_kms_ctx_init_kmip_register(mongocrypt_kms_ctx_t *kms,
const _mongocrypt_endpoint_t *endpoint,
const uint8_t *secretdata,
uint32_t secretdata_len,
+
+ const char *kmsid,
_mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
bool _mongocrypt_kms_ctx_init_kmip_activate(mongocrypt_kms_ctx_t *kms,
const _mongocrypt_endpoint_t *endpoint,
const char *unique_identifier,
+ const char *kmsid,
_mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
bool _mongocrypt_kms_ctx_init_kmip_get(mongocrypt_kms_ctx_t *kms,
const _mongocrypt_endpoint_t *endpoint,
const char *unique_identifier,
+ const char *kmsid,
_mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;
+bool _mongocrypt_kms_ctx_init_kmip_create(mongocrypt_kms_ctx_t *kms,
+ const _mongocrypt_endpoint_t *endpoint,
+ const char *kmsid,
+ _mongocrypt_log_t *log);
+
+bool _mongocrypt_kms_ctx_init_kmip_encrypt(mongocrypt_kms_ctx_t *kms,
+ const _mongocrypt_endpoint_t *endpoint,
+ const char *unique_identifier,
+ const char *kmsid,
+ _mongocrypt_buffer_t *plaintext,
+ _mongocrypt_log_t *log);
+
+bool _mongocrypt_kms_ctx_init_kmip_decrypt(mongocrypt_kms_ctx_t *kms,
+ const _mongocrypt_endpoint_t *endpoint,
+ const char *kmsid,
+ _mongocrypt_key_doc_t *key,
+ _mongocrypt_log_t *log);
+
#endif /* MONGOCRYPT_KMX_CTX_PRIVATE_H */
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-kms-ctx.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-kms-ctx.c
index 6886b557d47..1d431177b0e 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-kms-ctx.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-kms-ctx.c
@@ -14,15 +14,21 @@
* limitations under the License.
*/
+#include "kms_message/kms_kmip_request.h"
#include "mongocrypt-binary-private.h"
#include "mongocrypt-buffer-private.h"
+#include "mongocrypt-crypto-private.h"
#include "mongocrypt-ctx-private.h"
+#include "mongocrypt-endpoint-private.h"
+#include "mongocrypt-kek-private.h"
#include "mongocrypt-kms-ctx-private.h"
+#include "mongocrypt-log-private.h"
#include "mongocrypt-opts-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt-status-private.h"
#include "mongocrypt-util-private.h"
#include "mongocrypt.h"
+#include
#include
#include
#include
@@ -118,11 +124,16 @@ _set_kms_crypto_hooks(_mongocrypt_crypto_t *crypto, ctx_with_status_t *ctx_with_
static bool is_kms(_kms_request_type_t kms_type) {
return kms_type == MONGOCRYPT_KMS_KMIP_REGISTER || kms_type == MONGOCRYPT_KMS_KMIP_ACTIVATE
- || kms_type == MONGOCRYPT_KMS_KMIP_GET;
+ || kms_type == MONGOCRYPT_KMS_KMIP_GET || kms_type == MONGOCRYPT_KMS_KMIP_ENCRYPT
+ || kms_type == MONGOCRYPT_KMS_KMIP_DECRYPT || kms_type == MONGOCRYPT_KMS_KMIP_CREATE;
}
-static void _init_common(mongocrypt_kms_ctx_t *kms, _mongocrypt_log_t *log, _kms_request_type_t kms_type) {
+static void
+_init_common(mongocrypt_kms_ctx_t *kms, _mongocrypt_log_t *log, _kms_request_type_t kms_type, const char *kmsid) {
BSON_ASSERT_PARAM(kms);
+ BSON_ASSERT_PARAM(kmsid);
+
+ kms->kmsid = bson_strdup(kmsid);
if (is_kms(kms_type)) {
kms->parser = kms_kmip_response_parser_new(NULL /* reserved */);
@@ -133,13 +144,17 @@ static void _init_common(mongocrypt_kms_ctx_t *kms, _mongocrypt_log_t *log, _kms
kms->status = mongocrypt_status_new();
kms->req_type = kms_type;
_mongocrypt_buffer_init(&kms->result);
+ kms->sleep_usec = 0;
+ kms->attempts = 0;
+ kms->should_retry = false;
}
bool _mongocrypt_kms_ctx_init_aws_decrypt(mongocrypt_kms_ctx_t *kms,
_mongocrypt_opts_kms_providers_t *kms_providers,
_mongocrypt_key_doc_t *key,
- _mongocrypt_log_t *log,
- _mongocrypt_crypto_t *crypto) {
+ _mongocrypt_crypto_t *crypto,
+ const char *kmsid,
+ _mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms);
BSON_ASSERT_PARAM(key);
BSON_ASSERT_PARAM(kms_providers);
@@ -150,7 +165,7 @@ bool _mongocrypt_kms_ctx_init_aws_decrypt(mongocrypt_kms_ctx_t *kms,
ctx_with_status_t ctx_with_status;
bool ret = false;
- _init_common(kms, log, MONGOCRYPT_KMS_AWS_DECRYPT);
+ _init_common(kms, log, MONGOCRYPT_KMS_AWS_DECRYPT, kmsid);
status = kms->status;
ctx_with_status.ctx = crypto;
ctx_with_status.status = mongocrypt_status_new();
@@ -170,17 +185,19 @@ bool _mongocrypt_kms_ctx_init_aws_decrypt(mongocrypt_kms_ctx_t *kms,
goto done;
}
- if (0 == (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS)) {
- CLIENT_ERR("aws kms not configured");
+ mc_kms_creds_t kc;
+ if (!_mongocrypt_opts_kms_providers_lookup(kms_providers, key->kek.kmsid, &kc)) {
+ CLIENT_ERR("KMS provider `%s` is not configured", key->kek.kmsid);
goto done;
}
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_AWS);
- if (!kms_providers->aws.access_key_id) {
+ if (!kc.value.aws.access_key_id) {
CLIENT_ERR("aws access key id not provided");
goto done;
}
- if (!kms_providers->aws.secret_access_key) {
+ if (!kc.value.aws.secret_access_key) {
CLIENT_ERR("aws secret access key not provided");
goto done;
}
@@ -201,8 +218,8 @@ bool _mongocrypt_kms_ctx_init_aws_decrypt(mongocrypt_kms_ctx_t *kms,
goto done;
}
- if (kms_providers->aws.session_token) {
- if (!kms_request_add_header_field(kms->req, "X-Amz-Security-Token", kms_providers->aws.session_token)) {
+ if (kc.value.aws.session_token) {
+ if (!kms_request_add_header_field(kms->req, "X-Amz-Security-Token", kc.value.aws.session_token)) {
CLIENT_ERR("failed to set session token: %s", kms_request_get_error(kms->req));
_mongocrypt_status_append(status, ctx_with_status.status);
goto done;
@@ -230,12 +247,12 @@ bool _mongocrypt_kms_ctx_init_aws_decrypt(mongocrypt_kms_ctx_t *kms,
goto done;
}
- if (!kms_request_set_access_key_id(kms->req, kms_providers->aws.access_key_id)) {
+ if (!kms_request_set_access_key_id(kms->req, kc.value.aws.access_key_id)) {
CLIENT_ERR("failed to set aws access key id: %s", kms_request_get_error(kms->req));
_mongocrypt_status_append(status, ctx_with_status.status);
goto done;
}
- if (!kms_request_set_secret_key(kms->req, kms_providers->aws.secret_access_key)) {
+ if (!kms_request_set_secret_key(kms->req, kc.value.aws.secret_access_key)) {
CLIENT_ERR("failed to set aws secret access key: %s", kms_request_get_error(kms->req));
_mongocrypt_status_append(status, ctx_with_status.status);
goto done;
@@ -270,8 +287,9 @@ bool _mongocrypt_kms_ctx_init_aws_encrypt(mongocrypt_kms_ctx_t *kms,
_mongocrypt_opts_kms_providers_t *kms_providers,
_mongocrypt_ctx_opts_t *ctx_opts,
_mongocrypt_buffer_t *plaintext_key_material,
- _mongocrypt_log_t *log,
- _mongocrypt_crypto_t *crypto) {
+ _mongocrypt_crypto_t *crypto,
+ const char *kmsid,
+ _mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms);
BSON_ASSERT_PARAM(ctx_opts);
BSON_ASSERT_PARAM(kms_providers);
@@ -283,7 +301,7 @@ bool _mongocrypt_kms_ctx_init_aws_encrypt(mongocrypt_kms_ctx_t *kms,
ctx_with_status_t ctx_with_status;
bool ret = false;
- _init_common(kms, log, MONGOCRYPT_KMS_AWS_ENCRYPT);
+ _init_common(kms, log, MONGOCRYPT_KMS_AWS_ENCRYPT, kmsid);
status = kms->status;
ctx_with_status.ctx = crypto;
ctx_with_status.status = mongocrypt_status_new();
@@ -303,17 +321,19 @@ bool _mongocrypt_kms_ctx_init_aws_encrypt(mongocrypt_kms_ctx_t *kms,
goto done;
}
- if (0 == (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS)) {
- CLIENT_ERR("aws kms not configured");
+ mc_kms_creds_t kc;
+ if (!_mongocrypt_opts_kms_providers_lookup(kms_providers, ctx_opts->kek.kmsid, &kc)) {
+ CLIENT_ERR("KMS provider `%s` is not configured", ctx_opts->kek.kmsid);
goto done;
}
+ BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_AWS);
- if (!kms_providers->aws.access_key_id) {
+ if (!kc.value.aws.access_key_id) {
CLIENT_ERR("aws access key id not provided");
goto done;
}
- if (!kms_providers->aws.secret_access_key) {
+ if (!kc.value.aws.secret_access_key) {
CLIENT_ERR("aws secret access key not provided");
goto done;
}
@@ -337,8 +357,8 @@ bool _mongocrypt_kms_ctx_init_aws_encrypt(mongocrypt_kms_ctx_t *kms,
goto done;
}
- if (kms_providers->aws.session_token) {
- if (!kms_request_add_header_field(kms->req, "X-Amz-Security-Token", kms_providers->aws.session_token)) {
+ if (kc.value.aws.session_token) {
+ if (!kms_request_add_header_field(kms->req, "X-Amz-Security-Token", kc.value.aws.session_token)) {
CLIENT_ERR("failed to set session token: %s", kms_request_get_error(kms->req));
_mongocrypt_status_append(status, ctx_with_status.status);
goto done;
@@ -366,12 +386,12 @@ bool _mongocrypt_kms_ctx_init_aws_encrypt(mongocrypt_kms_ctx_t *kms,
goto done;
}
- if (!kms_request_set_access_key_id(kms->req, kms_providers->aws.access_key_id)) {
+ if (!kms_request_set_access_key_id(kms->req, kc.value.aws.access_key_id)) {
CLIENT_ERR("failed to set aws access key id: %s", kms_request_get_error(kms->req));
_mongocrypt_status_append(status, ctx_with_status.status);
goto done;
}
- if (!kms_request_set_secret_key(kms->req, kms_providers->aws.secret_access_key)) {
+ if (!kms_request_set_secret_key(kms->req, kc.value.aws.secret_access_key)) {
CLIENT_ERR("failed to set aws secret access key: %s", kms_request_get_error(kms->req));
_mongocrypt_status_append(status, ctx_with_status.status);
goto done;
@@ -412,11 +432,21 @@ uint32_t mongocrypt_kms_ctx_bytes_needed(mongocrypt_kms_ctx_t *kms) {
if (!mongocrypt_status_ok(kms->status) || !_mongocrypt_buffer_empty(&kms->result)) {
return 0;
}
+ if (kms->should_retry) {
+ return 0;
+ }
want_bytes = kms_response_parser_wants_bytes(kms->parser, DEFAULT_MAX_KMS_BYTE_REQUEST);
BSON_ASSERT(want_bytes >= 0);
return (uint32_t)want_bytes;
}
+int64_t mongocrypt_kms_ctx_usleep(mongocrypt_kms_ctx_t *kms) {
+ if (!kms) {
+ return 0;
+ }
+ return kms->sleep_usec;
+}
+
static void
_handle_non200_http_status(int http_status, const char *body, size_t body_len, mongocrypt_status_t *status) {
BSON_ASSERT_PARAM(body);
@@ -440,6 +470,56 @@ _handle_non200_http_status(int http_status, const char *body, size_t body_len, m
CLIENT_ERR("Error in KMS response. HTTP status=%d. Response body=\n%s", http_status, body);
}
+static int64_t backoff_time_usec(int64_t attempts) {
+ static bool seeded = false;
+ if (!seeded) {
+ srand((uint32_t)time(NULL));
+ seeded = true;
+ }
+
+ /* Exponential backoff with jitter. */
+ const int64_t base = 200000; /* 0.2 seconds */
+ const int64_t max = 20000000; /* 20 seconds */
+ BSON_ASSERT(attempts > 0);
+ int64_t backoff = base * ((int64_t)1 << (attempts - 1));
+ if (backoff > max) {
+ backoff = max;
+ }
+
+ /* Full jitter: between 1 and current max */
+ return (int64_t)((double)rand() / (double)RAND_MAX * (double)backoff) + 1;
+}
+
+static bool should_retry_http(int http_status, _kms_request_type_t t) {
+ static const int retryable_aws[] = {408, 429, 500, 502, 503, 509};
+ static const int retryable_azure[] = {408, 429, 500, 502, 503, 504};
+ if (t == MONGOCRYPT_KMS_AWS_ENCRYPT || t == MONGOCRYPT_KMS_AWS_DECRYPT) {
+ for (size_t i = 0; i < sizeof(retryable_aws) / sizeof(retryable_aws[0]); i++) {
+ if (http_status == retryable_aws[i]) {
+ return true;
+ }
+ }
+ } else if (t == MONGOCRYPT_KMS_AZURE_WRAPKEY || t == MONGOCRYPT_KMS_AZURE_UNWRAPKEY
+ || t == MONGOCRYPT_KMS_AZURE_OAUTH) {
+ for (size_t i = 0; i < sizeof(retryable_azure) / sizeof(retryable_azure[0]); i++) {
+ if (http_status == retryable_azure[i]) {
+ return true;
+ }
+ }
+ } else if (t == MONGOCRYPT_KMS_GCP_ENCRYPT || t == MONGOCRYPT_KMS_GCP_DECRYPT || t == MONGOCRYPT_KMS_GCP_OAUTH) {
+ if (http_status == 408 || http_status == 429 || http_status / 500 == 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void set_retry(mongocrypt_kms_ctx_t *kms) {
+ kms->should_retry = true;
+ kms->attempts++;
+ kms->sleep_usec = backoff_time_usec(kms->attempts);
+}
+
/* An AWS KMS context has received full response. Parse out the result or error.
*/
static bool _ctx_done_aws(mongocrypt_kms_ctx_t *kms, const char *json_field) {
@@ -464,8 +544,27 @@ static bool _ctx_done_aws(mongocrypt_kms_ctx_t *kms, const char *json_field) {
/* Parse out the {en|de}crypted result. */
http_status = kms_response_parser_status(kms->parser);
response = kms_response_parser_get_response(kms->parser);
+ if (!response) {
+ CLIENT_ERR("Failed to get response from parser: %s", kms_response_parser_error(kms->parser));
+ goto fail;
+ }
body = kms_response_get_body(response, &body_len);
+ if (kms->retry_enabled && should_retry_http(http_status, kms->req_type)) {
+ if (kms->attempts >= kms_max_attempts) {
+ // Wrap error to indicate maximum retries occurred.
+ _handle_non200_http_status(http_status, body, body_len, status);
+ CLIENT_ERR("KMS request failed after maximum of %d retries: %s",
+ kms_max_attempts,
+ mongocrypt_status_message(status, NULL));
+ goto fail;
+ } else {
+ ret = true;
+ set_retry(kms);
+ goto fail;
+ }
+ }
+
if (http_status != 200) {
_handle_non200_http_status(http_status, body, body_len, status);
goto fail;
@@ -541,8 +640,27 @@ static bool _ctx_done_oauth(mongocrypt_kms_ctx_t *kms) {
/* Parse out the oauth token result (or error). */
http_status = kms_response_parser_status(kms->parser);
response = kms_response_parser_get_response(kms->parser);
+ if (!response) {
+ CLIENT_ERR("Failed to get response from parser: %s", kms_response_parser_error(kms->parser));
+ goto fail;
+ }
body = kms_response_get_body(response, &body_len);
+ if (kms->retry_enabled && should_retry_http(http_status, kms->req_type)) {
+ if (kms->attempts >= kms_max_attempts) {
+ // Wrap error to indicate maximum retries occurred.
+ _handle_non200_http_status(http_status, body, body_len, status);
+ CLIENT_ERR("KMS request failed after maximum of %d retries: %s",
+ kms_max_attempts,
+ mongocrypt_status_message(status, NULL));
+ goto fail;
+ } else {
+ ret = true;
+ set_retry(kms);
+ goto fail;
+ }
+ }
+
if (body_len == 0) {
CLIENT_ERR("Empty KMS response. HTTP status=%d", http_status);
goto fail;
@@ -614,8 +732,27 @@ static bool _ctx_done_azure_wrapkey_unwrapkey(mongocrypt_kms_ctx_t *kms) {
/* Parse out the oauth token result (or error). */
http_status = kms_response_parser_status(kms->parser);
response = kms_response_parser_get_response(kms->parser);
+ if (!response) {
+ CLIENT_ERR("Failed to get response from parser: %s", kms_response_parser_error(kms->parser));
+ goto fail;
+ }
body = kms_response_get_body(response, &body_len);
+ if (kms->retry_enabled && should_retry_http(http_status, kms->req_type)) {
+ if (kms->attempts >= kms_max_attempts) {
+ // Wrap error to indicate maximum retries occurred.
+ _handle_non200_http_status(http_status, body, body_len, status);
+ CLIENT_ERR("KMS request failed after maximum of %d retries: %s",
+ kms_max_attempts,
+ mongocrypt_status_message(status, NULL));
+ goto fail;
+ } else {
+ ret = true;
+ set_retry(kms);
+ goto fail;
+ }
+ }
+
if (body_len == 0) {
CLIENT_ERR("Empty KMS response. HTTP status=%d", http_status);
goto fail;
@@ -704,8 +841,27 @@ static bool _ctx_done_gcp(mongocrypt_kms_ctx_t *kms, const char *json_field) {
/* Parse out the {en|de}crypted result. */
http_status = kms_response_parser_status(kms->parser);
response = kms_response_parser_get_response(kms->parser);
+ if (!response) {
+ CLIENT_ERR("Failed to get response from parser: %s", kms_response_parser_error(kms->parser));
+ goto fail;
+ }
body = kms_response_get_body(response, &body_len);
+ if (kms->retry_enabled && should_retry_http(http_status, kms->req_type)) {
+ if (kms->attempts >= kms_max_attempts) {
+ // Wrap error to indicate maximum retries occurred.
+ _handle_non200_http_status(http_status, body, body_len, status);
+ CLIENT_ERR("KMS request failed after maximum of %d retries: %s",
+ kms_max_attempts,
+ mongocrypt_status_message(status, NULL));
+ goto fail;
+ } else {
+ ret = true;
+ set_retry(kms);
+ goto fail;
+ }
+ }
+
if (http_status != 200) {
_handle_non200_http_status(http_status, body, body_len, status);
goto fail;
@@ -826,6 +982,193 @@ done:
return ret;
}
+static bool _ctx_done_kmip_create(mongocrypt_kms_ctx_t *kms_ctx) {
+ BSON_ASSERT_PARAM(kms_ctx);
+
+ kms_response_t *res = NULL;
+
+ mongocrypt_status_t *status = kms_ctx->status;
+ bool ret = false;
+ char *uid;
+
+ res = kms_response_parser_get_response(kms_ctx->parser);
+ if (!res) {
+ CLIENT_ERR("Error getting KMIP response: %s", kms_response_parser_error(kms_ctx->parser));
+ goto done;
+ }
+
+ uid = kms_kmip_response_get_unique_identifier(res);
+ if (!uid) {
+ CLIENT_ERR("Error getting UniqueIdentifer from KMIP Create response: %s", kms_response_get_error(res));
+ goto done;
+ }
+
+ if (!_mongocrypt_buffer_steal_from_string(&kms_ctx->result, uid)) {
+ CLIENT_ERR("Error storing KMS UniqueIdentifer result");
+ bson_free(uid);
+ goto done;
+ }
+ ret = true;
+
+done:
+ kms_response_destroy(res);
+ return ret;
+}
+
+static bool _ctx_done_kmip_encrypt(mongocrypt_kms_ctx_t *kms_ctx) {
+ BSON_ASSERT_PARAM(kms_ctx);
+
+ kms_response_t *res = NULL;
+
+ mongocrypt_status_t *status = kms_ctx->status;
+ bool ret = false;
+ uint8_t *ciphertext;
+ size_t ciphertext_len;
+ uint8_t *iv;
+ size_t iv_len;
+ _mongocrypt_buffer_t data_buf, iv_buf;
+ _mongocrypt_buffer_init(&data_buf);
+ _mongocrypt_buffer_init(&iv_buf);
+
+ res = kms_response_parser_get_response(kms_ctx->parser);
+ if (!res) {
+ CLIENT_ERR("Error getting KMIP response: %s", kms_response_parser_error(kms_ctx->parser));
+ goto done;
+ }
+
+ ciphertext = kms_kmip_response_get_data(res, &ciphertext_len);
+ if (!ciphertext) {
+ CLIENT_ERR("Error getting data from KMIP Encrypt response: %s", kms_response_get_error(res));
+ goto done;
+ }
+
+ iv = kms_kmip_response_get_iv(res, &iv_len);
+ if (!iv) {
+ CLIENT_ERR("Error getting IV from KMIP Encrypt response: %s", kms_response_get_error(res));
+ bson_free(ciphertext);
+ goto done;
+ }
+
+ if (iv_len != MONGOCRYPT_IV_LEN) {
+ CLIENT_ERR("KMIP IV response has unexpected length: %zu", iv_len);
+ bson_free(ciphertext);
+ bson_free(iv);
+ goto done;
+ }
+
+ if (!_mongocrypt_buffer_steal_from_data_and_size(&data_buf, ciphertext, ciphertext_len)) {
+ CLIENT_ERR("Error storing KMS Encrypt result");
+ bson_free(ciphertext);
+ bson_free(iv);
+ goto done;
+ }
+
+ if (!_mongocrypt_buffer_steal_from_data_and_size(&iv_buf, iv, iv_len)) {
+ CLIENT_ERR("Error storing KMS Encrypt IV");
+ bson_free(ciphertext);
+ bson_free(iv);
+ goto done;
+ }
+
+ const _mongocrypt_buffer_t results_buf[2] = {iv_buf, data_buf};
+ if (!_mongocrypt_buffer_concat(&kms_ctx->result, results_buf, 2)) {
+ CLIENT_ERR("Error concatenating IV and ciphertext");
+ goto done;
+ }
+
+ ret = true;
+
+done:
+ kms_response_destroy(res);
+ _mongocrypt_buffer_cleanup(&iv_buf);
+ _mongocrypt_buffer_cleanup(&data_buf);
+ return ret;
+}
+
+static bool _ctx_done_kmip_decrypt(mongocrypt_kms_ctx_t *kms_ctx) {
+ BSON_ASSERT_PARAM(kms_ctx);
+
+ kms_response_t *res = NULL;
+
+ mongocrypt_status_t *status = kms_ctx->status;
+ bool ret = false;
+ uint8_t *ciphertext;
+ size_t ciphertext_len;
+
+ res = kms_response_parser_get_response(kms_ctx->parser);
+ if (!res) {
+ CLIENT_ERR("Error getting KMIP response: %s", kms_response_parser_error(kms_ctx->parser));
+ goto done;
+ }
+
+ ciphertext = kms_kmip_response_get_data(res, &ciphertext_len);
+ if (!ciphertext) {
+ CLIENT_ERR("Error getting data from KMIP Decrypt response: %s", kms_response_get_error(res));
+ goto done;
+ }
+
+ if (!_mongocrypt_buffer_steal_from_data_and_size(&kms_ctx->result, ciphertext, ciphertext_len)) {
+ CLIENT_ERR("Error storing KMS Decrypt result");
+ bson_free(ciphertext);
+ goto done;
+ }
+
+ ret = true;
+
+done:
+ kms_response_destroy(res);
+ return ret;
+}
+
+bool mongocrypt_kms_ctx_fail(mongocrypt_kms_ctx_t *kms) {
+ if (!kms || !kms->retry_enabled) {
+ return false;
+ }
+
+ kms->should_retry = false;
+ mongocrypt_status_t *status = kms->status;
+
+ if (!kms->retry_enabled) {
+ CLIENT_ERR("KMS request failed due to network error");
+ return false;
+ }
+
+ if (kms->attempts >= kms_max_attempts) {
+ CLIENT_ERR("KMS request failed after %d retries due to a network error", kms_max_attempts);
+ return false;
+ }
+
+ // Check if request type is retryable. Some requests are non-idempotent and cannot be safely retried.
+ _kms_request_type_t retryable_types[] = {MONGOCRYPT_KMS_AZURE_OAUTH,
+ MONGOCRYPT_KMS_GCP_OAUTH,
+ MONGOCRYPT_KMS_AWS_ENCRYPT,
+ MONGOCRYPT_KMS_AWS_DECRYPT,
+ MONGOCRYPT_KMS_AZURE_WRAPKEY,
+ MONGOCRYPT_KMS_AZURE_UNWRAPKEY,
+ MONGOCRYPT_KMS_GCP_ENCRYPT,
+ MONGOCRYPT_KMS_GCP_DECRYPT};
+ bool is_retryable = false;
+ for (size_t i = 0; i < sizeof(retryable_types) / sizeof(retryable_types[0]); i++) {
+ if (retryable_types[i] == kms->req_type) {
+ is_retryable = true;
+ break;
+ }
+ }
+ if (!is_retryable) {
+ CLIENT_ERR("KMS request failed due to network error");
+ return false;
+ }
+
+ // Mark KMS context as retryable. Return again in `mongocrypt_ctx_next_kms_ctx`.
+ set_retry(kms);
+
+ // Reset intermediate state of parser.
+ if (kms->parser) {
+ kms_response_parser_reset(kms->parser);
+ }
+ return true;
+}
+
bool mongocrypt_kms_ctx_feed(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *bytes) {
if (!kms) {
return false;
@@ -889,6 +1232,9 @@ bool mongocrypt_kms_ctx_feed(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *byt
case MONGOCRYPT_KMS_KMIP_REGISTER: return _ctx_done_kmip_register(kms);
case MONGOCRYPT_KMS_KMIP_ACTIVATE: return _ctx_done_kmip_activate(kms);
case MONGOCRYPT_KMS_KMIP_GET: return _ctx_done_kmip_get(kms);
+ case MONGOCRYPT_KMS_KMIP_ENCRYPT: return _ctx_done_kmip_encrypt(kms);
+ case MONGOCRYPT_KMS_KMIP_DECRYPT: return _ctx_done_kmip_decrypt(kms);
+ case MONGOCRYPT_KMS_KMIP_CREATE: return _ctx_done_kmip_create(kms);
}
}
return true;
@@ -948,6 +1294,7 @@ void _mongocrypt_kms_ctx_cleanup(mongocrypt_kms_ctx_t *kms) {
_mongocrypt_buffer_cleanup(&kms->msg);
_mongocrypt_buffer_cleanup(&kms->result);
bson_free(kms->endpoint);
+ bson_free(kms->kmsid);
}
bool mongocrypt_kms_ctx_message(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *msg) {
@@ -979,24 +1326,27 @@ bool mongocrypt_kms_ctx_endpoint(mongocrypt_kms_ctx_t *kms, const char **endpoin
}
bool _mongocrypt_kms_ctx_init_azure_auth(mongocrypt_kms_ctx_t *kms,
- _mongocrypt_log_t *log,
- _mongocrypt_opts_kms_providers_t *kms_providers,
- _mongocrypt_endpoint_t *key_vault_endpoint) {
+ const mc_kms_creds_t *kc,
+ _mongocrypt_endpoint_t *key_vault_endpoint,
+ const char *kmsid,
+ _mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms);
- BSON_ASSERT_PARAM(kms_providers);
+ BSON_ASSERT_PARAM(kc);
kms_request_opt_t *opt = NULL;
mongocrypt_status_t *status;
- _mongocrypt_endpoint_t *identity_platform_endpoint;
+ const _mongocrypt_endpoint_t *identity_platform_endpoint;
char *scope = NULL;
const char *hostname;
char *request_string;
bool ret = false;
- _init_common(kms, log, MONGOCRYPT_KMS_AZURE_OAUTH);
+ _init_common(kms, log, MONGOCRYPT_KMS_AZURE_OAUTH, kmsid);
status = kms->status;
- identity_platform_endpoint = kms_providers->azure.identity_platform_endpoint;
+ BSON_ASSERT(kc->type == MONGOCRYPT_KMS_PROVIDER_AZURE);
+
+ identity_platform_endpoint = kc->value.azure.identity_platform_endpoint;
if (identity_platform_endpoint) {
kms->endpoint = bson_strdup(identity_platform_endpoint->host_and_port);
@@ -1022,9 +1372,9 @@ bool _mongocrypt_kms_ctx_init_azure_auth(mongocrypt_kms_ctx_t *kms,
kms_request_opt_set_provider(opt, KMS_REQUEST_PROVIDER_AZURE);
kms->req = kms_azure_request_oauth_new(hostname,
scope,
- kms_providers->azure.tenant_id,
- kms_providers->azure.client_id,
- kms_providers->azure.client_secret,
+ kc->value.azure.tenant_id,
+ kc->value.azure.client_id,
+ kc->value.azure.client_secret,
opt);
if (kms_request_get_error(kms->req)) {
CLIENT_ERR("error constructing KMS message: %s", kms_request_get_error(kms->req));
@@ -1049,11 +1399,12 @@ fail:
}
bool _mongocrypt_kms_ctx_init_azure_wrapkey(mongocrypt_kms_ctx_t *kms,
- _mongocrypt_log_t *log,
_mongocrypt_opts_kms_providers_t *kms_providers,
struct __mongocrypt_ctx_opts_t *ctx_opts,
const char *access_token,
- _mongocrypt_buffer_t *plaintext_key_material) {
+ _mongocrypt_buffer_t *plaintext_key_material,
+ const char *kmsid,
+ _mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms);
BSON_ASSERT_PARAM(ctx_opts);
BSON_ASSERT_PARAM(plaintext_key_material);
@@ -1066,7 +1417,7 @@ bool _mongocrypt_kms_ctx_init_azure_wrapkey(mongocrypt_kms_ctx_t *kms,
char *request_string;
bool ret = false;
- _init_common(kms, log, MONGOCRYPT_KMS_AZURE_WRAPKEY);
+ _init_common(kms, log, MONGOCRYPT_KMS_AZURE_WRAPKEY, kmsid);
status = kms->status;
BSON_ASSERT(ctx_opts->kek.provider.azure.key_vault_endpoint);
@@ -1114,6 +1465,7 @@ bool _mongocrypt_kms_ctx_init_azure_unwrapkey(mongocrypt_kms_ctx_t *kms,
_mongocrypt_opts_kms_providers_t *kms_providers,
const char *access_token,
_mongocrypt_key_doc_t *key,
+ const char *kmsid,
_mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms);
BSON_ASSERT_PARAM(key);
@@ -1126,7 +1478,7 @@ bool _mongocrypt_kms_ctx_init_azure_unwrapkey(mongocrypt_kms_ctx_t *kms,
char *request_string;
bool ret = false;
- _init_common(kms, log, MONGOCRYPT_KMS_AZURE_UNWRAPKEY);
+ _init_common(kms, log, MONGOCRYPT_KMS_AZURE_UNWRAPKEY, kmsid);
status = kms->status;
BSON_ASSERT(key->kek.provider.azure.key_vault_endpoint);
@@ -1212,17 +1564,18 @@ static bool _sign_rsaes_pkcs1_v1_5_trampoline(void *ctx,
}
bool _mongocrypt_kms_ctx_init_gcp_auth(mongocrypt_kms_ctx_t *kms,
- _mongocrypt_log_t *log,
_mongocrypt_opts_t *crypt_opts,
- _mongocrypt_opts_kms_providers_t *kms_providers,
- _mongocrypt_endpoint_t *kms_endpoint) {
+ const mc_kms_creds_t *kc,
+ _mongocrypt_endpoint_t *kms_endpoint,
+ const char *kmsid,
+ _mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms);
- BSON_ASSERT_PARAM(kms_providers);
+ BSON_ASSERT_PARAM(kc);
BSON_ASSERT_PARAM(crypt_opts);
kms_request_opt_t *opt = NULL;
mongocrypt_status_t *status;
- _mongocrypt_endpoint_t *auth_endpoint;
+ const _mongocrypt_endpoint_t *auth_endpoint;
char *scope = NULL;
char *audience = NULL;
const char *hostname;
@@ -1230,12 +1583,14 @@ bool _mongocrypt_kms_ctx_init_gcp_auth(mongocrypt_kms_ctx_t *kms,
bool ret = false;
ctx_with_status_t ctx_with_status;
- _init_common(kms, log, MONGOCRYPT_KMS_GCP_OAUTH);
+ _init_common(kms, log, MONGOCRYPT_KMS_GCP_OAUTH, kmsid);
status = kms->status;
ctx_with_status.ctx = crypt_opts;
ctx_with_status.status = mongocrypt_status_new();
- auth_endpoint = kms_providers->gcp.endpoint;
+ BSON_ASSERT(kc->type == MONGOCRYPT_KMS_PROVIDER_GCP);
+
+ auth_endpoint = kc->value.gcp.endpoint;
if (auth_endpoint) {
kms->endpoint = bson_strdup(auth_endpoint->host_and_port);
hostname = auth_endpoint->host;
@@ -1262,11 +1617,11 @@ bool _mongocrypt_kms_ctx_init_gcp_auth(mongocrypt_kms_ctx_t *kms,
kms_request_opt_set_crypto_hook_sign_rsaes_pkcs1_v1_5(opt, _sign_rsaes_pkcs1_v1_5_trampoline, &ctx_with_status);
}
kms->req = kms_gcp_request_oauth_new(hostname,
- kms_providers->gcp.email,
+ kc->value.gcp.email,
audience,
scope,
- (const char *)kms_providers->gcp.private_key.data,
- kms_providers->gcp.private_key.len,
+ (const char *)kc->value.gcp.private_key.data,
+ kc->value.gcp.private_key.len,
opt);
if (kms_request_get_error(kms->req)) {
CLIENT_ERR("error constructing KMS message: %s", kms_request_get_error(kms->req));
@@ -1295,11 +1650,12 @@ fail:
}
bool _mongocrypt_kms_ctx_init_gcp_encrypt(mongocrypt_kms_ctx_t *kms,
- _mongocrypt_log_t *log,
_mongocrypt_opts_kms_providers_t *kms_providers,
struct __mongocrypt_ctx_opts_t *ctx_opts,
const char *access_token,
- _mongocrypt_buffer_t *plaintext_key_material) {
+ _mongocrypt_buffer_t *plaintext_key_material,
+ const char *kmsid,
+ _mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms);
BSON_ASSERT_PARAM(ctx_opts);
BSON_ASSERT_PARAM(kms_providers);
@@ -1314,7 +1670,7 @@ bool _mongocrypt_kms_ctx_init_gcp_encrypt(mongocrypt_kms_ctx_t *kms,
char *request_string;
bool ret = false;
- _init_common(kms, log, MONGOCRYPT_KMS_GCP_ENCRYPT);
+ _init_common(kms, log, MONGOCRYPT_KMS_GCP_ENCRYPT, kmsid);
status = kms->status;
if (ctx_opts->kek.provider.gcp.endpoint) {
@@ -1368,6 +1724,7 @@ bool _mongocrypt_kms_ctx_init_gcp_decrypt(mongocrypt_kms_ctx_t *kms,
_mongocrypt_opts_kms_providers_t *kms_providers,
const char *access_token,
_mongocrypt_key_doc_t *key,
+ const char *kmsid,
_mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms);
BSON_ASSERT_PARAM(kms_providers);
@@ -1382,7 +1739,7 @@ bool _mongocrypt_kms_ctx_init_gcp_decrypt(mongocrypt_kms_ctx_t *kms,
char *request_string;
bool ret = false;
- _init_common(kms, log, MONGOCRYPT_KMS_GCP_DECRYPT);
+ _init_common(kms, log, MONGOCRYPT_KMS_GCP_DECRYPT, kmsid);
status = kms->status;
if (key->kek.provider.gcp.endpoint) {
@@ -1435,6 +1792,7 @@ bool _mongocrypt_kms_ctx_init_kmip_register(mongocrypt_kms_ctx_t *kms_ctx,
const _mongocrypt_endpoint_t *endpoint,
const uint8_t *secretdata,
uint32_t secretdata_len,
+ const char *kmsid,
_mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms_ctx);
BSON_ASSERT_PARAM(endpoint);
@@ -1445,7 +1803,7 @@ bool _mongocrypt_kms_ctx_init_kmip_register(mongocrypt_kms_ctx_t *kms_ctx,
const uint8_t *reqdata;
size_t reqlen;
- _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_REGISTER);
+ _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_REGISTER, kmsid);
status = kms_ctx->status;
kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
@@ -1471,6 +1829,7 @@ done:
bool _mongocrypt_kms_ctx_init_kmip_activate(mongocrypt_kms_ctx_t *kms_ctx,
const _mongocrypt_endpoint_t *endpoint,
const char *unique_identifier,
+ const char *kmsid,
_mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms_ctx);
BSON_ASSERT_PARAM(endpoint);
@@ -1481,7 +1840,7 @@ bool _mongocrypt_kms_ctx_init_kmip_activate(mongocrypt_kms_ctx_t *kms_ctx,
size_t reqlen;
const uint8_t *reqdata;
- _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_ACTIVATE);
+ _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_ACTIVATE, kmsid);
status = kms_ctx->status;
kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
@@ -1507,6 +1866,7 @@ done:
bool _mongocrypt_kms_ctx_init_kmip_get(mongocrypt_kms_ctx_t *kms_ctx,
const _mongocrypt_endpoint_t *endpoint,
const char *unique_identifier,
+ const char *kmsid,
_mongocrypt_log_t *log) {
BSON_ASSERT_PARAM(kms_ctx);
BSON_ASSERT_PARAM(endpoint);
@@ -1517,7 +1877,7 @@ bool _mongocrypt_kms_ctx_init_kmip_get(mongocrypt_kms_ctx_t *kms_ctx,
size_t reqlen;
const uint8_t *reqdata;
- _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_GET);
+ _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_GET, kmsid);
status = kms_ctx->status;
kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
@@ -1540,6 +1900,129 @@ done:
return ret;
}
+bool _mongocrypt_kms_ctx_init_kmip_create(mongocrypt_kms_ctx_t *kms_ctx,
+ const _mongocrypt_endpoint_t *endpoint,
+ const char *kmsid,
+ _mongocrypt_log_t *log) {
+ BSON_ASSERT_PARAM(kms_ctx);
+ BSON_ASSERT_PARAM(endpoint);
+ bool ret = false;
+
+ _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_CREATE, kmsid);
+ mongocrypt_status_t *status = kms_ctx->status;
+ kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
+ _mongocrypt_apply_default_port(&kms_ctx->endpoint, DEFAULT_KMIP_PORT);
+
+ kms_ctx->req = kms_kmip_request_create_new(NULL /* reserved */);
+
+ if (kms_request_get_error(kms_ctx->req)) {
+ CLIENT_ERR("Error creating KMIP create request: %s", kms_request_get_error(kms_ctx->req));
+ goto done;
+ }
+
+ size_t reqlen;
+ const uint8_t *reqdata = kms_request_to_bytes(kms_ctx->req, &reqlen);
+ if (!_mongocrypt_buffer_copy_from_data_and_size(&kms_ctx->msg, reqdata, reqlen)) {
+ CLIENT_ERR("Error storing KMS request payload");
+ goto done;
+ }
+
+ ret = true;
+done:
+ return ret;
+}
+
+bool _mongocrypt_kms_ctx_init_kmip_encrypt(mongocrypt_kms_ctx_t *kms_ctx,
+ const _mongocrypt_endpoint_t *endpoint,
+ const char *unique_identifier,
+ const char *kmsid,
+ _mongocrypt_buffer_t *plaintext,
+ _mongocrypt_log_t *log) {
+ BSON_ASSERT_PARAM(kms_ctx);
+ BSON_ASSERT_PARAM(endpoint);
+ BSON_ASSERT_PARAM(plaintext);
+ bool ret = false;
+
+ _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_ENCRYPT, kmsid);
+ mongocrypt_status_t *status = kms_ctx->status;
+ kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
+ _mongocrypt_apply_default_port(&kms_ctx->endpoint, DEFAULT_KMIP_PORT);
+
+ kms_ctx->req =
+ kms_kmip_request_encrypt_new(NULL /* reserved */, unique_identifier, plaintext->data, plaintext->len);
+
+ if (kms_request_get_error(kms_ctx->req)) {
+ CLIENT_ERR("Error creating KMIP encrypt request: %s", kms_request_get_error(kms_ctx->req));
+ goto done;
+ }
+
+ size_t reqlen;
+ const uint8_t *reqdata = kms_request_to_bytes(kms_ctx->req, &reqlen);
+ if (!_mongocrypt_buffer_copy_from_data_and_size(&kms_ctx->msg, reqdata, reqlen)) {
+ CLIENT_ERR("Error storing KMS request payload");
+ goto done;
+ }
+
+ ret = true;
+done:
+ return ret;
+}
+
+bool _mongocrypt_kms_ctx_init_kmip_decrypt(mongocrypt_kms_ctx_t *kms_ctx,
+ const _mongocrypt_endpoint_t *endpoint,
+ const char *kmsid,
+ _mongocrypt_key_doc_t *key,
+ _mongocrypt_log_t *log) {
+ BSON_ASSERT_PARAM(kms_ctx);
+ BSON_ASSERT_PARAM(endpoint);
+ BSON_ASSERT_PARAM(key);
+ bool ret = false;
+
+ _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_DECRYPT, kmsid);
+ mongocrypt_status_t *status = kms_ctx->status;
+ kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
+ _mongocrypt_apply_default_port(&kms_ctx->endpoint, DEFAULT_KMIP_PORT);
+
+ _mongocrypt_buffer_t iv;
+ if (!_mongocrypt_buffer_from_subrange(&iv, &key->key_material, 0, MONGOCRYPT_IV_LEN)) {
+ CLIENT_ERR("Error getting IV from key material");
+ goto done;
+ }
+ _mongocrypt_buffer_t ciphertext;
+ if (!_mongocrypt_buffer_from_subrange(&ciphertext,
+ &key->key_material,
+ MONGOCRYPT_IV_LEN,
+ key->key_material.len - MONGOCRYPT_IV_LEN)) {
+ CLIENT_ERR("Error getting ciphertext from key material");
+ goto done;
+ }
+
+ BSON_ASSERT(key->kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_KMIP);
+ BSON_ASSERT(key->kek.provider.kmip.delegated);
+ kms_ctx->req = kms_kmip_request_decrypt_new(NULL /* reserved */,
+ key->kek.provider.kmip.key_id,
+ ciphertext.data,
+ ciphertext.len,
+ iv.data,
+ iv.len);
+
+ if (kms_request_get_error(kms_ctx->req)) {
+ CLIENT_ERR("Error creating KMIP decrypt request: %s", kms_request_get_error(kms_ctx->req));
+ goto done;
+ }
+
+ size_t reqlen;
+ const uint8_t *reqdata = kms_request_to_bytes(kms_ctx->req, &reqlen);
+ if (!_mongocrypt_buffer_copy_from_data_and_size(&kms_ctx->msg, reqdata, reqlen)) {
+ CLIENT_ERR("Error storing KMS request payload");
+ goto done;
+ }
+
+ ret = true;
+done:
+ return ret;
+}
+
static const char *set_and_ret(const char *what, uint32_t *len) {
BSON_ASSERT_PARAM(what);
@@ -1553,18 +2036,5 @@ const char *mongocrypt_kms_ctx_get_kms_provider(mongocrypt_kms_ctx_t *kms, uint3
BSON_ASSERT_PARAM(kms);
/* len is checked in set_and_ret () before it is used */
- switch (kms->req_type) {
- default: BSON_ASSERT(false && "unknown KMS request type");
- case MONGOCRYPT_KMS_AWS_ENCRYPT:
- case MONGOCRYPT_KMS_AWS_DECRYPT: return set_and_ret("aws", len);
- case MONGOCRYPT_KMS_AZURE_OAUTH:
- case MONGOCRYPT_KMS_AZURE_WRAPKEY:
- case MONGOCRYPT_KMS_AZURE_UNWRAPKEY: return set_and_ret("azure", len);
- case MONGOCRYPT_KMS_GCP_OAUTH:
- case MONGOCRYPT_KMS_GCP_ENCRYPT:
- case MONGOCRYPT_KMS_GCP_DECRYPT: return set_and_ret("gcp", len);
- case MONGOCRYPT_KMS_KMIP_REGISTER:
- case MONGOCRYPT_KMS_KMIP_ACTIVATE:
- case MONGOCRYPT_KMS_KMIP_GET: return set_and_ret("kmip", len);
- }
+ return set_and_ret(kms->kmsid, len);
}
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-log.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-log.c
index 3aee7725a1e..072104aba73 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-log.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-log.c
@@ -37,7 +37,6 @@ void _mongocrypt_log_cleanup(_mongocrypt_log_t *log) {
}
_mongocrypt_mutex_cleanup(&log->mutex);
- memset(log, 0, sizeof(*log));
}
void _mongocrypt_stdout_log_fn(mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx) {
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-marking-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-marking-private.h
index 16dbc1d2e0f..f759c15a797 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-marking-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-marking-private.h
@@ -45,6 +45,10 @@ typedef struct {
};
} _mongocrypt_marking_t;
+// `_mongocrypt_marking_t` inherits extended alignment from libbson. To dynamically allocate, use aligned allocation
+// (e.g. BSON_ALIGNED_ALLOC)
+BSON_STATIC_ASSERT2(alignof__mongocrypt_marking_t, BSON_ALIGNOF(_mongocrypt_marking_t) >= BSON_ALIGNOF(bson_iter_t));
+
void _mongocrypt_marking_init(_mongocrypt_marking_t *marking);
void _mongocrypt_marking_cleanup(_mongocrypt_marking_t *marking);
@@ -63,6 +67,7 @@ bool _mongocrypt_marking_to_ciphertext(void *ctx,
mc_mincover_t *mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec,
size_t sparsity,
- mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;
+ mongocrypt_status_t *status,
+ bool use_range_v2) MONGOCRYPT_WARN_UNUSED_RESULT;
#endif /* MONGOCRYPT_MARKING_PRIVATE_H */
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-marking.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-marking.c
index 443356abc9e..d974b106dbe 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-marking.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-marking.c
@@ -24,6 +24,7 @@
#include "mc-fle2-insert-update-payload-private.h"
#include "mc-fle2-payload-uev-private.h"
#include "mc-fle2-payload-uev-v2-private.h"
+#include "mc-optional-private.h"
#include "mc-range-edge-generation-private.h"
#include "mc-range-encoding-private.h"
#include "mc-range-mincover-private.h"
@@ -33,6 +34,7 @@
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-key-broker-private.h"
#include "mongocrypt-marking-private.h"
+#include "mongocrypt-private.h"
#include "mongocrypt-util-private.h" // mc_bson_type_to_string
#include "mongocrypt.h"
@@ -40,7 +42,7 @@
static bool
_mongocrypt_marking_parse_fle1_placeholder(const bson_t *in, _mongocrypt_marking_t *out, mongocrypt_status_t *status) {
- bson_iter_t iter;
+ bson_iter_t iter = {0};
bool has_ki = false, has_ka = false, has_a = false, has_v = false;
BSON_ASSERT_PARAM(in);
@@ -199,22 +201,24 @@ void _mongocrypt_marking_cleanup(_mongocrypt_marking_t *marking) {
* Calculates:
* E?CToken = HMAC(collectionLevel1Token, n)
* E?CDerivedFromDataToken = HMAC(E?CToken, value)
- * E?CDerivedFromDataTokenAndCounter = HMAC(E?CDerivedFromDataToken, c)
+ * E?CDerivedFromDataTokenAndContentionFactor = HMAC(E?CDerivedFromDataToken, cf)
*
* E?C = EDC|ESC|ECC
* n = 1 for EDC, 2 for ESC, 3 for ECC
- * c = maxContentionCounter
+ * cf = contentionFactor
*
- * E?CDerivedFromDataTokenAndCounter is saved to out,
- * which is initialized even on failure.
+ * If {useContentionFactor} is False, E?CDerivedFromDataToken is saved to out, and {contentionFactor} is ignored.
+ * Otherwise, E?CDerivedFromDataTokenAndContentionFactor is saved to out using {contentionFactor}.
+ *
+ * Note that {out} is initialized even on failure.
*/
#define DERIVE_TOKEN_IMPL(Name) \
static bool _fle2_derive_##Name##_token(_mongocrypt_crypto_t *crypto, \
_mongocrypt_buffer_t *out, \
const mc_CollectionsLevel1Token_t *level1Token, \
const _mongocrypt_buffer_t *value, \
- bool useCounter, \
- int64_t counter, \
+ bool useContentionFactor, \
+ int64_t contentionFactor, \
mongocrypt_status_t *status) { \
BSON_ASSERT_PARAM(crypto); \
BSON_ASSERT_PARAM(out); \
@@ -235,24 +239,29 @@ void _mongocrypt_marking_cleanup(_mongocrypt_marking_t *marking) {
return false; \
} \
\
- if (!useCounter) { \
+ if (!useContentionFactor) { \
/* FindEqualityPayload uses *fromDataToken */ \
_mongocrypt_buffer_copy_to(mc_##Name##DerivedFromDataToken_get(fromDataToken), out); \
mc_##Name##DerivedFromDataToken_destroy(fromDataToken); \
return true; \
} \
\
- BSON_ASSERT(counter >= 0); \
- /* InsertUpdatePayload continues through *fromDataTokenAndCounter */ \
- mc_##Name##DerivedFromDataTokenAndCounter_t *fromTokenAndCounter = \
- mc_##Name##DerivedFromDataTokenAndCounter_new(crypto, fromDataToken, (uint64_t)counter, status); \
+ BSON_ASSERT(contentionFactor >= 0); \
+ /* InsertUpdatePayload continues through *fromDataTokenAndContentionFactor */ \
+ mc_##Name##DerivedFromDataTokenAndContentionFactor_t *fromTokenAndContentionFactor = \
+ mc_##Name##DerivedFromDataTokenAndContentionFactor_new(crypto, \
+ fromDataToken, \
+ (uint64_t)contentionFactor, \
+ status); \
mc_##Name##DerivedFromDataToken_destroy(fromDataToken); \
- if (!fromTokenAndCounter) { \
+ if (!fromTokenAndContentionFactor) { \
return false; \
} \
\
- _mongocrypt_buffer_copy_to(mc_##Name##DerivedFromDataTokenAndCounter_get(fromTokenAndCounter), out); \
- mc_##Name##DerivedFromDataTokenAndCounter_destroy(fromTokenAndCounter); \
+ _mongocrypt_buffer_copy_to( \
+ mc_##Name##DerivedFromDataTokenAndContentionFactor_get(fromTokenAndContentionFactor), \
+ out); \
+ mc_##Name##DerivedFromDataTokenAndContentionFactor_destroy(fromTokenAndContentionFactor); \
\
return true; \
}
@@ -366,32 +375,61 @@ static bool _fle2_placeholder_aes_aead_encrypt(_mongocrypt_key_broker_t *kb,
return true;
}
-// p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndCounter ||
-// ECCDerivedFromDataTokenAndCounter)
+// FLE V1: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor ||
+// ECCDerivedFromDataTokenAndContentionFactor)
+// FLE V2: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor)
+// Range V2: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || isLeaf)
static bool _fle2_derive_encrypted_token(_mongocrypt_crypto_t *crypto,
_mongocrypt_buffer_t *out,
+ bool use_range_v2,
const mc_CollectionsLevel1Token_t *collectionsLevel1Token,
const _mongocrypt_buffer_t *escDerivedToken,
const _mongocrypt_buffer_t *eccDerivedToken,
+ mc_optional_bool_t is_leaf,
mongocrypt_status_t *status) {
mc_ECOCToken_t *ecocToken = mc_ECOCToken_new(crypto, collectionsLevel1Token, status);
if (!ecocToken) {
return false;
}
+ bool ok = false;
_mongocrypt_buffer_t tmp;
_mongocrypt_buffer_init(&tmp);
const _mongocrypt_buffer_t *p = &tmp;
if (!eccDerivedToken) {
// FLE2v2
- p = escDerivedToken;
+ if (use_range_v2 && is_leaf.set) {
+ // Range V2; concat isLeaf
+ _mongocrypt_buffer_t isLeafBuf;
+ if (!_mongocrypt_buffer_copy_from_data_and_size(&isLeafBuf, (uint8_t[]){is_leaf.value}, 1)) {
+ CLIENT_ERR("failed to create is_leaf buffer");
+ goto fail;
+ }
+ if (!_mongocrypt_buffer_concat(&tmp, (_mongocrypt_buffer_t[]){*escDerivedToken, isLeafBuf}, 2)) {
+ CLIENT_ERR("failed to allocate buffer");
+ _mongocrypt_buffer_cleanup(&isLeafBuf);
+ goto fail;
+ }
+ _mongocrypt_buffer_cleanup(&isLeafBuf);
+ } else {
+ p = escDerivedToken;
+ }
+
} else {
// FLE2v1
const _mongocrypt_buffer_t tokens[] = {*escDerivedToken, *eccDerivedToken};
- _mongocrypt_buffer_concat(&tmp, tokens, 2);
+ if (!_mongocrypt_buffer_concat(&tmp, tokens, 2)) {
+ CLIENT_ERR("failed to allocate buffer");
+ goto fail;
+ }
}
- const bool ok = _fle2_placeholder_aes_ctr_encrypt(crypto, mc_ECOCToken_get(ecocToken), p, out, status);
+ if (!_fle2_placeholder_aes_ctr_encrypt(crypto, mc_ECOCToken_get(ecocToken), p, out, status)) {
+ goto fail;
+ }
+
+ ok = true;
+fail:
_mongocrypt_buffer_cleanup(&tmp);
mc_ECOCToken_destroy(ecocToken);
return ok;
@@ -422,7 +460,8 @@ static void _FLE2EncryptedPayloadCommon_cleanup(_FLE2EncryptedPayloadCommon_t *c
_mongocrypt_buffer_cleanup(&common->escDerivedToken);
_mongocrypt_buffer_cleanup(&common->eccDerivedToken);
_mongocrypt_buffer_cleanup(&common->serverDerivedFromDataToken);
- memset(common, 0, sizeof(*common));
+ // Zero out memory so `_FLE2EncryptedPayloadCommon_cleanup` is safe to call twice.
+ *common = (_FLE2EncryptedPayloadCommon_t){{0}};
}
// _get_tokenKey returns the tokenKey identified by indexKeyId.
@@ -466,8 +505,8 @@ static bool _mongocrypt_fle2_placeholder_common(_mongocrypt_key_broker_t *kb,
_FLE2EncryptedPayloadCommon_t *ret,
const _mongocrypt_buffer_t *indexKeyId,
const _mongocrypt_buffer_t *value,
- bool useCounter,
- int64_t maxContentionCounter,
+ bool useContentionFactor,
+ int64_t contentionFactor,
mongocrypt_status_t *status) {
BSON_ASSERT_PARAM(kb);
BSON_ASSERT_PARAM(ret);
@@ -498,8 +537,8 @@ static bool _mongocrypt_fle2_placeholder_common(_mongocrypt_key_broker_t *kb,
&ret->edcDerivedToken,
ret->collectionsLevel1Token,
value,
- useCounter,
- maxContentionCounter,
+ useContentionFactor,
+ contentionFactor,
status)) {
goto fail;
}
@@ -508,8 +547,8 @@ static bool _mongocrypt_fle2_placeholder_common(_mongocrypt_key_broker_t *kb,
&ret->escDerivedToken,
ret->collectionsLevel1Token,
value,
- useCounter,
- maxContentionCounter,
+ useContentionFactor,
+ contentionFactor,
status)) {
goto fail;
}
@@ -535,8 +574,8 @@ static bool _mongocrypt_fle2_placeholder_common(_mongocrypt_key_broker_t *kb,
&ret->eccDerivedToken,
ret->collectionsLevel1Token,
value,
- useCounter,
- maxContentionCounter,
+ useContentionFactor,
+ contentionFactor,
status)) {
goto fail;
}
@@ -566,6 +605,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_common_v1(_mongocrypt_
BSON_ASSERT_PARAM(value_iter);
BSON_ASSERT(kb->crypt);
BSON_ASSERT(kb->crypt->opts.use_fle2_v2 == false);
+ BSON_ASSERT(kb->crypt->opts.use_range_v2 == false);
BSON_ASSERT(placeholder->type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT);
_mongocrypt_crypto_t *crypto = kb->crypt->crypto;
@@ -573,10 +613,10 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_common_v1(_mongocrypt_
bool res = false;
*contentionFactor = 0;
- if (placeholder->maxContentionCounter > 0) {
+ if (placeholder->maxContentionFactor > 0) {
/* Choose a random contentionFactor in the inclusive range [0,
- * placeholder->maxContentionCounter] */
- if (!_mongocrypt_random_int64(crypto, placeholder->maxContentionCounter + 1, contentionFactor, status)) {
+ * placeholder->maxContentionFactor] */
+ if (!_mongocrypt_random_int64(crypto, placeholder->maxContentionFactor + 1, contentionFactor, status)) {
goto fail;
}
}
@@ -586,7 +626,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_common_v1(_mongocrypt_
common,
&placeholder->index_key_id,
&value,
- true, /* derive tokens using counter */
+ true, /* derive tokens using contentionFactor */
*contentionFactor,
status)) {
goto fail;
@@ -599,13 +639,15 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_common_v1(_mongocrypt_
// c := ECCDerivedToken
_mongocrypt_buffer_steal(&out->eccDerivedToken, &common->eccDerivedToken);
- // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndCounter ||
- // ECCDerivedFromDataTokenAndCounter)
+ // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor ||
+ // ECCDerivedFromDataTokenAndContentionFactor)
if (!_fle2_derive_encrypted_token(crypto,
&out->encryptedTokens,
+ false, // Can't use range V2 with FLE V1
common->collectionsLevel1Token,
&out->escDerivedToken,
&out->eccDerivedToken,
+ (mc_optional_bool_t){0}, // Unset is_leaf as it's not used in V1
status)) {
goto fail;
}
@@ -719,10 +761,10 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_common(_mongocrypt_key
bool res = false;
out->contentionFactor = 0; // k
- if (placeholder->maxContentionCounter > 0) {
+ if (placeholder->maxContentionFactor > 0) {
/* Choose a random contentionFactor in the inclusive range [0,
- * placeholder->maxContentionCounter] */
- if (!_mongocrypt_random_int64(crypto, placeholder->maxContentionCounter + 1, &out->contentionFactor, status)) {
+ * placeholder->maxContentionFactor] */
+ if (!_mongocrypt_random_int64(crypto, placeholder->maxContentionFactor + 1, &out->contentionFactor, status)) {
goto fail;
}
}
@@ -732,7 +774,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_common(_mongocrypt_key
common,
&placeholder->index_key_id,
&value,
- true, /* derive tokens using counter */
+ true, /* derive tokens using contentionFactor */
out->contentionFactor,
status)) {
goto fail;
@@ -744,13 +786,18 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_common(_mongocrypt_key
_mongocrypt_buffer_steal(&out->escDerivedToken, &common->escDerivedToken);
BSON_ASSERT(common->eccDerivedToken.data == NULL);
- // p := EncryptCBC(ECOCToken, ESCDerivedFromDataTokenAndCounter)
- if (!_fle2_derive_encrypted_token(crypto,
- &out->encryptedTokens,
- common->collectionsLevel1Token,
- &out->escDerivedToken,
- NULL, // unused in v2
- status)) {
+ // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor)
+ // Or in Range V2, when using range: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || 0x00)
+ if (!_fle2_derive_encrypted_token(
+ crypto,
+ &out->encryptedTokens,
+ kb->crypt->opts.use_range_v2,
+ common->collectionsLevel1Token,
+ &out->escDerivedToken,
+ NULL, // unused in v2
+ // If this is a range insert, we append isLeaf to the encryptedTokens. Otherwise, we don't.
+ placeholder->algorithm == MONGOCRYPT_FLE2_ALGORITHM_RANGE ? OPT_BOOL(false) : (mc_optional_bool_t){0},
+ status)) {
goto fail;
}
@@ -850,7 +897,8 @@ fail:
// get_edges creates and returns edges from an FLE2RangeInsertSpec. Returns NULL
// on error.
-static mc_edges_t *get_edges(mc_FLE2RangeInsertSpec_t *insertSpec, size_t sparsity, mongocrypt_status_t *status) {
+static mc_edges_t *
+get_edges(mc_FLE2RangeInsertSpec_t *insertSpec, size_t sparsity, mongocrypt_status_t *status, bool use_range_v2) {
BSON_ASSERT_PARAM(insertSpec);
bson_type_t value_type = bson_iter_type(&insertSpec->v);
@@ -859,28 +907,36 @@ static mc_edges_t *get_edges(mc_FLE2RangeInsertSpec_t *insertSpec, size_t sparsi
return mc_getEdgesInt32((mc_getEdgesInt32_args_t){.value = bson_iter_int32(&insertSpec->v),
.min = OPT_I32(bson_iter_int32(&insertSpec->min)),
.max = OPT_I32(bson_iter_int32(&insertSpec->max)),
- .sparsity = sparsity},
- status);
+ .sparsity = sparsity,
+ .trimFactor = insertSpec->trimFactor},
+ status,
+ use_range_v2);
}
else if (value_type == BSON_TYPE_INT64) {
return mc_getEdgesInt64((mc_getEdgesInt64_args_t){.value = bson_iter_int64(&insertSpec->v),
.min = OPT_I64(bson_iter_int64(&insertSpec->min)),
.max = OPT_I64(bson_iter_int64(&insertSpec->max)),
- .sparsity = sparsity},
- status);
+ .sparsity = sparsity,
+ .trimFactor = insertSpec->trimFactor},
+ status,
+ use_range_v2);
}
else if (value_type == BSON_TYPE_DATE_TIME) {
return mc_getEdgesInt64((mc_getEdgesInt64_args_t){.value = bson_iter_date_time(&insertSpec->v),
.min = OPT_I64(bson_iter_date_time(&insertSpec->min)),
.max = OPT_I64(bson_iter_date_time(&insertSpec->max)),
- .sparsity = sparsity},
- status);
+ .sparsity = sparsity,
+ .trimFactor = insertSpec->trimFactor},
+ status,
+ use_range_v2);
}
else if (value_type == BSON_TYPE_DOUBLE) {
- mc_getEdgesDouble_args_t args = {.value = bson_iter_double(&insertSpec->v), .sparsity = sparsity};
+ mc_getEdgesDouble_args_t args = {.value = bson_iter_double(&insertSpec->v),
+ .sparsity = sparsity,
+ .trimFactor = insertSpec->trimFactor};
if (insertSpec->precision.set) {
// If precision is set, pass min/max/precision to mc_getEdgesDouble.
// Do not pass min/max if precision is not set. All three must be set
@@ -890,7 +946,7 @@ static mc_edges_t *get_edges(mc_FLE2RangeInsertSpec_t *insertSpec, size_t sparsi
args.precision = insertSpec->precision;
}
- return mc_getEdgesDouble(args, status);
+ return mc_getEdgesDouble(args, status, use_range_v2);
}
else if (value_type == BSON_TYPE_DECIMAL128) {
@@ -899,6 +955,7 @@ static mc_edges_t *get_edges(mc_FLE2RangeInsertSpec_t *insertSpec, size_t sparsi
mc_getEdgesDecimal128_args_t args = {
.value = value,
.sparsity = sparsity,
+ .trimFactor = insertSpec->trimFactor,
};
if (insertSpec->precision.set) {
const mc_dec128 min = mc_dec128_from_bson_iter(&insertSpec->min);
@@ -907,7 +964,7 @@ static mc_edges_t *get_edges(mc_FLE2RangeInsertSpec_t *insertSpec, size_t sparsi
args.max = OPT_MC_DEC128(max);
args.precision = insertSpec->precision;
}
- return mc_getEdgesDecimal128(args, status);
+ return mc_getEdgesDecimal128(args, status, use_range_v2);
#else // ↑↑↑↑↑↑↑↑ With Decimal128 / Without ↓↓↓↓↓↓↓↓↓↓
CLIENT_ERR("unsupported BSON type (Decimal128) for range: libmongocrypt "
"was built without extended Decimal128 support");
@@ -939,6 +996,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange_v1(
BSON_ASSERT_PARAM(status);
BSON_ASSERT(kb->crypt);
BSON_ASSERT(kb->crypt->opts.use_fle2_v2 == false);
+ BSON_ASSERT(kb->crypt->opts.use_range_v2 == false);
BSON_ASSERT(marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION);
BSON_ASSERT(marking->fle2.algorithm == MONGOCRYPT_FLE2_ALGORITHM_RANGE);
@@ -952,7 +1010,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange_v1(
// Parse the value ("v"), min ("min"), and max ("max") from
// FLE2EncryptionPlaceholder for range insert.
mc_FLE2RangeInsertSpec_t insertSpec;
- if (!mc_FLE2RangeInsertSpec_parse(&insertSpec, &placeholder->v_iter, status)) {
+ if (!mc_FLE2RangeInsertSpec_parse(&insertSpec, &placeholder->v_iter, kb->crypt->opts.use_range_v2, status)) {
goto fail;
}
@@ -970,7 +1028,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange_v1(
// g:= array
{
BSON_ASSERT(placeholder->sparsity >= 0 && (uint64_t)placeholder->sparsity <= (uint64_t)SIZE_MAX);
- edges = get_edges(&insertSpec, (size_t)placeholder->sparsity, status);
+ edges = get_edges(&insertSpec, (size_t)placeholder->sparsity, status, kb->crypt->opts.use_range_v2);
if (!edges) {
goto fail;
}
@@ -993,7 +1051,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange_v1(
&edge_tokens,
&placeholder->index_key_id,
&edge_buf,
- true, /* derive tokens using counter */
+ true, /* derive tokens using contentionFactor */
contentionFactor,
status)) {
goto fail_loop;
@@ -1006,13 +1064,15 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange_v1(
// c := ECCDerivedToken
_mongocrypt_buffer_steal(&etc.eccDerivedToken, &edge_tokens.eccDerivedToken);
- // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndCounter ||
- // ECCDerivedFromDataTokenAndCounter)
+ // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor ||
+ // ECCDerivedFromDataTokenAndContentionFactor)
if (!_fle2_derive_encrypted_token(kb->crypt->crypto,
&etc.encryptedTokens,
+ false, // Range V2 is incompatible with FLE V1
edge_tokens.collectionsLevel1Token,
&etc.escDerivedToken,
&etc.eccDerivedToken,
+ (mc_optional_bool_t){0}, // Dummy value for isLeaf, unused in FLE V1
status)) {
goto fail_loop;
}
@@ -1071,6 +1131,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
BSON_ASSERT_PARAM(ciphertext);
BSON_ASSERT(kb->crypt);
BSON_ASSERT(marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION);
+ const bool use_range_v2 = kb->crypt->opts.use_range_v2;
if (!kb->crypt->opts.use_fle2_v2) {
return _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange_v1(kb, marking, ciphertext, status);
@@ -1086,7 +1147,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
// Parse the value ("v"), min ("min"), and max ("max") from
// FLE2EncryptionPlaceholder for range insert.
mc_FLE2RangeInsertSpec_t insertSpec;
- if (!mc_FLE2RangeInsertSpec_parse(&insertSpec, &placeholder->v_iter, status)) {
+ if (!mc_FLE2RangeInsertSpec_parse(&insertSpec, &placeholder->v_iter, use_range_v2, status)) {
goto fail;
}
@@ -1102,7 +1163,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
// g:= array
{
BSON_ASSERT(placeholder->sparsity >= 0 && (uint64_t)placeholder->sparsity <= (uint64_t)SIZE_MAX);
- edges = get_edges(&insertSpec, (size_t)placeholder->sparsity, status);
+ edges = get_edges(&insertSpec, (size_t)placeholder->sparsity, status, kb->crypt->opts.use_range_v2);
if (!edges) {
goto fail;
}
@@ -1111,6 +1172,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
// Create an EdgeTokenSet from each edge.
bool loop_ok = false;
const char *edge = mc_edges_get(edges, i);
+ bool is_leaf = mc_edges_is_leaf(edges, edge);
_mongocrypt_buffer_t edge_buf = {0};
_FLE2EncryptedPayloadCommon_t edge_tokens = {{0}};
_mongocrypt_buffer_t encryptedTokens = {0};
@@ -1125,7 +1187,7 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
&edge_tokens,
&placeholder->index_key_id,
&edge_buf,
- true, /* derive tokens using counter */
+ true, /* derive tokens using contentionFactor */
payload.contentionFactor,
status)) {
goto fail_loop;
@@ -1140,12 +1202,15 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
// l := serverDerivedFromDataToken
_mongocrypt_buffer_steal(&etc.serverDerivedFromDataToken, &edge_tokens.serverDerivedFromDataToken);
- // p := EncryptCBC(ECOCToken, ESCDerivedFromDataTokenAndCounter)
+ // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor)
+ // Or in Range V2: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || isLeaf)
if (!_fle2_derive_encrypted_token(kb->crypt->crypto,
&etc.encryptedTokens,
+ kb->crypt->opts.use_range_v2,
edge_tokens.collectionsLevel1Token,
&etc.escDerivedToken,
NULL, // ecc unsed in FLE2v2
+ OPT_BOOL(is_leaf),
status)) {
goto fail_loop;
}
@@ -1163,10 +1228,17 @@ static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mo
}
}
+ // Include "range" payload fields introduced in SERVER-91889.
+ payload.sparsity = OPT_I64(placeholder->sparsity);
+ payload.precision = insertSpec.precision;
+ payload.trimFactor = OPT_I32(mc_edges_get_used_trimFactor(edges));
+ bson_value_copy(bson_iter_value(&insertSpec.min), &payload.indexMin);
+ bson_value_copy(bson_iter_value(&insertSpec.max), &payload.indexMax);
+
{
bson_t out;
bson_init(&out);
- mc_FLE2InsertUpdatePayloadV2_serializeForRange(&payload, &out);
+ mc_FLE2InsertUpdatePayloadV2_serializeForRange(&payload, &out, use_range_v2);
_mongocrypt_buffer_steal_from_bson(&ciphertext->data, &out);
}
// Do not set ciphertext->original_bson_type and ciphertext->key_id. They are
@@ -1185,7 +1257,7 @@ fail:
/**
* Payload subtype 5: FLE2FindEqualityPayload
*
- * {d: EDC, s: ESC, c: ECC, e: serverToken, cm: contentionCounter}
+ * {d: EDC, s: ESC, c: ECC, e: serverToken, cm: maxContentionFactor}
*/
static bool _mongocrypt_fle2_placeholder_to_find_ciphertext_v1(_mongocrypt_key_broker_t *kb,
_mongocrypt_marking_t *marking,
@@ -1213,8 +1285,8 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertext_v1(_mongocrypt_key_b
&common,
&placeholder->index_key_id,
&value,
- false, /* derive tokens without counter */
- placeholder->maxContentionCounter,
+ false, /* derive tokens without contentionFactor */
+ placeholder->maxContentionFactor, /* ignored */
status)) {
goto fail;
}
@@ -1230,7 +1302,7 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertext_v1(_mongocrypt_key_b
_mongocrypt_buffer_copy_to(mc_ServerDataEncryptionLevel1Token_get(common.serverDataEncryptionLevel1Token),
&payload.serverEncryptionToken);
- payload.maxContentionCounter = placeholder->maxContentionCounter;
+ payload.maxContentionFactor = placeholder->maxContentionFactor;
{
bson_t out;
@@ -1255,7 +1327,7 @@ fail:
* Payload subtype 12: FLE2FindEqualityPayloadV2
* Delegates to ..._find_ciphertext_v1 when crypt->opts.use_fle2_v2 == false.
*
- * {d: EDC, s: ESC, l: serverDerivedFromDataToken, cm: contentionCounter}
+ * {d: EDC, s: ESC, l: serverDerivedFromDataToken, cm: maxContentionFactor}
*/
static bool _mongocrypt_fle2_placeholder_to_find_ciphertext(_mongocrypt_key_broker_t *kb,
_mongocrypt_marking_t *marking,
@@ -1287,8 +1359,8 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertext(_mongocrypt_key_brok
&common,
&placeholder->index_key_id,
&value,
- false, /* derive tokens without counter */
- placeholder->maxContentionCounter,
+ false, /* derive tokens without contentionFactor */
+ placeholder->maxContentionFactor, /* ignored */
status)) {
goto fail;
}
@@ -1301,8 +1373,8 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertext(_mongocrypt_key_brok
// l := serverDerivedFromDataToken
_mongocrypt_buffer_steal(&payload.serverDerivedFromDataToken, &common.serverDerivedFromDataToken);
- // cm := maxContentionCounter
- payload.maxContentionCounter = placeholder->maxContentionCounter;
+ // cm := maxContentionFactor
+ payload.maxContentionFactor = placeholder->maxContentionFactor;
{
bson_t out;
@@ -1329,8 +1401,10 @@ static bool isInfinite(bson_iter_t *iter) {
// mc_get_mincover_from_FLE2RangeFindSpec creates and returns a mincover from an
// FLE2RangeFindSpec. Returns NULL on error.
-mc_mincover_t *
-mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec, size_t sparsity, mongocrypt_status_t *status) {
+mc_mincover_t *mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec,
+ size_t sparsity,
+ mongocrypt_status_t *status,
+ bool use_range_v2) {
BSON_ASSERT_PARAM(findSpec);
BSON_ASSERT(findSpec->edgesInfo.set);
@@ -1381,6 +1455,7 @@ mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec, size_t
BSON_ASSERT(bson_iter_type(&upperBound) == BSON_TYPE_INT32);
BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMin) == BSON_TYPE_INT32);
BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMax) == BSON_TYPE_INT32);
+
return mc_getMincoverInt32(
(mc_getMincoverInt32_args_t){.lowerBound = bson_iter_int32(&lowerBound),
.includeLowerBound = includeLowerBound,
@@ -1388,8 +1463,10 @@ mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec, size_t
.includeUpperBound = includeUpperBound,
.min = OPT_I32(bson_iter_int32(&findSpec->edgesInfo.value.indexMin)),
.max = OPT_I32(bson_iter_int32(&findSpec->edgesInfo.value.indexMax)),
- .sparsity = sparsity},
- status);
+ .sparsity = sparsity,
+ .trimFactor = findSpec->edgesInfo.value.trimFactor},
+ status,
+ use_range_v2);
case BSON_TYPE_INT64:
BSON_ASSERT(bson_iter_type(&lowerBound) == BSON_TYPE_INT64);
@@ -1403,8 +1480,10 @@ mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec, size_t
.includeUpperBound = includeUpperBound,
.min = OPT_I64(bson_iter_int64(&findSpec->edgesInfo.value.indexMin)),
.max = OPT_I64(bson_iter_int64(&findSpec->edgesInfo.value.indexMax)),
- .sparsity = sparsity},
- status);
+ .sparsity = sparsity,
+ .trimFactor = findSpec->edgesInfo.value.trimFactor},
+ status,
+ use_range_v2);
case BSON_TYPE_DATE_TIME:
BSON_ASSERT(bson_iter_type(&lowerBound) == BSON_TYPE_DATE_TIME);
BSON_ASSERT(bson_iter_type(&upperBound) == BSON_TYPE_DATE_TIME);
@@ -1417,8 +1496,10 @@ mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec, size_t
.includeUpperBound = includeUpperBound,
.min = OPT_I64(bson_iter_date_time(&findSpec->edgesInfo.value.indexMin)),
.max = OPT_I64(bson_iter_date_time(&findSpec->edgesInfo.value.indexMax)),
- .sparsity = sparsity},
- status);
+ .sparsity = sparsity,
+ .trimFactor = findSpec->edgesInfo.value.trimFactor},
+ status,
+ use_range_v2);
case BSON_TYPE_DOUBLE: {
BSON_ASSERT(bson_iter_type(&lowerBound) == BSON_TYPE_DOUBLE);
BSON_ASSERT(bson_iter_type(&upperBound) == BSON_TYPE_DOUBLE);
@@ -1429,7 +1510,8 @@ mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec, size_t
.includeLowerBound = includeLowerBound,
.upperBound = bson_iter_double(&upperBound),
.includeUpperBound = includeUpperBound,
- .sparsity = sparsity};
+ .sparsity = sparsity,
+ .trimFactor = findSpec->edgesInfo.value.trimFactor};
if (findSpec->edgesInfo.value.precision.set) {
// If precision is set, pass min/max/precision to mc_getMincoverDouble.
// Do not pass min/max if precision is not set. All three must be set
@@ -1438,7 +1520,7 @@ mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec, size_t
args.max = OPT_DOUBLE(bson_iter_double(&findSpec->edgesInfo.value.indexMax));
args.precision = findSpec->edgesInfo.value.precision;
}
- return mc_getMincoverDouble(args, status);
+ return mc_getMincoverDouble(args, status, use_range_v2);
}
case BSON_TYPE_DECIMAL128: {
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
@@ -1447,19 +1529,18 @@ mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec, size_t
BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMin) == BSON_TYPE_DECIMAL128);
BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMax) == BSON_TYPE_DECIMAL128);
- mc_getMincoverDecimal128_args_t args = {
- .lowerBound = mc_dec128_from_bson_iter(&lowerBound),
- .includeLowerBound = includeLowerBound,
- .upperBound = mc_dec128_from_bson_iter(&upperBound),
- .includeUpperBound = includeUpperBound,
- .sparsity = sparsity,
- };
+ mc_getMincoverDecimal128_args_t args = {.lowerBound = mc_dec128_from_bson_iter(&lowerBound),
+ .includeLowerBound = includeLowerBound,
+ .upperBound = mc_dec128_from_bson_iter(&upperBound),
+ .includeUpperBound = includeUpperBound,
+ .sparsity = sparsity,
+ .trimFactor = findSpec->edgesInfo.value.trimFactor};
if (findSpec->edgesInfo.value.precision.set) {
args.min = OPT_MC_DEC128(mc_dec128_from_bson_iter(&findSpec->edgesInfo.value.indexMin));
args.max = OPT_MC_DEC128(mc_dec128_from_bson_iter(&findSpec->edgesInfo.value.indexMax));
args.precision = findSpec->edgesInfo.value.precision;
}
- return mc_getMincoverDecimal128(args, status);
+ return mc_getMincoverDecimal128(args, status, use_range_v2);
#else // ↑↑↑↑↑↑↑↑ With Decimal128 / Without ↓↓↓↓↓↓↓↓↓↓
CLIENT_ERR("FLE2 find is not supported for Decimal128: libmongocrypt "
"was built without Decimal128 support");
@@ -1491,7 +1572,7 @@ mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec, size_t
/**
* Payload subtype 10: FLE2FindRangePayload
*
- * {e: serverToken, cm: contentionCounter,
+ * {e: serverToken, cm: maxContentionFactor,
* g: [{d: EDC, s: ESC, c: ECC}, ...]}
*/
static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange_v1(_mongocrypt_key_broker_t *kb,
@@ -1503,6 +1584,7 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange_v1(_mongocry
BSON_ASSERT_PARAM(ciphertext);
BSON_ASSERT(kb->crypt);
+ const bool use_range_v2 = kb->crypt->opts.use_range_v2;
_mongocrypt_crypto_t *crypto = kb->crypt->crypto;
mc_FLE2EncryptionPlaceholder_t *placeholder = &marking->fle2;
mc_FLE2FindRangePayload_t payload;
@@ -1520,13 +1602,13 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange_v1(_mongocry
// Parse the query bounds and index bounds from FLE2EncryptionPlaceholder for
// range find.
mc_FLE2RangeFindSpec_t findSpec;
- if (!mc_FLE2RangeFindSpec_parse(&findSpec, &placeholder->v_iter, status)) {
+ if (!mc_FLE2RangeFindSpec_parse(&findSpec, &placeholder->v_iter, use_range_v2, status)) {
goto fail;
}
if (findSpec.edgesInfo.set) {
- // cm := Queryable Encryption max counter
- payload.payload.value.maxContentionCounter = placeholder->maxContentionCounter;
+ // cm := Queryable Encryption max contentionFactor
+ payload.payload.value.maxContentionFactor = placeholder->maxContentionFactor;
// e := ServerDataEncryptionLevel1Token
{
@@ -1547,7 +1629,8 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange_v1(_mongocry
// g:= array
{
BSON_ASSERT(placeholder->sparsity >= 0 && (uint64_t)placeholder->sparsity <= (uint64_t)SIZE_MAX);
- mincover = mc_get_mincover_from_FLE2RangeFindSpec(&findSpec, (size_t)placeholder->sparsity, status);
+ mincover =
+ mc_get_mincover_from_FLE2RangeFindSpec(&findSpec, (size_t)placeholder->sparsity, status, use_range_v2);
if (!mincover) {
goto fail;
}
@@ -1569,8 +1652,8 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange_v1(_mongocry
&edge_tokens,
&placeholder->index_key_id,
&edge_buf,
- false, /* derive tokens using counter */
- placeholder->maxContentionCounter,
+ false, /* derive tokens using contentionFactor */
+ placeholder->maxContentionFactor, /* ignored */
status)) {
goto fail_loop;
}
@@ -1626,7 +1709,7 @@ fail:
* Delegates to ..._find_ciphertextForRange_v1
* when crypt->opts.use_fle2_v2 is false
*
- * {cm: contentionCounter,
+ * {cm: maxContentionFactor,
* g: [{d: EDC, s: ESC, l: serverDerivedFromDataToken}, ...]}
*/
static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange(_mongocrypt_key_broker_t *kb,
@@ -1641,6 +1724,7 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange(_mongocrypt_
return _mongocrypt_fle2_placeholder_to_find_ciphertextForRange_v1(kb, marking, ciphertext, status);
}
+ const bool use_range_v2 = kb->crypt->opts.use_range_v2;
mc_FLE2EncryptionPlaceholder_t *placeholder = &marking->fle2;
mc_FLE2FindRangePayloadV2_t payload;
bool res = false;
@@ -1656,18 +1740,19 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange(_mongocrypt_
// Parse the query bounds and index bounds from FLE2EncryptionPlaceholder for
// range find.
mc_FLE2RangeFindSpec_t findSpec;
- if (!mc_FLE2RangeFindSpec_parse(&findSpec, &placeholder->v_iter, status)) {
+ if (!mc_FLE2RangeFindSpec_parse(&findSpec, &placeholder->v_iter, use_range_v2, status)) {
goto fail;
}
if (findSpec.edgesInfo.set) {
- // cm := Queryable Encryption max counter
- payload.payload.value.maxContentionCounter = placeholder->maxContentionCounter;
+ // cm := Queryable Encryption max contentionFactor
+ payload.payload.value.maxContentionFactor = placeholder->maxContentionFactor;
// g:= array
{
BSON_ASSERT(placeholder->sparsity >= 0 && (uint64_t)placeholder->sparsity <= (uint64_t)SIZE_MAX);
- mincover = mc_get_mincover_from_FLE2RangeFindSpec(&findSpec, (size_t)placeholder->sparsity, status);
+ mincover =
+ mc_get_mincover_from_FLE2RangeFindSpec(&findSpec, (size_t)placeholder->sparsity, status, use_range_v2);
if (!mincover) {
goto fail;
}
@@ -1689,8 +1774,8 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange(_mongocrypt_
&edge_tokens,
&placeholder->index_key_id,
&edge_buf,
- false, /* derive tokens using counter */
- placeholder->maxContentionCounter,
+ false, /* derive tokens without using contentionFactor */
+ placeholder->maxContentionFactor, /* ignored */
status)) {
goto fail_loop;
}
@@ -1715,6 +1800,15 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange(_mongocrypt_
}
}
payload.payload.set = true;
+
+ if (use_range_v2) {
+ // Include "range" payload fields introduced in SERVER-91889.
+ payload.sparsity = OPT_I64(placeholder->sparsity);
+ payload.precision = findSpec.edgesInfo.value.precision;
+ payload.trimFactor = OPT_I32(mc_mincover_get_used_trimFactor(mincover));
+ bson_value_copy(bson_iter_value(&findSpec.edgesInfo.value.indexMin), &payload.indexMin);
+ bson_value_copy(bson_iter_value(&findSpec.edgesInfo.value.indexMax), &payload.indexMax);
+ }
}
payload.payloadId = findSpec.payloadId;
@@ -1724,7 +1818,7 @@ static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange(_mongocrypt_
// Serialize.
{
bson_t out = BSON_INITIALIZER;
- mc_FLE2FindRangePayloadV2_serialize(&payload, &out);
+ mc_FLE2FindRangePayloadV2_serialize(&payload, &out, use_range_v2);
_mongocrypt_buffer_steal_from_bson(&ciphertext->data, &out);
}
_mongocrypt_buffer_steal(&ciphertext->key_id, &placeholder->index_key_id);
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-opts-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-opts-private.h
index 92061bd22c9..4c7b77d0e87 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-opts-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-opts-private.h
@@ -26,6 +26,7 @@
#include "mongocrypt-kek-private.h"
#include "mongocrypt-log-private.h"
#include "mongocrypt.h"
+#include
typedef struct {
char *tenant_id;
@@ -56,16 +57,44 @@ typedef struct {
_mongocrypt_endpoint_t *endpoint;
} _mongocrypt_opts_kms_provider_kmip_t;
+typedef struct {
+ // `type` identifies the set field in `value`.
+ _mongocrypt_kms_provider_t type;
+
+ union {
+ _mongocrypt_opts_kms_provider_local_t local;
+ _mongocrypt_opts_kms_provider_aws_t aws;
+ _mongocrypt_opts_kms_provider_azure_t azure;
+ _mongocrypt_opts_kms_provider_gcp_t gcp;
+ _mongocrypt_opts_kms_provider_kmip_t kmip;
+ } value;
+} mc_kms_creds_t;
+
typedef struct {
int configured_providers; /* A bit set of _mongocrypt_kms_provider_t */
int need_credentials; /* A bit set of _mongocrypt_kms_provider_t */
- _mongocrypt_opts_kms_provider_local_t local;
- _mongocrypt_opts_kms_provider_aws_t aws;
- _mongocrypt_opts_kms_provider_azure_t azure;
- _mongocrypt_opts_kms_provider_gcp_t gcp;
- _mongocrypt_opts_kms_provider_kmip_t kmip;
+ // Fields suffixed with `_mut` are mutated when constructing the `_mongocrypt_opts_kms_providers_t`.
+ // Prefer using `_mongocrypt_opts_kms_providers_lookup` to read the values.
+ _mongocrypt_opts_kms_provider_local_t local_mut;
+ _mongocrypt_opts_kms_provider_aws_t aws_mut;
+ _mongocrypt_opts_kms_provider_azure_t azure_mut;
+ _mongocrypt_opts_kms_provider_gcp_t gcp_mut;
+ _mongocrypt_opts_kms_provider_kmip_t kmip_mut;
+ // `named_mut` stores a list of named KMS providers.
+ mc_array_t named_mut;
} _mongocrypt_opts_kms_providers_t;
+void _mongocrypt_opts_kms_providers_init(_mongocrypt_opts_kms_providers_t *kms_providers);
+
+bool _mongocrypt_parse_kms_providers(mongocrypt_binary_t *kms_providers_definition,
+ _mongocrypt_opts_kms_providers_t *kms_providers,
+ mongocrypt_status_t *status,
+ _mongocrypt_log_t *log);
+
+bool _mongocrypt_opts_kms_providers_lookup(const _mongocrypt_opts_kms_providers_t *kms_providers,
+ const char *kmsid,
+ mc_kms_creds_t *out);
+
typedef struct {
mongocrypt_log_fn_t log_fn;
void *log_ctx;
@@ -86,11 +115,15 @@ typedef struct {
mstr crypt_shared_lib_override_path;
bool use_need_kms_credentials_state;
+ bool use_need_mongo_collinfo_with_db_state;
bool bypass_query_analysis;
// When creating new encrypted payloads,
// use V2 variants of the FLE2 datatypes.
bool use_fle2_v2;
+
+ // Use the Queryable Encryption Range V2 protocol.
+ bool use_range_v2;
} _mongocrypt_opts_t;
void _mongocrypt_opts_kms_providers_cleanup(_mongocrypt_opts_kms_providers_t *kms_providers);
@@ -120,6 +153,14 @@ bool _mongocrypt_opts_kms_providers_validate(_mongocrypt_opts_t *opts,
*/
bool _mongocrypt_parse_optional_utf8(const bson_t *bson, const char *dotkey, char **out, mongocrypt_status_t *status);
+/*
+ * Parse an optional boolean value from BSON.
+ * @dotkey may be a dot separated key like: "a.b.c".
+ * @*out is set to a copy of the value if found, false otherwise.
+ * Returns true if no error occured.
+ */
+bool _mongocrypt_parse_optional_bool(const bson_t *bson, const char *dotkey, bool *out, mongocrypt_status_t *status);
+
/*
* Parse a required UTF-8 value from BSON.
* @dotkey may be a dot separated key like: "a.b.c".
@@ -200,4 +241,8 @@ bool _mongocrypt_check_allowed_fields_va(const bson_t *bson, const char *dotkey,
#define _mongocrypt_check_allowed_fields(bson, path, status, ...) \
_mongocrypt_check_allowed_fields_va(bson, path, status, __VA_ARGS__, NULL)
+bool mc_kmsid_parse(const char *kmsid,
+ _mongocrypt_kms_provider_t *type_out,
+ const char **name_out,
+ mongocrypt_status_t *status);
#endif /* MONGOCRYPT_OPTS_PRIVATE_H */
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-opts.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-opts.c
index a0288110780..7f823622476 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-opts.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-opts.c
@@ -19,12 +19,24 @@
#include "mongocrypt-log-private.h"
#include "mongocrypt-opts-private.h"
#include "mongocrypt-private.h"
+#include // mc_iter_document_as_bson
#include
+typedef struct {
+ mc_kms_creds_t creds;
+ char *kmsid;
+} mc_kms_creds_with_id_t;
+
+void _mongocrypt_opts_kms_providers_init(_mongocrypt_opts_kms_providers_t *kms_providers) {
+ _mc_array_init(&kms_providers->named_mut, sizeof(mc_kms_creds_with_id_t));
+}
+
void _mongocrypt_opts_init(_mongocrypt_opts_t *opts) {
BSON_ASSERT_PARAM(opts);
memset(opts, 0, sizeof(*opts));
+ opts->use_range_v2 = true;
+ _mongocrypt_opts_kms_providers_init(&opts->kms_providers);
}
static void _mongocrypt_opts_kms_provider_azure_cleanup(_mongocrypt_opts_kms_provider_azure_t *kms_provider_azure) {
@@ -48,17 +60,58 @@ static void _mongocrypt_opts_kms_provider_gcp_cleanup(_mongocrypt_opts_kms_provi
bson_free(kms_provider_gcp->access_token);
}
+static void _mongocrypt_opts_kms_provider_local_cleanup(_mongocrypt_opts_kms_provider_local_t *kms_provider_local) {
+ _mongocrypt_buffer_cleanup(&kms_provider_local->key);
+}
+
+static void _mongocrypt_opts_kms_provider_aws_cleanup(_mongocrypt_opts_kms_provider_aws_t *kms_provider_aws) {
+ bson_free(kms_provider_aws->secret_access_key);
+ bson_free(kms_provider_aws->access_key_id);
+ bson_free(kms_provider_aws->session_token);
+}
+
+static void _mongocrypt_opts_kms_provider_kmip_cleanup(_mongocrypt_opts_kms_provider_kmip_t *kms_provider_kmip) {
+ _mongocrypt_endpoint_destroy(kms_provider_kmip->endpoint);
+}
+
void _mongocrypt_opts_kms_providers_cleanup(_mongocrypt_opts_kms_providers_t *kms_providers) {
if (!kms_providers) {
return;
}
- bson_free(kms_providers->aws.secret_access_key);
- bson_free(kms_providers->aws.access_key_id);
- bson_free(kms_providers->aws.session_token);
- _mongocrypt_buffer_cleanup(&kms_providers->local.key);
- _mongocrypt_opts_kms_provider_azure_cleanup(&kms_providers->azure);
- _mongocrypt_opts_kms_provider_gcp_cleanup(&kms_providers->gcp);
- _mongocrypt_endpoint_destroy(kms_providers->kmip.endpoint);
+ _mongocrypt_opts_kms_provider_aws_cleanup(&kms_providers->aws_mut);
+ _mongocrypt_opts_kms_provider_local_cleanup(&kms_providers->local_mut);
+ _mongocrypt_opts_kms_provider_azure_cleanup(&kms_providers->azure_mut);
+ _mongocrypt_opts_kms_provider_gcp_cleanup(&kms_providers->gcp_mut);
+ _mongocrypt_opts_kms_provider_kmip_cleanup(&kms_providers->kmip_mut);
+ for (size_t i = 0; i < kms_providers->named_mut.len; i++) {
+ mc_kms_creds_with_id_t kcwid = _mc_array_index(&kms_providers->named_mut, mc_kms_creds_with_id_t, i);
+ switch (kcwid.creds.type) {
+ default:
+ case MONGOCRYPT_KMS_PROVIDER_NONE: break;
+ case MONGOCRYPT_KMS_PROVIDER_AWS: {
+ _mongocrypt_opts_kms_provider_aws_cleanup(&kcwid.creds.value.aws);
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_LOCAL: {
+ _mongocrypt_opts_kms_provider_local_cleanup(&kcwid.creds.value.local);
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_AZURE: {
+ _mongocrypt_opts_kms_provider_azure_cleanup(&kcwid.creds.value.azure);
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_GCP: {
+ _mongocrypt_opts_kms_provider_gcp_cleanup(&kcwid.creds.value.gcp);
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_KMIP: {
+ _mongocrypt_endpoint_destroy(kcwid.creds.value.kmip.endpoint);
+ break;
+ }
+ }
+ bson_free(kcwid.kmsid);
+ }
+ _mc_array_destroy(&kms_providers->named_mut);
}
void _mongocrypt_opts_merge_kms_providers(_mongocrypt_opts_kms_providers_t *dest,
@@ -67,23 +120,23 @@ void _mongocrypt_opts_merge_kms_providers(_mongocrypt_opts_kms_providers_t *dest
BSON_ASSERT_PARAM(source);
if (source->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS) {
- memcpy(&dest->aws, &source->aws, sizeof(source->aws));
+ memcpy(&dest->aws_mut, &source->aws_mut, sizeof(source->aws_mut));
dest->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AWS;
}
if (source->configured_providers & MONGOCRYPT_KMS_PROVIDER_LOCAL) {
- memcpy(&dest->local, &source->local, sizeof(source->local));
+ memcpy(&dest->local_mut, &source->local_mut, sizeof(source->local_mut));
dest->configured_providers |= MONGOCRYPT_KMS_PROVIDER_LOCAL;
}
if (source->configured_providers & MONGOCRYPT_KMS_PROVIDER_AZURE) {
- memcpy(&dest->azure, &source->azure, sizeof(source->azure));
+ memcpy(&dest->azure_mut, &source->azure_mut, sizeof(source->azure_mut));
dest->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AZURE;
}
if (source->configured_providers & MONGOCRYPT_KMS_PROVIDER_GCP) {
- memcpy(&dest->gcp, &source->gcp, sizeof(source->gcp));
+ memcpy(&dest->gcp_mut, &source->gcp_mut, sizeof(source->gcp_mut));
dest->configured_providers |= MONGOCRYPT_KMS_PROVIDER_GCP;
}
if (source->configured_providers & MONGOCRYPT_KMS_PROVIDER_KMIP) {
- memcpy(&dest->kmip, &source->kmip, sizeof(source->kmip));
+ memcpy(&dest->kmip_mut, &source->kmip_mut, sizeof(source->kmip_mut));
dest->configured_providers |= MONGOCRYPT_KMS_PROVIDER_KMIP;
}
/* ensure all providers were copied */
@@ -111,20 +164,20 @@ bool _mongocrypt_opts_kms_providers_validate(_mongocrypt_opts_t *opts,
BSON_ASSERT_PARAM(opts);
BSON_ASSERT_PARAM(kms_providers);
- if (!kms_providers->configured_providers && !kms_providers->need_credentials) {
+ if (!kms_providers->configured_providers && !kms_providers->need_credentials && kms_providers->named_mut.len == 0) {
CLIENT_ERR("no kms provider set");
return false;
}
if (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS) {
- if (!kms_providers->aws.access_key_id || !kms_providers->aws.secret_access_key) {
+ if (!kms_providers->aws_mut.access_key_id || !kms_providers->aws_mut.secret_access_key) {
CLIENT_ERR("aws credentials unset");
return false;
}
}
if (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_LOCAL) {
- if (_mongocrypt_buffer_empty(&kms_providers->local.key)) {
+ if (_mongocrypt_buffer_empty(&kms_providers->local_mut.key)) {
CLIENT_ERR("local data key unset");
return false;
}
@@ -223,6 +276,51 @@ bool _mongocrypt_opts_validate(_mongocrypt_opts_t *opts, mongocrypt_status_t *st
return _mongocrypt_opts_kms_providers_validate(opts, &opts->kms_providers, status);
}
+bool _mongocrypt_opts_kms_providers_lookup(const _mongocrypt_opts_kms_providers_t *kms_providers,
+ const char *kmsid,
+ mc_kms_creds_t *out) {
+ *out = (mc_kms_creds_t){0};
+ if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS) && 0 == strcmp(kmsid, "aws")) {
+ out->type = MONGOCRYPT_KMS_PROVIDER_AWS;
+ out->value.aws = kms_providers->aws_mut;
+ return true;
+ }
+ if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AZURE) && 0 == strcmp(kmsid, "azure")) {
+ out->type = MONGOCRYPT_KMS_PROVIDER_AZURE;
+ out->value.azure = kms_providers->azure_mut;
+ return true;
+ }
+
+ if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_GCP) && 0 == strcmp(kmsid, "gcp")) {
+ out->type = MONGOCRYPT_KMS_PROVIDER_GCP;
+ out->value.gcp = kms_providers->gcp_mut;
+ return true;
+ }
+
+ if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_LOCAL) && 0 == strcmp(kmsid, "local")) {
+ out->type = MONGOCRYPT_KMS_PROVIDER_LOCAL;
+ out->value.local = kms_providers->local_mut;
+ return true;
+ }
+
+ if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_KMIP) && 0 == strcmp(kmsid, "kmip")) {
+ out->type = MONGOCRYPT_KMS_PROVIDER_KMIP;
+ out->value.kmip = kms_providers->kmip_mut;
+ return true;
+ }
+
+ // Check for KMS providers with a name.
+ for (size_t i = 0; i < kms_providers->named_mut.len; i++) {
+ mc_kms_creds_with_id_t kcwi = _mc_array_index(&kms_providers->named_mut, mc_kms_creds_with_id_t, i);
+ if (0 == strcmp(kmsid, kcwi.kmsid)) {
+ *out = kcwi.creds;
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool _mongocrypt_parse_optional_utf8(const bson_t *bson, const char *dotkey, char **out, mongocrypt_status_t *status) {
bson_iter_t iter;
bson_iter_t child;
@@ -250,6 +348,33 @@ bool _mongocrypt_parse_optional_utf8(const bson_t *bson, const char *dotkey, cha
return true;
}
+bool _mongocrypt_parse_optional_bool(const bson_t *bson, const char *dotkey, bool *out, mongocrypt_status_t *status) {
+ bson_iter_t iter;
+ bson_iter_t child;
+
+ BSON_ASSERT_PARAM(bson);
+ BSON_ASSERT_PARAM(dotkey);
+ BSON_ASSERT_PARAM(out);
+
+ *out = false;
+
+ if (!bson_iter_init(&iter, bson)) {
+ CLIENT_ERR("invalid BSON");
+ return false;
+ }
+ if (!bson_iter_find_descendant(&iter, dotkey, &child)) {
+ /* Not found. Not an error. */
+ return true;
+ }
+ if (!BSON_ITER_HOLDS_BOOL(&child)) {
+ CLIENT_ERR("expected bool %s", dotkey);
+ return false;
+ }
+
+ *out = bson_iter_bool(&child);
+ return true;
+}
+
bool _mongocrypt_parse_required_utf8(const bson_t *bson, const char *dotkey, char **out, mongocrypt_status_t *status) {
BSON_ASSERT_PARAM(bson);
BSON_ASSERT_PARAM(dotkey);
@@ -421,3 +546,454 @@ bool _mongocrypt_check_allowed_fields_va(const bson_t *bson, const char *dotkey,
}
return true;
}
+
+#define KEY_HELP "Expected `` or `:`. Example: `local` or `local:name`."
+
+bool mc_kmsid_parse(const char *kmsid,
+ _mongocrypt_kms_provider_t *type_out,
+ const char **name_out,
+ mongocrypt_status_t *status) {
+ BSON_ASSERT_PARAM(kmsid);
+ BSON_ASSERT_PARAM(type_out);
+ BSON_ASSERT_PARAM(name_out);
+ BSON_ASSERT(status || true); // Optional.
+
+ *type_out = MONGOCRYPT_KMS_PROVIDER_NONE;
+ *name_out = NULL;
+
+ const char *type_end = strstr(kmsid, ":");
+ size_t type_nchars;
+
+ if (type_end == NULL) {
+ // Parse `kmsid` as ``.
+ type_nchars = strlen(kmsid);
+ } else {
+ // Parse `kmsid` as `:`.
+ ptrdiff_t diff = type_end - kmsid;
+ BSON_ASSERT(diff >= 0 && (uint64_t)diff < SIZE_MAX);
+ type_nchars = (size_t)diff;
+ }
+
+ if (0 == strncmp("aws", kmsid, type_nchars)) {
+ *type_out = MONGOCRYPT_KMS_PROVIDER_AWS;
+ } else if (0 == strncmp("azure", kmsid, type_nchars)) {
+ *type_out = MONGOCRYPT_KMS_PROVIDER_AZURE;
+ } else if (0 == strncmp("gcp", kmsid, type_nchars)) {
+ *type_out = MONGOCRYPT_KMS_PROVIDER_GCP;
+ } else if (0 == strncmp("kmip", kmsid, type_nchars)) {
+ *type_out = MONGOCRYPT_KMS_PROVIDER_KMIP;
+ } else if (0 == strncmp("local", kmsid, type_nchars)) {
+ *type_out = MONGOCRYPT_KMS_PROVIDER_LOCAL;
+ } else {
+ CLIENT_ERR("unrecognized KMS provider `%s`: unrecognized type. " KEY_HELP, kmsid);
+ return false;
+ }
+
+ if (type_end != NULL) {
+ // Parse name.
+ *name_out = type_end + 1;
+ if (0 == strlen(*name_out)) {
+ CLIENT_ERR("unrecognized KMS provider `%s`: empty name. " KEY_HELP, kmsid);
+ return false;
+ }
+
+ // Validate name only contains: [a-zA-Z0-9_]
+ for (const char *cp = *name_out; *cp != '\0'; cp++) {
+ char c = *cp;
+ if (c >= 'a' && c <= 'z') {
+ continue;
+ }
+ if (c >= 'A' && c <= 'Z') {
+ continue;
+ }
+ if (c >= '0' && c <= '9') {
+ continue;
+ }
+ if (c == '_') {
+ continue;
+ }
+ CLIENT_ERR("unrecognized KMS provider `%s`: unsupported character `%c`. Must be of the form `:` where `` only contain characters [a-zA-Z0-9_]",
+ kmsid,
+ c);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool _mongocrypt_opts_kms_provider_local_parse(_mongocrypt_opts_kms_provider_local_t *local,
+ const char *kmsid,
+ const bson_t *def,
+ mongocrypt_status_t *status) {
+ bool ok = false;
+ if (!_mongocrypt_parse_required_binary(def, "key", &local->key, status)) {
+ goto fail;
+ }
+
+ if (local->key.len != MONGOCRYPT_KEY_LEN) {
+ CLIENT_ERR("local key must be %d bytes", MONGOCRYPT_KEY_LEN);
+ goto fail;
+ }
+
+ if (!_mongocrypt_check_allowed_fields(def, NULL /* root */, status, "key")) {
+ goto fail;
+ }
+ ok = true;
+fail:
+ if (!ok) {
+ // Wrap error to identify the failing `kmsid`.
+ CLIENT_ERR("Failed to parse KMS provider `%s`: %s", kmsid, mongocrypt_status_message(status, NULL /* len */));
+ }
+ return ok;
+}
+
+static bool _mongocrypt_opts_kms_provider_azure_parse(_mongocrypt_opts_kms_provider_azure_t *azure,
+ const char *kmsid,
+ const bson_t *def,
+ mongocrypt_status_t *status) {
+ bool ok = false;
+ if (!_mongocrypt_parse_optional_utf8(def, "accessToken", &azure->access_token, status)) {
+ goto done;
+ }
+
+ if (azure->access_token) {
+ // Caller provides an accessToken directly
+ if (!_mongocrypt_check_allowed_fields(def, NULL /* root */, status, "accessToken")) {
+ goto done;
+ }
+ ok = true;
+ goto done;
+ }
+
+ // No accessToken given, so we'll need to look one up on our own later
+ // using the Azure API
+
+ if (!_mongocrypt_parse_required_utf8(def, "tenantId", &azure->tenant_id, status)) {
+ goto done;
+ }
+
+ if (!_mongocrypt_parse_required_utf8(def, "clientId", &azure->client_id, status)) {
+ goto done;
+ }
+
+ if (!_mongocrypt_parse_required_utf8(def, "clientSecret", &azure->client_secret, status)) {
+ goto done;
+ }
+
+ if (!_mongocrypt_parse_optional_endpoint(def,
+ "identityPlatformEndpoint",
+ &azure->identity_platform_endpoint,
+ NULL /* opts */,
+ status)) {
+ goto done;
+ }
+
+ if (!_mongocrypt_check_allowed_fields(def,
+ NULL /* root */,
+ status,
+ "tenantId",
+ "clientId",
+ "clientSecret",
+ "identityPlatformEndpoint")) {
+ goto done;
+ }
+
+ ok = true;
+done:
+ if (!ok) {
+ // Wrap error to identify the failing `kmsid`.
+ CLIENT_ERR("Failed to parse KMS provider `%s`: %s", kmsid, mongocrypt_status_message(status, NULL /* len */));
+ }
+ return ok;
+}
+
+static bool _mongocrypt_opts_kms_provider_gcp_parse(_mongocrypt_opts_kms_provider_gcp_t *gcp,
+ const char *kmsid,
+ const bson_t *def,
+ mongocrypt_status_t *status) {
+ bool ok = false;
+ if (!_mongocrypt_parse_optional_utf8(def, "accessToken", &gcp->access_token, status)) {
+ goto done;
+ }
+
+ if (gcp->access_token) {
+ // Caller provides an accessToken directly
+ if (!_mongocrypt_check_allowed_fields(def, NULL /* root */, status, "accessToken")) {
+ goto done;
+ }
+ ok = true;
+ goto done;
+ }
+
+ // No accessToken given, so we'll need to look one up on our own later
+ // using the GCP API
+
+ if (!_mongocrypt_parse_required_utf8(def, "email", &gcp->email, status)) {
+ goto done;
+ }
+
+ if (!_mongocrypt_parse_required_binary(def, "privateKey", &gcp->private_key, status)) {
+ goto done;
+ }
+
+ if (!_mongocrypt_parse_optional_endpoint(def, "endpoint", &gcp->endpoint, NULL /* opts */, status)) {
+ goto done;
+ }
+
+ if (!_mongocrypt_check_allowed_fields(def, NULL /* root */, status, "email", "privateKey", "endpoint")) {
+ goto done;
+ }
+
+ ok = true;
+done:
+ if (!ok) {
+ // Wrap error to identify the failing `kmsid`.
+ CLIENT_ERR("Failed to parse KMS provider `%s`: %s", kmsid, mongocrypt_status_message(status, NULL /* len */));
+ }
+ return ok;
+}
+
+static bool _mongocrypt_opts_kms_provider_aws_parse(_mongocrypt_opts_kms_provider_aws_t *aws,
+ const char *kmsid,
+ const bson_t *def,
+ mongocrypt_status_t *status) {
+ bool ok = false;
+
+ if (!_mongocrypt_parse_required_utf8(def, "accessKeyId", &aws->access_key_id, status)) {
+ goto done;
+ }
+ if (!_mongocrypt_parse_required_utf8(def, "secretAccessKey", &aws->secret_access_key, status)) {
+ goto done;
+ }
+
+ if (!_mongocrypt_parse_optional_utf8(def, "sessionToken", &aws->session_token, status)) {
+ goto done;
+ }
+
+ if (!_mongocrypt_check_allowed_fields(def,
+ NULL /* root */,
+ status,
+ "accessKeyId",
+ "secretAccessKey",
+ "sessionToken")) {
+ goto done;
+ }
+
+ ok = true;
+done:
+ if (!ok) {
+ // Wrap error to identify the failing `kmsid`.
+ CLIENT_ERR("Failed to parse KMS provider `%s`: %s", kmsid, mongocrypt_status_message(status, NULL /* len */));
+ }
+ return ok;
+}
+
+static bool _mongocrypt_opts_kms_provider_kmip_parse(_mongocrypt_opts_kms_provider_kmip_t *kmip,
+ const char *kmsid,
+ const bson_t *def,
+ mongocrypt_status_t *status) {
+ bool ok = false;
+
+ _mongocrypt_endpoint_parse_opts_t opts = {0};
+
+ opts.allow_empty_subdomain = true;
+ if (!_mongocrypt_parse_required_endpoint(def, "endpoint", &kmip->endpoint, &opts, status)) {
+ goto done;
+ }
+
+ if (!_mongocrypt_check_allowed_fields(def, NULL /* root */, status, "endpoint")) {
+ goto done;
+ }
+
+ ok = true;
+done:
+ if (!ok) {
+ // Wrap error to identify the failing `kmsid`.
+ CLIENT_ERR("Failed to parse KMS provider `%s`: %s", kmsid, mongocrypt_status_message(status, NULL /* len */));
+ }
+ return ok;
+}
+
+bool _mongocrypt_parse_kms_providers(mongocrypt_binary_t *kms_providers_definition,
+ _mongocrypt_opts_kms_providers_t *kms_providers,
+ mongocrypt_status_t *status,
+ _mongocrypt_log_t *log) {
+ bson_t as_bson;
+ bson_iter_t iter;
+
+ BSON_ASSERT_PARAM(kms_providers_definition);
+ BSON_ASSERT_PARAM(kms_providers);
+ if (!_mongocrypt_binary_to_bson(kms_providers_definition, &as_bson) || !bson_iter_init(&iter, &as_bson)) {
+ CLIENT_ERR("invalid BSON");
+ return false;
+ }
+
+ while (bson_iter_next(&iter)) {
+ const char *field_name;
+ bson_t field_bson;
+
+ field_name = bson_iter_key(&iter);
+ if (!mc_iter_document_as_bson(&iter, &field_bson, status)) {
+ return false;
+ }
+
+ const char *name;
+ _mongocrypt_kms_provider_t type;
+ if (!mc_kmsid_parse(field_name, &type, &name, status)) {
+ return false;
+ }
+
+ if (name != NULL) {
+ // Check if named provider already is configured.
+ for (size_t i = 0; i < kms_providers->named_mut.len; i++) {
+ mc_kms_creds_with_id_t kcwi = _mc_array_index(&kms_providers->named_mut, mc_kms_creds_with_id_t, i);
+ if (0 == strcmp(kcwi.kmsid, field_name)) {
+ CLIENT_ERR("Got unexpected duplicate entry for KMS provider: `%s`", field_name);
+ return false;
+ }
+ }
+ // Prohibit configuring with an empty document. Named KMS providers do not support on-demand credentials.
+ if (bson_empty(&field_bson)) {
+ CLIENT_ERR("Unexpected empty document for named KMS provider: '%s'. On-demand credentials are not "
+ "supported for named KMS providers.",
+ field_name);
+ return false;
+ }
+ switch (type) {
+ default:
+ case MONGOCRYPT_KMS_PROVIDER_NONE: {
+ CLIENT_ERR("Unexpected parsing KMS type: none");
+ return false;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_AWS: {
+ _mongocrypt_opts_kms_provider_aws_t aws = {0};
+ if (!_mongocrypt_opts_kms_provider_aws_parse(&aws, field_name, &field_bson, status)) {
+ _mongocrypt_opts_kms_provider_aws_cleanup(&aws);
+ return false;
+ }
+ mc_kms_creds_with_id_t kcwi = {.kmsid = bson_strdup(field_name),
+ .creds = {.type = type, .value = {.aws = aws}}};
+ _mc_array_append_val(&kms_providers->named_mut, kcwi);
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_LOCAL: {
+ _mongocrypt_opts_kms_provider_local_t local = {
+ // specify .key to avoid erroneous missing-braces warning in GCC. Refer:
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119
+ .key = {0}};
+ if (!_mongocrypt_opts_kms_provider_local_parse(&local, field_name, &field_bson, status)) {
+ _mongocrypt_opts_kms_provider_local_cleanup(&local);
+ return false;
+ }
+ mc_kms_creds_with_id_t kcwi = {.kmsid = bson_strdup(field_name),
+ .creds = {.type = type, .value = {.local = local}}};
+ _mc_array_append_val(&kms_providers->named_mut, kcwi);
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_AZURE: {
+ _mongocrypt_opts_kms_provider_azure_t azure = {0};
+ if (!_mongocrypt_opts_kms_provider_azure_parse(&azure, field_name, &field_bson, status)) {
+ _mongocrypt_opts_kms_provider_azure_cleanup(&azure);
+ return false;
+ }
+ mc_kms_creds_with_id_t kcwi = {.kmsid = bson_strdup(field_name),
+ .creds = {.type = type, .value = {.azure = azure}}};
+ _mc_array_append_val(&kms_providers->named_mut, kcwi);
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_GCP: {
+ _mongocrypt_opts_kms_provider_gcp_t gcp = {0};
+ if (!_mongocrypt_opts_kms_provider_gcp_parse(&gcp, field_name, &field_bson, status)) {
+ _mongocrypt_opts_kms_provider_gcp_cleanup(&gcp);
+ return false;
+ }
+ mc_kms_creds_with_id_t kcwi = {.kmsid = bson_strdup(field_name),
+ .creds = {.type = type, .value = {.gcp = gcp}}};
+ _mc_array_append_val(&kms_providers->named_mut, kcwi);
+ break;
+ }
+ case MONGOCRYPT_KMS_PROVIDER_KMIP: {
+ _mongocrypt_opts_kms_provider_kmip_t kmip = {0};
+ if (!_mongocrypt_opts_kms_provider_kmip_parse(&kmip, field_name, &field_bson, status)) {
+ _mongocrypt_opts_kms_provider_kmip_cleanup(&kmip);
+ return false;
+ }
+ mc_kms_creds_with_id_t kcwi = {.kmsid = bson_strdup(field_name),
+ .creds = {.type = type, .value = {.kmip = kmip}}};
+ _mc_array_append_val(&kms_providers->named_mut, kcwi);
+ break;
+ }
+ }
+ } else if (0 == strcmp(field_name, "azure") && bson_empty(&field_bson)) {
+ kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_AZURE;
+ } else if (0 == strcmp(field_name, "azure")) {
+ if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AZURE)) {
+ CLIENT_ERR("azure KMS provider already set");
+ return false;
+ }
+
+ if (!_mongocrypt_opts_kms_provider_azure_parse(&kms_providers->azure_mut,
+ field_name,
+ &field_bson,
+ status)) {
+ return false;
+ }
+ kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AZURE;
+ } else if (0 == strcmp(field_name, "gcp") && bson_empty(&field_bson)) {
+ kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_GCP;
+ } else if (0 == strcmp(field_name, "gcp")) {
+ if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_GCP)) {
+ CLIENT_ERR("gcp KMS provider already set");
+ return false;
+ }
+ if (!_mongocrypt_opts_kms_provider_gcp_parse(&kms_providers->gcp_mut, field_name, &field_bson, status)) {
+ return false;
+ }
+ kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_GCP;
+ } else if (0 == strcmp(field_name, "local") && bson_empty(&field_bson)) {
+ kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_LOCAL;
+ } else if (0 == strcmp(field_name, "local")) {
+ if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_LOCAL)) {
+ CLIENT_ERR("local KMS provider already set");
+ return false;
+ }
+ if (!_mongocrypt_opts_kms_provider_local_parse(&kms_providers->local_mut,
+ field_name,
+ &field_bson,
+ status)) {
+ return false;
+ }
+ kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_LOCAL;
+ } else if (0 == strcmp(field_name, "aws") && bson_empty(&field_bson)) {
+ kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_AWS;
+ } else if (0 == strcmp(field_name, "aws")) {
+ if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS)) {
+ CLIENT_ERR("aws KMS provider already set");
+ return false;
+ }
+ if (!_mongocrypt_opts_kms_provider_aws_parse(&kms_providers->aws_mut, field_name, &field_bson, status)) {
+ return false;
+ }
+ kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AWS;
+ } else if (0 == strcmp(field_name, "kmip") && bson_empty(&field_bson)) {
+ kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_KMIP;
+ } else if (0 == strcmp(field_name, "kmip")) {
+ if (!_mongocrypt_opts_kms_provider_kmip_parse(&kms_providers->kmip_mut, field_name, &field_bson, status)) {
+ return false;
+ }
+ kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_KMIP;
+ } else {
+ CLIENT_ERR("unsupported KMS provider: %s", field_name);
+ return false;
+ }
+ }
+
+ if (log && log->trace_enabled) {
+ char *as_str = bson_as_relaxed_extended_json(&as_bson, NULL);
+ _mongocrypt_log(log, MONGOCRYPT_LOG_LEVEL_TRACE, "%s (%s=\"%s\")", BSON_FUNC, "kms_providers", as_str);
+ bson_free(as_str);
+ }
+
+ return true;
+}
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-private.h
index 15ec3630748..147bd1e7c2c 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-private.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-private.h
@@ -39,7 +39,7 @@
#define CLIENT_ERR_W_CODE(code, ...) _mongocrypt_set_error(status, MONGOCRYPT_STATUS_ERROR_CLIENT, code, __VA_ARGS__)
-#define CLIENT_ERR(...) CLIENT_ERR_W_CODE(MONGOCRYPT_GENERIC_ERROR_CODE, __VA_ARGS__)
+#define CLIENT_ERR(fmt, ...) CLIENT_ERR_W_CODE(MONGOCRYPT_GENERIC_ERROR_CODE, fmt, ##__VA_ARGS__)
#define KMS_ERR_W_CODE(code, ...) _mongocrypt_set_error(status, MONGOCRYPT_STATUS_ERROR_KMS, code, __VA_ARGS__)
@@ -49,9 +49,6 @@
#define MONGOCRYPT_DATA_AND_LEN(x) ((uint8_t *)x), (sizeof(x) / sizeof((x)[0]) - 1)
-/* TODO: remove after integrating into libmongoc */
-#define BSON_SUBTYPE_ENCRYPTED 6
-
/* TODO: Move these to mongocrypt-log-private.h? */
const char *tmp_json(const bson_t *bson);
@@ -123,12 +120,12 @@ struct _mongocrypt_t {
_mongocrypt_crypto_t *crypto;
/* A counter, protected by mutex, for generating unique context ids */
uint32_t ctx_counter;
- _mongocrypt_cache_oauth_t *cache_oauth_azure;
- _mongocrypt_cache_oauth_t *cache_oauth_gcp;
+ mc_mapof_kmsid_to_token_t *cache_oauth;
/// A CSFLE DLL vtable, initialized by mongocrypt_init
_mongo_crypt_v1_vtable csfle;
/// Pointer to the global csfle_lib object. Should not be freed directly.
mongo_crypt_v1_lib *csfle_lib;
+ bool retry_enabled;
};
typedef enum {
@@ -154,18 +151,15 @@ char *_mongocrypt_new_string_from_bytes(const void *in, int len);
char *_mongocrypt_new_json_string_from_binary(mongocrypt_binary_t *binary);
-bool _mongocrypt_parse_kms_providers(mongocrypt_binary_t *kms_providers_definition,
- _mongocrypt_opts_kms_providers_t *kms_providers,
- mongocrypt_status_t *status,
- _mongocrypt_log_t *log);
-
/* _mongocrypt_needs_credentials returns true if @crypt was configured to
* request credentials for any KMS provider. */
bool _mongocrypt_needs_credentials(mongocrypt_t *crypt);
/* _mongocrypt_needs_credentials returns true if @crypt was configured to
- * request credentials for @provider. */
-bool _mongocrypt_needs_credentials_for_provider(mongocrypt_t *crypt, _mongocrypt_kms_provider_t provider);
+ * request credentials for @provider and optional @name. @name may be NULL. */
+bool _mongocrypt_needs_credentials_for_provider(mongocrypt_t *crypt,
+ _mongocrypt_kms_provider_t provider,
+ const char *name);
/**
* Enable/disable the use of FLE2v2 payload types for write.
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-util.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-util.c
index 205ce264d27..202c49586aa 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt-util.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-util.c
@@ -81,7 +81,7 @@ current_module_result current_module_path(void) {
#elif defined(_GNU_SOURCE) || defined(_DARWIN_C_SOURCE) || defined(__FreeBSD__)
// Darwin/BSD/glibc define extensions for finding dynamic library info from
// the address of a symbol.
- Dl_info info;
+ Dl_info info = {0};
int rc = dladdr((const void *)current_module_path, &info);
if (rc == 0) {
// Failed to resolve the symbol
@@ -90,7 +90,8 @@ current_module_result current_module_path(void) {
ret_str = mstr_copy_cstr(info.dli_fname);
}
#else
-#error "Don't know how to get the module path on this platform"
+ // Not supported on this system.
+ ret_error = ENOSYS;
#endif
return (current_module_result){.path = ret_str, .error = ret_error};
}
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt.c b/src/third_party/libmongocrypt/dist/src/mongocrypt.c
index c5adefbdfc3..14f427b8bdb 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt.c
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt.c
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "mongocrypt.h"
#include "mlib/error.h"
#include "mlib/path.h"
#include "mlib/thread.h"
@@ -24,6 +25,7 @@
#include "mongocrypt-binary-private.h"
#include "mongocrypt-cache-collinfo-private.h"
#include "mongocrypt-cache-key-private.h"
+#include "mongocrypt-cache-private.h"
#include "mongocrypt-config.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-log-private.h"
@@ -119,8 +121,7 @@ mongocrypt_t *mongocrypt_new(void) {
// Default to using FLEv2 (aka QEv2)
crypt->opts.use_fle2_v2 = true;
crypt->ctx_counter = 1;
- crypt->cache_oauth_azure = _mongocrypt_cache_oauth_new();
- crypt->cache_oauth_gcp = _mongocrypt_cache_oauth_new();
+ crypt->cache_oauth = mc_mapof_kmsid_to_token_new();
crypt->csfle = (_mongo_crypt_v1_vtable){.okay = false};
static mlib_once_flag init_flag = MLIB_ONCE_INITIALIZER;
@@ -154,6 +155,13 @@ bool mongocrypt_setopt_fle2v2(mongocrypt_t *crypt, bool enable) {
return true;
}
+bool mongocrypt_setopt_use_range_v2(mongocrypt_t *crypt) {
+ ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);
+
+ // Nothing to do. As of MONGOCRYPT-661, rangeV2 is the default.
+ return true;
+}
+
bool mongocrypt_setopt_log_handler(mongocrypt_t *crypt, mongocrypt_log_fn_t log_fn, void *log_ctx) {
ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);
crypt->opts.log_fn = log_fn;
@@ -161,6 +169,12 @@ bool mongocrypt_setopt_log_handler(mongocrypt_t *crypt, mongocrypt_log_fn_t log_
return true;
}
+bool mongocrypt_setopt_retry_kms(mongocrypt_t *crypt, bool enable) {
+ ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);
+ crypt->retry_enabled = enable;
+ return true;
+}
+
bool mongocrypt_setopt_kms_provider_aws(mongocrypt_t *crypt,
const char *aws_access_key_id,
int32_t aws_access_key_id_len,
@@ -178,14 +192,14 @@ bool mongocrypt_setopt_kms_provider_aws(mongocrypt_t *crypt,
if (!_mongocrypt_validate_and_copy_string(aws_access_key_id,
aws_access_key_id_len,
- &kms_providers->aws.access_key_id)) {
+ &kms_providers->aws_mut.access_key_id)) {
CLIENT_ERR("invalid aws access key id");
return false;
}
if (!_mongocrypt_validate_and_copy_string(aws_secret_access_key,
aws_secret_access_key_len,
- &kms_providers->aws.secret_access_key)) {
+ &kms_providers->aws_mut.secret_access_key)) {
CLIENT_ERR("invalid aws secret access key");
return false;
}
@@ -196,11 +210,11 @@ bool mongocrypt_setopt_kms_provider_aws(mongocrypt_t *crypt,
"%s (%s=\"%s\", %s=%d, %s=\"%s\", %s=%d)",
BSON_FUNC,
"aws_access_key_id",
- kms_providers->aws.access_key_id,
+ kms_providers->aws_mut.access_key_id,
"aws_access_key_id_len",
aws_access_key_id_len,
"aws_secret_access_key",
- kms_providers->aws.secret_access_key,
+ kms_providers->aws_mut.secret_access_key,
"aws_secret_access_key_len",
aws_secret_access_key_len);
}
@@ -208,6 +222,17 @@ bool mongocrypt_setopt_kms_provider_aws(mongocrypt_t *crypt,
return true;
}
+bool mongocrypt_setopt_key_expiration(mongocrypt_t *crypt, uint64_t cache_expiration_ms) {
+ ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);
+ if (cache_expiration_ms > INT64_MAX) {
+ mongocrypt_status_t *status = crypt->status;
+ CLIENT_ERR("expiration time must be less than %" PRId64 ", but got %" PRIu64, INT64_MAX, cache_expiration_ms);
+ return false;
+ }
+ crypt->cache_key.expiration = cache_expiration_ms;
+ return true;
+}
+
char *_mongocrypt_new_string_from_bytes(const void *in, int len) {
const int max_bytes = 100;
const int chars_per_byte = 2;
@@ -345,7 +370,7 @@ bool mongocrypt_setopt_kms_provider_local(mongocrypt_t *crypt, mongocrypt_binary
bson_free(key_val);
}
- _mongocrypt_buffer_copy_from_binary(&kms_providers->local.key, key);
+ _mongocrypt_buffer_copy_from_binary(&kms_providers->local_mut.key, key);
kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_LOCAL;
return true;
}
@@ -365,7 +390,7 @@ typedef struct {
*
* @param status is an optional status to set an error message if `mcr_dll_open` fails.
*/
-static _loaded_csfle _try_load_csfle(const char *filepath, _mongocrypt_log_t *log, mongocrypt_status_t *status) {
+static _loaded_csfle _try_load_csfle(const char *filepath, mongocrypt_status_t *status, _mongocrypt_log_t *log) {
// Try to open the dynamic lib
mcr_dll lib = mcr_dll_open(filepath);
// Check for errors, which are represented by strings
@@ -479,7 +504,7 @@ static _loaded_csfle _try_find_csfle(mongocrypt_t *crypt) {
// Do not allow a plain filename to go through, as that will cause the
// DLL load to search the system.
mstr_assign(&csfle_cand_filepath, mpath_absolute(csfle_cand_filepath.view, MPATH_NATIVE));
- candidate_csfle = _try_load_csfle(csfle_cand_filepath.data, &crypt->log, crypt->status);
+ candidate_csfle = _try_load_csfle(csfle_cand_filepath.data, crypt->status, &crypt->log);
}
} else {
// No override path was specified, so try to find it on the provided
@@ -501,7 +526,7 @@ static _loaded_csfle _try_find_csfle(mongocrypt_t *crypt) {
}
}
// Try to load the file:
- candidate_csfle = _try_load_csfle(csfle_cand_filepath.data, &crypt->log, NULL /* status */);
+ candidate_csfle = _try_load_csfle(csfle_cand_filepath.data, NULL /* status */, &crypt->log);
if (candidate_csfle.okay) {
// Stop searching:
break;
@@ -552,6 +577,14 @@ static bool _validate_csfle_singleton(mongocrypt_t *crypt, _loaded_csfle found)
BSON_ASSERT_PARAM(crypt);
+ if (!mcr_dll_path_supported()) {
+ _mongocrypt_log(&crypt->log,
+ MONGOCRYPT_LOG_LEVEL_WARNING,
+ "Cannot get path of loaded library on this platform. Skipping validation to ensure "
+ "exactly one csfle library is loaded.");
+ return true;
+ }
+
status = crypt->status;
// Path to the existing loaded csfle:
@@ -884,6 +917,14 @@ bool mongocrypt_init(mongocrypt_t *crypt) {
return _try_enable_csfle(crypt);
}
+bool mongocrypt_is_crypto_available(void) {
+#ifdef MONGOCRYPT_ENABLE_CRYPTO
+ return true;
+#else
+ return false;
+#endif
+}
+
bool mongocrypt_status(mongocrypt_t *crypt, mongocrypt_status_t *out) {
BSON_ASSERT_PARAM(crypt);
@@ -912,8 +953,7 @@ void mongocrypt_destroy(mongocrypt_t *crypt) {
_mongocrypt_log_cleanup(&crypt->log);
mongocrypt_status_destroy(crypt->status);
bson_free(crypt->crypto);
- _mongocrypt_cache_oauth_destroy(crypt->cache_oauth_azure);
- _mongocrypt_cache_oauth_destroy(crypt->cache_oauth_gcp);
+ mc_mapof_kmsid_to_token_destroy(crypt->cache_oauth);
if (crypt->csfle.okay) {
_csfle_drop_global_ref();
@@ -1097,236 +1137,6 @@ bool mongocrypt_setopt_kms_providers(mongocrypt_t *crypt, mongocrypt_binary_t *k
&crypt->log);
}
-bool _mongocrypt_parse_kms_providers(mongocrypt_binary_t *kms_providers_definition,
- _mongocrypt_opts_kms_providers_t *kms_providers,
- mongocrypt_status_t *status,
- _mongocrypt_log_t *log) {
- bson_t as_bson;
- bson_iter_t iter;
-
- BSON_ASSERT_PARAM(kms_providers_definition);
- BSON_ASSERT_PARAM(kms_providers);
- if (!_mongocrypt_binary_to_bson(kms_providers_definition, &as_bson) || !bson_iter_init(&iter, &as_bson)) {
- CLIENT_ERR("invalid BSON");
- return false;
- }
-
- while (bson_iter_next(&iter)) {
- const char *field_name;
- bson_t field_bson;
-
- field_name = bson_iter_key(&iter);
- if (!mc_iter_document_as_bson(&iter, &field_bson, status)) {
- return false;
- }
-
- if (0 == strcmp(field_name, "azure") && bson_empty(&field_bson)) {
- kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_AZURE;
- } else if (0 == strcmp(field_name, "azure")) {
- if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AZURE)) {
- CLIENT_ERR("azure KMS provider already set");
- return false;
- }
-
- if (!_mongocrypt_parse_optional_utf8(&as_bson,
- "azure.accessToken",
- &kms_providers->azure.access_token,
- status)) {
- return false;
- }
-
- if (kms_providers->azure.access_token) {
- // Caller provides an accessToken directly
- if (!_mongocrypt_check_allowed_fields(&as_bson, "azure", status, "accessToken")) {
- return false;
- }
- kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AZURE;
- continue;
- }
-
- // No accessToken given, so we'll need to look one up on our own later
- // using the Azure API
-
- if (!_mongocrypt_parse_required_utf8(&as_bson, "azure.tenantId", &kms_providers->azure.tenant_id, status)) {
- return false;
- }
-
- if (!_mongocrypt_parse_required_utf8(&as_bson, "azure.clientId", &kms_providers->azure.client_id, status)) {
- return false;
- }
-
- if (!_mongocrypt_parse_required_utf8(&as_bson,
- "azure.clientSecret",
- &kms_providers->azure.client_secret,
- status)) {
- return false;
- }
-
- if (!_mongocrypt_parse_optional_endpoint(&as_bson,
- "azure.identityPlatformEndpoint",
- &kms_providers->azure.identity_platform_endpoint,
- NULL /* opts */,
- status)) {
- return false;
- }
-
- if (!_mongocrypt_check_allowed_fields(&as_bson,
- "azure",
- status,
- "tenantId",
- "clientId",
- "clientSecret",
- "identityPlatformEndpoint")) {
- return false;
- }
- kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AZURE;
- } else if (0 == strcmp(field_name, "gcp") && bson_empty(&field_bson)) {
- kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_GCP;
- } else if (0 == strcmp(field_name, "gcp")) {
- if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_GCP)) {
- CLIENT_ERR("gcp KMS provider already set");
- return false;
- }
-
- if (!_mongocrypt_parse_optional_utf8(&as_bson,
- "gcp.accessToken",
- &kms_providers->gcp.access_token,
- status)) {
- return false;
- }
-
- if (NULL != kms_providers->gcp.access_token) {
- /* "gcp" document has form:
- * {
- * "accessToken":
- * }
- */
- if (!_mongocrypt_check_allowed_fields(&as_bson, "gcp", status, "accessToken")) {
- return false;
- }
- kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_GCP;
- continue;
- }
-
- /* "gcp" document has form:
- * {
- * "email":
- * "privateKey":
- * }
- */
- if (!_mongocrypt_parse_required_utf8(&as_bson, "gcp.email", &kms_providers->gcp.email, status)) {
- return false;
- }
-
- if (!_mongocrypt_parse_required_binary(&as_bson,
- "gcp.privateKey",
- &kms_providers->gcp.private_key,
- status)) {
- return false;
- }
-
- if (!_mongocrypt_parse_optional_endpoint(&as_bson,
- "gcp.endpoint",
- &kms_providers->gcp.endpoint,
- NULL /* opts */,
- status)) {
- return false;
- }
-
- if (!_mongocrypt_check_allowed_fields(&as_bson, "gcp", status, "email", "privateKey", "endpoint")) {
- return false;
- }
- kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_GCP;
- } else if (0 == strcmp(field_name, "local") && bson_empty(&field_bson)) {
- kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_LOCAL;
- } else if (0 == strcmp(field_name, "local")) {
- if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_LOCAL)) {
- CLIENT_ERR("local KMS provider already set");
- return false;
- }
- if (!_mongocrypt_parse_required_binary(&as_bson, "local.key", &kms_providers->local.key, status)) {
- return false;
- }
-
- if (kms_providers->local.key.len != MONGOCRYPT_KEY_LEN) {
- CLIENT_ERR("local key must be %d bytes", MONGOCRYPT_KEY_LEN);
- return false;
- }
-
- if (!_mongocrypt_check_allowed_fields(&as_bson, "local", status, "key")) {
- return false;
- }
- kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_LOCAL;
- } else if (0 == strcmp(field_name, "aws") && bson_empty(&field_bson)) {
- kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_AWS;
- } else if (0 == strcmp(field_name, "aws")) {
- if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS)) {
- CLIENT_ERR("aws KMS provider already set");
- return false;
- }
-
- if (!_mongocrypt_parse_required_utf8(&as_bson,
- "aws.accessKeyId",
- &kms_providers->aws.access_key_id,
- status)) {
- return false;
- }
- if (!_mongocrypt_parse_required_utf8(&as_bson,
- "aws.secretAccessKey",
- &kms_providers->aws.secret_access_key,
- status)) {
- return false;
- }
-
- if (!_mongocrypt_parse_optional_utf8(&as_bson,
- "aws.sessionToken",
- &kms_providers->aws.session_token,
- status)) {
- return false;
- }
-
- if (!_mongocrypt_check_allowed_fields(&as_bson,
- "aws",
- status,
- "accessKeyId",
- "secretAccessKey",
- "sessionToken")) {
- return false;
- }
- kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AWS;
- } else if (0 == strcmp(field_name, "kmip") && bson_empty(&field_bson)) {
- kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_KMIP;
- } else if (0 == strcmp(field_name, "kmip")) {
- _mongocrypt_endpoint_parse_opts_t opts = {0};
-
- opts.allow_empty_subdomain = true;
- if (!_mongocrypt_parse_required_endpoint(&as_bson,
- "kmip.endpoint",
- &kms_providers->kmip.endpoint,
- &opts,
- status)) {
- return false;
- }
-
- if (!_mongocrypt_check_allowed_fields(&as_bson, "kmip", status, "endpoint")) {
- return false;
- }
- kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_KMIP;
- } else {
- CLIENT_ERR("unsupported KMS provider: %s", field_name);
- return false;
- }
- }
-
- if (log && log->trace_enabled) {
- char *as_str = bson_as_json(&as_bson, NULL);
- _mongocrypt_log(log, MONGOCRYPT_LOG_LEVEL_TRACE, "%s (%s=\"%s\")", BSON_FUNC, "kms_providers", as_str);
- bson_free(as_str);
- }
-
- return true;
-}
-
void mongocrypt_setopt_append_crypt_shared_lib_search_path(mongocrypt_t *crypt, const char *path) {
BSON_ASSERT_PARAM(crypt);
BSON_ASSERT_PARAM(path);
@@ -1352,6 +1162,12 @@ void mongocrypt_setopt_use_need_kms_credentials_state(mongocrypt_t *crypt) {
crypt->opts.use_need_kms_credentials_state = true;
}
+void mongocrypt_setopt_use_need_mongo_collinfo_with_db_state(mongocrypt_t *crypt) {
+ BSON_ASSERT_PARAM(crypt);
+
+ crypt->opts.use_need_mongo_collinfo_with_db_state = true;
+}
+
void mongocrypt_setopt_set_crypt_shared_lib_path_override(mongocrypt_t *crypt, const char *path) {
BSON_ASSERT_PARAM(crypt);
BSON_ASSERT_PARAM(path);
@@ -1369,9 +1185,16 @@ bool _mongocrypt_needs_credentials(mongocrypt_t *crypt) {
return crypt->opts.kms_providers.need_credentials != 0;
}
-bool _mongocrypt_needs_credentials_for_provider(mongocrypt_t *crypt, _mongocrypt_kms_provider_t provider) {
+bool _mongocrypt_needs_credentials_for_provider(mongocrypt_t *crypt,
+ _mongocrypt_kms_provider_t provider,
+ const char *name) {
BSON_ASSERT_PARAM(crypt);
+ if (name != NULL) {
+ // Named KMS providers do not support on-demand credentials.
+ return false;
+ }
+
if (!crypt->opts.use_need_kms_credentials_state) {
return false;
}
diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt.h b/src/third_party/libmongocrypt/dist/src/mongocrypt.h
index e4ca0b133cd..7227e0ef7ec 100644
--- a/src/third_party/libmongocrypt/dist/src/mongocrypt.h
+++ b/src/third_party/libmongocrypt/dist/src/mongocrypt.h
@@ -48,6 +48,17 @@
MONGOCRYPT_EXPORT
const char *mongocrypt_version(uint32_t *len);
+/**
+ * Returns true if libmongocrypt was built with native crypto support.
+ *
+ * If libmongocrypt was not built with native crypto support, setting crypto
+ * hooks is required.
+ *
+ * @returns True if libmongocrypt was built with native crypto support.
+ */
+MONGOCRYPT_EXPORT
+bool mongocrypt_is_crypto_available(void);
+
/**
* A non-owning view of a byte buffer.
*
@@ -69,8 +80,14 @@ const char *mongocrypt_version(uint32_t *len);
* mongocrypt_ctx_mongo_op guarantees that the viewed data of
* mongocrypt_binary_t is valid until the parent ctx is destroyed with @ref
* mongocrypt_ctx_destroy.
+ *
+ * The `mongocrypt_binary_t` struct definition is public.
+ * Consumers may rely on the struct layout.
*/
-typedef struct _mongocrypt_binary_t mongocrypt_binary_t;
+typedef struct _mongocrypt_binary_t {
+ void *data;
+ uint32_t len;
+} mongocrypt_binary_t;
/**
* Create a new non-owning view of a buffer (data + length).
@@ -293,6 +310,18 @@ mongocrypt_t *mongocrypt_new(void);
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_log_handler(mongocrypt_t *crypt, mongocrypt_log_fn_t log_fn, void *log_ctx);
+/**
+ * Enable or disable KMS retry behavior.
+ *
+ * @param[in] crypt The @ref mongocrypt_t object.
+ * @param[in] enable A boolean indicating whether to retry operations.
+ * @pre @ref mongocrypt_init has not been called on @p crypt.
+ * @returns A boolean indicating success. If false, an error status is set.
+ * Retrieve it with @ref mongocrypt_ctx_status
+ */
+MONGOCRYPT_EXPORT
+bool mongocrypt_setopt_retry_kms(mongocrypt_t *crypt, bool enable);
+
/**
* Configure an AWS KMS provider on the @ref mongocrypt_t object.
*
@@ -456,6 +485,18 @@ void mongocrypt_setopt_set_crypt_shared_lib_path_override(mongocrypt_t *crypt, c
MONGOCRYPT_EXPORT
void mongocrypt_setopt_use_need_kms_credentials_state(mongocrypt_t *crypt);
+/**
+ * @brief Opt-into handling the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB state.
+ *
+ * A context enters the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB state when
+ * processing a `bulkWrite` command. The target database of the `bulkWrite` may differ from the command database
+ * ("admin").
+ *
+ * @param[in] crypt The @ref mongocrypt_t object to update
+ */
+MONGOCRYPT_EXPORT
+void mongocrypt_setopt_use_need_mongo_collinfo_with_db_state(mongocrypt_t *crypt);
+
/**
* Initialize new @ref mongocrypt_t object.
*
@@ -517,7 +558,7 @@ const char *mongocrypt_crypt_shared_lib_version_string(const mongocrypt_t *crypt
* @brief Obtain a 64-bit constant encoding the version of the loaded
* crypt_shared library, if available.
*
- * @param[in] crypt The mongocrypt_t object after a successul call to
+ * @param[in] crypt The mongocrypt_t object after a successful call to
* mongocrypt_init.
*
* @return A 64-bit encoded version number, with the version encoded as four
@@ -657,10 +698,9 @@ bool mongocrypt_ctx_setopt_algorithm(mongocrypt_ctx_t *ctx, const char *algorith
#define MONGOCRYPT_ALGORITHM_INDEXED_STR "Indexed"
/// String constant for setopt_algorithm "Unindexed" explicit encryption
#define MONGOCRYPT_ALGORITHM_UNINDEXED_STR "Unindexed"
-/// String constant for setopt_algorithm "rangePreview" explicit encryption
-/// NOTE: The RangePreview algorithm is experimental only. It is not intended
-/// for public use.
-#define MONGOCRYPT_ALGORITHM_RANGEPREVIEW_STR "RangePreview"
+// DEPRECATED: support "RangePreview" has been removed in favor of "range".
+#define MONGOCRYPT_ALGORITHM_RANGEPREVIEW_DEPRECATED_STR "RangePreview"
+#define MONGOCRYPT_ALGORITHM_RANGE_STR "Range"
/**
* Identify the AWS KMS master key to use for creating a data key.
@@ -849,9 +889,7 @@ bool mongocrypt_ctx_explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_bina
/**
* Explicit helper method to encrypt a Match Expression or Aggregate Expression.
* Contexts created for explicit encryption will not go through mongocryptd.
- * Requires query_type to be "rangePreview".
- * NOTE: The RangePreview algorithm is experimental only. It is not intended for
- * public use.
+ * Requires query_type to be "range".
*
* This method expects the passed-in BSON to be of the form:
* { "v" : FLE2RangeFindDriverSpec }
@@ -948,9 +986,10 @@ bool mongocrypt_ctx_rewrap_many_datakey_init(mongocrypt_ctx_t *ctx, mongocrypt_b
*/
typedef enum {
MONGOCRYPT_CTX_ERROR = 0,
- MONGOCRYPT_CTX_NEED_MONGO_COLLINFO = 1, /* run on main MongoClient */
- MONGOCRYPT_CTX_NEED_MONGO_MARKINGS = 2, /* run on mongocryptd. */
- MONGOCRYPT_CTX_NEED_MONGO_KEYS = 3, /* run on key vault */
+ MONGOCRYPT_CTX_NEED_MONGO_COLLINFO = 1, /* run on main MongoClient */
+ MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB = 8, /* run on main MongoClient */
+ MONGOCRYPT_CTX_NEED_MONGO_MARKINGS = 2, /* run on mongocryptd. */
+ MONGOCRYPT_CTX_NEED_MONGO_KEYS = 3, /* run on key vault */
MONGOCRYPT_CTX_NEED_KMS = 4,
MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS = 7, /* fetch/renew KMS credentials */
MONGOCRYPT_CTX_READY = 5, /* ready for encryption/decryption */
@@ -971,7 +1010,7 @@ mongocrypt_ctx_state_t mongocrypt_ctx_state(mongocrypt_ctx_t *ctx);
* is in MONGOCRYPT_CTX_NEED_MONGO_* states.
*
* @p op_bson is a BSON document to be used for the operation.
- * - For MONGOCRYPT_CTX_NEED_MONGO_COLLINFO it is a listCollections filter.
+ * - For MONGOCRYPT_CTX_NEED_MONGO_COLLINFO(_WITH_DB) it is a listCollections filter.
* - For MONGOCRYPT_CTX_NEED_MONGO_KEYS it is a find filter.
* - For MONGOCRYPT_CTX_NEED_MONGO_MARKINGS it is a command to send to
* mongocryptd.
@@ -989,13 +1028,29 @@ mongocrypt_ctx_state_t mongocrypt_ctx_state(mongocrypt_ctx_t *ctx);
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_mongo_op(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *op_bson);
+/**
+ * Get the database to run the mongo operation.
+ *
+ * Only applies when mongocrypt_ctx_t is in the state:
+ * MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB.
+ *
+ * The lifetime of the returned string is tied to the lifetime of @p ctx. It is
+ * valid until @ref mongocrypt_ctx_destroy is called.
+ *
+ * @param[in] ctx The @ref mongocrypt_ctx_t object.
+ * @returns A string or NULL. If NULL, an error status is set. Retrieve it with
+ * @ref mongocrypt_ctx_status
+ */
+MONGOCRYPT_EXPORT
+const char *mongocrypt_ctx_mongo_db(mongocrypt_ctx_t *ctx);
+
/**
* Feed a BSON reply or result when mongocrypt_ctx_t is in
* MONGOCRYPT_CTX_NEED_MONGO_* states. This may be called multiple times
* depending on the operation.
*
* reply is a BSON document result being fed back for this operation.
- * - For MONGOCRYPT_CTX_NEED_MONGO_COLLINFO it is a doc from a listCollections
+ * - For MONGOCRYPT_CTX_NEED_MONGO_COLLINFO(_WITH_DB) it is a doc from a listCollections
* cursor. (Note, if listCollections returned no result, do not call this
* function.)
* - For MONGOCRYPT_CTX_NEED_MONGO_KEYS it is a doc from a find cursor.
@@ -1039,6 +1094,8 @@ typedef struct _mongocrypt_kms_ctx_t mongocrypt_kms_ctx_t;
* If KMS handles are being handled synchronously, the driver can reuse the same
* TLS socket to send HTTP requests and receive responses.
*
+ * The returned KMS handle does not outlive `ctx`.
+ *
* @param[in] ctx A @ref mongocrypt_ctx_t.
* @returns a new @ref mongocrypt_kms_ctx_t or NULL.
*/
@@ -1087,6 +1144,15 @@ bool mongocrypt_kms_ctx_endpoint(mongocrypt_kms_ctx_t *kms, const char **endpoin
MONGOCRYPT_EXPORT
uint32_t mongocrypt_kms_ctx_bytes_needed(mongocrypt_kms_ctx_t *kms);
+/**
+ * Indicates how long to sleep before sending this request.
+ *
+ * @param[in] kms The @ref mongocrypt_kms_ctx_t.
+ * @returns How long to sleep in microseconds.
+ */
+MONGOCRYPT_EXPORT
+int64_t mongocrypt_kms_ctx_usleep(mongocrypt_kms_ctx_t *kms);
+
/**
* Feed bytes from the HTTP response.
*
@@ -1102,6 +1168,15 @@ uint32_t mongocrypt_kms_ctx_bytes_needed(mongocrypt_kms_ctx_t *kms);
MONGOCRYPT_EXPORT
bool mongocrypt_kms_ctx_feed(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *bytes);
+/**
+ * Indicate a network-level failure.
+ *
+ * @param[in] kms The @ref mongocrypt_kms_ctx_t.
+ * @return A boolean indicating whether the failed request may be retried.
+ */
+MONGOCRYPT_EXPORT
+bool mongocrypt_kms_ctx_fail(mongocrypt_kms_ctx_t *kms);
+
/**
* Get the status associated with a @ref mongocrypt_kms_ctx_t object.
*
@@ -1221,7 +1296,7 @@ void mongocrypt_ctx_destroy(mongocrypt_ctx_t *ctx);
* @param[out] status An optional status to pass error messages. See @ref
* mongocrypt_status_set.
* @returns A boolean indicating success. If returning false, set @p status
- * with a message indiciating the error using @ref mongocrypt_status_set.
+ * with a message indicating the error using @ref mongocrypt_status_set.
*/
typedef bool (*mongocrypt_crypto_fn)(void *ctx,
mongocrypt_binary_t *key,
@@ -1246,7 +1321,7 @@ typedef bool (*mongocrypt_crypto_fn)(void *ctx,
* @param[out] status An optional status to pass error messages. See @ref
* mongocrypt_status_set.
* @returns A boolean indicating success. If returning false, set @p status
- * with a message indiciating the error using @ref mongocrypt_status_set.
+ * with a message indicating the error using @ref mongocrypt_status_set.
*/
typedef bool (*mongocrypt_hmac_fn)(void *ctx,
mongocrypt_binary_t *key,
@@ -1265,7 +1340,7 @@ typedef bool (*mongocrypt_hmac_fn)(void *ctx,
* @param[out] status An optional status to pass error messages. See @ref
* mongocrypt_status_set.
* @returns A boolean indicating success. If returning false, set @p status
- * with a message indiciating the error using @ref mongocrypt_status_set.
+ * with a message indicating the error using @ref mongocrypt_status_set.
*/
typedef bool (*mongocrypt_hash_fn)(void *ctx,
mongocrypt_binary_t *in,
@@ -1283,7 +1358,7 @@ typedef bool (*mongocrypt_hash_fn)(void *ctx,
* @param[out] status An optional status to pass error messages. See @ref
* mongocrypt_status_set.
* @returns A boolean indicating success. If returning false, set @p status
- * with a message indiciating the error using @ref mongocrypt_status_set.
+ * with a message indicating the error using @ref mongocrypt_status_set.
*/
typedef bool (*mongocrypt_random_fn)(void *ctx, mongocrypt_binary_t *out, uint32_t count, mongocrypt_status_t *status);
@@ -1305,8 +1380,7 @@ bool mongocrypt_setopt_crypto_hooks(mongocrypt_t *crypt,
* operation.
* @param[in] aes_256_ctr_decrypt The crypto callback function for decrypt
* operation.
- * @param[in] ctx A context passed as an argument to the crypto callback
- * every invocation.
+ * @param[in] ctx Unused.
* @pre @ref mongocrypt_init has not been called on @p crypt.
* @returns A boolean indicating success. If false, an error status is set.
* Retrieve it with @ref mongocrypt_status
@@ -1326,8 +1400,7 @@ bool mongocrypt_setopt_aes_256_ctr(mongocrypt_t *crypt,
* @param[in] crypt The @ref mongocrypt_t object.
* @param[in] aes_256_ecb_encrypt The crypto callback function for encrypt
* operation.
- * @param[in] ctx A context passed as an argument to the crypto callback
- * every invocation.
+ * @param[in] ctx Unused.
* @pre @ref mongocrypt_init has not been called on @p crypt.
* @returns A boolean indicating success. If false, an error status is set.
* Retrieve it with @ref mongocrypt_status
@@ -1370,6 +1443,17 @@ bool mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5(mongocrypt_t *crypt,
MONGOCRYPT_EXPORT
void mongocrypt_setopt_bypass_query_analysis(mongocrypt_t *crypt);
+/**
+ * DEPRECATED: Use of `mongocrypt_setopt_use_range_v2` is deprecated. Range V2 is always enabled.
+ *
+ * @param[in] crypt The @ref mongocrypt_t object.
+ *
+ * @returns A boolean indicating success. If false, an error status is set.
+ * Retrieve it with @ref mongocrypt_status
+ */
+MONGOCRYPT_EXPORT
+bool mongocrypt_setopt_use_range_v2(mongocrypt_t *crypt);
+
/**
* Set the contention factor used for explicit encryption.
* The contention factor is only used for indexed Queryable Encryption.
@@ -1415,16 +1499,15 @@ MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_query_type(mongocrypt_ctx_t *ctx, const char *query_type, int len);
/**
- * Set options for explicit encryption with the "rangePreview" algorithm.
- * NOTE: The RangePreview algorithm is experimental only. It is not intended for
- * public use.
+ * Set options for explicit encryption with the "range" algorithm.
*
* @p opts is a BSON document of the form:
* {
* "min": Optional,
* "max": Optional,
- * "sparsity": Int64,
- * "precision": Optional
+ * "sparsity": Optional,
+ * "precision": Optional,
+ * "trimFactor": Optional
* }
*
* @param[in] ctx The @ref mongocrypt_ctx_t object.
@@ -1436,10 +1519,20 @@ bool mongocrypt_ctx_setopt_query_type(mongocrypt_ctx_t *ctx, const char *query_t
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_algorithm_range(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts);
+/**
+ * Set the expiration time for the data encryption key cache. Defaults to 60 seconds if not set.
+ *
+ * @param[in] ctx The @ref mongocrypt_ctx_t object.
+ * @param[in] cache_expiration_ms The cache expiration time in milliseconds. If zero, the cache
+ * never expires.
+ */
+MONGOCRYPT_EXPORT
+bool mongocrypt_setopt_key_expiration(mongocrypt_t *crypt, uint64_t cache_expiration_ms);
+
/// String constants for setopt_query_type
#define MONGOCRYPT_QUERY_TYPE_EQUALITY_STR "equality"
-// NOTE: The RangePreview algorithm is experimental only. It is not intended for
-// public use.
-#define MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_STR "rangePreview"
+// DEPRECATED: Support "rangePreview" has been removed in favor of "range".
+#define MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED_STR "rangePreview"
+#define MONGOCRYPT_QUERY_TYPE_RANGE_STR "range"
#endif /* MONGOCRYPT_H */
diff --git a/src/third_party/libmongocrypt/dist/src/os_posix/os_dll.c b/src/third_party/libmongocrypt/dist/src/os_posix/os_dll.c
index 425f39fb241..21149e6ebde 100644
--- a/src/third_party/libmongocrypt/dist/src/os_posix/os_dll.c
+++ b/src/third_party/libmongocrypt/dist/src/os_posix/os_dll.c
@@ -95,26 +95,42 @@ mcr_dll_path_result mcr_dll_path(mcr_dll dll) {
return (mcr_dll_path_result){.error_string = mstr_copy_cstr("Handle not found in loaded modules")};
}
+bool mcr_dll_path_supported(void) {
+ return true;
+}
+
#elif defined(__linux__) || defined(__FreeBSD__)
#include
mcr_dll_path_result mcr_dll_path(mcr_dll dll) {
- struct link_map *map;
+ struct link_map *map = NULL;
int rc = dlinfo(dll._native_handle, RTLD_DI_LINKMAP, &map);
if (rc == 0) {
+ assert(NULL != map);
return (mcr_dll_path_result){.path = mstr_copy_cstr(map->l_name)};
} else {
return (mcr_dll_path_result){.error_string = mstr_copy_cstr(dlerror())};
}
}
+bool mcr_dll_path_supported(void) {
+ return true;
+}
+
#elif defined(_WIN32)
// Handled in os_win/os_dll.c
#else
-#error "Don't know how to do mcr_dll_path() on this platform"
+mcr_dll_path_result mcr_dll_path(mcr_dll dll) {
+ return (mcr_dll_path_result){.error_string =
+ mstr_copy_cstr("Don't know how to do mcr_dll_path() on this platform")};
+}
+
+bool mcr_dll_path_supported(void) {
+ return false;
+}
#endif
diff --git a/src/third_party/libmongocrypt/dist/src/os_win/os_dll.c b/src/third_party/libmongocrypt/dist/src/os_win/os_dll.c
index b01179438c7..e6e4f1b5a0b 100644
--- a/src/third_party/libmongocrypt/dist/src/os_win/os_dll.c
+++ b/src/third_party/libmongocrypt/dist/src/os_win/os_dll.c
@@ -76,4 +76,8 @@ mcr_dll_path_result mcr_dll_path(mcr_dll dll) {
};
}
+bool mcr_dll_path_supported(void) {
+ return true;
+}
+
#endif
diff --git a/src/third_party/libmongocrypt/import.sh b/src/third_party/libmongocrypt/import.sh
index 9988824ce0e..20357db1819 100755
--- a/src/third_party/libmongocrypt/import.sh
+++ b/src/third_party/libmongocrypt/import.sh
@@ -18,7 +18,7 @@ if grep -q Microsoft /proc/version; then
fi
NAME=libmongocrypt
-VERSION=1.8.4
+VERSION=1.12.0
if grep -q Microsoft /proc/version; then
SRC_ROOT=$(wslpath -u $(powershell.exe -Command "Get-ChildItem Env:TEMP | Get-Content | Write-Host"))