mirror of https://github.com/mongodb/mongo
SERVER-96236 Update libmongocrypt to 1.12.0 (#28899)
GitOrigin-RevId: e2576facb200fbbe4ca03e22e147505c02d03f6a
This commit is contained in:
parent
da8da2d4cf
commit
7d32e888ed
|
|
@ -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 | | ✗ |
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
<RequestMessage tag="0x420078" type="Structure">
|
||||
<RequestHeader tag="0x420077" type="Structure">
|
||||
<ProtocolVersion tag="0x420069" type="Structure">
|
||||
<ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
|
||||
<ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
|
||||
</ProtocolVersion>
|
||||
<BatchCount tag="0x42000d" type="Integer" value="1"/>
|
||||
</RequestHeader>
|
||||
<BatchItem tag="0x42000f" type="Structure">
|
||||
<Operation tag="0x42005c" type="Enumeration" value="1"/>
|
||||
<RequestPayload tag="0x420079" type="Structure">
|
||||
<ObjectType tag="0x420057" type="Enumeration" value="2"/>
|
||||
<TemplateAttribute tag="0x420091" type="Structure">
|
||||
<Attribute tag="0x420008" type="Structure">
|
||||
<AttributeName tag="0x42000a" type="TextString" value="Cryptographic Algorithm"/>
|
||||
<AttributeValue tag="0x42000b" type="Enumeration" value="3"/>
|
||||
</Attribute>
|
||||
<Attribute tag="0x420008" type="Structure">
|
||||
<AttributeName tag="0x42000a" type="TextString" value="Cryptographic Length"/>
|
||||
<AttributeValue tag="0x42000b" type="Integer" value="256"/>
|
||||
</Attribute>
|
||||
<Attribute tag="0x420008" type="Structure">
|
||||
<AttributeName tag="0x42000a" type="TextString" value="Cryptographic
|
||||
Usage Mask"/> <AttributeValue tag="0x42000b" type="Integer" value="12"/>
|
||||
</Attribute>
|
||||
</TemplateAttribute>
|
||||
</RequestPayload>
|
||||
</BatchItem>
|
||||
</RequestMessage>
|
||||
*/
|
||||
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:
|
||||
<RequestMessage tag="0x420078" type="Structure">
|
||||
<RequestHeader tag="0x420077" type="Structure">
|
||||
<ProtocolVersion tag="0x420069" type="Structure">
|
||||
<ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
|
||||
<ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
|
||||
</ProtocolVersion>
|
||||
<BatchCount tag="0x42000d" type="Integer" value="1"/>
|
||||
</RequestHeader>
|
||||
<BatchItem tag="0x42000f" type="Structure">
|
||||
<Operation tag="0x42005c" type="Enumeration" value="31"/>
|
||||
<RequestPayload tag="0x420079" type="Structure">
|
||||
<UniqueIdentifier tag="0x420094" type="TextString" value="..."/>
|
||||
<CryptographicParameters tag="0x42002b" type="Structure">
|
||||
<BlockCipherMode tag="0x420011" type="Enumeration" value="1"/>
|
||||
<PaddingMethod tag="0x42005f" type="Enumeration" value="3"/>
|
||||
<CryptographicAlgorithm tag="0x420028" type="Enumeration" value="3"/>
|
||||
<RandomIV tag="0x4200c5" type="Boolean" value="True"/>
|
||||
</CryptographicParameters>
|
||||
<Data tag="0x4200c2" type="ByteString" value="..."/>
|
||||
</RequestPayload>
|
||||
</BatchItem>
|
||||
</RequestMessage>
|
||||
*/
|
||||
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:
|
||||
<RequestMessage tag="0x420078" type="Structure">
|
||||
<RequestHeader tag="0x420077" type="Structure">
|
||||
<ProtocolVersion tag="0x420069" type="Structure">
|
||||
<ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
|
||||
<ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
|
||||
</ProtocolVersion>
|
||||
<BatchCount tag="0x42000d" type="Integer" value="1"/>
|
||||
</RequestHeader>
|
||||
<BatchItem tag="0x42000f" type="Structure">
|
||||
<Operation tag="0x42005c" type="Enumeration" value="32"/>
|
||||
<RequestPayload tag="0x420079" type="Structure">
|
||||
<UniqueIdentifier tag="0x420094" type="TextString" value="..."/>
|
||||
<CryptographicParameters tag="0x42002b" type="Structure">
|
||||
<BlockCipherMode tag="0x420011" type="Enumeration" value="1"/>
|
||||
<PaddingMethod tag="0x42005f" type="Enumeration" value="3"/>
|
||||
<CryptographicAlgorithm tag="0x420028" type="Enumeration" value="3"/>
|
||||
</CryptographicParameters>
|
||||
<Data tag="0x4200c2" type="ByteString" value="..."/>
|
||||
<IVCounterNonce tag="0x42003d" type="ByteString" value="..."/>
|
||||
</RequestPayload>
|
||||
</BatchItem>
|
||||
</RequestMessage>
|
||||
*/
|
||||
return kmip_encrypt_decrypt(unique_identifer, ciphertext, len, iv_data, iv_len, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
<ResponseMessage tag="0x42007b" type="Structure">
|
||||
<ResponseHeader tag="0x42007a" type="Structure">
|
||||
<ProtocolVersion tag="0x420069" type="Structure">
|
||||
<ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
|
||||
<ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
|
||||
</ProtocolVersion>
|
||||
<TimeStamp tag="0x420092" type="DateTime" value="2021-10-12T14:09:25-0500"/>
|
||||
<BatchCount tag="0x42000d" type="Integer" value="1"/>
|
||||
</ResponseHeader>
|
||||
<BatchItem tag="0x42000f" type="Structure">
|
||||
<Operation tag="0x42005c" type="Enumeration" value="31"/>
|
||||
<ResultStatus tag="0x42007f" type="Enumeration" value="0"/>
|
||||
<ResponsePayload tag="0x42007c" type="Structure">
|
||||
<UniqueIdentifier tag="0x420094" type="TextString" value="39"/>
|
||||
<Data tag="0x4200c2" type="ByteString" value="..."/>
|
||||
<IVCounterNonce tag="0x42003d" type="ByteString" value="..."/>
|
||||
</ResponsePayload>
|
||||
</BatchItem>
|
||||
</ResponseMessage>
|
||||
*/
|
||||
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:
|
||||
<ResponseMessage tag="0x42007b" type="Structure">
|
||||
<ResponseHeader tag="0x42007a" type="Structure">
|
||||
<ProtocolVersion tag="0x420069" type="Structure">
|
||||
<ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
|
||||
<ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
|
||||
</ProtocolVersion>
|
||||
<TimeStamp tag="0x420092" type="DateTime" value="2021-10-12T14:09:25-0500"/>
|
||||
<BatchCount tag="0x42000d" type="Integer" value="1"/>
|
||||
</ResponseHeader>
|
||||
<BatchItem tag="0x42000f" type="Structure">
|
||||
<Operation tag="0x42005c" type="Enumeration" value="32"/>
|
||||
<ResultStatus tag="0x42007f" type="Enumeration" value="0"/>
|
||||
<ResponsePayload tag="0x42007c" type="Structure">
|
||||
<UniqueIdentifier tag="0x420094" type="TextString" value="39"/>
|
||||
<Data tag="0x4200c2" type="ByteString" value="..."/>
|
||||
</ResponsePayload>
|
||||
</BatchItem>
|
||||
</ResponseMessage>
|
||||
*/
|
||||
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:
|
||||
<ResponseMessage tag="0x42007b" type="Structure">
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
#if defined(_WIN32)
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
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
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 */)) {
|
||||
|
|
|
|||
|
|
@ -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 <bson/bson.h> /* ssize_t + BSON_CONCAT */
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* 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 */
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -19,8 +19,19 @@
|
|||
#include "mongocrypt-buffer-private.h"
|
||||
#include <bson/bson.h>
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<EdgeFindTokenSetV2> // Array of Edges
|
||||
* cm: <int64> // Queryable Encryption max counter
|
||||
* payload: <document>
|
||||
* g: array<EdgeFindTokenSetV2> // Array of Edges
|
||||
* cm: <int64> // Queryable Encryption max counter
|
||||
* payloadId: <int32> // Payload ID.
|
||||
* firstOperator: <int32>
|
||||
* secondOperator: <int32>
|
||||
* sp: optional<int64> // Sparsity.
|
||||
* pn: optional<int32> // Precision.
|
||||
* tf: optional<int32> // Trim Factor.
|
||||
* mn: optional<any> // Index Min.
|
||||
* mx: optional<any> // 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: <binary> // EDCDerivedFromDataTokenAndCounter
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndCounter
|
||||
* d: <binary> // EDCDerivedFromDataTokenAndContentionFactor
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndContentionFactor
|
||||
* l: <binary> // 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<EdgeFindTokenSet> // Array of Edges
|
||||
* e: <binary> // ServerDataEncryptionLevel1Token
|
||||
* cm: <int64> // Queryable Encryption max counter
|
||||
* cm: <int64> // Queryable Encryption max contentionFactor
|
||||
*/
|
||||
typedef struct {
|
||||
struct {
|
||||
|
|
@ -69,9 +69,9 @@ typedef struct {
|
|||
|
||||
/**
|
||||
* EdgeFindTokenSet is the following BSON document:
|
||||
* d: <binary> // EDCDerivedFromDataTokenAndCounter
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndCounter
|
||||
* c: <binary> // ECCDerivedFromDataTokenAndCounter
|
||||
* d: <binary> // EDCDerivedFromDataTokenAndContentionFactor
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndContentionFactor
|
||||
* c: <binary> // ECCDerivedFromDataTokenAndContentionFactor
|
||||
*
|
||||
* Instances of mc_EdgeFindTokenSet_t are expected to be owned by
|
||||
* mc_FLE2FindRangePayload_t and are freed in
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <bson/bson.h>
|
||||
|
||||
#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: <binary> // EDCDerivedFromDataTokenAndCounter
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndCounter
|
||||
* d: <binary> // EDCDerivedFromDataTokenAndContentionFactor
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndContentionFactor
|
||||
* p: <binary> // Encrypted Tokens
|
||||
* u: <UUID> // Index KeyId
|
||||
* t: <int32> // Encrypted type
|
||||
|
|
@ -45,13 +46,18 @@
|
|||
* e: <binary> // ServerDataEncryptionLevel1Token
|
||||
* l: <binary> // ServerDerivedFromDataToken
|
||||
* k: <int64> // Randomly sampled contention factor value
|
||||
* g: array<EdgeTokenSetV2> // Array of Edges
|
||||
* g: array<EdgeTokenSetV2> // Array of Edges. Only included for range payloads.
|
||||
* sp: optional<int64> // Sparsity. Only included for range payloads.
|
||||
* pn: optional<int32> // Precision. Only included for range payloads.
|
||||
* tf: optional<int32> // Trim Factor. Only included for range payloads.
|
||||
* mn: optional<any> // Index Min. Only included for range payloads.
|
||||
* mx: optional<any> // 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: <binary> // EDCDerivedFromDataTokenAndCounter
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndCounter
|
||||
* d: <binary> // EDCDerivedFromDataTokenAndContentionFactor
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndContentionFactor
|
||||
* l: <binary> // ServerDerivedFromDataToken
|
||||
* p: <binary> // 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@
|
|||
* } FLE2InsertUpdatePayload;
|
||||
*
|
||||
* bson is a BSON document of this form:
|
||||
* d: <binary> // EDCDerivedFromDataTokenAndCounter
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndCounter
|
||||
* c: <binary> // ECCDerivedFromDataTokenAndCounter
|
||||
* d: <binary> // EDCDerivedFromDataTokenAndContentionFactor
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndContentionFactor
|
||||
* c: <binary> // ECCDerivedFromDataTokenAndContentionFactor
|
||||
* p: <binary> // Encrypted Tokens
|
||||
* u: <UUID> // Index KeyId
|
||||
* t: <int32> // 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: <binary> // EDCDerivedFromDataTokenAndCounter
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndCounter
|
||||
* c: <binary> // ECCDerivedFromDataTokenAndCounter
|
||||
* d: <binary> // EDCDerivedFromDataTokenAndContentionFactor
|
||||
* s: <binary> // ESCDerivedFromDataTokenAndContentionFactor
|
||||
* c: <binary> // ECCDerivedFromDataTokenAndContentionFactor
|
||||
* p: <binary> // Encrypted Tokens
|
||||
*
|
||||
* Instances of mc_EdgeTokenSet_t are expected to be owned by
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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__);
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
44
src/third_party/libmongocrypt/dist/src/mc-fle2-tag-and-encrypted-metadata-block-private.h
vendored
Normal file
44
src/third_party/libmongocrypt/dist/src/mc-fle2-tag-and-encrypted-metadata-block-private.h
vendored
Normal file
|
|
@ -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 */
|
||||
81
src/third_party/libmongocrypt/dist/src/mc-fle2-tag-and-encrypted-metadata-block.c
vendored
Normal file
81
src/third_party/libmongocrypt/dist/src/mc-fle2-tag-and-encrypted-metadata-block.c
vendored
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Int32>
|
||||
* "sparsity": Optional<Int64>,
|
||||
* "precision": Optional<Int32>,
|
||||
* "trimFactor": Optional<Int32>,
|
||||
* }
|
||||
*/
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 <float.h> // 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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@
|
|||
#include "mongocrypt-util-private.h"
|
||||
#include <bson/bson.h>
|
||||
|
||||
// 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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
#endif /* MONGOCRYPT_CACHE_OAUTH_PRIVATE_H */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
#endif /* MONGOCRYPT_CACHE_PRIVATE */
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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 "<target_db>.<target_coll>" 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. <db_name>.<coll_name> 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
#endif /* MONGOCRYPT_KEK_PRIVATE_H */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 <bson/bson.h>
|
||||
#include <kms_message/kms_azure_request.h>
|
||||
#include <kms_message/kms_b64.h>
|
||||
#include <kms_message/kms_gcp_request.h>
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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<EdgeTokenSet>
|
||||
{
|
||||
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<EdgeTokenSetV2>
|
||||
{
|
||||
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<EdgeFindTokenSet>
|
||||
{
|
||||
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<EdgeFindTokenSet>
|
||||
{
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "mongocrypt-kek-private.h"
|
||||
#include "mongocrypt-log-private.h"
|
||||
#include "mongocrypt.h"
|
||||
#include <mc-array-private.h>
|
||||
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -19,12 +19,24 @@
|
|||
#include "mongocrypt-log-private.h"
|
||||
#include "mongocrypt-opts-private.h"
|
||||
#include "mongocrypt-private.h"
|
||||
#include <mongocrypt-util-private.h> // mc_iter_document_as_bson
|
||||
|
||||
#include <kms_message/kms_b64.h>
|
||||
|
||||
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 `<type>` or `<type>:<name>`. 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>`.
|
||||
type_nchars = strlen(kmsid);
|
||||
} else {
|
||||
// Parse `kmsid` as `<type>:<name>`.
|
||||
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 `<provider "
|
||||
"type>:<name>` where `<name>` 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": <required UTF-8>
|
||||
* }
|
||||
*/
|
||||
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": <required UTF-8>
|
||||
* "privateKey": <required UTF-8 or Binary>
|
||||
* }
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<BSON value>,
|
||||
* "max": Optional<BSON value>,
|
||||
* "sparsity": Int64,
|
||||
* "precision": Optional<Int32>
|
||||
* "sparsity": Optional<Int64>,
|
||||
* "precision": Optional<Int32>,
|
||||
* "trimFactor": Optional<Int32>
|
||||
* }
|
||||
*
|
||||
* @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 */
|
||||
|
|
|
|||
|
|
@ -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 <link.h>
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -76,4 +76,8 @@ mcr_dll_path_result mcr_dll_path(mcr_dll dll) {
|
|||
};
|
||||
}
|
||||
|
||||
bool mcr_dll_path_supported(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
Loading…
Reference in New Issue