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