/* * Copyright 2019-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 #include #include #include #include static void _log_to_stderr (mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx) { switch (level) { case MONGOCRYPT_LOG_LEVEL_FATAL: fprintf (stderr, "FATAL"); break; case MONGOCRYPT_LOG_LEVEL_ERROR: fprintf (stderr, "ERROR"); break; case MONGOCRYPT_LOG_LEVEL_WARNING: fprintf (stderr, "WARNING"); break; case MONGOCRYPT_LOG_LEVEL_INFO: fprintf (stderr, "INFO"); break; case MONGOCRYPT_LOG_LEVEL_TRACE: fprintf (stderr, "TRACE"); break; default: fprintf (stderr, "UNKNOWN"); break; } fprintf (stderr, " %s\n", message); } static void _load_json_as_bson (const char *path, bson_t *as_bson) { bson_error_t error; bson_json_reader_t *reader; reader = bson_json_reader_new_from_file (path, &error); if (!reader) { fprintf (stderr, "could not open: %s\n", path); abort (); } bson_init (as_bson); if (!bson_json_reader_read (reader, as_bson, &error)) { fprintf (stderr, "could not read json from: %s\n", path); abort (); } bson_json_reader_destroy (reader); } static mongocrypt_binary_t * _read_json (const char *path, uint8_t **data) { bson_t as_bson; uint32_t len; _load_json_as_bson (path, &as_bson); *data = bson_destroy_with_steal (&as_bson, true, &len); return mongocrypt_binary_new_from_data (*data, len); } static mongocrypt_binary_t * _read_http (const char *path, uint8_t **data) { int fd; char *contents = NULL; int n_read; int filesize = 0; char storage[512]; int i; uint32_t len; fd = open (path, O_RDONLY); while ((n_read = read (fd, storage, sizeof (storage))) > 0) { filesize += n_read; contents = bson_realloc (contents, filesize); memcpy (contents + (filesize - n_read), storage, n_read); } if (n_read < 0) { fprintf (stderr, "failed to read %s\n", path); abort (); } close (fd); len = 0; /* Copy and fix newlines: \n becomes \r\n. */ *data = bson_malloc0 (filesize * 2); BSON_ASSERT (*data); for (i = 0; i < filesize; i++) { if (contents[i] == '\n' && contents[i - 1] != '\r') { (*data)[len++] = '\r'; } (*data)[len++] = contents[i]; } bson_free (contents); return mongocrypt_binary_new_from_data (*data, len); } static void _print_binary_as_bson (mongocrypt_binary_t *binary) { bson_t as_bson; char *str; BSON_ASSERT (binary); bson_init_static (&as_bson, mongocrypt_binary_data (binary), mongocrypt_binary_len (binary)); str = bson_as_json (&as_bson, NULL); printf ("%s\n", str); bson_free (str); } static void _print_binary_as_text (mongocrypt_binary_t *binary) { uint32_t i; uint8_t *ptr; ptr = (uint8_t *) mongocrypt_binary_data (binary); for (i = 0; i < mongocrypt_binary_len (binary); i++) { if (ptr[i] == '\r') printf ("\\r"); else if (ptr[i] == '\n') printf ("\\n"); else printf ("%c", (char) ptr[i]); } printf ("\n"); } #define CHECK(stmt) \ if (!stmt) { \ continue; \ } void _run_state_machine (mongocrypt_ctx_t *ctx, bson_t *result) { mongocrypt_binary_t *input, *output = NULL; bson_t tmp; mongocrypt_kms_ctx_t *kms; mongocrypt_ctx_state_t state; mongocrypt_status_t *status; uint8_t *data; bool done; done = false; status = mongocrypt_status_new (); bson_init (result); while (!done) { state = mongocrypt_ctx_state (ctx); switch (state) { case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: output = mongocrypt_binary_new (); CHECK (mongocrypt_ctx_mongo_op (ctx, output)); printf ("\nrunning listCollections on mongod with this filter:\n"); _print_binary_as_bson (output); mongocrypt_binary_destroy (output); printf ("\nmocking reply from file:\n"); input = _read_json ("./test/example/collection-info.json", &data); _print_binary_as_bson (input); CHECK (mongocrypt_ctx_mongo_feed (ctx, input)); mongocrypt_binary_destroy (input); bson_free (data); CHECK (mongocrypt_ctx_mongo_done (ctx)); break; case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: output = mongocrypt_binary_new (); CHECK (mongocrypt_ctx_mongo_op (ctx, output)); printf ("\nrunning cmd on mongocryptd with this schema:\n"); _print_binary_as_bson (output); mongocrypt_binary_destroy (output); printf ("\nmocking reply from file:\n"); input = _read_json ("./test/example/mongocryptd-reply.json", &data); _print_binary_as_bson (input); CHECK (mongocrypt_ctx_mongo_feed (ctx, input)); mongocrypt_binary_destroy (input); bson_free (data); CHECK (mongocrypt_ctx_mongo_done (ctx)); break; case MONGOCRYPT_CTX_NEED_MONGO_KEYS: output = mongocrypt_binary_new (); CHECK (mongocrypt_ctx_mongo_op (ctx, output)); printf ("\nrunning a find on the key vault coll with this filter:\n"); _print_binary_as_bson (output); mongocrypt_binary_destroy (output); printf ("\nmocking reply from file:\n"); input = _read_json ("./test/example/key-document.json", &data); _print_binary_as_bson (input); CHECK (mongocrypt_ctx_mongo_feed (ctx, input)); mongocrypt_binary_destroy (input); bson_free (data); CHECK (mongocrypt_ctx_mongo_done (ctx)); break; case MONGOCRYPT_CTX_NEED_KMS: while ((kms = mongocrypt_ctx_next_kms_ctx (ctx))) { output = mongocrypt_binary_new (); CHECK (mongocrypt_kms_ctx_message (kms, output)); printf ("\nsending the following to kms:\n"); _print_binary_as_text (output); mongocrypt_binary_destroy (output); printf ("\nmocking reply from file\n"); input = _read_http ("./test/example/kms-decrypt-reply.txt", &data); _print_binary_as_text (input); CHECK (mongocrypt_kms_ctx_feed (kms, input)); mongocrypt_binary_destroy (input); bson_free (data); assert (mongocrypt_kms_ctx_bytes_needed (kms) == 0); } mongocrypt_ctx_kms_done (ctx); break; case MONGOCRYPT_CTX_READY: output = mongocrypt_binary_new (); CHECK (mongocrypt_ctx_finalize (ctx, output)); printf ("\nfinal bson is:\n"); _print_binary_as_bson (output); bson_init_static (&tmp, mongocrypt_binary_data (output), mongocrypt_binary_len (output)); bson_destroy (result); bson_copy_to (&tmp, result); bson_destroy (&tmp); mongocrypt_binary_destroy (output); break; case MONGOCRYPT_CTX_DONE: done = true; break; case MONGOCRYPT_CTX_ERROR: mongocrypt_ctx_status (ctx, status); printf ("\ngot error: %s\n", mongocrypt_status_message (status, NULL)); abort (); break; case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS: // We don't handle KMS credentials // fallthrough default: BSON_ASSERT (0); break; } } mongocrypt_status_destroy (status); } static mongocrypt_binary_t * _iter_to_binary (bson_iter_t *iter) { uint8_t *data; uint32_t len; BSON_ASSERT (BSON_ITER_HOLDS_BINARY (iter)); bson_iter_binary (iter, NULL, &len, (const uint8_t **) &data); return mongocrypt_binary_new_from_data (data, len); } int main () { bson_iter_t iter; bson_t result; bson_t key_doc; bson_t *wrapped; mongocrypt_t *crypt; mongocrypt_ctx_t *ctx; mongocrypt_binary_t *msg; mongocrypt_binary_t *key_id; mongocrypt_binary_t *input; uint8_t *data; printf ("******* ENCRYPTION *******\n\n"); crypt = mongocrypt_new (); mongocrypt_setopt_kms_provider_aws (crypt, "example", -1, "example", -1); mongocrypt_setopt_log_handler (crypt, _log_to_stderr, NULL); if (!mongocrypt_init (crypt)) { fprintf (stderr, "failed to initialize"); abort (); } ctx = mongocrypt_ctx_new (crypt); msg = _read_json ("./test/example/cmd.json", &data); mongocrypt_ctx_encrypt_init (ctx, "test", -1, msg); mongocrypt_binary_destroy (msg); bson_free (data); _run_state_machine (ctx, &result); mongocrypt_ctx_destroy (ctx); printf ("\n******* DECRYPTION *******\n\n"); ctx = mongocrypt_ctx_new (crypt); input = mongocrypt_binary_new_from_data ((uint8_t *) bson_get_data (&result), result.len); mongocrypt_ctx_decrypt_init (ctx, input); mongocrypt_binary_destroy (input); bson_destroy (&result); _run_state_machine (ctx, &result); bson_destroy (&result); mongocrypt_ctx_destroy (ctx); printf ("\n******* EXPLICIT ENCRYPTION *******\n"); ctx = mongocrypt_ctx_new (crypt); /* Explicit encryption requires a key_id option */ _load_json_as_bson ("./test/example/key-document.json", &key_doc); bson_iter_init_find (&iter, &key_doc, "_id"); key_id = _iter_to_binary (&iter); mongocrypt_ctx_setopt_key_id (ctx, key_id); bson_destroy (&key_doc); mongocrypt_ctx_setopt_algorithm (ctx, MONGOCRYPT_ALGORITHM_RANDOM_STR, -1); wrapped = BCON_NEW ("v", "hello"); msg = mongocrypt_binary_new_from_data ((uint8_t *) bson_get_data (wrapped), wrapped->len); mongocrypt_ctx_explicit_encrypt_init (ctx, msg); mongocrypt_binary_destroy (msg); _run_state_machine (ctx, &result); mongocrypt_ctx_destroy (ctx); printf ("\n******* EXPLICIT DECRYPTION *******\n"); ctx = mongocrypt_ctx_new (crypt); input = mongocrypt_binary_new_from_data ((uint8_t *) bson_get_data (&result), result.len); mongocrypt_ctx_explicit_decrypt_init (ctx, input); mongocrypt_binary_destroy (input); bson_destroy (&result); _run_state_machine (ctx, &result); bson_destroy (&result); mongocrypt_ctx_destroy (ctx); bson_destroy (wrapped); mongocrypt_binary_destroy (key_id); mongocrypt_destroy (crypt); return 0; }