libmongocrypt-helper 1.20.3.0.1001 → 1.20.5.0.1001
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/libmongocrypt/libmongocrypt/CHANGELOG.md +12 -0
- data/ext/libmongocrypt/libmongocrypt/etc/cyclonedx.sbom.json +2 -2
- data/ext/libmongocrypt/libmongocrypt/src/mc-fle2-insert-update-payload-v2.c +6 -1
- data/ext/libmongocrypt/libmongocrypt/src/mc-fle2-insert-update-payload.c +6 -1
- data/ext/libmongocrypt/libmongocrypt/src/mc-fle2-payload-iev.c +10 -2
- data/ext/libmongocrypt/libmongocrypt/src/mc-fle2-payload-uev-common.c +1 -1
- data/ext/libmongocrypt/libmongocrypt/src/mongocrypt-crypto.c +7 -3
- data/ext/libmongocrypt/libmongocrypt/src/mongocrypt-ctx-decrypt.c +28 -1
- data/ext/libmongocrypt/libmongocrypt/src/mongocrypt-endpoint.c +122 -10
- data/ext/libmongocrypt/libmongocrypt/src/mongocrypt-kek.c +19 -7
- data/ext/libmongocrypt/libmongocrypt/src/mongocrypt-key.c +4 -0
- data/ext/libmongocrypt/libmongocrypt/test/data/kek-tests.json +3 -3
- data/ext/libmongocrypt/libmongocrypt/test/test-mc-fle2-payload-iup-v2.c +64 -0
- data/ext/libmongocrypt/libmongocrypt/test/test-mc-fle2-payload-iup.c +67 -0
- data/ext/libmongocrypt/libmongocrypt/test/test-mongocrypt-ctx-decrypt.c +46 -20
- data/ext/libmongocrypt/libmongocrypt/test/test-mongocrypt-endpoint.c +170 -0
- data/ext/libmongocrypt/libmongocrypt/test/test-mongocrypt-kek.c +36 -0
- data/ext/libmongocrypt/libmongocrypt/test/test-mongocrypt-key.c +26 -0
- data/lib/libmongocrypt_helper/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b483fc7b479e693858209265fa305b4bd51e34592b452bbbd36c32c13f262802
|
|
4
|
+
data.tar.gz: 352d1052b07f14081865b5c02fe8b33ce2b0a739550db43a6e57d6c26cd1a08b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 945910ccf2515b64cbd9dd1d97a9de2822562a2544533006e3aa5fa7837107697f6b38e5f43d0da1ce3218fa98d36362a9f883362d9e1efb16bb90fbee7d5236
|
|
7
|
+
data.tar.gz: ef89918bdf09d579c60cce8aa5ecdd779a655edffc7223fef5a97fc00fa63ef787c3d5b360ce880812dc3d96562c1ebda7ca158ba4968377e1fd95fcb9c9a81c
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# ChangeLog
|
|
2
2
|
|
|
3
|
+
## 1.20.5
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Fix possible abort parsing endpoints.
|
|
7
|
+
- Reject more payloads that cannot be decrypted in explicit decryption.
|
|
8
|
+
- Fix more degenerate cases of masterKey parsing.
|
|
9
|
+
|
|
10
|
+
## 1.20.4
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Reject undersized decryption payloads.
|
|
14
|
+
|
|
3
15
|
## 1.20.3
|
|
4
16
|
|
|
5
17
|
### Fixed
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
}
|
|
58
58
|
],
|
|
59
59
|
"metadata": {
|
|
60
|
-
"timestamp": "2026-
|
|
60
|
+
"timestamp": "2026-09-03T12:34:15.836217+00:00",
|
|
61
61
|
"tools": [
|
|
62
62
|
{
|
|
63
63
|
"externalReferences": [
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
}
|
|
101
101
|
]
|
|
102
102
|
},
|
|
103
|
-
"serialNumber": "urn:uuid:
|
|
103
|
+
"serialNumber": "urn:uuid:f38989f9-86cb-4a6e-94b5-bbcbf0a8fc1f",
|
|
104
104
|
"version": 1,
|
|
105
105
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
106
106
|
"bomFormat": "CycloneDX",
|
|
@@ -523,7 +523,12 @@ const _mongocrypt_buffer_t *mc_FLE2InsertUpdatePayloadV2_decrypt(_mongocrypt_cry
|
|
|
523
523
|
return NULL;
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
-
|
|
526
|
+
const uint32_t plaintext_len = fle2v2->get_plaintext_len(ciphertext.len, status);
|
|
527
|
+
if (!mongocrypt_status_ok(status)) {
|
|
528
|
+
return NULL;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
_mongocrypt_buffer_resize(&iup->plaintext, plaintext_len);
|
|
527
532
|
uint32_t bytes_written;
|
|
528
533
|
|
|
529
534
|
if (!fle2v2->do_decrypt(crypto, &iup->userKeyId, user_key, &ciphertext, &iup->plaintext, &bytes_written, status)) {
|
|
@@ -252,7 +252,12 @@ const _mongocrypt_buffer_t *mc_FLE2InsertUpdatePayload_decrypt(_mongocrypt_crypt
|
|
|
252
252
|
return NULL;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
|
|
255
|
+
const uint32_t plaintext_len = fle2aead->get_plaintext_len(ciphertext.len, status);
|
|
256
|
+
if (!mongocrypt_status_ok(status)) {
|
|
257
|
+
return NULL;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
_mongocrypt_buffer_resize(&iup->plaintext, plaintext_len);
|
|
256
261
|
uint32_t bytes_written; /* ignored */
|
|
257
262
|
|
|
258
263
|
if (!fle2aead
|
|
@@ -353,7 +353,11 @@ static bool mc_FLE2IndexedEncryptedValue_decrypt(_mongocrypt_crypto_t *crypto,
|
|
|
353
353
|
const _mongocrypt_buffer_t *token_buf = mc_ServerDataEncryptionLevel1Token_get(token);
|
|
354
354
|
uint32_t bytes_written;
|
|
355
355
|
|
|
356
|
-
|
|
356
|
+
const uint32_t inner_len = fle2alg->get_plaintext_len(iev->InnerEncrypted.len, status);
|
|
357
|
+
if (!mongocrypt_status_ok(status)) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
_mongocrypt_buffer_resize(&iev->Inner, inner_len);
|
|
357
361
|
|
|
358
362
|
/* Decrypt InnerEncrypted. */
|
|
359
363
|
if (!fle2alg->do_decrypt(crypto,
|
|
@@ -445,7 +449,11 @@ bool mc_FLE2IndexedEncryptedValue_add_K_Key(_mongocrypt_crypto_t *crypto,
|
|
|
445
449
|
return false;
|
|
446
450
|
}
|
|
447
451
|
/* Attempt to decrypt ClientEncryptedValue */
|
|
448
|
-
|
|
452
|
+
const uint32_t client_value_len = fle2aead->get_plaintext_len(iev->ClientEncryptedValue.len, status);
|
|
453
|
+
if (!mongocrypt_status_ok(status)) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
_mongocrypt_buffer_resize(&iev->ClientValue, client_value_len);
|
|
449
457
|
uint32_t bytes_written;
|
|
450
458
|
if (!fle2aead->do_decrypt(crypto,
|
|
451
459
|
&iev->K_KeyId,
|
|
@@ -93,7 +93,7 @@ const _mongocrypt_buffer_t *_mc_FLE2UnindexedEncryptedValueCommon_decrypt(_mongo
|
|
|
93
93
|
memcpy(AD.data + 1, key_uuid->data, key_uuid->len);
|
|
94
94
|
AD.data[1 + key_uuid->len] = (uint8_t)original_bson_type;
|
|
95
95
|
const uint32_t plaintext_len = fle2aead->get_plaintext_len(ciphertext->len, status);
|
|
96
|
-
if (
|
|
96
|
+
if (!mongocrypt_status_ok(status)) {
|
|
97
97
|
_mongocrypt_buffer_cleanup(&AD);
|
|
98
98
|
return NULL;
|
|
99
99
|
}
|
|
@@ -443,9 +443,9 @@ static uint32_t _mongocrypt_calculate_plaintext_len(uint32_t inlen,
|
|
|
443
443
|
_mongocrypt_hmac_type_t hmac,
|
|
444
444
|
mongocrypt_status_t *status) {
|
|
445
445
|
const uint32_t hmaclen = (hmac == HMAC_NONE) ? 0 : MONGOCRYPT_HMAC_LEN;
|
|
446
|
-
const uint32_t mincipher = (mode == MODE_CTR) ?
|
|
446
|
+
const uint32_t mincipher = (mode == MODE_CTR) ? 1 : MONGOCRYPT_BLOCK_SIZE;
|
|
447
447
|
if (inlen < (MONGOCRYPT_IV_LEN + mincipher + hmaclen)) {
|
|
448
|
-
CLIENT_ERR("input ciphertext too small. Must be
|
|
448
|
+
CLIENT_ERR("input ciphertext too small. Must be more than %" PRIu32 " bytes",
|
|
449
449
|
MONGOCRYPT_IV_LEN + mincipher + hmaclen);
|
|
450
450
|
return 0;
|
|
451
451
|
}
|
|
@@ -1346,7 +1346,11 @@ bool _mongocrypt_unwrap_key(_mongocrypt_crypto_t *crypto,
|
|
|
1346
1346
|
|
|
1347
1347
|
// _mongocrypt_wrap_key() uses FLE1 algorithm parameters.
|
|
1348
1348
|
_mongocrypt_buffer_init(dek);
|
|
1349
|
-
|
|
1349
|
+
const uint32_t plaintext_len = fle1alg->get_plaintext_len(encrypted_dek->len, status);
|
|
1350
|
+
if (!mongocrypt_status_ok(status)) {
|
|
1351
|
+
return false;
|
|
1352
|
+
}
|
|
1353
|
+
_mongocrypt_buffer_resize(dek, plaintext_len);
|
|
1350
1354
|
|
|
1351
1355
|
if (!fle1alg->do_decrypt(crypto, NULL /* associated data. */, kek, encrypted_dek, dek, &bytes_written, status)) {
|
|
1352
1356
|
return false;
|
|
@@ -344,7 +344,7 @@ static bool _replace_FLE1Payload_with_plaintext(void *ctx,
|
|
|
344
344
|
|
|
345
345
|
const _mongocrypt_value_encryption_algorithm_t *fle1alg = _mcFLE1Algorithm();
|
|
346
346
|
plaintext.len = fle1alg->get_plaintext_len(ciphertext.data.len, status);
|
|
347
|
-
CHECK_AND_RETURN(
|
|
347
|
+
CHECK_AND_RETURN(mongocrypt_status_ok(status));
|
|
348
348
|
plaintext.data = bson_malloc0(plaintext.len);
|
|
349
349
|
BSON_ASSERT(plaintext.data);
|
|
350
350
|
|
|
@@ -766,6 +766,25 @@ static void _cleanup(mongocrypt_ctx_t *ctx) {
|
|
|
766
766
|
_mongocrypt_buffer_cleanup(&dctx->decrypted_doc);
|
|
767
767
|
}
|
|
768
768
|
|
|
769
|
+
/* Returns true if a subtype-6 payload's first byte names a recognized FLE blob
|
|
770
|
+
* subtype. */
|
|
771
|
+
static bool _is_decryptable_fle_blob_subtype(uint8_t byte) {
|
|
772
|
+
switch (byte) {
|
|
773
|
+
case MC_SUBTYPE_FLE1DeterministicEncryptedValue:
|
|
774
|
+
case MC_SUBTYPE_FLE1RandomEncryptedValue:
|
|
775
|
+
case MC_SUBTYPE_FLE2InsertUpdatePayload:
|
|
776
|
+
case MC_SUBTYPE_FLE2UnindexedEncryptedValue:
|
|
777
|
+
case MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue:
|
|
778
|
+
case MC_SUBTYPE_FLE2IndexedRangeEncryptedValue:
|
|
779
|
+
case MC_SUBTYPE_FLE2InsertUpdatePayloadV2:
|
|
780
|
+
case MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2:
|
|
781
|
+
case MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2:
|
|
782
|
+
case MC_SUBTYPE_FLE2UnindexedEncryptedValueV2:
|
|
783
|
+
case MC_SUBTYPE_FLE2IndexedTextEncryptedValue: return true;
|
|
784
|
+
default: return false;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
769
788
|
bool mongocrypt_ctx_explicit_decrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *msg) {
|
|
770
789
|
bson_iter_t iter;
|
|
771
790
|
bson_t as_bson;
|
|
@@ -804,6 +823,14 @@ bool mongocrypt_ctx_explicit_decrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_bina
|
|
|
804
823
|
CLIENT_ERR("decryption expected BSON binary subtype %d, got %d", (int)BSON_SUBTYPE_ENCRYPTED, (int)subtype);
|
|
805
824
|
return _mongocrypt_ctx_fail(ctx);
|
|
806
825
|
}
|
|
826
|
+
if (binary_len == 0) {
|
|
827
|
+
CLIENT_ERR("unsupported FLE blob subtype: expected non-empty payload");
|
|
828
|
+
return _mongocrypt_ctx_fail(ctx);
|
|
829
|
+
}
|
|
830
|
+
if (!_is_decryptable_fle_blob_subtype(binary[0])) {
|
|
831
|
+
CLIENT_ERR("unsupported FLE blob subtype: %hhu", binary[0]);
|
|
832
|
+
return _mongocrypt_ctx_fail(ctx);
|
|
833
|
+
}
|
|
807
834
|
}
|
|
808
835
|
|
|
809
836
|
if (!mongocrypt_ctx_decrypt_init(ctx, msg)) {
|
|
@@ -34,6 +34,104 @@ void _mongocrypt_endpoint_destroy(_mongocrypt_endpoint_t *endpoint) {
|
|
|
34
34
|
bson_free(endpoint);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/* RFC 3986 section 2.3: unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" */
|
|
38
|
+
static bool _is_unreserved(char c) {
|
|
39
|
+
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.'
|
|
40
|
+
|| c == '_' || c == '~';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* RFC 3986 section 2.2: reserved = gen-delims / sub-delims */
|
|
44
|
+
static bool _is_reserved(char c) {
|
|
45
|
+
return NULL
|
|
46
|
+
!= strchr(":/?#[]@" /* gen-delims */
|
|
47
|
+
"!$&'()*+,;=" /* sub-delims */,
|
|
48
|
+
c);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static bool _is_hexdig(char c) {
|
|
52
|
+
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* _endpoint_chars_are_valid returns true if `endpoint_raw` contains only characters permitted in a URI
|
|
56
|
+
* by RFC 3986. Useful to prevent malformed endpoints being used to construct HTTP requests. */
|
|
57
|
+
static bool _endpoint_chars_are_valid(const char *endpoint_raw, mongocrypt_status_t *status) {
|
|
58
|
+
for (const char *c = endpoint_raw; *c != '\0'; c++) {
|
|
59
|
+
if (_is_unreserved(*c) || _is_reserved(*c)) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (*c == '%') {
|
|
63
|
+
/* Returns false if either character is the NUL terminator. */
|
|
64
|
+
if (!_is_hexdig(c[1])) {
|
|
65
|
+
CLIENT_ERR("Invalid percent-encoding in endpoint at offset %zu: 0x%02x",
|
|
66
|
+
(size_t)(c - endpoint_raw) + 1,
|
|
67
|
+
(unsigned char)c[1]);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
if (!_is_hexdig(c[2])) {
|
|
71
|
+
CLIENT_ERR("Invalid percent-encoding in endpoint at offset %zu: 0x%02x",
|
|
72
|
+
(size_t)(c - endpoint_raw) + 2,
|
|
73
|
+
(unsigned char)c[2]);
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
c += 2;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
CLIENT_ERR("Invalid character in endpoint at offset %zu: 0x%02x",
|
|
80
|
+
(size_t)(c - endpoint_raw),
|
|
81
|
+
(unsigned char)*c);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* RFC 3986 section 2.2: sub-delims. Permitted in a reg-name; gen-delims are not. */
|
|
88
|
+
static bool _is_sub_delim(char c) {
|
|
89
|
+
return NULL != strchr("!$&'()*+,;=", c);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* _host_chars_are_valid returns true if `host` is an RFC 3986 section 3.2.2 `reg-name`:
|
|
93
|
+
*
|
|
94
|
+
* reg-name = *( unreserved / pct-encoded / sub-delims )
|
|
95
|
+
*
|
|
96
|
+
* The gen-delims (":/?#[]@") are not permitted. The parse above already ends the host at ':', '/', and '?',
|
|
97
|
+
* so in practice this rejects '#', '@', and the brackets of an IP-literal.
|
|
98
|
+
*
|
|
99
|
+
* This is defense in depth. `host` reaches the Host header, where CR and LF are the only characters that can
|
|
100
|
+
* alter the message and kms-message rejects them, and `host_and_port` is passed to the consumer as the
|
|
101
|
+
* address to connect to, where a gen-delim yields a name that does not resolve rather than a structural
|
|
102
|
+
* problem. An `IP-literal` (RFC 3986 section 3.2.2) is not supported: the parse splits the host at the first
|
|
103
|
+
* colon with no bracket handling, so "[2001:db8::1]" yielded a host of "[2001" and a port of "db8::1]". */
|
|
104
|
+
static bool _host_chars_are_valid(const char *host, mongocrypt_status_t *status) {
|
|
105
|
+
for (const char *c = host; *c != '\0'; c++) {
|
|
106
|
+
if (_is_unreserved(*c) || _is_sub_delim(*c)) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (*c == '%') {
|
|
110
|
+
/* Already validated by _endpoint_chars_are_valid. */
|
|
111
|
+
c += 2;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
CLIENT_ERR("Invalid character in endpoint host: 0x%02x", (unsigned char)*c);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* _port_chars_are_valid returns true if `port` is a non-empty run of digits. */
|
|
121
|
+
static bool _port_chars_are_valid(const char *port, mongocrypt_status_t *status) {
|
|
122
|
+
if (*port == '\0') {
|
|
123
|
+
CLIENT_ERR("Invalid endpoint, expected a port after the colon");
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
for (const char *c = port; *c != '\0'; c++) {
|
|
127
|
+
if (*c < '0' || *c > '9') {
|
|
128
|
+
CLIENT_ERR("Invalid character in endpoint port: 0x%02x", (unsigned char)*c);
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
37
135
|
/* Parses a subset of URIs of the form:
|
|
38
136
|
* [protocol://][host[:port]][path][?query]
|
|
39
137
|
*/
|
|
@@ -61,6 +159,11 @@ _mongocrypt_endpoint_t *_mongocrypt_endpoint_new(const char *endpoint_raw,
|
|
|
61
159
|
goto fail;
|
|
62
160
|
}
|
|
63
161
|
|
|
162
|
+
/* Validate before parsing: every field parsed below is a substring of `original`. */
|
|
163
|
+
if (!_endpoint_chars_are_valid(endpoint->original, status)) {
|
|
164
|
+
goto fail;
|
|
165
|
+
}
|
|
166
|
+
|
|
64
167
|
/* Parse optional protocol. */
|
|
65
168
|
pos = strstr(endpoint->original, "://");
|
|
66
169
|
if (pos) {
|
|
@@ -92,14 +195,15 @@ _mongocrypt_endpoint_t *_mongocrypt_endpoint_new(const char *endpoint_raw,
|
|
|
92
195
|
colon = strstr(pos, ":");
|
|
93
196
|
qmark = strstr(pos, "?");
|
|
94
197
|
slash = strstr(pos, "/");
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
198
|
+
/* The host ends at the first delimiter. A colon delimits a port only if it
|
|
199
|
+
* precedes the path and query: in "example.com/path:8080" the colon is part
|
|
200
|
+
* of the path, not a port separator. */
|
|
201
|
+
host_end = colon;
|
|
202
|
+
if (slash && (!host_end || slash < host_end)) {
|
|
98
203
|
host_end = slash;
|
|
99
|
-
}
|
|
204
|
+
}
|
|
205
|
+
if (qmark && (!host_end || qmark < host_end)) {
|
|
100
206
|
host_end = qmark;
|
|
101
|
-
} else {
|
|
102
|
-
host_end = NULL;
|
|
103
207
|
}
|
|
104
208
|
|
|
105
209
|
if (host_end) {
|
|
@@ -112,12 +216,16 @@ _mongocrypt_endpoint_t *_mongocrypt_endpoint_new(const char *endpoint_raw,
|
|
|
112
216
|
endpoint->host = bson_strdup(host_start);
|
|
113
217
|
}
|
|
114
218
|
|
|
219
|
+
if (!_host_chars_are_valid(endpoint->host, status)) {
|
|
220
|
+
goto fail;
|
|
221
|
+
}
|
|
222
|
+
|
|
115
223
|
/* Parse optional port */
|
|
116
|
-
if (colon) {
|
|
224
|
+
if (colon && colon == host_end) {
|
|
117
225
|
prev = colon + 1;
|
|
118
|
-
qmark = strstr(
|
|
119
|
-
slash = strstr(
|
|
120
|
-
if (slash) {
|
|
226
|
+
qmark = strstr(prev, "?");
|
|
227
|
+
slash = strstr(prev, "/");
|
|
228
|
+
if (slash && (!qmark || slash < qmark)) {
|
|
121
229
|
endpoint->port = bson_strndup(prev, (size_t)(slash - prev));
|
|
122
230
|
} else if (qmark) {
|
|
123
231
|
BSON_ASSERT(qmark >= prev);
|
|
@@ -125,6 +233,10 @@ _mongocrypt_endpoint_t *_mongocrypt_endpoint_new(const char *endpoint_raw,
|
|
|
125
233
|
} else {
|
|
126
234
|
endpoint->port = bson_strdup(prev);
|
|
127
235
|
}
|
|
236
|
+
|
|
237
|
+
if (!_port_chars_are_valid(endpoint->port, status)) {
|
|
238
|
+
goto fail;
|
|
239
|
+
}
|
|
128
240
|
}
|
|
129
241
|
|
|
130
242
|
/* Parse optional path */
|
|
@@ -171,18 +171,25 @@ bool _mongocrypt_kek_parse_owned(const bson_t *bson, _mongocrypt_kek_t *kek, mon
|
|
|
171
171
|
BSON_ASSERT_PARAM(bson);
|
|
172
172
|
BSON_ASSERT_PARAM(kek);
|
|
173
173
|
|
|
174
|
+
/* Parse into a scratch KEK and only commit to `kek` once the whole
|
|
175
|
+
* definition is valid. Writing the provider tag before the union payload is
|
|
176
|
+
* populated would leave `kek` in a state where the tag and the union
|
|
177
|
+
* disagree, which _mongocrypt_kek_cleanup would act on. */
|
|
178
|
+
_mongocrypt_kek_t parsed;
|
|
179
|
+
memset(&parsed, 0, sizeof(parsed));
|
|
180
|
+
|
|
174
181
|
if (!_mongocrypt_parse_required_utf8(bson, "provider", &kms_provider, status)) {
|
|
175
182
|
goto done;
|
|
176
183
|
}
|
|
177
184
|
|
|
178
|
-
|
|
185
|
+
parsed.kmsid = bson_strdup(kms_provider);
|
|
179
186
|
|
|
180
187
|
_mongocrypt_kms_provider_t type;
|
|
181
|
-
if (!mc_kmsid_parse(
|
|
188
|
+
if (!mc_kmsid_parse(parsed.kmsid, &type, &parsed.kmsid_name, status)) {
|
|
182
189
|
goto done;
|
|
183
190
|
}
|
|
184
191
|
|
|
185
|
-
|
|
192
|
+
parsed.kms_provider = type;
|
|
186
193
|
switch (type) {
|
|
187
194
|
default:
|
|
188
195
|
case MONGOCRYPT_KMS_PROVIDER_NONE: {
|
|
@@ -190,7 +197,7 @@ bool _mongocrypt_kek_parse_owned(const bson_t *bson, _mongocrypt_kek_t *kek, mon
|
|
|
190
197
|
goto done;
|
|
191
198
|
}
|
|
192
199
|
case MONGOCRYPT_KMS_PROVIDER_AWS: {
|
|
193
|
-
if (!_mongocrypt_aws_kek_parse(&
|
|
200
|
+
if (!_mongocrypt_aws_kek_parse(&parsed.provider.aws, parsed.kmsid, bson, status)) {
|
|
194
201
|
goto done;
|
|
195
202
|
}
|
|
196
203
|
break;
|
|
@@ -202,27 +209,32 @@ bool _mongocrypt_kek_parse_owned(const bson_t *bson, _mongocrypt_kek_t *kek, mon
|
|
|
202
209
|
break;
|
|
203
210
|
}
|
|
204
211
|
case MONGOCRYPT_KMS_PROVIDER_AZURE: {
|
|
205
|
-
if (!_mongocrypt_azure_kek_parse(&
|
|
212
|
+
if (!_mongocrypt_azure_kek_parse(&parsed.provider.azure, parsed.kmsid, bson, status)) {
|
|
206
213
|
goto done;
|
|
207
214
|
}
|
|
208
215
|
break;
|
|
209
216
|
}
|
|
210
217
|
case MONGOCRYPT_KMS_PROVIDER_GCP: {
|
|
211
|
-
if (!_mongocrypt_gcp_kek_parse(&
|
|
218
|
+
if (!_mongocrypt_gcp_kek_parse(&parsed.provider.gcp, parsed.kmsid, bson, status)) {
|
|
212
219
|
goto done;
|
|
213
220
|
}
|
|
214
221
|
break;
|
|
215
222
|
}
|
|
216
223
|
case MONGOCRYPT_KMS_PROVIDER_KMIP: {
|
|
217
|
-
if (!_mongocrypt_kmip_kek_parse(&
|
|
224
|
+
if (!_mongocrypt_kmip_kek_parse(&parsed.provider.kmip, parsed.kmsid, bson, status)) {
|
|
218
225
|
goto done;
|
|
219
226
|
}
|
|
220
227
|
break;
|
|
221
228
|
}
|
|
222
229
|
}
|
|
223
230
|
|
|
231
|
+
/* Success: replace any prior contents of `kek` and take ownership. */
|
|
232
|
+
_mongocrypt_kek_cleanup(kek);
|
|
233
|
+
*kek = parsed;
|
|
234
|
+
memset(&parsed, 0, sizeof(parsed));
|
|
224
235
|
ret = true;
|
|
225
236
|
done:
|
|
237
|
+
_mongocrypt_kek_cleanup(&parsed);
|
|
226
238
|
bson_free(kms_provider);
|
|
227
239
|
return ret;
|
|
228
240
|
}
|
|
@@ -181,6 +181,10 @@ bool _mongocrypt_key_parse_owned(const bson_t *bson, _mongocrypt_key_doc_t *out,
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
if (0 == strcmp("masterKey", field)) {
|
|
184
|
+
if (has_master_key) {
|
|
185
|
+
CLIENT_ERR("duplicate field 'masterKey' in key document");
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
184
188
|
has_master_key = true;
|
|
185
189
|
if (!_parse_masterkey(&iter, out, status)) {
|
|
186
190
|
return false;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"keyName": "example keyName",
|
|
16
16
|
"provider": "azure"
|
|
17
17
|
},
|
|
18
|
-
"expect": "
|
|
18
|
+
"expect": "Invalid character in endpoint"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"input": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"key": "example arn",
|
|
48
48
|
"provider": "aws"
|
|
49
49
|
},
|
|
50
|
-
"expect": "
|
|
50
|
+
"expect": "Invalid character in endpoint"
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
"input": {
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"keyRing": "example keyRing",
|
|
80
80
|
"keyName": "example keyName"
|
|
81
81
|
},
|
|
82
|
-
"expect": "
|
|
82
|
+
"expect": "Invalid character in endpoint"
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
"input": {
|
|
@@ -289,10 +289,74 @@ static void _test_mc_FLE2InsertUpdatePayloadV2_parse_errors(_mongocrypt_tester_t
|
|
|
289
289
|
}
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
/* Builds a minimal parseable FLE2InsertUpdatePayloadV2 with a 'v' of `vlen` bytes.
|
|
293
|
+
* 'v' is a 16 byte user key UUID followed by (vlen - 16) bytes of ciphertext. */
|
|
294
|
+
static void _make_iup_v2_with_value_len(_mongocrypt_buffer_t *out, uint32_t vlen) {
|
|
295
|
+
BSON_ASSERT(vlen >= UUID_LEN);
|
|
296
|
+
uint8_t token[32] = {0};
|
|
297
|
+
uint8_t uuid[UUID_LEN];
|
|
298
|
+
uint8_t value[128];
|
|
299
|
+
|
|
300
|
+
memset(uuid, 0xAB, sizeof(uuid));
|
|
301
|
+
memset(value, 0x41, sizeof(value));
|
|
302
|
+
BSON_ASSERT(vlen <= sizeof(value));
|
|
303
|
+
memcpy(value, uuid, sizeof(uuid));
|
|
304
|
+
|
|
305
|
+
bson_t payload;
|
|
306
|
+
bson_init(&payload);
|
|
307
|
+
ASSERT(bson_append_binary(&payload, "d", -1, BSON_SUBTYPE_BINARY, token, sizeof(token)));
|
|
308
|
+
ASSERT(bson_append_binary(&payload, "s", -1, BSON_SUBTYPE_BINARY, token, sizeof(token)));
|
|
309
|
+
ASSERT(bson_append_binary(&payload, "p", -1, BSON_SUBTYPE_BINARY, token, sizeof(token)));
|
|
310
|
+
ASSERT(bson_append_binary(&payload, "u", -1, BSON_SUBTYPE_UUID, uuid, sizeof(uuid)));
|
|
311
|
+
ASSERT(bson_append_int32(&payload, "t", -1, (int32_t)BSON_TYPE_UTF8));
|
|
312
|
+
ASSERT(bson_append_binary(&payload, "v", -1, BSON_SUBTYPE_BINARY, value, vlen));
|
|
313
|
+
ASSERT(bson_append_binary(&payload, "e", -1, BSON_SUBTYPE_BINARY, token, sizeof(token)));
|
|
314
|
+
ASSERT(bson_append_binary(&payload, "l", -1, BSON_SUBTYPE_BINARY, token, sizeof(token)));
|
|
315
|
+
ASSERT(bson_append_int64(&payload, "k", -1, 0));
|
|
316
|
+
|
|
317
|
+
_mongocrypt_buffer_init_size(out, 1 + (uint32_t)payload.len);
|
|
318
|
+
out->data[0] = (uint8_t)MC_SUBTYPE_FLE2InsertUpdatePayloadV2;
|
|
319
|
+
memcpy(out->data + 1, bson_get_data(&payload), payload.len);
|
|
320
|
+
bson_destroy(&payload);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* A 'v' short enough that the plaintext length would be zero must be rejected
|
|
324
|
+
* with an error, not crash. */
|
|
325
|
+
static void _test_mc_FLE2InsertUpdatePayloadV2_decrypt_short_ciphertext(_mongocrypt_tester_t *tester) {
|
|
326
|
+
mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
|
|
327
|
+
_mongocrypt_buffer_t user_key;
|
|
328
|
+
_mongocrypt_buffer_init_size(&user_key, MONGOCRYPT_KEY_LEN);
|
|
329
|
+
memset(user_key.data, 0xCD, user_key.len);
|
|
330
|
+
|
|
331
|
+
/* FLE2v2AEAD is CBC + HMAC-SHA256: ciphertext must be at least
|
|
332
|
+
* IV (16) + one block (16) + HMAC (32) bytes. */
|
|
333
|
+
const uint32_t vlens[] = {UUID_LEN, UUID_LEN + 1, UUID_LEN + 48, UUID_LEN + 63};
|
|
334
|
+
for (size_t i = 0; i < sizeof(vlens) / sizeof(vlens[0]); i++) {
|
|
335
|
+
_mongocrypt_buffer_t input;
|
|
336
|
+
_make_iup_v2_with_value_len(&input, vlens[i]);
|
|
337
|
+
|
|
338
|
+
mc_FLE2InsertUpdatePayloadV2_t iup;
|
|
339
|
+
mc_FLE2InsertUpdatePayloadV2_init(&iup);
|
|
340
|
+
mongocrypt_status_t *status = mongocrypt_status_new();
|
|
341
|
+
ASSERT_OK_STATUS(mc_FLE2InsertUpdatePayloadV2_parse(&iup, &input, status), status);
|
|
342
|
+
|
|
343
|
+
const _mongocrypt_buffer_t *got = mc_FLE2InsertUpdatePayloadV2_decrypt(crypt->crypto, &iup, &user_key, status);
|
|
344
|
+
ASSERT_FAILS_STATUS(got != NULL, status, "input ciphertext too small");
|
|
345
|
+
|
|
346
|
+
mc_FLE2InsertUpdatePayloadV2_cleanup(&iup);
|
|
347
|
+
mongocrypt_status_destroy(status);
|
|
348
|
+
_mongocrypt_buffer_cleanup(&input);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
_mongocrypt_buffer_cleanup(&user_key);
|
|
352
|
+
mongocrypt_destroy(crypt);
|
|
353
|
+
}
|
|
354
|
+
|
|
292
355
|
void _mongocrypt_tester_install_fle2_payload_iup_v2(_mongocrypt_tester_t *tester) {
|
|
293
356
|
INSTALL_TEST(_test_FLE2InsertUpdatePayloadV2_parse);
|
|
294
357
|
INSTALL_TEST(_test_mc_FLE2InsertUpdatePayloadV2_decrypt);
|
|
295
358
|
INSTALL_TEST(_test_mc_FLE2InsertUpdatePayloadV2_includes_crypto_params);
|
|
296
359
|
INSTALL_TEST(_test_mc_FLE2InsertUpdatePayloadV2_parses_crypto_params);
|
|
297
360
|
INSTALL_TEST(_test_mc_FLE2InsertUpdatePayloadV2_parse_errors);
|
|
361
|
+
INSTALL_TEST(_test_mc_FLE2InsertUpdatePayloadV2_decrypt_short_ciphertext);
|
|
298
362
|
}
|
|
@@ -182,8 +182,75 @@ static void test_FLE2InsertUpdatePayload_parse_errors(_mongocrypt_tester_t *test
|
|
|
182
182
|
_mongocrypt_buffer_cleanup(&input_buf);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
/* Builds a minimal parseable FLE2InsertUpdatePayload with a 'v' of `vlen` bytes.
|
|
186
|
+
* 'v' is a 16 byte user key UUID followed by (vlen - 16) bytes of ciphertext. */
|
|
187
|
+
static void _make_iup_with_value_len(_mongocrypt_buffer_t *out, uint32_t vlen) {
|
|
188
|
+
BSON_ASSERT(vlen >= UUID_LEN);
|
|
189
|
+
uint8_t token[32] = {0};
|
|
190
|
+
uint8_t uuid[UUID_LEN];
|
|
191
|
+
uint8_t value[128];
|
|
192
|
+
|
|
193
|
+
memset(uuid, 0xAB, sizeof(uuid));
|
|
194
|
+
memset(value, 0x41, sizeof(value));
|
|
195
|
+
BSON_ASSERT(vlen <= sizeof(value));
|
|
196
|
+
memcpy(value, uuid, sizeof(uuid));
|
|
197
|
+
|
|
198
|
+
bson_t payload;
|
|
199
|
+
bson_init(&payload);
|
|
200
|
+
ASSERT(bson_append_binary(&payload, "d", -1, BSON_SUBTYPE_BINARY, token, sizeof(token)));
|
|
201
|
+
ASSERT(bson_append_binary(&payload, "s", -1, BSON_SUBTYPE_BINARY, token, sizeof(token)));
|
|
202
|
+
ASSERT(bson_append_binary(&payload, "c", -1, BSON_SUBTYPE_BINARY, token, sizeof(token)));
|
|
203
|
+
ASSERT(bson_append_binary(&payload, "p", -1, BSON_SUBTYPE_BINARY, token, sizeof(token)));
|
|
204
|
+
ASSERT(bson_append_binary(&payload, "u", -1, BSON_SUBTYPE_UUID, uuid, sizeof(uuid)));
|
|
205
|
+
ASSERT(bson_append_int32(&payload, "t", -1, (int32_t)BSON_TYPE_UTF8));
|
|
206
|
+
ASSERT(bson_append_binary(&payload, "v", -1, BSON_SUBTYPE_BINARY, value, vlen));
|
|
207
|
+
ASSERT(bson_append_binary(&payload, "e", -1, BSON_SUBTYPE_BINARY, token, sizeof(token)));
|
|
208
|
+
|
|
209
|
+
_mongocrypt_buffer_init_size(out, 1 + (uint32_t)payload.len);
|
|
210
|
+
out->data[0] = (uint8_t)MC_SUBTYPE_FLE2InsertUpdatePayload;
|
|
211
|
+
memcpy(out->data + 1, bson_get_data(&payload), payload.len);
|
|
212
|
+
bson_destroy(&payload);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* A 'v' short enough that the plaintext length would be zero must be rejected
|
|
216
|
+
* with an error, not crash. */
|
|
217
|
+
static void test_FLE2InsertUpdatePayload_decrypt_short_ciphertext(_mongocrypt_tester_t *tester) {
|
|
218
|
+
if (!_aes_ctr_is_supported_by_os) {
|
|
219
|
+
TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
|
|
224
|
+
_mongocrypt_buffer_t user_key;
|
|
225
|
+
_mongocrypt_buffer_init_size(&user_key, MONGOCRYPT_KEY_LEN);
|
|
226
|
+
memset(user_key.data, 0xCD, user_key.len);
|
|
227
|
+
|
|
228
|
+
/* FLE2AEAD is CTR + HMAC-SHA256: ciphertext must exceed IV (16) + HMAC (32). */
|
|
229
|
+
const uint32_t vlens[] = {UUID_LEN, UUID_LEN + 1, UUID_LEN + 47, UUID_LEN + 48};
|
|
230
|
+
for (size_t i = 0; i < sizeof(vlens) / sizeof(vlens[0]); i++) {
|
|
231
|
+
_mongocrypt_buffer_t input;
|
|
232
|
+
_make_iup_with_value_len(&input, vlens[i]);
|
|
233
|
+
|
|
234
|
+
mc_FLE2InsertUpdatePayload_t iup;
|
|
235
|
+
mc_FLE2InsertUpdatePayload_init(&iup);
|
|
236
|
+
mongocrypt_status_t *status = mongocrypt_status_new();
|
|
237
|
+
ASSERT_OK_STATUS(mc_FLE2InsertUpdatePayload_parse(&iup, &input, status), status);
|
|
238
|
+
|
|
239
|
+
const _mongocrypt_buffer_t *got = mc_FLE2InsertUpdatePayload_decrypt(crypt->crypto, &iup, &user_key, status);
|
|
240
|
+
ASSERT_FAILS_STATUS(got != NULL, status, "input ciphertext too small");
|
|
241
|
+
|
|
242
|
+
mc_FLE2InsertUpdatePayload_cleanup(&iup);
|
|
243
|
+
mongocrypt_status_destroy(status);
|
|
244
|
+
_mongocrypt_buffer_cleanup(&input);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
_mongocrypt_buffer_cleanup(&user_key);
|
|
248
|
+
mongocrypt_destroy(crypt);
|
|
249
|
+
}
|
|
250
|
+
|
|
185
251
|
void _mongocrypt_tester_install_fle2_payload_iup(_mongocrypt_tester_t *tester) {
|
|
186
252
|
INSTALL_TEST(test_FLE2InsertUpdatePayload_parse);
|
|
187
253
|
INSTALL_TEST(test_FLE2InsertUpdatePayload_decrypt);
|
|
188
254
|
INSTALL_TEST(test_FLE2InsertUpdatePayload_parse_errors);
|
|
255
|
+
INSTALL_TEST(test_FLE2InsertUpdatePayload_decrypt_short_ciphertext);
|
|
189
256
|
}
|
|
@@ -46,6 +46,38 @@ static void _test_explicit_decrypt_init(_mongocrypt_tester_t *tester) {
|
|
|
46
46
|
mongocrypt_destroy(crypt);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/* Regression test for MONGOCRYPT-966 */
|
|
50
|
+
static void _test_explicit_decrypt_unknown_subtype(_mongocrypt_tester_t *tester) {
|
|
51
|
+
mongocrypt_t *const crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
|
|
52
|
+
|
|
53
|
+
const struct {
|
|
54
|
+
const char *desc;
|
|
55
|
+
const char *base64;
|
|
56
|
+
} cases[] = {
|
|
57
|
+
{"unrecognized subtype 0x08", "CKq7zA=="},
|
|
58
|
+
{"FLE1EncryptionPlaceholder (0x00)", "AA=="},
|
|
59
|
+
{"FLE2EncryptionPlaceholder (0x03)", "Aw=="},
|
|
60
|
+
{"FLE2FindEqualityPayload (0x05)", "BQ=="},
|
|
61
|
+
{"FLE2FindRangePayload (0x0a)", "Cg=="},
|
|
62
|
+
{"FLE2FindTextPayload (0x12)", "Eg=="},
|
|
63
|
+
{"zero-length payload", ""},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
|
67
|
+
mongocrypt_binary_t *const msg =
|
|
68
|
+
TEST_BSON("{ 'v': { '$binary': { 'subType': '06', 'base64': '%s' } } }", cases[i].base64);
|
|
69
|
+
|
|
70
|
+
mongocrypt_ctx_t *const ctx = mongocrypt_ctx_new(crypt);
|
|
71
|
+
|
|
72
|
+
ASSERT_FAILS(mongocrypt_ctx_explicit_decrypt_init(ctx, msg), ctx, "unsupported FLE blob subtype");
|
|
73
|
+
BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);
|
|
74
|
+
|
|
75
|
+
mongocrypt_ctx_destroy(ctx);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
mongocrypt_destroy(crypt);
|
|
79
|
+
}
|
|
80
|
+
|
|
49
81
|
/* Test individual ctx states. */
|
|
50
82
|
static void _test_decrypt_init(_mongocrypt_tester_t *tester) {
|
|
51
83
|
mongocrypt_t *crypt;
|
|
@@ -788,28 +820,21 @@ typedef struct {
|
|
|
788
820
|
mongocrypt_binary_t *msg;
|
|
789
821
|
mongocrypt_binary_t *keys_to_feed[3]; // NULL terminated list.
|
|
790
822
|
mongocrypt_binary_t *expect;
|
|
791
|
-
bool
|
|
823
|
+
bool expect_error;
|
|
792
824
|
} ed_testcase;
|
|
793
825
|
|
|
794
826
|
static void ed_testcase_run(ed_testcase *tc) {
|
|
795
827
|
printf(" explicit decrypt test: %s ... begin\n", tc->desc);
|
|
796
828
|
mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
|
|
797
829
|
mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
|
|
798
|
-
ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, tc->msg), ctx);
|
|
799
830
|
|
|
800
|
-
if (tc->
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
mongocrypt_binary_t *got = mongocrypt_binary_new();
|
|
804
|
-
bool ret = mongocrypt_ctx_finalize(ctx, got);
|
|
805
|
-
ASSERT_OK(ret, ctx);
|
|
806
|
-
// Expect input is returned unchanged.
|
|
807
|
-
ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(tc->msg, got);
|
|
808
|
-
mongocrypt_binary_destroy(got);
|
|
809
|
-
}
|
|
831
|
+
if (tc->expect_error) {
|
|
832
|
+
ASSERT_FAILS(mongocrypt_ctx_explicit_decrypt_init(ctx, tc->msg), ctx, "unsupported FLE blob subtype");
|
|
833
|
+
ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_ERROR);
|
|
810
834
|
goto cleanup;
|
|
811
835
|
}
|
|
812
836
|
|
|
837
|
+
ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, tc->msg), ctx);
|
|
813
838
|
ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
|
|
814
839
|
{
|
|
815
840
|
for (size_t i = 0; i < sizeof(tc->keys_to_feed) / sizeof(tc->keys_to_feed[0]); i++) {
|
|
@@ -858,12 +883,12 @@ static void _test_explicit_decrypt(_mongocrypt_tester_t *tester) {
|
|
|
858
883
|
ed_testcase_run(&tc);
|
|
859
884
|
}
|
|
860
885
|
|
|
861
|
-
// FLE1EncryptionPlaceholder is
|
|
886
|
+
// FLE1EncryptionPlaceholder is recognized but not decryptable: explicit decryption must error.
|
|
862
887
|
// Payload is returned by query analysis and consumed by libmongocrypt. Payload is not expected to be given to user.
|
|
863
888
|
{
|
|
864
889
|
ed_testcase tc = {.desc = "FLE1EncryptionPlaceholder",
|
|
865
890
|
.msg = TEST_FILE("./test/data/explicit-decrypt/FLE1EncryptionPlaceholder.json"),
|
|
866
|
-
.
|
|
891
|
+
.expect_error = true};
|
|
867
892
|
ed_testcase_run(&tc);
|
|
868
893
|
}
|
|
869
894
|
|
|
@@ -878,12 +903,12 @@ static void _test_explicit_decrypt(_mongocrypt_tester_t *tester) {
|
|
|
878
903
|
ed_testcase_run(&tc);
|
|
879
904
|
}
|
|
880
905
|
|
|
881
|
-
// FLE2EncryptionPlaceholder is
|
|
906
|
+
// FLE2EncryptionPlaceholder is recognized but not decryptable: explicit decryption must error.
|
|
882
907
|
// Payload is returned by query analysis and consumed by libmongocrypt. Payload is not expected to be given to user.
|
|
883
908
|
{
|
|
884
909
|
ed_testcase tc = {.desc = "FLE2EncryptionPlaceholder",
|
|
885
910
|
.msg = TEST_FILE("./test/data/explicit-decrypt/FLE2EncryptionPlaceholder.json"),
|
|
886
|
-
.
|
|
911
|
+
.expect_error = true};
|
|
887
912
|
ed_testcase_run(&tc);
|
|
888
913
|
}
|
|
889
914
|
|
|
@@ -952,12 +977,12 @@ static void _test_explicit_decrypt(_mongocrypt_tester_t *tester) {
|
|
|
952
977
|
ed_testcase_run(&tc);
|
|
953
978
|
}
|
|
954
979
|
|
|
955
|
-
// FLE2FindEqualityPayload is
|
|
980
|
+
// FLE2FindEqualityPayload is recognized but not decryptable: explicit decryption must error.
|
|
956
981
|
// Payload does not contain encrypted ciphertext (only lookup tokens).
|
|
957
982
|
{
|
|
958
983
|
ed_testcase tc = {.desc = "FLE2FindEqualityPayload",
|
|
959
984
|
.msg = TEST_FILE("./test/data/explicit-decrypt/FLE2FindEqualityPayload.json"),
|
|
960
|
-
.
|
|
985
|
+
.expect_error = true};
|
|
961
986
|
ed_testcase_run(&tc);
|
|
962
987
|
}
|
|
963
988
|
|
|
@@ -1030,18 +1055,19 @@ static void _test_explicit_decrypt(_mongocrypt_tester_t *tester) {
|
|
|
1030
1055
|
ed_testcase_run(&tc);
|
|
1031
1056
|
}
|
|
1032
1057
|
|
|
1033
|
-
// FLE2FindEqualityPayloadV2 is
|
|
1058
|
+
// FLE2FindEqualityPayloadV2 is recognized but not decryptable: explicit decryption must error.
|
|
1034
1059
|
// Payload does not contain encrypted ciphertext (only lookup tokens).
|
|
1035
1060
|
{
|
|
1036
1061
|
ed_testcase tc = {.desc = "FLE2FindEqualityPayloadV2",
|
|
1037
1062
|
.msg = TEST_FILE("./test/data/explicit-decrypt/FLE2FindEqualityPayloadV2.json"),
|
|
1038
|
-
.
|
|
1063
|
+
.expect_error = true};
|
|
1039
1064
|
ed_testcase_run(&tc);
|
|
1040
1065
|
}
|
|
1041
1066
|
}
|
|
1042
1067
|
|
|
1043
1068
|
void _mongocrypt_tester_install_ctx_decrypt(_mongocrypt_tester_t *tester) {
|
|
1044
1069
|
INSTALL_TEST(_test_explicit_decrypt_init);
|
|
1070
|
+
INSTALL_TEST(_test_explicit_decrypt_unknown_subtype);
|
|
1045
1071
|
INSTALL_TEST(_test_decrypt_init);
|
|
1046
1072
|
INSTALL_TEST(_test_decrypt_need_keys);
|
|
1047
1073
|
INSTALL_TEST(_test_decrypt_ready);
|
|
@@ -66,6 +66,176 @@ static void _test_mongocrypt_endpoint(_mongocrypt_tester_t *tester) {
|
|
|
66
66
|
ASSERT_STATUS_CONTAINS(status, "Invalid endpoint, expected dot separator in host, but got: malformed");
|
|
67
67
|
_mongocrypt_endpoint_destroy(endpoint);
|
|
68
68
|
|
|
69
|
+
/* Test a colon in the path does not parse as a port. */
|
|
70
|
+
endpoint = _mongocrypt_endpoint_new("vault.example.com/path:8080", -1, NULL /* opts */, status);
|
|
71
|
+
BSON_ASSERT(endpoint);
|
|
72
|
+
ASSERT_STREQUAL(endpoint->host, "vault.example.com");
|
|
73
|
+
ASSERT_STREQUAL(endpoint->domain, "example.com");
|
|
74
|
+
BSON_ASSERT(!endpoint->port);
|
|
75
|
+
ASSERT_STREQUAL(endpoint->path, "path:8080");
|
|
76
|
+
ASSERT_STREQUAL(endpoint->host_and_port, "vault.example.com");
|
|
77
|
+
BSON_ASSERT(mongocrypt_status_ok(status));
|
|
78
|
+
_mongocrypt_endpoint_destroy(endpoint);
|
|
79
|
+
|
|
80
|
+
/* Test a colon in the query does not parse as a port. */
|
|
81
|
+
endpoint = _mongocrypt_endpoint_new("vault.example.com?query=a:b", -1, NULL /* opts */, status);
|
|
82
|
+
BSON_ASSERT(endpoint);
|
|
83
|
+
ASSERT_STREQUAL(endpoint->host, "vault.example.com");
|
|
84
|
+
BSON_ASSERT(!endpoint->port);
|
|
85
|
+
ASSERT_STREQUAL(endpoint->query, "query=a:b");
|
|
86
|
+
BSON_ASSERT(mongocrypt_status_ok(status));
|
|
87
|
+
_mongocrypt_endpoint_destroy(endpoint);
|
|
88
|
+
|
|
89
|
+
/* Test a port followed by a query, with no path. */
|
|
90
|
+
endpoint = _mongocrypt_endpoint_new("vault.example.com:443?query=value", -1, NULL /* opts */, status);
|
|
91
|
+
BSON_ASSERT(endpoint);
|
|
92
|
+
ASSERT_STREQUAL(endpoint->host, "vault.example.com");
|
|
93
|
+
ASSERT_STREQUAL(endpoint->port, "443");
|
|
94
|
+
ASSERT_STREQUAL(endpoint->query, "query=value");
|
|
95
|
+
ASSERT_STREQUAL(endpoint->host_and_port, "vault.example.com:443");
|
|
96
|
+
BSON_ASSERT(mongocrypt_status_ok(status));
|
|
97
|
+
_mongocrypt_endpoint_destroy(endpoint);
|
|
98
|
+
|
|
99
|
+
/* An endpoint may contain only the characters RFC 3986 permits in a URI. This is defense in depth:
|
|
100
|
+
* each context an endpoint reaches is validated where it is used. */
|
|
101
|
+
const char *const invalid[] = {
|
|
102
|
+
"a\r\nX-Injected: injected.example.com", /* forges a header */
|
|
103
|
+
"a\r\n\r\nGET /evil.example.com", /* ends the header section */
|
|
104
|
+
"example.com\", \"sub\": \"victim@example.com", /* injects a JWT claim */
|
|
105
|
+
"example.com\\x.example.com", /* backslash */
|
|
106
|
+
"example .com", /* space */
|
|
107
|
+
"example.com\x7f", /* DEL */
|
|
108
|
+
"exampl\xc3\xa9.com", /* non-ASCII */
|
|
109
|
+
};
|
|
110
|
+
for (size_t i = 0; i < sizeof(invalid) / sizeof(invalid[0]); i++) {
|
|
111
|
+
endpoint = _mongocrypt_endpoint_new(invalid[i], -1, NULL /* opts */, status);
|
|
112
|
+
BSON_ASSERT(!endpoint);
|
|
113
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid character in endpoint");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* The offending byte is reported by offset and hex value. The endpoint itself is not echoed: it is
|
|
117
|
+
* not yet validated here, and may contain a CR or LF that would forge a line in a consumer's log. */
|
|
118
|
+
endpoint = _mongocrypt_endpoint_new("example .com", -1, NULL /* opts */, status);
|
|
119
|
+
BSON_ASSERT(!endpoint);
|
|
120
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid character in endpoint at offset 7: 0x20");
|
|
121
|
+
|
|
122
|
+
endpoint = _mongocrypt_endpoint_new("a\r\nX-Injected: injected.example.com", -1, NULL /* opts */, status);
|
|
123
|
+
BSON_ASSERT(!endpoint);
|
|
124
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid character in endpoint at offset 1: 0x0d");
|
|
125
|
+
|
|
126
|
+
/* An IPv6 literal is rejected. The parse has no bracket handling, so such an endpoint was never
|
|
127
|
+
* usable. Without a dot it is rejected earlier still, for
|
|
128
|
+
* lacking a subdomain separator. */
|
|
129
|
+
memset(&opts, 0, sizeof(opts));
|
|
130
|
+
opts.allow_empty_subdomain = true;
|
|
131
|
+
endpoint = _mongocrypt_endpoint_new("[2001:db8::1]:443", -1, &opts, status);
|
|
132
|
+
BSON_ASSERT(!endpoint);
|
|
133
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid character in endpoint host");
|
|
134
|
+
|
|
135
|
+
/* One with an embedded dot reaches the host check without the option. */
|
|
136
|
+
endpoint = _mongocrypt_endpoint_new("[::ffff:192.0.2.1]", -1, NULL /* opts */, status);
|
|
137
|
+
BSON_ASSERT(!endpoint);
|
|
138
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid character in endpoint host");
|
|
139
|
+
|
|
140
|
+
/* An IPv4 literal is accepted, as before. */
|
|
141
|
+
endpoint = _mongocrypt_endpoint_new("192.0.2.1:443", -1, NULL /* opts */, status);
|
|
142
|
+
BSON_ASSERT(endpoint);
|
|
143
|
+
ASSERT_STREQUAL(endpoint->host, "192.0.2.1");
|
|
144
|
+
ASSERT_STREQUAL(endpoint->port, "443");
|
|
145
|
+
ASSERT_STREQUAL(endpoint->host_and_port, "192.0.2.1:443");
|
|
146
|
+
BSON_ASSERT(mongocrypt_status_ok(status));
|
|
147
|
+
_mongocrypt_endpoint_destroy(endpoint);
|
|
148
|
+
|
|
149
|
+
/* Percent-encoding must be well formed. */
|
|
150
|
+
endpoint = _mongocrypt_endpoint_new("example.com/%zz", -1, NULL /* opts */, status);
|
|
151
|
+
BSON_ASSERT(!endpoint);
|
|
152
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid percent-encoding in endpoint at offset 13: 0x7a");
|
|
153
|
+
|
|
154
|
+
/* A '%' at the end of the string: the second hex digit is the NUL terminator. */
|
|
155
|
+
endpoint = _mongocrypt_endpoint_new("example.com/%2", -1, NULL /* opts */, status);
|
|
156
|
+
BSON_ASSERT(!endpoint);
|
|
157
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid percent-encoding in endpoint at offset 14: 0x00");
|
|
158
|
+
|
|
159
|
+
endpoint = _mongocrypt_endpoint_new("example.com/a%2Fb", -1, NULL /* opts */, status);
|
|
160
|
+
BSON_ASSERT(endpoint);
|
|
161
|
+
ASSERT_STREQUAL(endpoint->path, "a%2Fb");
|
|
162
|
+
BSON_ASSERT(mongocrypt_status_ok(status));
|
|
163
|
+
_mongocrypt_endpoint_destroy(endpoint);
|
|
164
|
+
|
|
165
|
+
/* A host must be an RFC 3986 `reg-name`. The gen-delims are rejected; the parse already ends the host
|
|
166
|
+
* at ':', '/', and '?', so this covers '#', '@', and the brackets of an IP-literal. */
|
|
167
|
+
const char *const invalid_hosts[] = {
|
|
168
|
+
"vault.example.com@evil.example.com", /* userinfo separator */
|
|
169
|
+
"vault.example.com#frag",
|
|
170
|
+
"[2001:db8::1]",
|
|
171
|
+
};
|
|
172
|
+
memset(&opts, 0, sizeof(opts));
|
|
173
|
+
opts.allow_empty_subdomain = true;
|
|
174
|
+
for (size_t i = 0; i < sizeof(invalid_hosts) / sizeof(invalid_hosts[0]); i++) {
|
|
175
|
+
endpoint = _mongocrypt_endpoint_new(invalid_hosts[i], -1, &opts, status);
|
|
176
|
+
BSON_ASSERT(!endpoint);
|
|
177
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid character in endpoint host");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
endpoint = _mongocrypt_endpoint_new("vault.example.com@evil.example.com", -1, NULL /* opts */, status);
|
|
181
|
+
BSON_ASSERT(!endpoint);
|
|
182
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid character in endpoint host: 0x40");
|
|
183
|
+
|
|
184
|
+
/* The sub-delims are permitted in a reg-name, so they are permitted here. An '&' reaching the Azure
|
|
185
|
+
* OAuth request body is prevented by percent-encoding it into the scope, not by this check. */
|
|
186
|
+
const char *const valid_hosts[] = {
|
|
187
|
+
"vault.example.com&x",
|
|
188
|
+
"vault.example.com=x",
|
|
189
|
+
"vault.example.com;x",
|
|
190
|
+
"vault.example.com,x",
|
|
191
|
+
"vault.example.com~x",
|
|
192
|
+
"vault.example.com%41",
|
|
193
|
+
};
|
|
194
|
+
for (size_t i = 0; i < sizeof(valid_hosts) / sizeof(valid_hosts[0]); i++) {
|
|
195
|
+
endpoint = _mongocrypt_endpoint_new(valid_hosts[i], -1, NULL /* opts */, status);
|
|
196
|
+
BSON_ASSERT(endpoint);
|
|
197
|
+
ASSERT_STREQUAL(endpoint->host, valid_hosts[i]);
|
|
198
|
+
BSON_ASSERT(mongocrypt_status_ok(status));
|
|
199
|
+
_mongocrypt_endpoint_destroy(endpoint);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* The host check does not apply to the path or query, which may contain reserved characters. */
|
|
203
|
+
endpoint = _mongocrypt_endpoint_new("vault.example.com/p&q?a=b&c=d", -1, NULL /* opts */, status);
|
|
204
|
+
BSON_ASSERT(endpoint);
|
|
205
|
+
ASSERT_STREQUAL(endpoint->host, "vault.example.com");
|
|
206
|
+
ASSERT_STREQUAL(endpoint->path, "p&q");
|
|
207
|
+
ASSERT_STREQUAL(endpoint->query, "a=b&c=d");
|
|
208
|
+
BSON_ASSERT(mongocrypt_status_ok(status));
|
|
209
|
+
_mongocrypt_endpoint_destroy(endpoint);
|
|
210
|
+
|
|
211
|
+
/* A port may contain only digits. The port is appended to host_and_port, which is used as the Host
|
|
212
|
+
* header and as the address to connect to. */
|
|
213
|
+
const char *const invalid_ports[] = {
|
|
214
|
+
"vault.example.com:44&3",
|
|
215
|
+
"vault.example.com:443x",
|
|
216
|
+
"vault.example.com:-1",
|
|
217
|
+
"vault.example.com:44.3",
|
|
218
|
+
};
|
|
219
|
+
for (size_t i = 0; i < sizeof(invalid_ports) / sizeof(invalid_ports[0]); i++) {
|
|
220
|
+
endpoint = _mongocrypt_endpoint_new(invalid_ports[i], -1, NULL /* opts */, status);
|
|
221
|
+
BSON_ASSERT(!endpoint);
|
|
222
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid character in endpoint port");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* The offset is relative to the start of the port. */
|
|
226
|
+
endpoint = _mongocrypt_endpoint_new("vault.example.com:44&3", -1, NULL /* opts */, status);
|
|
227
|
+
BSON_ASSERT(!endpoint);
|
|
228
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid character in endpoint port: 0x26");
|
|
229
|
+
|
|
230
|
+
/* A colon with no port is rejected. */
|
|
231
|
+
endpoint = _mongocrypt_endpoint_new("vault.example.com:", -1, NULL /* opts */, status);
|
|
232
|
+
BSON_ASSERT(!endpoint);
|
|
233
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid endpoint, expected a port after the colon");
|
|
234
|
+
|
|
235
|
+
endpoint = _mongocrypt_endpoint_new("vault.example.com:/path", -1, NULL /* opts */, status);
|
|
236
|
+
BSON_ASSERT(!endpoint);
|
|
237
|
+
ASSERT_STATUS_CONTAINS(status, "Invalid endpoint, expected a port after the colon");
|
|
238
|
+
|
|
69
239
|
/* A host without a dot separator is valid if the "allow_empty_subdomain"
|
|
70
240
|
* option is true. */
|
|
71
241
|
memset(&opts, 0, sizeof(opts));
|
|
@@ -96,6 +96,42 @@ static void test_mongocrypt_kek_parsing(_mongocrypt_tester_t *tester) {
|
|
|
96
96
|
bson_destroy(&test_file);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/* A failed parse must leave the KEK exactly as it was. Committing the new
|
|
100
|
+
* provider tag before the provider-specific parse repopulates the union would
|
|
101
|
+
* leave the previous provider's pointers readable under the new tag, which
|
|
102
|
+
* _mongocrypt_kek_cleanup would then free as the wrong type. See
|
|
103
|
+
* MONGOCRYPT-964. */
|
|
104
|
+
static void test_mongocrypt_kek_parse_failure_leaves_kek_intact(_mongocrypt_tester_t *tester) {
|
|
105
|
+
mongocrypt_status_t *const status = mongocrypt_status_new();
|
|
106
|
+
_mongocrypt_kek_t kek;
|
|
107
|
+
|
|
108
|
+
memset(&kek, 0, sizeof(_mongocrypt_kek_t));
|
|
109
|
+
|
|
110
|
+
ASSERT_OK_STATUS(_mongocrypt_kek_parse_owned(TMP_BSON("{'provider': 'gcp', 'projectId': 'project', "
|
|
111
|
+
"'location': 'global', 'keyRing': 'ring', "
|
|
112
|
+
"'keyName': 'name'}"),
|
|
113
|
+
&kek,
|
|
114
|
+
status),
|
|
115
|
+
status);
|
|
116
|
+
|
|
117
|
+
/* An AWS definition missing the required 'region' aborts mid-parse, after
|
|
118
|
+
* the provider tag would have been switched. */
|
|
119
|
+
ASSERT_FAILS_STATUS(_mongocrypt_kek_parse_owned(TMP_BSON("{'provider': 'aws', 'key': 'cmk-string'}"), &kek, status),
|
|
120
|
+
status,
|
|
121
|
+
"expected UTF-8 region");
|
|
122
|
+
|
|
123
|
+
/* The KEK still describes the GCP key it was parsed from. */
|
|
124
|
+
ASSERT_CMPINT(kek.kms_provider, ==, MONGOCRYPT_KMS_PROVIDER_GCP);
|
|
125
|
+
ASSERT_STREQUAL(kek.kmsid, "gcp");
|
|
126
|
+
ASSERT_STREQUAL(kek.provider.gcp.key_ring, "ring");
|
|
127
|
+
ASSERT_STREQUAL(kek.provider.gcp.key_name, "name");
|
|
128
|
+
|
|
129
|
+
/* Cleanup frees the GCP fields, matching the tag. */
|
|
130
|
+
_mongocrypt_kek_cleanup(&kek);
|
|
131
|
+
mongocrypt_status_destroy(status);
|
|
132
|
+
}
|
|
133
|
+
|
|
99
134
|
void _mongocrypt_tester_install_kek(_mongocrypt_tester_t *tester) {
|
|
100
135
|
INSTALL_TEST(test_mongocrypt_kek_parsing);
|
|
136
|
+
INSTALL_TEST(test_mongocrypt_kek_parse_failure_leaves_kek_intact);
|
|
101
137
|
}
|
|
@@ -182,6 +182,31 @@ static void test_mongocrypt_key_parsing(_mongocrypt_tester_t *tester) {
|
|
|
182
182
|
bson_destroy(&key_bson);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
/* Regression test for MONGOCRYPT-964. */
|
|
186
|
+
static void test_mongocrypt_key_parsing_duplicate_master_key(_mongocrypt_tester_t *tester) {
|
|
187
|
+
bson_t key_bson = BSON_INITIALIZER;
|
|
188
|
+
mongocrypt_status_t *const status = mongocrypt_status_new();
|
|
189
|
+
|
|
190
|
+
/* Two identical masterKey fields. */
|
|
191
|
+
_recreate_and_reset(tester, &key_bson, status, "masterKey", NULL);
|
|
192
|
+
bson_concat(&key_bson, TMP_BSON("{'masterKey': { 'provider': 'local' }}"));
|
|
193
|
+
bson_concat(&key_bson, TMP_BSON("{'masterKey': { 'provider': 'local' }}"));
|
|
194
|
+
_parse_fails(&key_bson, status, "duplicate field 'masterKey'");
|
|
195
|
+
|
|
196
|
+
/* A GCP masterKey followed by an AWS masterKey missing 'region'. The
|
|
197
|
+
* aborted second parse leaves the GCP 'keyRing' string in the union slot
|
|
198
|
+
* shared with aws.endpoint, which cleanup then frees as an endpoint. */
|
|
199
|
+
_recreate_and_reset(tester, &key_bson, status, "masterKey", NULL);
|
|
200
|
+
bson_concat(&key_bson,
|
|
201
|
+
TMP_BSON("{'masterKey': { 'provider': 'gcp', 'projectId': 'project', 'location': "
|
|
202
|
+
"'global', 'keyRing': 'ring', 'keyName': 'name' }}"));
|
|
203
|
+
bson_concat(&key_bson, TMP_BSON("{'masterKey': { 'provider': 'aws', 'key': 'cmk-string' }}"));
|
|
204
|
+
_parse_fails(&key_bson, status, "duplicate field 'masterKey'");
|
|
205
|
+
|
|
206
|
+
mongocrypt_status_destroy(status);
|
|
207
|
+
bson_destroy(&key_bson);
|
|
208
|
+
}
|
|
209
|
+
|
|
185
210
|
static void test_mongocrypt_key_alt_name_from_iter(_mongocrypt_tester_t *tester) {
|
|
186
211
|
mongocrypt_status_t *status;
|
|
187
212
|
bson_iter_t iter;
|
|
@@ -230,5 +255,6 @@ static void test_mongocrypt_key_alt_name_from_iter(_mongocrypt_tester_t *tester)
|
|
|
230
255
|
|
|
231
256
|
void _mongocrypt_tester_install_key(_mongocrypt_tester_t *tester) {
|
|
232
257
|
INSTALL_TEST(test_mongocrypt_key_parsing);
|
|
258
|
+
INSTALL_TEST(test_mongocrypt_key_parsing_duplicate_master_key);
|
|
233
259
|
INSTALL_TEST(test_mongocrypt_key_alt_name_from_iter);
|
|
234
260
|
}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: libmongocrypt-helper
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.20.
|
|
4
|
+
version: 1.20.5.0.1001
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The MongoDB Ruby Team
|
|
@@ -1250,7 +1250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
1250
1250
|
- !ruby/object:Gem::Version
|
|
1251
1251
|
version: '0'
|
|
1252
1252
|
requirements: []
|
|
1253
|
-
rubygems_version: 4.0.
|
|
1253
|
+
rubygems_version: 4.0.23
|
|
1254
1254
|
specification_version: 4
|
|
1255
1255
|
summary: libmongocrypt convenience package
|
|
1256
1256
|
test_files: []
|