Skip to content

Commit 3742138

Browse files
authored
MONGOCRYPT-550 Add support for FLE2UnindexedEncryptedValueV2 encrypt/decrypt (#589)
MONGOCRYPT-550 Add support for FLE2UnindexedEncryptedValueV2 encrypt/decrypt
1 parent f7472f7 commit 3742138

12 files changed

+1004
-165
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ set (MONGOCRYPT_SOURCES
107107
src/mc-fle2-find-equality-payload-v2.c
108108
src/mc-fle2-payload-iev.c
109109
src/mc-fle2-payload-uev.c
110+
src/mc-fle2-payload-uev-common.c
111+
src/mc-fle2-payload-uev-v2.c
110112
src/mc-fle2-rfds.c
111113
src/mc-range-edge-generation.c
112114
src/mc-range-mincover.c
@@ -468,6 +470,7 @@ set (TEST_MONGOCRYPT_SOURCES
468470
test/test-mc-fle2-payload-iev.c
469471
test/test-mc-fle2-payload-iup.c
470472
test/test-mc-fle2-payload-uev.c
473+
test/test-mc-fle2-payload-uev-v2.c
471474
test/test-mc-fle2-rfds.c
472475
test/test-mc-range-edge-generation.c
473476
test/test-mc-range-mincover.c
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2023-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_COMMON_PRIVATE_H
18+
#define MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_COMMON_PRIVATE_H
19+
20+
#include "mongocrypt-buffer-private.h"
21+
#include "mongocrypt-status-private.h"
22+
#include "mongocrypt-crypto-private.h"
23+
#include "mc-fle-blob-subtype-private.h"
24+
25+
/**
26+
* Deserializes the data in @buf and assigns the parsed values to
27+
* to the output parameters.
28+
* Callers are expected to call _mongocrypt_buffer_init() on the
29+
* @key_uuid and @ciphertext output buffers prior to this call, and
30+
* call _mongocrypt_buffer_cleanup() afterwards.
31+
* Returns false and sets @status on error.
32+
*/
33+
bool
34+
_mc_FLE2UnindexedEncryptedValueCommon_parse (const _mongocrypt_buffer_t *buf,
35+
uint8_t *fle_blob_subtype,
36+
uint8_t *original_bson_type,
37+
_mongocrypt_buffer_t *key_uuid,
38+
_mongocrypt_buffer_t *ciphertext,
39+
mongocrypt_status_t *status);
40+
41+
/**
42+
* Decrypts @ciphertext onto the @plaintext buffer. The @plaintext
43+
* pointer is returned on success. The @fle_blob_subtype must be an
44+
* unindexed type, and determines the decryption algorithm to use.
45+
* Returns NULL and sets @status on error.
46+
*/
47+
const _mongocrypt_buffer_t *
48+
_mc_FLE2UnindexedEncryptedValueCommon_decrypt (
49+
_mongocrypt_crypto_t *crypto,
50+
mc_fle_blob_subtype_t fle_blob_subtype,
51+
const _mongocrypt_buffer_t *key_uuid,
52+
bson_type_t original_bson_type,
53+
const _mongocrypt_buffer_t *ciphertext,
54+
const _mongocrypt_buffer_t *key,
55+
_mongocrypt_buffer_t *plaintext,
56+
mongocrypt_status_t *status);
57+
58+
/**
59+
* Encrypts @plaintext onto the @out buffer. The @fle_blob_subtype must
60+
* be an unindexed type, and determines the encryption algorithm to use.
61+
* Returns false and sets @status on error.
62+
*/
63+
bool
64+
_mc_FLE2UnindexedEncryptedValueCommon_encrypt (
65+
_mongocrypt_crypto_t *crypto,
66+
mc_fle_blob_subtype_t fle_blob_subtype,
67+
const _mongocrypt_buffer_t *key_uuid,
68+
bson_type_t original_bson_type,
69+
const _mongocrypt_buffer_t *plaintext,
70+
const _mongocrypt_buffer_t *key,
71+
_mongocrypt_buffer_t *out,
72+
mongocrypt_status_t *status);
73+
74+
#endif /* MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_COMMON_PRIVATE_H */

src/mc-fle2-payload-uev-common.c

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*
2+
* Copyright 2023-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "mc-fle2-payload-uev-common-private.h"
18+
#include "mc-reader-private.h"
19+
#include "mongocrypt-private.h"
20+
21+
#define CHECK_AND_RETURN(x) \
22+
if (!(x)) { \
23+
return false; \
24+
}
25+
26+
bool
27+
_mc_FLE2UnindexedEncryptedValueCommon_parse (const _mongocrypt_buffer_t *buf,
28+
uint8_t *fle_blob_subtype,
29+
uint8_t *original_bson_type,
30+
_mongocrypt_buffer_t *key_uuid,
31+
_mongocrypt_buffer_t *ciphertext,
32+
mongocrypt_status_t *status)
33+
{
34+
BSON_ASSERT_PARAM (buf);
35+
BSON_ASSERT_PARAM (fle_blob_subtype);
36+
BSON_ASSERT_PARAM (original_bson_type);
37+
BSON_ASSERT_PARAM (key_uuid);
38+
BSON_ASSERT_PARAM (ciphertext);
39+
40+
mc_reader_t reader;
41+
mc_reader_init_from_buffer (&reader, buf, __FUNCTION__);
42+
43+
/* Read fle_blob_subtype. */
44+
CHECK_AND_RETURN (mc_reader_read_u8 (&reader, fle_blob_subtype, status));
45+
46+
/* Read key_uuid. */
47+
CHECK_AND_RETURN (mc_reader_read_buffer (&reader, key_uuid, 16, status));
48+
key_uuid->subtype = BSON_SUBTYPE_UUID;
49+
50+
/* Read original_bson_type. */
51+
CHECK_AND_RETURN (mc_reader_read_u8 (&reader, original_bson_type, status));
52+
53+
/* Read ciphertext. */
54+
CHECK_AND_RETURN (mc_reader_read_buffer (
55+
&reader, ciphertext, mc_reader_get_remaining_length (&reader), status));
56+
57+
return true;
58+
}
59+
60+
const _mongocrypt_buffer_t *
61+
_mc_FLE2UnindexedEncryptedValueCommon_decrypt (
62+
_mongocrypt_crypto_t *crypto,
63+
mc_fle_blob_subtype_t fle_blob_subtype,
64+
const _mongocrypt_buffer_t *key_uuid,
65+
bson_type_t original_bson_type,
66+
const _mongocrypt_buffer_t *ciphertext,
67+
const _mongocrypt_buffer_t *key,
68+
_mongocrypt_buffer_t *plaintext,
69+
mongocrypt_status_t *status)
70+
{
71+
BSON_ASSERT_PARAM (crypto);
72+
BSON_ASSERT_PARAM (key_uuid);
73+
BSON_ASSERT_PARAM (ciphertext);
74+
BSON_ASSERT_PARAM (key);
75+
BSON_ASSERT_PARAM (plaintext);
76+
77+
BSON_ASSERT (MC_SUBTYPE_FLE2UnindexedEncryptedValue == fle_blob_subtype ||
78+
MC_SUBTYPE_FLE2UnindexedEncryptedValueV2 == fle_blob_subtype);
79+
80+
const _mongocrypt_value_encryption_algorithm_t *fle2aead =
81+
(MC_SUBTYPE_FLE2UnindexedEncryptedValue == fle_blob_subtype)
82+
? _mcFLE2AEADAlgorithm ()
83+
: _mcFLE2v2AEADAlgorithm ();
84+
85+
/* Serialize associated data: fle_blob_subtype || key_uuid ||
86+
* original_bson_type */
87+
_mongocrypt_buffer_t AD;
88+
_mongocrypt_buffer_init (&AD);
89+
if (key_uuid->len > UINT32_MAX - 2) {
90+
CLIENT_ERR ("mc_FLE2UnindexedEncryptedValueCommon_decrypt expected "
91+
"key UUID length <= %" PRIu32 " got: %" PRIu32,
92+
UINT32_MAX - 2u,
93+
key_uuid->len);
94+
return NULL;
95+
}
96+
_mongocrypt_buffer_resize (&AD, 1 + key_uuid->len + 1);
97+
98+
AD.data[0] = (uint8_t) fle_blob_subtype;
99+
memcpy (AD.data + 1, key_uuid->data, key_uuid->len);
100+
AD.data[1 + key_uuid->len] = (uint8_t) original_bson_type;
101+
const uint32_t plaintext_len =
102+
fle2aead->get_plaintext_len (ciphertext->len, status);
103+
if (plaintext_len == 0) {
104+
_mongocrypt_buffer_cleanup (&AD);
105+
return NULL;
106+
}
107+
_mongocrypt_buffer_resize (plaintext, plaintext_len);
108+
109+
uint32_t bytes_written;
110+
111+
if (!fle2aead->do_decrypt (
112+
crypto, &AD, key, ciphertext, plaintext, &bytes_written, status)) {
113+
_mongocrypt_buffer_cleanup (&AD);
114+
return NULL;
115+
}
116+
117+
// Some block cipher modes (eg. CBC) may write fewer bytes than the size
118+
// estimate that the plaintext buffer was allocated with. Therefore, the
119+
// plaintext buffer length must be updated to the actual size written.
120+
plaintext->len = bytes_written;
121+
122+
_mongocrypt_buffer_cleanup (&AD);
123+
return plaintext;
124+
}
125+
126+
bool
127+
_mc_FLE2UnindexedEncryptedValueCommon_encrypt (
128+
_mongocrypt_crypto_t *crypto,
129+
mc_fle_blob_subtype_t fle_blob_subtype,
130+
const _mongocrypt_buffer_t *key_uuid,
131+
bson_type_t original_bson_type,
132+
const _mongocrypt_buffer_t *plaintext,
133+
const _mongocrypt_buffer_t *key,
134+
_mongocrypt_buffer_t *out,
135+
mongocrypt_status_t *status)
136+
{
137+
_mongocrypt_buffer_t iv = {0};
138+
_mongocrypt_buffer_t AD = {0};
139+
bool res = false;
140+
141+
BSON_ASSERT_PARAM (crypto);
142+
BSON_ASSERT_PARAM (key_uuid);
143+
BSON_ASSERT_PARAM (plaintext);
144+
BSON_ASSERT_PARAM (key);
145+
BSON_ASSERT_PARAM (out);
146+
147+
BSON_ASSERT (MC_SUBTYPE_FLE2UnindexedEncryptedValue == fle_blob_subtype ||
148+
MC_SUBTYPE_FLE2UnindexedEncryptedValueV2 == fle_blob_subtype);
149+
150+
const _mongocrypt_value_encryption_algorithm_t *fle2aead =
151+
(MC_SUBTYPE_FLE2UnindexedEncryptedValue == fle_blob_subtype)
152+
? _mcFLE2AEADAlgorithm ()
153+
: _mcFLE2v2AEADAlgorithm ();
154+
155+
_mongocrypt_buffer_resize (&iv, MONGOCRYPT_IV_LEN);
156+
if (!_mongocrypt_random (crypto, &iv, MONGOCRYPT_IV_LEN, status)) {
157+
goto fail;
158+
}
159+
160+
/* Serialize associated data: fle_blob_subtype || key_uuid ||
161+
* original_bson_type */
162+
{
163+
if (key_uuid->len > UINT32_MAX - 2) {
164+
CLIENT_ERR ("mc_FLE2UnindexedEncryptedValueCommon_encrypt expected "
165+
"key UUID length <= %" PRIu32 " got: %" PRIu32,
166+
UINT32_MAX - 2u,
167+
key_uuid->len);
168+
goto fail;
169+
}
170+
_mongocrypt_buffer_resize (&AD, 1 + key_uuid->len + 1);
171+
AD.data[0] = (uint8_t) fle_blob_subtype;
172+
memcpy (AD.data + 1, key_uuid->data, key_uuid->len);
173+
AD.data[1 + key_uuid->len] = (uint8_t) original_bson_type;
174+
}
175+
176+
/* Encrypt. */
177+
{
178+
const uint32_t cipherlen =
179+
fle2aead->get_ciphertext_len (plaintext->len, status);
180+
if (cipherlen == 0) {
181+
goto fail;
182+
}
183+
_mongocrypt_buffer_resize (out, cipherlen);
184+
uint32_t bytes_written; /* unused. */
185+
if (!fle2aead->do_encrypt (
186+
crypto, &iv, &AD, key, plaintext, out, &bytes_written, status)) {
187+
goto fail;
188+
}
189+
}
190+
191+
res = true;
192+
193+
fail:
194+
_mongocrypt_buffer_cleanup (&AD);
195+
_mongocrypt_buffer_cleanup (&iv);
196+
return res;
197+
}

src/mc-fle2-payload-uev-private.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
* } FLE2UnindexedEncryptedValue;
3636
*
3737
* ciphertext is the output of:
38-
* EncryptAEAD(
38+
* EncryptAEAD_AES_256_CTR_HMAC_SHA_256(
3939
* key=K_Key,
4040
* plaintext=ClientValue,
4141
* associated_data=(fle_blob_subtype || key_uuid || original_bson_type))
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2023-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_V2_PRIVATE_H
18+
#define MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_V2_PRIVATE_H
19+
20+
#include "mongocrypt-buffer-private.h"
21+
#include "mongocrypt-status-private.h"
22+
#include "mongocrypt-crypto-private.h"
23+
24+
/**
25+
* FLE2UnindexedEncryptedValueV2 represents a FLE2 protocol version 2
26+
* unindexed encrypted value. It is created client side.
27+
*
28+
* FLE2UnindexedEncryptedValueV2 has the following data layout:
29+
*
30+
* struct {
31+
* uint8_t fle_blob_subtype = 16;
32+
* uint8_t key_uuid[16];
33+
* uint8_t original_bson_type;
34+
* uint8_t ciphertext[ciphertext_length];
35+
* } FLE2UnindexedEncryptedValueV2;
36+
*
37+
* ciphertext is the output of:
38+
* EncryptAEAD_AES_256_CBC_HMAC_SHA_256(
39+
* key=K_Key,
40+
* plaintext=ClientValue,
41+
* associated_data=(fle_blob_subtype || key_uuid || original_bson_type))
42+
*/
43+
44+
typedef struct _mc_FLE2UnindexedEncryptedValueV2_t
45+
mc_FLE2UnindexedEncryptedValueV2_t;
46+
47+
mc_FLE2UnindexedEncryptedValueV2_t *
48+
mc_FLE2UnindexedEncryptedValueV2_new (void);
49+
50+
bool
51+
mc_FLE2UnindexedEncryptedValueV2_parse (mc_FLE2UnindexedEncryptedValueV2_t *uev,
52+
const _mongocrypt_buffer_t *buf,
53+
mongocrypt_status_t *status);
54+
55+
/* mc_FLE2UnindexedEncryptedValueV2_get_original_bson_type returns
56+
* original_bson_type. Returns 0 and sets @status on error.
57+
* It is an error to call before mc_FLE2UnindexedEncryptedValueV2_parse. */
58+
bson_type_t
59+
mc_FLE2UnindexedEncryptedValueV2_get_original_bson_type (
60+
const mc_FLE2UnindexedEncryptedValueV2_t *uev, mongocrypt_status_t *status);
61+
62+
/* mc_FLE2UnindexedEncryptedValueV2_get_key_uuid returns key_uuid. Returns
63+
* NULL and sets @status on error. It is an error to call before
64+
* mc_FLE2UnindexedEncryptedValueV2_parse. */
65+
const _mongocrypt_buffer_t *
66+
mc_FLE2UnindexedEncryptedValueV2_get_key_uuid (
67+
const mc_FLE2UnindexedEncryptedValueV2_t *uev, mongocrypt_status_t *status);
68+
69+
/* mc_FLE2UnindexedEncryptedValueV2_decrypt decrypts ciphertext.
70+
* Returns NULL and sets @status on error. It is an error to call before
71+
* mc_FLE2UnindexedEncryptedValueV2_parse. */
72+
const _mongocrypt_buffer_t *
73+
mc_FLE2UnindexedEncryptedValueV2_decrypt (
74+
_mongocrypt_crypto_t *crypto,
75+
mc_FLE2UnindexedEncryptedValueV2_t *uev,
76+
const _mongocrypt_buffer_t *key,
77+
mongocrypt_status_t *status);
78+
79+
/* mc_FLE2UnindexedEncryptedValueV2_encrypt outputs the ciphertext field of
80+
* FLEUnindexedEncryptedValueV2 into @out. Returns false and sets @status on
81+
* error. */
82+
bool
83+
mc_FLE2UnindexedEncryptedValueV2_encrypt (_mongocrypt_crypto_t *crypto,
84+
const _mongocrypt_buffer_t *key_uuid,
85+
bson_type_t original_bson_type,
86+
const _mongocrypt_buffer_t *plaintext,
87+
const _mongocrypt_buffer_t *key,
88+
_mongocrypt_buffer_t *out,
89+
mongocrypt_status_t *status);
90+
91+
void
92+
mc_FLE2UnindexedEncryptedValueV2_destroy (
93+
mc_FLE2UnindexedEncryptedValueV2_t *uev);
94+
95+
#endif /* MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_V2_PRIVATE_H */

0 commit comments

Comments
 (0)