1 /*
2 * Copyright 2019-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 "mongocrypt.h"
18 #include "mongocrypt-buffer-private.h"
19 #include "mongocrypt-ciphertext-private.h"
20 #include "mongocrypt-crypto-private.h"
21 #include "mongocrypt-key-broker-private.h"
22 #include "mongocrypt-marking-private.h"
23
24 bool
_mongocrypt_marking_parse_unowned(const _mongocrypt_buffer_t * in,_mongocrypt_marking_t * out,mongocrypt_status_t * status)25 _mongocrypt_marking_parse_unowned (const _mongocrypt_buffer_t *in,
26 _mongocrypt_marking_t *out,
27 mongocrypt_status_t *status)
28 {
29 bson_t bson;
30 bson_iter_t iter;
31 bool has_ki = false, has_ka = false, has_a = false, has_v = false;
32
33 _mongocrypt_marking_init (out);
34
35 if (in->len < 5) {
36 CLIENT_ERR ("invalid marking, length < 5");
37 return false;
38 }
39
40 if (in->data[0] != 0) {
41 CLIENT_ERR ("invalid marking, first byte must be 0");
42 return false;
43 }
44
45 if (!bson_init_static (&bson, in->data + 1, in->len - 1)) {
46 CLIENT_ERR ("invalid BSON");
47 return false;
48 }
49
50 if (!bson_validate (&bson, BSON_VALIDATE_NONE, NULL) ||
51 !bson_iter_init (&iter, &bson)) {
52 CLIENT_ERR ("invalid BSON");
53 return false;
54 }
55
56 while (bson_iter_next (&iter)) {
57 const char *field;
58
59 field = bson_iter_key (&iter);
60 BSON_ASSERT (field);
61 if (0 == strcmp ("ki", field)) {
62 has_ki = true;
63 if (!_mongocrypt_buffer_from_uuid_iter (&out->key_id, &iter)) {
64 CLIENT_ERR ("key id must be a UUID");
65 return false;
66 }
67 continue;
68 }
69
70 if (0 == strcmp ("ka", field)) {
71 has_ka = true;
72 /* Some bson_value types are not allowed to be key alt names */
73 const bson_value_t *value;
74
75 value = bson_iter_value (&iter);
76
77 if (!BSON_ITER_HOLDS_UTF8 (&iter)) {
78 CLIENT_ERR ("key alt name must be a UTF8");
79 return false;
80 }
81 /* CDRIVER-3100 We must make a copy of this value; the result of
82 * bson_iter_value is ephemeral. */
83 bson_value_copy (value, &out->key_alt_name);
84 out->has_alt_name = true;
85 continue;
86 }
87
88 if (0 == strcmp ("v", field)) {
89 has_v = true;
90 memcpy (&out->v_iter, &iter, sizeof (bson_iter_t));
91 continue;
92 }
93
94
95 if (0 == strcmp ("a", field)) {
96 int32_t algorithm;
97
98 has_a = true;
99 if (!BSON_ITER_HOLDS_INT32 (&iter)) {
100 CLIENT_ERR ("invalid marking, 'a' must be an int32");
101 return false;
102 }
103 algorithm = bson_iter_int32 (&iter);
104 if (algorithm != MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC &&
105 algorithm != MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM) {
106 CLIENT_ERR ("invalid algorithm value: %d", algorithm);
107 return false;
108 }
109 out->algorithm = (mongocrypt_encryption_algorithm_t) algorithm;
110 continue;
111 }
112
113 CLIENT_ERR ("unrecognized field '%s'", field);
114 return false;
115 }
116
117 if (!has_v) {
118 CLIENT_ERR ("no 'v' specified");
119 return false;
120 }
121
122 if (!has_ki && !has_ka) {
123 CLIENT_ERR ("neither 'ki' nor 'ka' specified");
124 return false;
125 }
126
127 if (has_ki && has_ka) {
128 CLIENT_ERR ("both 'ki' and 'ka' specified");
129 return false;
130 }
131
132 if (!has_a) {
133 CLIENT_ERR ("no 'a' specified");
134 return false;
135 }
136
137 return true;
138 }
139
140
141 void
_mongocrypt_marking_init(_mongocrypt_marking_t * marking)142 _mongocrypt_marking_init (_mongocrypt_marking_t *marking)
143 {
144 memset (marking, 0, sizeof (*marking));
145 }
146
147
148 void
_mongocrypt_marking_cleanup(_mongocrypt_marking_t * marking)149 _mongocrypt_marking_cleanup (_mongocrypt_marking_t *marking)
150 {
151 bson_value_destroy (&marking->key_alt_name);
152 _mongocrypt_buffer_cleanup (&marking->key_id);
153 }
154
155
156 bool
_mongocrypt_marking_to_ciphertext(void * ctx,_mongocrypt_marking_t * marking,_mongocrypt_ciphertext_t * ciphertext,mongocrypt_status_t * status)157 _mongocrypt_marking_to_ciphertext (void *ctx,
158 _mongocrypt_marking_t *marking,
159 _mongocrypt_ciphertext_t *ciphertext,
160 mongocrypt_status_t *status)
161 {
162 _mongocrypt_buffer_t plaintext;
163 _mongocrypt_buffer_t iv;
164 _mongocrypt_key_broker_t *kb;
165 _mongocrypt_buffer_t associated_data;
166 _mongocrypt_buffer_t key_material;
167 _mongocrypt_buffer_t key_id;
168 bool ret = false;
169 bool key_found;
170 uint32_t bytes_written;
171
172 BSON_ASSERT (marking);
173 BSON_ASSERT (ciphertext);
174 BSON_ASSERT (status);
175 BSON_ASSERT (ctx);
176
177 _mongocrypt_buffer_init (&plaintext);
178 _mongocrypt_buffer_init (&associated_data);
179 _mongocrypt_buffer_init (&iv);
180 _mongocrypt_buffer_init (&key_id);
181 _mongocrypt_buffer_init (&key_material);
182
183 kb = (_mongocrypt_key_broker_t *) ctx;
184
185 /* Get the decrypted key for this marking. */
186 if (marking->has_alt_name) {
187 key_found = _mongocrypt_key_broker_decrypted_key_by_name (
188 kb, &marking->key_alt_name, &key_material, &key_id);
189 } else if (!_mongocrypt_buffer_empty (&marking->key_id)) {
190 key_found = _mongocrypt_key_broker_decrypted_key_by_id (
191 kb, &marking->key_id, &key_material);
192 _mongocrypt_buffer_copy_to (&marking->key_id, &key_id);
193 } else {
194 CLIENT_ERR ("marking must have either key_id or key_alt_name");
195 goto fail;
196 }
197
198 if (!key_found) {
199 _mongocrypt_status_copy_to (kb->status, status);
200 goto fail;
201 }
202
203 _mongocrypt_ciphertext_init (ciphertext);
204 ciphertext->original_bson_type = (uint8_t) bson_iter_type (&marking->v_iter);
205 ciphertext->blob_subtype = marking->algorithm;
206 _mongocrypt_buffer_copy_to (&key_id, &ciphertext->key_id);
207 if (!_mongocrypt_ciphertext_serialize_associated_data (ciphertext,
208 &associated_data)) {
209 CLIENT_ERR ("could not serialize associated data");
210 goto fail;
211 }
212
213 _mongocrypt_buffer_from_iter (&plaintext, &marking->v_iter);
214 ciphertext->data.len = _mongocrypt_calculate_ciphertext_len (plaintext.len);
215 ciphertext->data.data = bson_malloc (ciphertext->data.len);
216 BSON_ASSERT (ciphertext->data.data);
217
218 ciphertext->data.owned = true;
219
220 switch (marking->algorithm) {
221 case MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC:
222 /* Use deterministic encryption. */
223 _mongocrypt_buffer_resize (&iv, MONGOCRYPT_IV_LEN);
224 ret = _mongocrypt_calculate_deterministic_iv (kb->crypt->crypto,
225 &key_material,
226 &plaintext,
227 &associated_data,
228 &iv,
229 status);
230 if (!ret) {
231 goto fail;
232 }
233
234 ret = _mongocrypt_do_encryption (kb->crypt->crypto,
235 &iv,
236 &associated_data,
237 &key_material,
238 &plaintext,
239 &ciphertext->data,
240 &bytes_written,
241 status);
242 break;
243 case MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM:
244 /* Use randomized encryption.
245 * In this case, we must generate a new, random iv. */
246 _mongocrypt_buffer_resize (&iv, MONGOCRYPT_IV_LEN);
247 if (!_mongocrypt_random (
248 kb->crypt->crypto, &iv, MONGOCRYPT_IV_LEN, status)) {
249 goto fail;
250 }
251 ret = _mongocrypt_do_encryption (kb->crypt->crypto,
252 &iv,
253 &associated_data,
254 &key_material,
255 &plaintext,
256 &ciphertext->data,
257 &bytes_written,
258 status);
259 break;
260 default:
261 /* Error. */
262 CLIENT_ERR ("Unsupported value for encryption algorithm");
263 goto fail;
264 }
265
266 if (!ret) {
267 goto fail;
268 }
269
270 BSON_ASSERT (bytes_written == ciphertext->data.len);
271
272 ret = true;
273
274 fail:
275 _mongocrypt_buffer_cleanup (&iv);
276 _mongocrypt_buffer_cleanup (&key_id);
277 _mongocrypt_buffer_cleanup (&plaintext);
278 _mongocrypt_buffer_cleanup (&associated_data);
279 _mongocrypt_buffer_cleanup (&key_material);
280 return ret;
281 }
282