summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2020-11-05 16:14:13 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-06 22:24:36 +0000
commitdcdb25c472827dc6ccab23c15b14c415f74c5fb3 (patch)
treeaef1abfa3ca931ed336eeeb0a4472275ff74b396
parent56833933c266fd88dd4e6683d2dbd85616ffe673 (diff)
downloadmongo-dcdb25c472827dc6ccab23c15b14c415f74c5fb3.tar.gz
SERVER-52644 Update kms-message for gcp and azure support
-rw-r--r--src/third_party/kms-message/README.md43
-rw-r--r--src/third_party/kms-message/SConscript14
-rw-r--r--src/third_party/kms-message/THIRD_PARTY_NOTICES2
-rw-r--r--src/third_party/kms-message/src/hexlify.c37
-rw-r--r--src/third_party/kms-message/src/hexlify.h5
-rw-r--r--src/third_party/kms-message/src/kms_azure_request.c219
-rw-r--r--src/third_party/kms-message/src/kms_b64.c160
-rw-r--r--src/third_party/kms-message/src/kms_caller_identity_request.c5
-rw-r--r--src/third_party/kms-message/src/kms_crypto.h16
-rw-r--r--src/third_party/kms-message/src/kms_crypto_apple.c99
-rw-r--r--src/third_party/kms-message/src/kms_crypto_libcrypto.c (renamed from src/third_party/kms-message/src/kms_crypto_openssl.c)48
-rw-r--r--src/third_party/kms-message/src/kms_crypto_none.c15
-rw-r--r--src/third_party/kms-message/src/kms_crypto_windows.c136
-rw-r--r--src/third_party/kms-message/src/kms_decrypt_request.c7
-rw-r--r--src/third_party/kms-message/src/kms_encrypt_request.c7
-rw-r--r--src/third_party/kms-message/src/kms_gcp_request.c286
-rw-r--r--src/third_party/kms-message/src/kms_kv_list.c11
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_azure_request.h110
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_b64.h25
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_caller_identity_request.h4
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_decrypt_request.h4
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_encrypt_request.h4
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_gcp_request.h124
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_message.h2
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_message_defines.h10
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_request.h16
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_request_opt.h23
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_response.h10
-rw-r--r--src/third_party/kms-message/src/kms_message/kms_response_parser.h6
-rw-r--r--src/third_party/kms-message/src/kms_message_private.h27
-rw-r--r--src/third_party/kms-message/src/kms_port.c33
-rw-r--r--src/third_party/kms-message/src/kms_port.h27
-rw-r--r--src/third_party/kms-message/src/kms_request.c102
-rw-r--r--src/third_party/kms-message/src/kms_request_opt.c28
-rw-r--r--src/third_party/kms-message/src/kms_request_opt_private.h1
-rw-r--r--src/third_party/kms-message/src/kms_request_str.c13
-rw-r--r--src/third_party/kms-message/src/kms_request_str.h7
-rw-r--r--src/third_party/kms-message/src/kms_response.c6
-rw-r--r--src/third_party/kms-message/src/kms_response_parser.c94
-rwxr-xr-xsrc/third_party/scripts/kms_message_get_sources.sh2
40 files changed, 1705 insertions, 83 deletions
diff --git a/src/third_party/kms-message/README.md b/src/third_party/kms-message/README.md
new file mode 100644
index 00000000000..b1242787f00
--- /dev/null
+++ b/src/third_party/kms-message/README.md
@@ -0,0 +1,43 @@
+This Repository is NOT a supported MongoDB product
+
+# kms-message
+Library used to generate requests for:
+- Amazon Web Services Key Management Service (KMS)
+- Azure Key Vault
+
+This library is *not* a complete implementation of a KMS client, it only
+implements the request format.
+
+## Testing kms-message
+- `test_kms_request` tests HTTP request generation and response parsing, but does not require internet or use any live servers.
+- `test_kms_azure_online` makes live requests, and has additional requirements (must have working credentials).
+
+### Requirements
+- A complete installation of the C driver. (libbson is needed for parsing JSON, and libmongoc is used for creating TLS streams). See http://mongoc.org/libmongoc/current/installing.html for installation instructions. For macOS, `brew install mongo-c-driver` will suffice.
+- An Azure key vault, and a service principal with an access policy allowing encrypt / decrypt key operations. The following environment variables must be set:
+ - AZURE_TENANT_ID
+ - AZURE_CLIENT_ID
+ - AZURE_CLIENT_SECRET
+ - AZURE_KEY_URL (e.g. `https://key-vault-kevinalbs.vault.azure.net/keys/test-key/9e1159e6ee5b447ba17e850b779bf652`)
+
+### Building
+Configure and build with cmake:
+```
+mkdir cmake-build
+cd cmake-build
+cmake ..
+cmake --build . --target all
+```
+
+If the C driver is installed in a non-default location, specify the location with `-DCMAKE_PREFIX_PATH=...`.
+
+To build tests with verbose (and insecure) tracing, define `TEST_TRACING_INSECURE` in compiler flags by specifying `-DCMAKE_C_FLAGS="-DTEST_TRACING_INSECURE"` on cmake configuration.
+
+Recommended: compile tests with address sanitizer (use a relatively new gcc / clang compiler) by specifying `-fsanitize=address` in the C flags. This can be done by specifygin `-DCMAKE_C_FLAGS="-fsanitize=address"` as an option to cmake. Enable leak detection with the environment variable `ASAN_OPTIONS='detect_leaks=1'. Example:
+
+```
+cd cmake-build
+cmake -DCMAKE_C_FLAGS="-fsanitize=address -DTEST_TRACING_INSECURE"
+export ASAN_OPTIONS='detect_leaks=1'
+./cmake-build/kms-message/test_kms_azure_online
+```
diff --git a/src/third_party/kms-message/SConscript b/src/third_party/kms-message/SConscript
index f1de7fda335..0ad6e82cfb2 100644
--- a/src/third_party/kms-message/SConscript
+++ b/src/third_party/kms-message/SConscript
@@ -9,7 +9,7 @@ def removeIfPresent(lst, item):
except ValueError:
pass
-for to_remove in ['-Werror', "-Wsign-compare","-Wall","-Werror=unused-result"]:
+for to_remove in ['-Werror', "-Wsign-compare", "-Wall", "-Werror=unused-result"]:
removeIfPresent(env['CCFLAGS'], to_remove)
removeIfPresent(env['CFLAGS'], to_remove)
@@ -18,29 +18,35 @@ env.Append(CPPDEFINES=['KMS_MSG_STATIC'])
additional_sources = []
if env.TargetOSIs('windows'):
+ env.Append(CPPDEFINES=['KMS_MESSAGE_ENABLE_CRYPTO_CNG'])
additional_sources.append(['src/kms_crypto_windows.c'])
# Disable warnings about deprecated functions
env.Append(CFLAGS=['/wd4996'])
elif env.TargetOSIs('darwin'):
+ env.Append(CPPDEFINES=['KMS_MESSAGE_ENABLE_CRYPTO_COMMON_CRYPTO'])
additional_sources.append(['src/kms_crypto_apple.c'])
else:
- additional_sources.append(['src/kms_crypto_openssl.c'])
+ env.Append(CPPDEFINES=['KMS_MESSAGE_ENABLE_CRYPTO_LIBCRYPTO'])
+ additional_sources.append(['src/kms_crypto_libcrypto.c'])
env.Library(
target="kms-message",
source=[
'src/hexlify.c',
+ 'src/kms_azure_request.c',
'src/kms_b64.c',
'src/kms_caller_identity_request.c',
'src/kms_decrypt_request.c',
'src/kms_encrypt_request.c',
+ 'src/kms_gcp_request.c',
'src/kms_kv_list.c',
'src/kms_message.c',
- 'src/kms_request.c',
+ 'src/kms_port.c',
'src/kms_request_opt.c',
'src/kms_request_str.c',
- 'src/kms_response.c',
+ 'src/kms_request.c',
'src/kms_response_parser.c',
+ 'src/kms_response.c',
'src/sort.c',
] + additional_sources,
LIBDEPS_TAGS=[
diff --git a/src/third_party/kms-message/THIRD_PARTY_NOTICES b/src/third_party/kms-message/THIRD_PARTY_NOTICES
index 3fc095170c5..4110c1b91e0 100644
--- a/src/third_party/kms-message/THIRD_PARTY_NOTICES
+++ b/src/third_party/kms-message/THIRD_PARTY_NOTICES
@@ -1,4 +1,4 @@
-License notice for common-b64.c
+License notice for kms_b64.c
-------------------------------------------------------------------------------
ISC License
diff --git a/src/third_party/kms-message/src/hexlify.c b/src/third_party/kms-message/src/hexlify.c
index be9ee030b93..2d70927148c 100644
--- a/src/third_party/kms-message/src/hexlify.c
+++ b/src/third_party/kms-message/src/hexlify.c
@@ -24,6 +24,8 @@ char *
hexlify (const uint8_t *buf, size_t len)
{
char *hex_chars = malloc (len * 2 + 1);
+ KMS_ASSERT (hex_chars);
+
char *p = hex_chars;
size_t i;
@@ -36,21 +38,30 @@ hexlify (const uint8_t *buf, size_t len)
return hex_chars;
}
-uint8_t *
-unhexlify (const char *hex_chars, size_t *len)
+/* Returns -1 on error. */
+int
+unhexlify (const char *in, size_t len)
{
- uint8_t *buf;
- uint8_t *pos;
+ int i;
+ int byte;
+ int total = 0;
+ int multiplier = 1;
- *len = strlen (hex_chars) / 2;
- buf = malloc (*len);
- pos = buf;
+ for (i = (int) len - 1; i >= 0; i--) {
+ char c = *(in + i);
- while (*hex_chars) {
- KMS_ASSERT (1 == sscanf (hex_chars, "%2hhx", pos));
- pos++;
- hex_chars += 2;
- }
+ if (c >= '0' && c <= '9') {
+ byte = c - 48;
+ } else if (c >= 'a' && c <= 'f') {
+ byte = c - 97 + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ byte = c - 65 + 10;
+ } else {
+ return -1;
+ }
- return buf;
+ total += byte * multiplier;
+ multiplier *= 16;
+ }
+ return total;
}
diff --git a/src/third_party/kms-message/src/hexlify.h b/src/third_party/kms-message/src/hexlify.h
index e0096eb6ca4..60bc93ea7fc 100644
--- a/src/third_party/kms-message/src/hexlify.h
+++ b/src/third_party/kms-message/src/hexlify.h
@@ -19,5 +19,6 @@
char *
hexlify (const uint8_t *buf, size_t len);
-uint8_t *
-unhexlify (const char *hex_chars, size_t *len);
+
+int
+unhexlify (const char *in, size_t len); \ No newline at end of file
diff --git a/src/third_party/kms-message/src/kms_azure_request.c b/src/third_party/kms-message/src/kms_azure_request.c
new file mode 100644
index 00000000000..5ce7488ff3d
--- /dev/null
+++ b/src/third_party/kms-message/src/kms_azure_request.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2020-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 "kms_message/kms_azure_request.h"
+
+#include "kms_message/kms_b64.h"
+#include "kms_message_private.h"
+#include "kms_request_opt_private.h"
+#include "kms_request_str.h"
+
+/*
+ * Request has the following form:
+ *
+ * POST /{tenant ID}/oauth2/v2.0/token HTTP/1.1
+ * Host: {host of identify platform URL}
+ * Content-Type: application/x-www-form-urlencoded
+ *
+ * client_id={client ID}
+ * &scope=https%3A%2F%2Fvault.azure.net%2F.default
+ * &client_secret={client secret}
+ * &grant_type=client_credentials
+*/
+kms_request_t *
+kms_azure_request_oauth_new (const char *host,
+ const char *scope,
+ const char *tenant_id,
+ const char *client_id,
+ const char *client_secret,
+ const kms_request_opt_t *opt)
+{
+ char *path_and_query = NULL;
+ char *payload = NULL;
+ kms_request_t *req;
+ kms_request_str_t *str;
+
+ str = kms_request_str_new ();
+ kms_request_str_appendf (str, "/%s/oauth2/v2.0/token", tenant_id);
+ path_and_query = kms_request_str_detach (str);
+ str = kms_request_str_new ();
+ kms_request_str_appendf (
+ str,
+ "client_id=%s&scope=%s&client_secret=%s&grant_type=client_credentials",
+ client_id,
+ scope,
+ client_secret);
+ payload = kms_request_str_detach (str);
+
+ req = kms_request_new ("POST", path_and_query, opt);
+
+ if (opt->provider != KMS_REQUEST_PROVIDER_AZURE) {
+ KMS_ERROR (req, "Expected KMS request with provider type: Azure");
+ goto done;
+ }
+
+ if (kms_request_get_error (req)) {
+ goto done;
+ }
+
+ if (!kms_request_add_header_field (
+ req, "Content-Type", "application/x-www-form-urlencoded")) {
+ goto done;
+ }
+ if (!kms_request_add_header_field (req, "Host", host)) {
+ goto done;
+ }
+ if (!kms_request_add_header_field (req, "Accept", "application/json")) {
+ goto done;
+ }
+
+ if (!kms_request_append_payload (req, payload, strlen (payload))) {
+ goto done;
+ }
+
+done:
+ kms_request_free_string (path_and_query);
+ kms_request_free_string (payload);
+ return req;
+}
+
+static kms_request_t *
+_wrap_unwrap_common (const char *wrap_unwrap,
+ const char *host,
+ const char *access_token,
+ const char *key_name,
+ const char *key_version,
+ const uint8_t *value,
+ size_t value_len,
+ const kms_request_opt_t *opt)
+{
+ char *path_and_query = NULL;
+ char *payload = NULL;
+ char *bearer_token_value = NULL;
+ char *value_base64url = NULL;
+ kms_request_t *req;
+ kms_request_str_t *str;
+
+ str = kms_request_str_new ();
+ /* {vaultBaseUrl}/keys/{key-name}/{key-version}/wrapkey?api-version=7.1 */
+ kms_request_str_appendf (str,
+ "/keys/%s/%s/%s?api-version=7.1",
+ key_name,
+ key_version ? key_version : "",
+ wrap_unwrap);
+ path_and_query = kms_request_str_detach (str);
+
+ req = kms_request_new ("POST", path_and_query, opt);
+
+ if (opt->provider != KMS_REQUEST_PROVIDER_AZURE) {
+ KMS_ERROR (req, "Expected KMS request with provider type: Azure");
+ goto done;
+ }
+
+ if (kms_request_get_error (req)) {
+ goto done;
+ }
+
+ value_base64url = kms_message_raw_to_b64url (value, value_len);
+ if (!value_base64url) {
+ KMS_ERROR (req, "Could not bases64url-encode plaintext");
+ goto done;
+ }
+
+ str = kms_request_str_new ();
+ kms_request_str_appendf (
+ str, "{\"alg\": \"RSA-OAEP-256\", \"value\": \"%s\"}", value_base64url);
+ payload = kms_request_str_detach (str);
+ str = kms_request_str_new ();
+ kms_request_str_appendf (str, "Bearer %s", access_token);
+ bearer_token_value = kms_request_str_detach (str);
+ if (!kms_request_add_header_field (
+ req, "Authorization", bearer_token_value)) {
+ goto done;
+ }
+ if (!kms_request_add_header_field (
+ req, "Content-Type", "application/json")) {
+ goto done;
+ }
+ if (!kms_request_add_header_field (req, "Host", host)) {
+ goto done;
+ }
+ if (!kms_request_add_header_field (req, "Accept", "application/json")) {
+ goto done;
+ }
+
+ if (!kms_request_append_payload (req, payload, strlen (payload))) {
+ goto done;
+ }
+
+done:
+ kms_request_free_string (path_and_query);
+ kms_request_free_string (payload);
+ kms_request_free_string (bearer_token_value);
+ kms_request_free_string (value_base64url);
+ return req;
+}
+
+/*
+ * Request has the following form:
+ *
+ * POST /keys/{key-name}/{key-version}/wrapkey?api-version=7.1
+ * Host: {host of key vault endpoint}
+ * Authentication: Bearer {token}
+ * Content-Type: application/json
+ *
+ * {
+ * "alg": "RSA-OAEP-256"
+ * "value": "base64url encoded data"
+ * }
+ */
+kms_request_t *
+kms_azure_request_wrapkey_new (const char *host,
+ const char *access_token,
+ const char *key_name,
+ const char *key_version,
+ const uint8_t *plaintext,
+ size_t plaintext_len,
+ const kms_request_opt_t *opt)
+{
+ return _wrap_unwrap_common ("wrapkey",
+ host,
+ access_token,
+ key_name,
+ key_version,
+ plaintext,
+ plaintext_len,
+ opt);
+}
+
+kms_request_t *
+kms_azure_request_unwrapkey_new (const char *host,
+ const char *access_token,
+ const char *key_name,
+ const char *key_version,
+ const uint8_t *ciphertext,
+ size_t ciphertext_len,
+ const kms_request_opt_t *opt)
+{
+ return _wrap_unwrap_common ("unwrapkey",
+ host,
+ access_token,
+ key_name,
+ key_version,
+ ciphertext,
+ ciphertext_len,
+ opt);
+} \ No newline at end of file
diff --git a/src/third_party/kms-message/src/kms_b64.c b/src/third_party/kms-message/src/kms_b64.c
index 713545d90e4..b7df107b6f0 100644
--- a/src/third_party/kms-message/src/kms_b64.c
+++ b/src/third_party/kms-message/src/kms_b64.c
@@ -41,8 +41,12 @@
*/
#include <ctype.h>
-#include "kms_message/kms_message.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "kms_message/kms_b64.h"
+#include "kms_message/kms_message.h"
#define Assert(Cond) \
if (!(Cond)) \
@@ -505,3 +509,157 @@ kms_message_b64_pton (char const *src, uint8_t *target, size_t targsize)
else
return b64_pton_len (src);
}
+
+int
+kms_message_b64_to_b64url (const char *src,
+ size_t srclength,
+ char *target,
+ size_t targsize)
+{
+ size_t i;
+
+ for (i = 0; i < srclength; i++) {
+ if (src[i] == '=') {
+ break;
+ }
+
+ if (i >= targsize) {
+ return -1;
+ }
+
+ target[i] = src[i];
+ if (target[i] == '+') {
+ target[i] = '-';
+ } else if (target[i] == '/') {
+ target[i] = '_';
+ }
+ }
+
+ /* NULL terminate if room. */
+ if (i < targsize) {
+ target[i] = '\0';
+ }
+
+ return (int) i;
+}
+
+int
+kms_message_b64url_to_b64 (const char *src,
+ size_t srclength,
+ char *target,
+ size_t targsize)
+{
+ size_t i;
+ size_t boundary;
+
+ for (i = 0; i < srclength; i++) {
+ if (src[i] == '=') {
+ break;
+ }
+
+ if (i >= targsize) {
+ return -1;
+ }
+
+ target[i] = src[i];
+ if (target[i] == '-') {
+ target[i] = '+';
+ } else if (target[i] == '_') {
+ target[i] = '/';
+ }
+ }
+
+ /* Pad to four byte boundary. */
+ boundary = 4 * ((i + 3) / 4);
+ for (; i < boundary; i++) {
+ if (i >= targsize) {
+ return -1;
+ }
+ target[i] = '=';
+ }
+
+ /* NULL terminate if room. */
+ if (i < targsize) {
+ target[i] = '\0';
+ }
+
+ return (int) i;
+}
+
+char *
+kms_message_raw_to_b64 (const uint8_t *raw, size_t raw_len)
+{
+ char *b64;
+ size_t b64_len;
+
+ b64_len = (raw_len / 3 + 1) * 4 + 1;
+ b64 = malloc (b64_len);
+ memset (b64, 0, b64_len);
+ if (-1 == kms_message_b64_ntop (raw, raw_len, b64, b64_len)) {
+ free (b64);
+ return NULL;
+ }
+ return b64;
+}
+
+uint8_t *
+kms_message_b64_to_raw (const char *b64, size_t *out)
+{
+ uint8_t *raw;
+ int ret;
+ size_t b64len;
+
+ b64len = strlen (b64);
+ raw = (uint8_t *) malloc (b64len + 1);
+ memset (raw, 0, b64len + 1);
+ ret = kms_message_b64_pton (b64, raw, b64len);
+ if (ret > 0) {
+ *out = (size_t) ret;
+ return raw;
+ }
+ free (raw);
+ return NULL;
+}
+
+char *
+kms_message_raw_to_b64url (const uint8_t *raw, size_t raw_len)
+{
+ char *b64;
+ size_t b64len;
+
+ b64 = kms_message_raw_to_b64 (raw, raw_len);
+ if (!b64) {
+ return NULL;
+ }
+
+ b64len = strlen (b64);
+ if (-1 == kms_message_b64_to_b64url (b64, b64len, b64, b64len)) {
+ free (b64);
+ return NULL;
+ }
+
+ return b64;
+}
+
+uint8_t *
+kms_message_b64url_to_raw (const char *b64url, size_t *out)
+{
+ char *b64;
+ size_t capacity;
+ uint8_t *raw;
+ size_t b64urllen;
+
+ b64urllen = strlen(b64url);
+ /* Add four for padding '=' characters. */
+ capacity = b64urllen + 4;
+ b64 = malloc (capacity);
+ memset (b64, 0, capacity);
+ if (-1 ==
+ kms_message_b64url_to_b64 (b64url, b64urllen, b64, capacity)) {
+ free (b64);
+ return NULL;
+ }
+ raw = kms_message_b64_to_raw (b64, out);
+ free (b64);
+ return raw;
+} \ No newline at end of file
diff --git a/src/third_party/kms-message/src/kms_caller_identity_request.c b/src/third_party/kms-message/src/kms_caller_identity_request.c
index d4c12188445..371d2d7bad2 100644
--- a/src/third_party/kms-message/src/kms_caller_identity_request.c
+++ b/src/third_party/kms-message/src/kms_caller_identity_request.c
@@ -40,7 +40,10 @@ kms_caller_identity_request_new (const kms_request_opt_t *opt)
payload = kms_request_str_new ();
kms_request_str_appendf (payload,
"Action=GetCallerIdentity&Version=2011-06-15");
- kms_request_append_payload (request, payload->str, payload->len);
+ if (!kms_request_append_payload (request, payload->str, payload->len)) {
+ KMS_ERROR (request, "Could not append payload");
+ goto done;
+ }
done:
kms_request_str_destroy (payload);
diff --git a/src/third_party/kms-message/src/kms_crypto.h b/src/third_party/kms-message/src/kms_crypto.h
index 0da50b7a12b..a9789451bd5 100644
--- a/src/third_party/kms-message/src/kms_crypto.h
+++ b/src/third_party/kms-message/src/kms_crypto.h
@@ -31,7 +31,14 @@ typedef struct {
const char *input,
size_t len,
unsigned char *hash_out);
+ bool (*sign_rsaes_pkcs1_v1_5) (void *sign_ctx,
+ const char *private_key,
+ size_t private_key_len,
+ const char *input,
+ size_t input_len,
+ unsigned char *signature_out);
void *ctx;
+ void *sign_ctx;
} _kms_crypto_t;
int
@@ -51,4 +58,13 @@ kms_sha256_hmac (void *ctx,
size_t len,
unsigned char *hash_out);
+/* signature_out must be a preallocated buffer of 256 bytes (or greater). */
+bool
+kms_sign_rsaes_pkcs1_v1_5 (void *sign_ctx,
+ const char *private_key,
+ size_t private_key_len,
+ const char *input,
+ size_t input_len,
+ unsigned char *signature_out);
+
#endif /* KMS_MESSAGE_KMS_CRYPTO_H */
diff --git a/src/third_party/kms-message/src/kms_crypto_apple.c b/src/third_party/kms-message/src/kms_crypto_apple.c
index 61da0a62887..c9212f10098 100644
--- a/src/third_party/kms-message/src/kms_crypto_apple.c
+++ b/src/third_party/kms-message/src/kms_crypto_apple.c
@@ -16,8 +16,14 @@
#include "kms_crypto.h"
+#ifdef KMS_MESSAGE_ENABLE_CRYPTO_COMMON_CRYPTO
+
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonHMAC.h>
+#include <CoreFoundation/CFArray.h>
+#include <Security/SecKey.h>
+#include <Security/SecItem.h>
+#include <Security/SecImportExport.h>
int
kms_crypto_init ()
@@ -54,3 +60,96 @@ kms_sha256_hmac (void *unused_ctx,
CCHmac (kCCHmacAlgSHA256, key_input, key_len, input, len, hash_out);
return true;
}
+
+static void
+safe_CFRelease (CFTypeRef ptr)
+{
+ if (ptr) {
+ CFRelease (ptr);
+ }
+}
+
+bool
+kms_sign_rsaes_pkcs1_v1_5 (void *unused_ctx,
+ const char *private_key,
+ size_t private_key_len,
+ const char *input,
+ size_t input_len,
+ unsigned char *signature_out)
+{
+ CFDataRef key_data_ref = NULL;
+ CFDataRef pass_ref = NULL;
+ SecItemImportExportKeyParameters import_params;
+ OSStatus status;
+ /* TODO: I think the expected format should be kSecFormatWrappedPKCS8, but
+ * GCP keys appear to only load for kSecFormatBSAFE. */
+ SecExternalFormat format = kSecFormatUnknown;
+ SecExternalItemType type = kSecItemTypePrivateKey;
+ CFArrayRef out_ref = NULL;
+ SecKeyRef key_ref = NULL;
+ CFDataRef data_to_sign_ref = NULL;
+ CFErrorRef error_ref;
+ CFDataRef signature_ref = NULL;
+ bool ret = false;
+
+ key_data_ref = CFDataCreate (NULL /* default allocator */,
+ (const uint8_t *) private_key,
+ (CFIndex) private_key_len);
+ if (!key_data_ref) {
+ goto cleanup;
+ }
+ memset (&import_params, 0, sizeof (SecItemImportExportKeyParameters));
+ import_params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+
+ /* Give an empty password. SecItemImport returns an error expecting a
+ * password. */
+ pass_ref = CFDataCreate (NULL, NULL, 0);
+ if (!pass_ref) {
+ goto cleanup;
+ }
+ import_params.passphrase = (CFTypeRef) pass_ref;
+
+ status = SecItemImport (key_data_ref,
+ NULL /* extension. */,
+ &format,
+ &type,
+ 0,
+ &import_params,
+ NULL /* keychain */,
+ &out_ref);
+ if (status != errSecSuccess) {
+ goto cleanup;
+ }
+ if (1 != CFArrayGetCount (out_ref)) {
+ goto cleanup;
+ }
+
+ key_ref = (SecKeyRef) CFArrayGetValueAtIndex (out_ref, 0);
+ data_to_sign_ref = CFDataCreate (NULL, (const uint8_t *) input, input_len);
+ if (!data_to_sign_ref) {
+ goto cleanup;
+ }
+ error_ref = NULL;
+ signature_ref =
+ SecKeyCreateSignature (key_ref,
+ kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256,
+ data_to_sign_ref,
+ &error_ref);
+ if (!signature_ref) {
+ goto cleanup;
+ }
+ memcpy (signature_out,
+ CFDataGetBytePtr (signature_ref),
+ CFDataGetLength (signature_ref));
+
+ ret = true;
+cleanup:
+ safe_CFRelease (key_data_ref);
+ safe_CFRelease (pass_ref);
+ safe_CFRelease (out_ref);
+ safe_CFRelease (data_to_sign_ref);
+ safe_CFRelease (signature_ref);
+ return ret;
+}
+
+#endif /* KMS_MESSAGE_ENABLE_CRYPTO_COMMON_CRYPTO */
diff --git a/src/third_party/kms-message/src/kms_crypto_openssl.c b/src/third_party/kms-message/src/kms_crypto_libcrypto.c
index f6202906eb5..52f6ef713c4 100644
--- a/src/third_party/kms-message/src/kms_crypto_openssl.c
+++ b/src/third_party/kms-message/src/kms_crypto_libcrypto.c
@@ -16,6 +16,8 @@
#include "kms_crypto.h"
+#ifdef KMS_MESSAGE_ENABLE_CRYPTO_LIBCRYPTO
+
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
@@ -88,3 +90,49 @@ kms_sha256_hmac (void *unused_ctx,
hash_out,
NULL) != NULL;
}
+
+bool
+kms_sign_rsaes_pkcs1_v1_5 (void *unused_ctx,
+ const char *private_key,
+ size_t private_key_len,
+ const char *input,
+ size_t input_len,
+ unsigned char *signature_out)
+{
+ EVP_MD_CTX *ctx;
+ EVP_PKEY *pkey = NULL;
+ bool ret = false;
+ size_t signature_out_len = 256;
+
+ ctx = EVP_MD_CTX_new ();
+ pkey = d2i_PrivateKey (EVP_PKEY_RSA,
+ NULL,
+ (const unsigned char **) &private_key,
+ private_key_len);
+ if (!pkey) {
+ goto cleanup;
+ }
+
+ ret = EVP_DigestSignInit (ctx, NULL, EVP_sha256 (), NULL /* engine */, pkey);
+ if (ret != 1) {
+ goto cleanup;
+ }
+
+ ret = EVP_DigestSignUpdate (ctx, input, input_len);
+ if (ret != 1) {
+ goto cleanup;
+ }
+
+ ret = EVP_DigestSignFinal (ctx, signature_out, &signature_out_len);
+ if (ret != 1) {
+ goto cleanup;
+ }
+
+ ret = true;
+cleanup:
+ EVP_MD_CTX_free (ctx);
+ EVP_PKEY_free (pkey);
+ return ret;
+}
+
+#endif /* KMS_MESSAGE_ENABLE_CRYPTO_LIBCRYPTO */
diff --git a/src/third_party/kms-message/src/kms_crypto_none.c b/src/third_party/kms-message/src/kms_crypto_none.c
index 9ef2147687f..dee69ffe0a7 100644
--- a/src/third_party/kms-message/src/kms_crypto_none.c
+++ b/src/third_party/kms-message/src/kms_crypto_none.c
@@ -16,6 +16,8 @@
#include "kms_crypto.h"
+#ifndef KMS_MESSAGE_ENABLE_CRYPTO
+
int
kms_crypto_init ()
{
@@ -48,3 +50,16 @@ kms_sha256_hmac (void *unused_ctx,
/* only gets called if hooks were mistakenly not set */
return false;
}
+
+bool
+kms_sign_rsaes_pkcs1_v1_5 (void *unused_ctx,
+ const char *private_key,
+ size_t private_key_len,
+ const char *input,
+ size_t input_len,
+ unsigned char *signature_out) {
+ /* only gets called if hooks were mistakenly not set */
+ return false;
+}
+
+#endif /* KMS_MESSAGE_ENABLE_CRYPTO */
diff --git a/src/third_party/kms-message/src/kms_crypto_windows.c b/src/third_party/kms-message/src/kms_crypto_windows.c
index ccdc7e095d4..5d41f7fd81f 100644
--- a/src/third_party/kms-message/src/kms_crypto_windows.c
+++ b/src/third_party/kms-message/src/kms_crypto_windows.c
@@ -16,6 +16,8 @@
#include "kms_crypto.h"
+#ifdef KMS_MESSAGE_ENABLE_CRYPTO_CNG
+
// tell windows.h not to include a bunch of headers we don't need:
#define WIN32_LEAN_AND_MEAN
@@ -36,9 +38,13 @@
#include <ntstatus.h>
#include <bcrypt.h>
+#include <wincrypt.h>
static BCRYPT_ALG_HANDLE _algoSHA256 = 0;
static BCRYPT_ALG_HANDLE _algoSHA256Hmac = 0;
+static BCRYPT_ALG_HANDLE _algoRSA = 0;
+
+#define SHA_256_HASH_LEN 32
int
kms_crypto_init ()
@@ -57,6 +63,12 @@ kms_crypto_init ()
return 2;
}
+ if (BCryptOpenAlgorithmProvider (
+ &_algoRSA, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0) !=
+ STATUS_SUCCESS) {
+ return 3;
+ }
+
return 0;
}
@@ -65,6 +77,7 @@ kms_crypto_cleanup ()
{
(void) BCryptCloseAlgorithmProvider (_algoSHA256, 0);
(void) BCryptCloseAlgorithmProvider (_algoSHA256Hmac, 0);
+ (void) BCryptCloseAlgorithmProvider (_algoRSA, 0);
}
bool
@@ -130,3 +143,126 @@ cleanup:
return status == STATUS_SUCCESS ? 1 : 0;
}
+
+bool
+kms_sign_rsaes_pkcs1_v1_5 (void *unused_ctx,
+ const char *private_key,
+ size_t private_key_len,
+ const char *input,
+ size_t input_len,
+ unsigned char *signature_out)
+{
+ bool success = false;
+ bool ret = false;
+ LPBYTE blob_private = NULL;
+ DWORD blob_private_len = 0;
+ LPBYTE raw_private = NULL;
+ DWORD raw_private_len = 0;
+
+ NTSTATUS status;
+ BCRYPT_KEY_HANDLE hKey = NULL;
+ BCRYPT_PKCS1_PADDING_INFO padding_PKCS1;
+
+ unsigned char *hash_value = NULL;
+ DWORD hash_length = 256;
+
+ success = CryptDecodeObjectEx (X509_ASN_ENCODING,
+ PKCS_PRIVATE_KEY_INFO,
+ private_key,
+ (DWORD) private_key_len,
+ 0,
+ NULL,
+ NULL,
+ &blob_private_len);
+ if (!success) {
+ goto cleanup;
+ }
+
+ blob_private = (LPBYTE) calloc (1, blob_private_len);
+
+ success = CryptDecodeObjectEx (X509_ASN_ENCODING,
+ PKCS_PRIVATE_KEY_INFO,
+ private_key,
+ (DWORD) private_key_len,
+ 0,
+ NULL,
+ blob_private,
+ &blob_private_len);
+ if (!success) {
+ goto cleanup;
+ }
+
+ CRYPT_PRIVATE_KEY_INFO *privateKeyInfo =
+ (CRYPT_PRIVATE_KEY_INFO *) blob_private;
+
+ success = CryptDecodeObjectEx (X509_ASN_ENCODING,
+ PKCS_RSA_PRIVATE_KEY,
+ privateKeyInfo->PrivateKey.pbData,
+ (DWORD) privateKeyInfo->PrivateKey.cbData,
+ 0,
+ NULL,
+ NULL,
+ &raw_private_len);
+ if (!success) {
+ goto cleanup;
+ }
+
+ raw_private = (LPBYTE) calloc (1, raw_private_len);
+
+ success = CryptDecodeObjectEx (X509_ASN_ENCODING,
+ PKCS_RSA_PRIVATE_KEY,
+ privateKeyInfo->PrivateKey.pbData,
+ (DWORD) privateKeyInfo->PrivateKey.cbData,
+ 0,
+ NULL,
+ raw_private,
+ &raw_private_len);
+ if (!success) {
+ goto cleanup;
+ }
+
+ status = BCryptImportKeyPair (
+ _algoRSA,
+ NULL,
+ LEGACY_RSAPRIVATE_BLOB,
+ &hKey,
+ raw_private,
+ raw_private_len,
+ 0);
+ if (!NT_SUCCESS (status)) {
+ goto cleanup;
+ }
+
+ hash_value = calloc (1, SHA_256_HASH_LEN);
+
+ if(!kms_sha256 (NULL, input, input_len, hash_value)) {
+ goto cleanup;
+ }
+
+ padding_PKCS1.pszAlgId = BCRYPT_SHA256_ALGORITHM;
+
+ status =
+ BCryptSignHash (hKey,
+ &padding_PKCS1,
+ hash_value,
+ SHA_256_HASH_LEN,
+ signature_out,
+ hash_length,
+ &hash_length,
+ BCRYPT_PAD_PKCS1);
+ if (!NT_SUCCESS (status)) {
+ goto cleanup;
+ }
+
+ ret = true;
+
+cleanup:
+ BCryptDestroyKey(hKey);
+ free (blob_private);
+ free (raw_private);
+ free (hash_value);
+
+ return ret;
+}
+
+#endif /* KMS_MESSAGE_ENABLE_CRYPTO_CNG */
diff --git a/src/third_party/kms-message/src/kms_decrypt_request.c b/src/third_party/kms-message/src/kms_decrypt_request.c
index 06faa431195..25cbecad237 100644
--- a/src/third_party/kms-message/src/kms_decrypt_request.c
+++ b/src/third_party/kms-message/src/kms_decrypt_request.c
@@ -48,7 +48,7 @@ kms_decrypt_request_new (const uint8_t *ciphertext_blob,
if (!(b64 = malloc (b64_len))) {
KMS_ERROR (request,
"Could not allocate %d bytes for base64-encoding payload",
- b64_len);
+ (int) b64_len);
goto done;
}
@@ -59,7 +59,10 @@ kms_decrypt_request_new (const uint8_t *ciphertext_blob,
payload = kms_request_str_new ();
kms_request_str_appendf (payload, "{\"CiphertextBlob\": \"%s\"}", b64);
- kms_request_append_payload (request, payload->str, payload->len);
+ if (!kms_request_append_payload (request, payload->str, payload->len)) {
+ KMS_ERROR (request, "Could not append payload");
+ goto done;
+ }
done:
free (b64);
diff --git a/src/third_party/kms-message/src/kms_encrypt_request.c b/src/third_party/kms-message/src/kms_encrypt_request.c
index b5f4d6436e6..3f922abc3a8 100644
--- a/src/third_party/kms-message/src/kms_encrypt_request.c
+++ b/src/third_party/kms-message/src/kms_encrypt_request.c
@@ -47,7 +47,7 @@ kms_encrypt_request_new (const uint8_t *plaintext,
if (!(b64 = malloc (b64_len))) {
KMS_ERROR (request,
"Could not allocate %d bytes for base64-encoding payload",
- b64_len);
+ (int) b64_len);
goto done;
}
@@ -60,7 +60,10 @@ kms_encrypt_request_new (const uint8_t *plaintext,
payload = kms_request_str_new ();
kms_request_str_appendf (
payload, "{\"Plaintext\": \"%s\", \"KeyId\": \"%s\"}", b64, key_id);
- kms_request_append_payload (request, payload->str, payload->len);
+ if (!kms_request_append_payload (request, payload->str, payload->len)) {
+ KMS_ERROR (request, "Could not append payload");
+ goto done;
+ }
done:
free (b64);
diff --git a/src/third_party/kms-message/src/kms_gcp_request.c b/src/third_party/kms-message/src/kms_gcp_request.c
new file mode 100644
index 00000000000..564cacc6113
--- /dev/null
+++ b/src/third_party/kms-message/src/kms_gcp_request.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2020-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 "kms_message/kms_gcp_request.h"
+
+#include "kms_message/kms_b64.h"
+#include "kms_message_private.h"
+#include "kms_request_opt_private.h"
+
+/* Set a default expiration of 5 minutes for JSON Web Tokens (GCP allows up to
+ * one hour) */
+#define JWT_EXPIRATION_SECS 5 * 60
+#define SIGNATURE_LEN 256
+
+kms_request_t *
+kms_gcp_request_oauth_new (const char *host,
+ const char *email,
+ const char *audience,
+ const char *scope,
+ const char *private_key_data,
+ size_t private_key_len,
+ const kms_request_opt_t *opt)
+{
+ kms_request_t *req = NULL;
+ kms_request_str_t *str = NULL;
+ time_t issued_at;
+ /* base64 encoding of {"alg":"RS256","typ":"JWT"} */
+ const char *jwt_header_b64url = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9";
+ char *jwt_claims_b64url = NULL;
+ char *jwt_header_and_claims_b64url = NULL;
+ uint8_t *jwt_signature = NULL;
+ char *jwt_signature_b64url = NULL;
+ char *jwt_assertion_b64url = NULL;
+ char *payload = NULL;
+
+ req = kms_request_new ("POST", "/token", opt);
+ if (opt->provider != KMS_REQUEST_PROVIDER_GCP) {
+ KMS_ERROR (req, "Expected KMS request with provider type: GCP");
+ goto done;
+ }
+
+ if (kms_request_get_error (req)) {
+ goto done;
+ }
+
+ /* Produce the signed JWT <base64url header>.<base64url claims>.<base64url
+ * signature> */
+ issued_at = time (NULL);
+ str = kms_request_str_new ();
+ kms_request_str_appendf (str,
+ "{\"iss\": \"%s\", \"aud\": \"%s\", \"scope\": "
+ "\"%s\", \"iat\": %lu, \"exp\": %lu}",
+ email,
+ audience,
+ scope,
+ (unsigned long) issued_at,
+ (unsigned long) issued_at + JWT_EXPIRATION_SECS);
+ jwt_claims_b64url =
+ kms_message_raw_to_b64url ((const uint8_t *) str->str, str->len);
+ kms_request_str_destroy (str);
+ if (!jwt_claims_b64url) {
+ KMS_ERROR (req, "Failed to base64url encode JWT claims");
+ goto done;
+ }
+
+ str = kms_request_str_new ();
+ kms_request_str_appendf (str, "%s.%s", jwt_header_b64url, jwt_claims_b64url);
+ jwt_header_and_claims_b64url = kms_request_str_detach (str);
+
+ /* Produce the signature of <base64url header>.<base64url claims> */
+ req->crypto.sign_rsaes_pkcs1_v1_5 = kms_sign_rsaes_pkcs1_v1_5;
+ if (opt->crypto.sign_rsaes_pkcs1_v1_5) {
+ req->crypto.sign_rsaes_pkcs1_v1_5 = opt->crypto.sign_rsaes_pkcs1_v1_5;
+ req->crypto.sign_ctx = opt->crypto.sign_ctx;
+ }
+
+ jwt_signature = malloc (SIGNATURE_LEN);
+ if (!req->crypto.sign_rsaes_pkcs1_v1_5 (
+ req->crypto.sign_ctx,
+ private_key_data,
+ private_key_len,
+ jwt_header_and_claims_b64url,
+ strlen (jwt_header_and_claims_b64url),
+ jwt_signature)) {
+ KMS_ERROR (req, "Failed to create GCP oauth request signature");
+ goto done;
+ }
+
+ jwt_signature_b64url =
+ kms_message_raw_to_b64url (jwt_signature, SIGNATURE_LEN);
+ if (!jwt_signature_b64url) {
+ KMS_ERROR (req, "Failed to base64url encode JWT signature");
+ goto done;
+ }
+ str = kms_request_str_new ();
+ kms_request_str_appendf (str,
+ "%s.%s.%s",
+ jwt_header_b64url,
+ jwt_claims_b64url,
+ jwt_signature_b64url);
+ jwt_assertion_b64url = kms_request_str_detach (str);
+
+ str =
+ kms_request_str_new_from_chars ("grant_type=urn%3Aietf%3Aparams%3Aoauth%"
+ "3Agrant-type%3Ajwt-bearer&assertion=",
+ -1);
+ kms_request_str_append_chars (str, jwt_assertion_b64url, -1);
+ payload = kms_request_str_detach (str);
+
+ if (!kms_request_add_header_field (
+ req, "Content-Type", "application/x-www-form-urlencoded")) {
+ goto done;
+ }
+ if (!kms_request_add_header_field (req, "Host", host)) {
+ goto done;
+ }
+ if (!kms_request_add_header_field (req, "Accept", "application/json")) {
+ goto done;
+ }
+
+ if (!kms_request_append_payload (req, payload, strlen (payload))) {
+ goto done;
+ }
+
+done:
+ free (jwt_signature);
+ free (jwt_signature_b64url);
+ free (jwt_claims_b64url);
+ free (jwt_header_and_claims_b64url);
+ free (jwt_assertion_b64url);
+ free (payload);
+ return req;
+}
+
+static kms_request_t *
+_encrypt_decrypt_common (const char *encrypt_decrypt,
+ const char *host,
+ const char *access_token,
+ const char *project_id,
+ const char *location,
+ const char *key_ring_name,
+ const char *key_name,
+ const char *key_version,
+ const uint8_t *value,
+ size_t value_len,
+ const kms_request_opt_t *opt)
+{
+ char *path_and_query = NULL;
+ char *payload = NULL;
+ char *bearer_token_value = NULL;
+ char *value_base64 = NULL;
+ kms_request_t *req;
+ kms_request_str_t *str;
+
+ str = kms_request_str_new ();
+ /* /v1/projects/{project-id}/locations/{location}/keyRings/{key-ring-name}/cryptoKeys/{key-name}
+ */
+ kms_request_str_appendf (
+ str,
+ "/v1/projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s",
+ project_id,
+ location,
+ key_ring_name,
+ key_name);
+ if (key_version && strlen (key_version) > 0) {
+ kms_request_str_appendf (str, "/cryptoKeyVersions/%s", key_version);
+ }
+ kms_request_str_appendf (str, ":%s", encrypt_decrypt);
+ path_and_query = kms_request_str_detach (str);
+
+ req = kms_request_new ("POST", path_and_query, opt);
+
+ if (opt->provider != KMS_REQUEST_PROVIDER_GCP) {
+ KMS_ERROR (req, "Expected KMS request with provider type: GCP");
+ goto done;
+ }
+
+ if (kms_request_get_error (req)) {
+ goto done;
+ }
+
+ value_base64 = kms_message_raw_to_b64 (value, value_len);
+ if (!value_base64) {
+ KMS_ERROR (req, "Could not bases64-encode plaintext");
+ goto done;
+ }
+
+ str = kms_request_str_new ();
+ if (0 == strcmp ("encrypt", encrypt_decrypt)) {
+ kms_request_str_appendf (str, "{\"plaintext\": \"%s\"}", value_base64);
+ } else {
+ kms_request_str_appendf (str, "{\"ciphertext\": \"%s\"}", value_base64);
+ }
+
+ payload = kms_request_str_detach (str);
+ str = kms_request_str_new ();
+ kms_request_str_appendf (str, "Bearer %s", access_token);
+ bearer_token_value = kms_request_str_detach (str);
+ if (!kms_request_add_header_field (
+ req, "Authorization", bearer_token_value)) {
+ goto done;
+ }
+ if (!kms_request_add_header_field (
+ req, "Content-Type", "application/json")) {
+ goto done;
+ }
+ if (!kms_request_add_header_field (req, "Host", host)) {
+ goto done;
+ }
+ if (!kms_request_add_header_field (req, "Accept", "application/json")) {
+ goto done;
+ }
+
+ if (!kms_request_append_payload (req, payload, strlen (payload))) {
+ goto done;
+ }
+
+done:
+ kms_request_free_string (path_and_query);
+ kms_request_free_string (payload);
+ kms_request_free_string (bearer_token_value);
+ kms_request_free_string (value_base64);
+ return req;
+}
+
+kms_request_t *
+kms_gcp_request_encrypt_new (const char *host,
+ const char *access_token,
+ const char *project_id,
+ const char *location,
+ const char *key_ring_name,
+ const char *key_name,
+ const char *key_version,
+ const uint8_t *plaintext,
+ size_t plaintext_len,
+ const kms_request_opt_t *opt)
+{
+ return _encrypt_decrypt_common ("encrypt",
+ host,
+ access_token,
+ project_id,
+ location,
+ key_ring_name,
+ key_name,
+ key_version,
+ plaintext,
+ plaintext_len,
+ opt);
+}
+
+kms_request_t *
+kms_gcp_request_decrypt_new (const char *host,
+ const char *access_token,
+ const char *project_id,
+ const char *location,
+ const char *key_ring_name,
+ const char *key_name,
+ const uint8_t *ciphertext,
+ size_t ciphertext_len,
+ const kms_request_opt_t *opt)
+{
+ return _encrypt_decrypt_common ("decrypt",
+ host,
+ access_token,
+ project_id,
+ location,
+ key_ring_name,
+ key_name,
+ NULL /* key_version */,
+ ciphertext,
+ ciphertext_len,
+ opt);
+} \ No newline at end of file
diff --git a/src/third_party/kms-message/src/kms_kv_list.c b/src/third_party/kms-message/src/kms_kv_list.c
index 2d6845a1aa0..0cff3dc2c64 100644
--- a/src/third_party/kms-message/src/kms_kv_list.c
+++ b/src/third_party/kms-message/src/kms_kv_list.c
@@ -17,6 +17,7 @@
#include "kms_kv_list.h"
#include "kms_message/kms_message.h"
+#include "kms_message_private.h"
#include "kms_request_str.h"
#include "kms_port.h"
#include "sort.h"
@@ -39,9 +40,12 @@ kms_kv_list_t *
kms_kv_list_new (void)
{
kms_kv_list_t *lst = malloc (sizeof (kms_kv_list_t));
+ KMS_ASSERT (lst);
lst->size = 16;
lst->kvs = malloc (lst->size * sizeof (kms_kv_t));
+ KMS_ASSERT (lst->kvs);
+
lst->len = 0;
return lst;
@@ -72,6 +76,7 @@ kms_kv_list_add (kms_kv_list_t *lst,
if (lst->len == lst->size) {
lst->size *= 2;
lst->kvs = realloc (lst->kvs, lst->size * sizeof (kms_kv_t));
+ KMS_ASSERT (lst->kvs);
}
kv_init (&lst->kvs[lst->len], key, value);
@@ -84,7 +89,7 @@ kms_kv_list_find (const kms_kv_list_t *lst, const char *key)
size_t i;
for (i = 0; i < lst->len; i++) {
- if (0 == strcasecmp (lst->kvs[i].key->str, key)) {
+ if (0 == kms_strcasecmp (lst->kvs[i].key->str, key)) {
return &lst->kvs[i];
}
}
@@ -119,8 +124,12 @@ kms_kv_list_dup (const kms_kv_list_t *lst)
}
dup = malloc (sizeof (kms_kv_list_t));
+ KMS_ASSERT (dup);
+
dup->size = dup->len = lst->len;
dup->kvs = malloc (lst->len * sizeof (kms_kv_t));
+ KMS_ASSERT (dup->kvs);
+
for (i = 0; i < lst->len; i++) {
kv_init (&dup->kvs[i], lst->kvs[i].key, lst->kvs[i].value);
diff --git a/src/third_party/kms-message/src/kms_message/kms_azure_request.h b/src/third_party/kms-message/src/kms_message/kms_azure_request.h
new file mode 100644
index 00000000000..2e9af68fd03
--- /dev/null
+++ b/src/third_party/kms-message/src/kms_message/kms_azure_request.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020-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.
+ */
+
+#ifndef KMS_AZURE_REQUEST_H
+#define KMS_AZURE_REQUEST_H
+
+#include "kms_message_defines.h"
+#include "kms_request.h"
+#include "kms_request_opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Constructs an oauth client credentials grant request for Azure.
+ * See
+ * https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#get-a-token.
+ *
+ * Parameters:
+ * All parameters must be NULL terminated strings.
+ * - host: The value of the Host header. This should be a custom host or
+ * "login.microsoftonline.com".
+ * - scope: The oauth scope. This should be a custom scope or
+ * "https%3A%2F%2Fvault.azure.net%2F.default". Must be URL encoded.
+ * - tenant_id: The Azure tenant ID.
+ * - client_id: The client ID to authenticate.
+ * - client_secret: The client secret to authenticate.
+ * - opt: Additional options. This must have the Azure provider set via
+ * kms_request_opt_set_provider.
+ *
+ * Returns: A new kms_request_t.
+ * Always returns a new kms_request_t, even on error.
+ * Caller must check if an error occurred by calling kms_request_get_error.
+ */
+KMS_MSG_EXPORT (kms_request_t *)
+kms_azure_request_oauth_new (const char *host,
+ const char *scope,
+ const char *tenant_id,
+ const char *client_id,
+ const char *client_secret,
+ const kms_request_opt_t *opt);
+
+/* Constructs a wrapkey request for Azure.
+ * See https://docs.microsoft.com/en-us/rest/api/keyvault/wrapkey/wrapkey
+ *
+ * Parameters:
+ * All parameters must be NULL terminated strings.
+ * - host: The value of the Host header, like "mykeyvault.vault.azure.net".
+ * - access_token: The access_token obtained from an oauth response as a
+ * base64url encoded string.
+ * - key_name: The azure key name.
+ * - key_version: An optional key version. May be NULL or empty string.
+ * - plaintext: The plaintext key to encrypt.
+ * - plaintext_len: The number of bytes of plaintext.
+ * - opt: Additional options. This must have the Azure provider set via
+ * kms_request_opt_set_provider.
+ */
+
+KMS_MSG_EXPORT (kms_request_t *)
+kms_azure_request_wrapkey_new (const char *host,
+ const char *access_token,
+ const char *key_name,
+ const char *key_version,
+ const uint8_t *plaintext,
+ size_t plaintext_len,
+ const kms_request_opt_t *opt);
+
+/* Constructs an unwrapkey request for Azure.
+ * See https://docs.microsoft.com/en-us/rest/api/keyvault/unwrapkey/unwrapkey
+ *
+ * Parameters:
+ * All parameters must be NULL terminated strings.
+ * - host: The value of the Host header, like "mykeyvault.vault.azure.net".
+ * - access_token: The access_token obtained from an oauth response as a
+ * base64url encoded string.
+ * - key_name: The azure key name.
+ * - key_version: An optional key version. May be NULL or empty string.
+ * - ciphertext: The ciphertext key to decrypt.
+ * - ciphertext_len: The number of bytes of ciphertext.
+ * - opt: Additional options. This must have the Azure provider set via
+ * kms_request_opt_set_provider.
+ */
+
+KMS_MSG_EXPORT (kms_request_t *)
+kms_azure_request_unwrapkey_new (const char *host,
+ const char *access_token,
+ const char *key_name,
+ const char *key_version,
+ const uint8_t *ciphertext,
+ size_t ciphertext_len,
+ const kms_request_opt_t *opt);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* KMS_AZURE_REQUEST_H */
diff --git a/src/third_party/kms-message/src/kms_message/kms_b64.h b/src/third_party/kms-message/src/kms_message/kms_b64.h
index a55c1ec9f6d..f0845cd331e 100644
--- a/src/third_party/kms-message/src/kms_message/kms_b64.h
+++ b/src/third_party/kms-message/src/kms_message/kms_b64.h
@@ -38,6 +38,31 @@ kms_message_b64_ntop (uint8_t const *src,
KMS_MSG_EXPORT (int)
kms_message_b64_pton (char const *src, uint8_t *target, size_t targsize);
+/* src and target may be the same string. Assumes no whitespace in src. */
+KMS_MSG_EXPORT (int)
+kms_message_b64_to_b64url (const char *src,
+ size_t srclength,
+ char *target,
+ size_t targsize);
+KMS_MSG_EXPORT (int)
+kms_message_b64url_to_b64 (const char *src,
+ size_t srclength,
+ char *target,
+ size_t targsize);
+
+/* Convenience conversions which return copies. */
+char *
+kms_message_raw_to_b64 (const uint8_t *raw, size_t raw_len);
+
+uint8_t *
+kms_message_b64_to_raw (const char *b64, size_t *out);
+
+char *
+kms_message_raw_to_b64url (const uint8_t *raw, size_t raw_len);
+
+uint8_t *
+kms_message_b64url_to_raw (const char *b64url, size_t *out);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/src/third_party/kms-message/src/kms_message/kms_caller_identity_request.h b/src/third_party/kms-message/src/kms_message/kms_caller_identity_request.h
index 9af8e2ccdd9..9f48e534235 100644
--- a/src/third_party/kms-message/src/kms_message/kms_caller_identity_request.h
+++ b/src/third_party/kms-message/src/kms_message/kms_caller_identity_request.h
@@ -17,7 +17,9 @@
#ifndef KMS_CALLER_IDENTITY_REQUEST_H
#define KMS_CALLER_IDENTITY_REQUEST_H
-#include "kms_message.h"
+#include "kms_message_defines.h"
+#include "kms_request.h"
+#include "kms_request_opt.h"
#ifdef __cplusplus
extern "C" {
diff --git a/src/third_party/kms-message/src/kms_message/kms_decrypt_request.h b/src/third_party/kms-message/src/kms_message/kms_decrypt_request.h
index b27a96d9917..db18d5f5e1c 100644
--- a/src/third_party/kms-message/src/kms_message/kms_decrypt_request.h
+++ b/src/third_party/kms-message/src/kms_message/kms_decrypt_request.h
@@ -17,7 +17,9 @@
#ifndef KMS_DECRYPT_REQUEST_H
#define KMS_DECRYPT_REQUEST_H
-#include "kms_message.h"
+#include "kms_message_defines.h"
+#include "kms_request.h"
+#include "kms_request_opt.h"
#ifdef __cplusplus
extern "C" {
diff --git a/src/third_party/kms-message/src/kms_message/kms_encrypt_request.h b/src/third_party/kms-message/src/kms_message/kms_encrypt_request.h
index ac30f1a3675..601ee36297f 100644
--- a/src/third_party/kms-message/src/kms_message/kms_encrypt_request.h
+++ b/src/third_party/kms-message/src/kms_message/kms_encrypt_request.h
@@ -17,7 +17,9 @@
#ifndef KMS_ENCRYPT_REQUEST_H
#define KMS_ENCRYPT_REQUEST_H
-#include "kms_message.h"
+#include "kms_message_defines.h"
+#include "kms_request.h"
+#include "kms_request_opt.h"
#ifdef __cplusplus
extern "C" {
diff --git a/src/third_party/kms-message/src/kms_message/kms_gcp_request.h b/src/third_party/kms-message/src/kms_message/kms_gcp_request.h
new file mode 100644
index 00000000000..1d1555fb0c6
--- /dev/null
+++ b/src/third_party/kms-message/src/kms_message/kms_gcp_request.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2020-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.
+ */
+
+#ifndef KMS_GCP_REQUEST_H
+#define KMS_GCP_REQUEST_H
+
+#include "kms_message_defines.h"
+#include "kms_request.h"
+#include "kms_request_opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Constructs an oauth client credentials request for GCP.
+ * See https://developers.google.com/identity/protocols/oauth2/service-account
+ *
+ * Parameters:
+ * - host: The host header, like "oauth2.googleapis.com".
+ * - email: The email for the service account to authenticate.
+ * - audience: The "aud" field in the JSON Web Token (JWT). Should be a URL
+ * like "https://oauth2.googleapis.com/token"
+ * - scope: The "scope" field in the JSON Web Token (JWT). Should be a URL
+ * like "https://www.googleapis.com/auth/cloudkms".
+ * - private_key_data: Bytes pointing to a PKCS#8 private key.
+ * - private_key_len: The length of private_key_data.
+ * - opt: Request options. The provider must be set to KMS_REQUEST_PROVIDER_GCP
+ * with kms_request_opt_set_provider. Callers that want to use a custom crypto
+ * callback to sign the request should set the callback on opt with
+ * kms_request_opt_set_crypto_hook_rsaes_pkcs1_v1_5.
+ *
+ * Returns: A new kms_request_t.
+ * Always returns a new kms_request_t, even on error.
+ * Caller must check if an error occurred by calling kms_request_get_error.
+ */
+KMS_MSG_EXPORT (kms_request_t *)
+kms_gcp_request_oauth_new (const char *host,
+ const char *email,
+ const char *audience,
+ const char *scope,
+ const char *private_key_data,
+ size_t private_key_len,
+ const kms_request_opt_t *opt);
+
+/* Constructs the encrypt request for GCP.
+ * See
+ * https://cloud.google.com/kms/docs/encrypt-decrypt#kms-encrypt-symmetric-api
+ *
+ * Parameters:
+ * - host: The value of the Host header, like "cloudkms.googleapis.com".
+ * - project_id: The project id.
+ * - location: The location id, like "global".
+ * - key_ring_name: The key ring name.
+ * - key_name: The key name.
+ * - key_version: The optional key version. May be NULL.
+ * - plaintext: The plaintext key to encrypt.
+ * - plaintext_len: The number of bytes of plaintext.
+ * - opt: Request options. The provider must be set to KMS_REQUEST_PROVIDER_GCP
+ * with kms_request_opt_set_provider.
+ *
+ * Returns: A new kms_request_t.
+ * Always returns a new kms_request_t, even on error.
+ * Caller must check if an error occurred by calling kms_request_get_error.
+ */
+KMS_MSG_EXPORT (kms_request_t *)
+kms_gcp_request_encrypt_new (const char *host,
+ const char *access_token,
+ const char *project_id,
+ const char *location,
+ const char *key_ring_name,
+ const char *key_name,
+ const char *key_version,
+ const uint8_t *plaintext,
+ size_t plaintext_len,
+ const kms_request_opt_t *opt);
+
+/* Constructs the decrypt request for GCP.
+ * See
+ * https://cloud.google.com/kms/docs/encrypt-decrypt#kms-decrypt-symmetric-api
+ *
+ * Parameters:
+ * - host: The value of the Host header, like "cloudkms.googleapis.com".
+ * - project_id: The project id.
+ * - location: The location id, like "global".
+ * - key_ring_name: The key ring name.
+ * - key_name: The key name.
+ * - ciphertext: The ciphertext key to encrypt.
+ * - ciphertext_len: The number of bytes of ciphertext.
+ * - opt: Request options. The provider must be set to KMS_REQUEST_PROVIDER_GCP
+ * with kms_request_opt_set_provider.
+ *
+ * Returns: A new kms_request_t.
+ * Always returns a new kms_request_t, even on error.
+ * Caller must check if an error occurred by calling kms_request_get_error.
+ */
+KMS_MSG_EXPORT (kms_request_t *)
+kms_gcp_request_decrypt_new (const char *host,
+ const char *access_token,
+ const char *project_id,
+ const char *location,
+ const char *key_ring_name,
+ const char *key_name,
+ const uint8_t *ciphertext,
+ size_t ciphertext_len,
+ const kms_request_opt_t *opt);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* KMS_GCP_REQUEST_H */
diff --git a/src/third_party/kms-message/src/kms_message/kms_message.h b/src/third_party/kms-message/src/kms_message/kms_message.h
index 6ea95dd04ca..8048528f2e0 100644
--- a/src/third_party/kms-message/src/kms_message/kms_message.h
+++ b/src/third_party/kms-message/src/kms_message/kms_message.h
@@ -17,6 +17,8 @@
#ifndef KMS_MESSAGE_H
#define KMS_MESSAGE_H
+#include <sys/types.h>
+
#include "kms_message_defines.h"
#include "kms_request_opt.h"
#include "kms_request.h"
diff --git a/src/third_party/kms-message/src/kms_message/kms_message_defines.h b/src/third_party/kms-message/src/kms_message/kms_message_defines.h
index a4d019bd774..a539d531ef6 100644
--- a/src/third_party/kms-message/src/kms_message/kms_message_defines.h
+++ b/src/third_party/kms-message/src/kms_message/kms_message_defines.h
@@ -53,4 +53,14 @@ kms_message_cleanup (void);
} /* extern "C" */
#endif
+#ifdef _MSC_VER
+#include <basetsd.h>
+#pragma warning(disable : 4142)
+#ifndef _SSIZE_T_DEFINED
+#define _SSIZE_T_DEFINED
+typedef SSIZE_T ssize_t;
+#endif
+#pragma warning(default : 4142)
+#endif
+
#endif /* KMS_MESSAGE_DEFINES_H */
diff --git a/src/third_party/kms-message/src/kms_message/kms_request.h b/src/third_party/kms-message/src/kms_message/kms_request.h
index cf49f0a31a9..0428c813491 100644
--- a/src/third_party/kms-message/src/kms_message/kms_request.h
+++ b/src/third_party/kms-message/src/kms_message/kms_request.h
@@ -17,7 +17,8 @@
#ifndef KMS_REQUEST_H
#define KMS_REQUEST_H
-#include "kms_message.h"
+#include "kms_message_defines.h"
+#include "kms_request_opt.h"
#include <stdbool.h>
#include <stdint.h>
@@ -28,6 +29,8 @@
extern "C" {
#endif
+/* A KMS request is general enough to create arbitrary HTTP requests, but also
+ * supports generating AWS signature v4. */
typedef struct _kms_request_t kms_request_t;
KMS_MSG_EXPORT (kms_request_t *)
@@ -38,6 +41,8 @@ KMS_MSG_EXPORT (void)
kms_request_destroy (kms_request_t *request);
KMS_MSG_EXPORT (const char *)
kms_request_get_error (kms_request_t *request);
+
+/* Begin: AWS specific */
KMS_MSG_EXPORT (bool)
kms_request_set_date (kms_request_t *request, const struct tm *tm);
KMS_MSG_EXPORT (bool)
@@ -48,6 +53,8 @@ KMS_MSG_EXPORT (bool)
kms_request_set_access_key_id (kms_request_t *request, const char *akid);
KMS_MSG_EXPORT (bool)
kms_request_set_secret_key (kms_request_t *request, const char *key);
+/* End: AWS specific */
+
KMS_MSG_EXPORT (bool)
kms_request_add_header_field (kms_request_t *request,
const char *field_name,
@@ -60,6 +67,8 @@ KMS_MSG_EXPORT (bool)
kms_request_append_payload (kms_request_t *request,
const char *payload,
size_t len);
+
+/* Begin: AWS specific */
KMS_MSG_EXPORT (char *)
kms_request_get_canonical (kms_request_t *request);
@@ -74,9 +83,14 @@ KMS_MSG_EXPORT (char *)
kms_request_get_signature (kms_request_t *request);
KMS_MSG_EXPORT (char *)
kms_request_get_signed (kms_request_t *request);
+/* End: AWS specific */
+
KMS_MSG_EXPORT (void)
kms_request_free_string (char *ptr);
+/* Finalize and obtain a plain HTTP request (no signing). */
+KMS_MSG_EXPORT (char *) kms_request_to_string (kms_request_t *request);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/src/third_party/kms-message/src/kms_message/kms_request_opt.h b/src/third_party/kms-message/src/kms_message/kms_request_opt.h
index f38652e9e74..74a3fb69771 100644
--- a/src/third_party/kms-message/src/kms_message/kms_request_opt.h
+++ b/src/third_party/kms-message/src/kms_message/kms_request_opt.h
@@ -28,13 +28,26 @@ extern "C" {
typedef struct _kms_request_opt_t kms_request_opt_t;
+typedef size_t kms_request_provider_t;
+
+#define KMS_REQUEST_PROVIDER_AWS 0
+#define KMS_REQUEST_PROVIDER_AZURE 1
+#define KMS_REQUEST_PROVIDER_GCP 2
+
KMS_MSG_EXPORT (kms_request_opt_t *)
kms_request_opt_new (void);
+
+/* The default provider is AWS. This will automatically set extra headers.
+ * Returns false if provider is invalid. */
+KMS_MSG_EXPORT (bool)
+kms_request_opt_set_provider (kms_request_opt_t *opt,
+ kms_request_provider_t provider);
KMS_MSG_EXPORT (void)
kms_request_opt_destroy (kms_request_opt_t *request);
KMS_MSG_EXPORT (void)
kms_request_opt_set_connection_close (kms_request_opt_t *opt,
bool connection_close);
+
KMS_MSG_EXPORT (void)
kms_request_opt_set_crypto_hooks (kms_request_opt_t *opt,
bool (*sha256) (void *ctx,
@@ -49,6 +62,16 @@ kms_request_opt_set_crypto_hooks (kms_request_opt_t *opt,
unsigned char *hash_out),
void *ctx);
+KMS_MSG_EXPORT (void)
+kms_request_opt_set_crypto_hook_sign_rsaes_pkcs1_v1_5 (
+ kms_request_opt_t *opt,
+ bool (*sign_rsaes_pkcs1_v1_5) (void *ctx,
+ const char *private_key,
+ size_t private_key_len,
+ const char *input,
+ size_t input_len,
+ unsigned char *signature_out),
+ void *ctx);
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/src/third_party/kms-message/src/kms_message/kms_response.h b/src/third_party/kms-message/src/kms_message/kms_response.h
index 545476d40bc..d270f248826 100644
--- a/src/third_party/kms-message/src/kms_message/kms_response.h
+++ b/src/third_party/kms-message/src/kms_message/kms_response.h
@@ -17,7 +17,9 @@
#ifndef KMS_RESPONSE_H
#define KMS_RESPONSE_H
-#include "kms_message.h"
+#include "kms_message_defines.h"
+
+#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
@@ -25,9 +27,11 @@ extern "C" {
typedef struct _kms_response_t kms_response_t;
+KMS_MSG_EXPORT (int)
+kms_response_get_status (kms_response_t *response);
KMS_MSG_EXPORT (const char *)
-kms_response_get_body (kms_response_t *reply, size_t *len);
-KMS_MSG_EXPORT (void) kms_response_destroy (kms_response_t *reply);
+kms_response_get_body (kms_response_t *response, size_t *len);
+KMS_MSG_EXPORT (void) kms_response_destroy (kms_response_t *response);
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/third_party/kms-message/src/kms_message/kms_response_parser.h b/src/third_party/kms-message/src/kms_message/kms_response_parser.h
index c355ff1cdb5..0bdf0809a00 100644
--- a/src/third_party/kms-message/src/kms_message/kms_response_parser.h
+++ b/src/third_party/kms-message/src/kms_message/kms_response_parser.h
@@ -17,9 +17,13 @@
#ifndef KMS_RESPONSE_PARSER_H
#define KMS_RESPONSE_PARSER_H
-#include "kms_message.h"
+#include "kms_message_defines.h"
#include "kms_response.h"
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/src/third_party/kms-message/src/kms_message_private.h b/src/third_party/kms-message/src/kms_message_private.h
index 6bd3b891de3..b41b56836ae 100644
--- a/src/third_party/kms-message/src/kms_message_private.h
+++ b/src/third_party/kms-message/src/kms_message_private.h
@@ -28,21 +28,24 @@ struct _kms_request_t {
char error[512];
bool failed;
bool finalized;
+ /* Begin: AWS specific */
kms_request_str_t *region;
kms_request_str_t *service;
kms_request_str_t *access_key_id;
kms_request_str_t *secret_key;
+ kms_request_str_t *datetime;
+ kms_request_str_t *date;
+ /* End: AWS specific */
kms_request_str_t *method;
kms_request_str_t *path;
kms_request_str_t *query;
kms_request_str_t *payload;
- kms_request_str_t *datetime;
- kms_request_str_t *date;
kms_kv_list_t *query_params;
kms_kv_list_t *header_fields;
/* turn off for tests only, not in public kms_request_opt_t API */
bool auto_content_length;
_kms_crypto_t crypto;
+ kms_request_provider_t provider;
};
struct _kms_response_t {
@@ -55,6 +58,8 @@ typedef enum {
PARSING_STATUS_LINE,
PARSING_HEADER,
PARSING_BODY,
+ PARSING_CHUNK_LENGTH,
+ PARSING_CHUNK,
PARSING_DONE
} kms_response_parser_state_t;
@@ -65,6 +70,14 @@ struct _kms_response_parser_t {
kms_request_str_t *raw_response;
int content_length;
int start; /* start of the current thing getting parsed. */
+
+ /* Support two types of HTTP 1.1 responses.
+ * - "Content-Length: x" header is present, indicating the body length.
+ * - "Transfer-Encoding: chunked" header is present, indicating a stream of
+ * chunks.
+ */
+ bool transfer_encoding_chunked;
+ int chunk_size;
kms_response_parser_state_t state;
};
@@ -84,10 +97,10 @@ set_error (char *error, size_t size, const char *fmt, ...);
set_error (obj->error, sizeof (obj->error), __VA_ARGS__); \
} while (0)
-#define KMS_ASSERT(stmt) \
-if (!(stmt)) { \
- fprintf (stderr, "%s failed\n", #stmt); \
- abort (); \
-}
+#define KMS_ASSERT(stmt) \
+ if (!(stmt)) { \
+ fprintf (stderr, "%s failed\n", #stmt); \
+ abort (); \
+ }
#endif /* KMS_MESSAGE_PRIVATE_H */
diff --git a/src/third_party/kms-message/src/kms_port.c b/src/third_party/kms-message/src/kms_port.c
new file mode 100644
index 00000000000..ee9e6ed9c90
--- /dev/null
+++ b/src/third_party/kms-message/src/kms_port.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020-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 "kms_port.h"
+#if defined(_WIN32)
+#include <stdlib.h>
+#include <string.h>
+char * kms_strndup (const char *src, size_t len)
+{
+ char *dst = (char *) malloc (len + 1);
+ if (!dst) {
+ return 0;
+ }
+
+ memcpy (dst, src, len);
+ dst[len] = '\0';
+
+ return dst;
+}
+#endif \ No newline at end of file
diff --git a/src/third_party/kms-message/src/kms_port.h b/src/third_party/kms-message/src/kms_port.h
index c3cbbac3693..2123a99dc95 100644
--- a/src/third_party/kms-message/src/kms_port.h
+++ b/src/third_party/kms-message/src/kms_port.h
@@ -15,21 +15,18 @@
* limitations under the License.
*/
-#if defined(_WIN32)
-#define strcasecmp _stricmp
-
-inline char *
-strndup (const char *src, size_t len)
-{
- char *dst = (char *) malloc (len + 1);
- if (!dst) {
- return 0;
- }
-
- memcpy (dst, src, len);
- dst[len] = '\0';
+#ifndef KMS_PORT_H
+#define KMS_PORT_H
- return dst;
-}
+#include <stddef.h>
+#if defined(_WIN32)
+#define kms_strcasecmp _stricmp
+char *
+kms_strndup (const char *src, size_t len);
+#else
+#define kms_strndup strndup
+#define kms_strcasecmp strcasecmp
#endif
+
+#endif /* KMS_PORT_H */ \ No newline at end of file
diff --git a/src/third_party/kms-message/src/kms_request.c b/src/third_party/kms-message/src/kms_request.c
index fa2d487123b..58bfb990b7d 100644
--- a/src/third_party/kms-message/src/kms_request.c
+++ b/src/third_party/kms-message/src/kms_request.c
@@ -61,6 +61,12 @@ kms_request_new (const char *method,
kms_request_t *request = calloc (1, sizeof (kms_request_t));
const char *question_mark;
+ KMS_ASSERT (request);
+ if (opt && opt->provider) {
+ request->provider = opt->provider;
+ } else {
+ request->provider = KMS_REQUEST_PROVIDER_AWS;
+ }
/* parsing may set failed to true */
request->failed = false;
@@ -92,10 +98,16 @@ kms_request_new (const char *method,
request->header_fields = kms_kv_list_new ();
request->auto_content_length = true;
- kms_request_set_date (request, NULL);
+ /* For AWS KMS requests, add a X-Amz-Date header. */
+ if (request->provider == KMS_REQUEST_PROVIDER_AWS &&
+ !kms_request_set_date (request, NULL)) {
+ return request;
+ }
if (opt && opt->connection_close) {
- kms_request_add_header_field (request, "Connection", "close");
+ if (!kms_request_add_header_field (request, "Connection", "close")) {
+ return request;
+ }
}
if (opt && opt->crypto.sha256) {
@@ -164,7 +176,9 @@ kms_request_set_date (kms_request_t *request, const struct tm *tm)
kms_request_str_set_chars (request->date, buf, sizeof "YYYYmmDD" - 1);
kms_request_str_set_chars (request->datetime, buf, sizeof AMZ_DT_FORMAT - 1);
kms_kv_list_del (request->header_fields, "X-Amz-Date");
- kms_request_add_header_field (request, "X-Amz-Date", buf);
+ if (!kms_request_add_header_field (request, "X-Amz-Date", buf)) {
+ return false;
+ }
return true;
}
@@ -309,7 +323,8 @@ append_canonical_headers (kms_kv_list_t *lst, kms_request_str_t *str)
* values in headers that have multiple values." */
for (i = 0; i < lst->len; i++) {
kv = &lst->kvs[i];
- if (previous_key && 0 == strcasecmp (previous_key->str, kv->key->str)) {
+ if (previous_key &&
+ 0 == kms_strcasecmp (previous_key->str, kv->key->str)) {
/* duplicate header */
kms_request_str_append_char (str, ',');
kms_request_str_append_stripped (str, kv->value);
@@ -339,12 +354,13 @@ append_signed_headers (kms_kv_list_t *lst, kms_request_str_t *str)
for (i = 0; i < lst->len; i++) {
kv = &lst->kvs[i];
- if (previous_key && 0 == strcasecmp (previous_key->str, kv->key->str)) {
+ if (previous_key &&
+ 0 == kms_strcasecmp (previous_key->str, kv->key->str)) {
/* duplicate header */
continue;
}
- if (0 == strcasecmp (kv->key->str, "connection")) {
+ if (0 == kms_strcasecmp (kv->key->str, "connection")) {
continue;
}
@@ -376,10 +392,13 @@ finalize (kms_request_t *request)
lst = request->header_fields;
- /* By default, if no explicit Host was set, it is derived from region +
- * service */
if (!kms_kv_list_find (lst, "Host")) {
- /* like "kms.us-east-1.amazonaws.com" */
+ if (request->provider != KMS_REQUEST_PROVIDER_AWS) {
+ KMS_ERROR (request, "Required Host header not set");
+ return false;
+ }
+ /* For AWS requests, derive a default Host header from region + service.
+ * E.g. "kms.us-east-1.amazonaws.com" */
k = kms_request_str_new_from_chars ("Host", -1);
v = kms_request_str_dup (request->service);
kms_request_str_append_char (v, '.');
@@ -412,7 +431,8 @@ finalize (kms_request_t *request)
static int
cmp_header_field_names (const void *a, const void *b)
{
- return strcasecmp (((kms_kv_t *) a)->key->str, ((kms_kv_t *) b)->key->str);
+ return kms_strcasecmp (((kms_kv_t *) a)->key->str,
+ ((kms_kv_t *) b)->key->str);
}
static kms_kv_list_t *
@@ -447,6 +467,7 @@ kms_request_get_canonical (kms_request_t *request)
kms_request_str_append_newline (canonical);
normalized = kms_request_str_path_normalized (request->path);
kms_request_str_append_escaped (canonical, normalized, false);
+ kms_request_str_destroy (normalized);
kms_request_str_append_newline (canonical);
append_canonical_query (request, canonical);
kms_request_str_append_newline (canonical);
@@ -454,12 +475,14 @@ kms_request_get_canonical (kms_request_t *request)
append_canonical_headers (lst, canonical);
kms_request_str_append_newline (canonical);
append_signed_headers (lst, canonical);
- kms_request_str_append_newline (canonical);
- kms_request_str_append_hashed (
- &request->crypto, canonical, request->payload);
-
- kms_request_str_destroy (normalized);
kms_kv_list_destroy (lst);
+ kms_request_str_append_newline (canonical);
+ if (!kms_request_str_append_hashed (
+ &request->crypto, canonical, request->payload)) {
+ KMS_ERROR (request, "could not generate hash");
+ kms_request_str_destroy (canonical);
+ return NULL;
+ }
return kms_request_str_detach (canonical);
}
@@ -514,6 +537,10 @@ kms_request_get_string_to_sign (kms_request_t *request)
kms_request_str_append_chars (sts, "/aws4_request\n", -1);
creq = kms_request_str_wrap (kms_request_get_canonical (request), -1);
+ if (!creq) {
+ goto done;
+ }
+
if (!kms_request_str_append_hashed (&request->crypto, sts, creq)) {
goto done;
}
@@ -739,6 +766,51 @@ done:
return kms_request_str_detach (sreq);
}
+char *
+kms_request_to_string (kms_request_t *request)
+{
+ kms_kv_list_t *lst = NULL;
+ kms_request_str_t *sreq = NULL;
+ size_t i;
+
+ if (!finalize (request)) {
+ return false;
+ }
+
+ sreq = kms_request_str_new ();
+ /* like "POST / HTTP/1.1" */
+ kms_request_str_append (sreq, request->method);
+ kms_request_str_append_char (sreq, ' ');
+ kms_request_str_append (sreq, request->path);
+ if (request->query->len) {
+ kms_request_str_append_char (sreq, '?');
+ kms_request_str_append (sreq, request->query);
+ }
+
+ kms_request_str_append_chars (sreq, " HTTP/1.1", -1);
+ kms_request_str_append_newline (sreq);
+
+ /* headers */
+ lst = kms_kv_list_dup (request->header_fields);
+ kms_kv_list_sort (lst, cmp_header_field_names);
+ for (i = 0; i < lst->len; i++) {
+ kms_request_str_append (sreq, lst->kvs[i].key);
+ kms_request_str_append_char (sreq, ':');
+ kms_request_str_append (sreq, lst->kvs[i].value);
+ kms_request_str_append_newline (sreq);
+ }
+
+ kms_request_str_append_newline (sreq);
+
+ /* body */
+ if (request->payload->len) {
+ kms_request_str_append (sreq, request->payload);
+ }
+
+ kms_kv_list_destroy (lst);
+ return kms_request_str_detach (sreq);
+}
+
void
kms_request_free_string (char *ptr)
{
diff --git a/src/third_party/kms-message/src/kms_request_opt.c b/src/third_party/kms-message/src/kms_request_opt.c
index f17bf3c3343..b0a184fad7a 100644
--- a/src/third_party/kms-message/src/kms_request_opt.c
+++ b/src/third_party/kms-message/src/kms_request_opt.c
@@ -56,4 +56,32 @@ kms_request_opt_set_crypto_hooks (kms_request_opt_t *opt,
opt->crypto.sha256 = sha256;
opt->crypto.sha256_hmac = sha256_hmac;
opt->crypto.ctx = ctx;
+}
+
+bool
+kms_request_opt_set_provider (kms_request_opt_t *opt,
+ kms_request_provider_t provider)
+{
+ if (provider != KMS_REQUEST_PROVIDER_AWS &&
+ provider != KMS_REQUEST_PROVIDER_AZURE &&
+ provider != KMS_REQUEST_PROVIDER_GCP) {
+ return false;
+ }
+ opt->provider = provider;
+ return true;
+}
+
+void
+kms_request_opt_set_crypto_hook_sign_rsaes_pkcs1_v1_5 (
+ kms_request_opt_t *opt,
+ bool (*sign_rsaes_pkcs1_v1_5) (void *sign_ctx,
+ const char *private_key,
+ size_t private_key_len,
+ const char *input,
+ size_t input_len,
+ unsigned char *signature_out),
+ void *sign_ctx)
+{
+ opt->crypto.sign_rsaes_pkcs1_v1_5 = sign_rsaes_pkcs1_v1_5;
+ opt->crypto.sign_ctx = sign_ctx;
} \ No newline at end of file
diff --git a/src/third_party/kms-message/src/kms_request_opt_private.h b/src/third_party/kms-message/src/kms_request_opt_private.h
index 5e971a9d890..8c25bdf2801 100644
--- a/src/third_party/kms-message/src/kms_request_opt_private.h
+++ b/src/third_party/kms-message/src/kms_request_opt_private.h
@@ -26,6 +26,7 @@
struct _kms_request_opt_t {
bool connection_close;
_kms_crypto_t crypto;
+ kms_request_provider_t provider;
};
#endif /* KMS_REQUEST_OPT_PRIVATE_H */
diff --git a/src/third_party/kms-message/src/kms_request_str.c b/src/third_party/kms-message/src/kms_request_str.c
index 0f7c19c9726..65207d2f4fa 100644
--- a/src/third_party/kms-message/src/kms_request_str.c
+++ b/src/third_party/kms-message/src/kms_request_str.c
@@ -51,10 +51,13 @@ kms_request_str_t *
kms_request_str_new (void)
{
kms_request_str_t *s = malloc (sizeof (kms_request_str_t));
+ KMS_ASSERT (s);
s->len = 0;
s->size = 16;
s->str = malloc (s->size);
+ KMS_ASSERT (s->str);
+
s->str[0] = '\0';
return s;
@@ -64,11 +67,15 @@ kms_request_str_t *
kms_request_str_new_from_chars (const char *chars, ssize_t len)
{
kms_request_str_t *s = malloc (sizeof (kms_request_str_t));
+ KMS_ASSERT (s);
+
size_t actual_len;
actual_len = len < 0 ? strlen (chars) : (size_t) len;
s->size = actual_len + 1;
s->str = malloc (s->size);
+ KMS_ASSERT (s->str);
+
memcpy (s->str, chars, actual_len);
s->str[actual_len] = '\0';
s->len = actual_len;
@@ -86,6 +93,8 @@ kms_request_str_wrap (char *chars, ssize_t len)
}
s = malloc (sizeof (kms_request_str_t));
+ KMS_ASSERT (s);
+
s->str = chars;
s->len = len < 0 ? strlen (chars) : (size_t) len;
@@ -148,8 +157,10 @@ kms_request_str_t *
kms_request_str_dup (kms_request_str_t *str)
{
kms_request_str_t *dup = malloc (sizeof (kms_request_str_t));
+ KMS_ASSERT (dup);
+
- dup->str = strndup (str->str, str->len);
+ dup->str = kms_strndup (str->str, str->len);
dup->len = str->len;
dup->size = str->len + 1;
diff --git a/src/third_party/kms-message/src/kms_request_str.h b/src/third_party/kms-message/src/kms_request_str.h
index f053a595aa7..4e33faa175f 100644
--- a/src/third_party/kms-message/src/kms_request_str.h
+++ b/src/third_party/kms-message/src/kms_request_str.h
@@ -25,11 +25,6 @@
#include <stdint.h>
#include <string.h>
-#if defined(_WIN32)
-#include <basetsd.h>
-typedef SSIZE_T ssize_t;
-#endif // _WIN32
-
typedef struct {
char *str;
size_t len;
@@ -72,6 +67,8 @@ kms_request_str_append_lowercase (kms_request_str_t *str,
KMS_MSG_EXPORT (void)
kms_request_str_appendf (kms_request_str_t *str, const char *format, ...);
KMS_MSG_EXPORT (void)
+kms_request_strdupf (kms_request_str_t *str, const char *format, ...);
+KMS_MSG_EXPORT (void)
kms_request_str_append_escaped (kms_request_str_t *str,
kms_request_str_t *appended,
bool escape_slash);
diff --git a/src/third_party/kms-message/src/kms_response.c b/src/third_party/kms-message/src/kms_response.c
index 593e39fd3d9..c90e772b14f 100644
--- a/src/third_party/kms-message/src/kms_response.c
+++ b/src/third_party/kms-message/src/kms_response.c
@@ -37,4 +37,10 @@ kms_response_get_body (kms_response_t *response, size_t *len)
*len = response->body->len;
}
return response->body->str;
+}
+
+int
+kms_response_get_status (kms_response_t *response)
+{
+ return response->status;
} \ No newline at end of file
diff --git a/src/third_party/kms-message/src/kms_response_parser.c b/src/third_party/kms-message/src/kms_response_parser.c
index 31e4868a68e..6f0c0487864 100644
--- a/src/third_party/kms-message/src/kms_response_parser.c
+++ b/src/third_party/kms-message/src/kms_response_parser.c
@@ -1,11 +1,13 @@
#include "kms_message/kms_response_parser.h"
#include "kms_message_private.h"
-#include "kms_message_private.h"
+#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
+#include "hexlify.h"
+
/* destroys the members of parser, but not the parser itself. */
static void
_parser_destroy (kms_response_parser_t *parser)
@@ -24,16 +26,21 @@ _parser_init (kms_response_parser_t *parser)
parser->raw_response = kms_request_str_new ();
parser->content_length = -1;
parser->response = calloc (1, sizeof (kms_response_t));
+ KMS_ASSERT (parser->response);
parser->response->headers = kms_kv_list_new ();
parser->state = PARSING_STATUS_LINE;
parser->start = 0;
parser->failed = false;
+ parser->chunk_size = 0;
+ parser->transfer_encoding_chunked = false;
}
kms_response_parser_t *
kms_response_parser_new (void)
{
kms_response_parser_t *parser = malloc (sizeof (kms_response_parser_t));
+ KMS_ASSERT (parser);
+
_parser_init (parser);
return parser;
}
@@ -47,6 +54,12 @@ kms_response_parser_wants_bytes (kms_response_parser_t *parser, int32_t max)
case PARSING_STATUS_LINE:
case PARSING_HEADER:
return max;
+ case PARSING_CHUNK_LENGTH:
+ return max;
+ case PARSING_CHUNK:
+ /* add 2 for trailing \r\n */
+ return (parser->chunk_size + 2) -
+ ((int) parser->raw_response->len - parser->start);
case PARSING_BODY:
KMS_ASSERT (parser->content_length != -1);
return parser->content_length -
@@ -59,11 +72,26 @@ static bool
_parse_int (const char *str, int *result)
{
char *endptr = NULL;
+ int64_t long_result;
- *result = (int) strtol (str, &endptr, 10);
- if (*endptr) {
+ errno = 0;
+ long_result = strtol (str, &endptr, 10);
+ if (endptr == str) {
+ /* No digits were parsed. Consider this an error */
+ return false;
+ }
+ if (endptr != NULL && *endptr != '\0') {
+ /* endptr points to the first invalid character. */
+ return false;
+ }
+ if (errno == EINVAL || errno == ERANGE) {
+ return false;
+ }
+ if (long_result > INT32_MAX || long_result < INT32_MIN) {
return false;
}
+ *result = (int) long_result;
+
return true;
}
@@ -72,6 +100,8 @@ static bool
_parse_int_from_view (const char *str, int start, int end, int *result)
{
char *num_str = malloc (end - start + 1);
+ KMS_ASSERT (num_str);
+
bool ret;
strncpy (num_str, str + start, end - start);
@@ -81,6 +111,16 @@ _parse_int_from_view (const char *str, int start, int end, int *result)
return ret;
}
+static bool
+_parse_hex_from_view (const char *str, int len, int *result)
+{
+ *result = unhexlify (str, len);
+ if (*result < 0) {
+ return false;
+ }
+ return true;
+}
+
/* returns true if char is "linear white space". This *ignores* the folding case
* of CRLF followed by WSP. See https://stackoverflow.com/a/21072806/774658 */
static bool
@@ -134,6 +174,9 @@ _parse_line (kms_response_parser_t *parser, int end)
if (i == end) {
/* empty line, this signals the start of the body. */
+ if (parser->transfer_encoding_chunked) {
+ return PARSING_CHUNK_LENGTH;
+ }
return PARSING_BODY;
}
@@ -181,9 +224,29 @@ _parse_line (kms_response_parser_t *parser, int end)
return PARSING_DONE;
}
}
+
+ if (0 == strcmp (key->str, "Transfer-Encoding")) {
+ if (0 == strcmp (val->str, "chunked")) {
+ parser->transfer_encoding_chunked = true;
+ } else {
+ KMS_ERROR (parser, "Unsupported Transfer-Encoding: %s", val->str);
+ kms_request_str_destroy (key);
+ kms_request_str_destroy (val);
+ return PARSING_DONE;
+ }
+ }
kms_request_str_destroy (key);
kms_request_str_destroy (val);
return PARSING_HEADER;
+ } else if (parser->state == PARSING_CHUNK_LENGTH) {
+ int result = 0;
+
+ if (!_parse_hex_from_view (raw + i, end - i, &result)) {
+ KMS_ERROR (parser, "Failed to parse hex chunk length.");
+ return PARSING_DONE;
+ }
+ parser->chunk_size = result;
+ return PARSING_CHUNK;
}
return PARSING_DONE;
}
@@ -194,7 +257,7 @@ kms_response_parser_feed (kms_response_parser_t *parser,
uint32_t len)
{
kms_request_str_t *raw = parser->raw_response;
- int curr, body_read;
+ int curr, body_read, chunk_read;
curr = (int) raw->len;
kms_request_str_append_chars (raw, (char *) buf, len);
@@ -203,6 +266,7 @@ kms_response_parser_feed (kms_response_parser_t *parser,
switch (parser->state) {
case PARSING_STATUS_LINE:
case PARSING_HEADER:
+ case PARSING_CHUNK_LENGTH:
/* find the next \r\n. */
if (curr && strncmp (raw->str + (curr - 1), "\r\n", 2) == 0) {
parser->state = _parse_line (parser, curr - 1);
@@ -234,6 +298,28 @@ kms_response_parser_feed (kms_response_parser_t *parser,
curr = (int) raw->len;
break;
+ case PARSING_CHUNK:
+ chunk_read = (int) raw->len - parser->start;
+ /* check if we've read the full chunk and the trailing \r\n */
+ if (chunk_read >= parser->chunk_size + 2) {
+ if (!parser->response->body) {
+ parser->response->body = kms_request_str_new ();
+ }
+ kms_request_str_append_chars (parser->response->body,
+ raw->str + parser->start,
+ parser->chunk_size);
+ curr = parser->start + parser->chunk_size + 2;
+ parser->start = curr;
+ if (parser->chunk_size == 0) {
+ /* last chunk. */
+ parser->state = PARSING_DONE;
+ } else {
+ parser->state = PARSING_CHUNK_LENGTH;
+ }
+ } else {
+ curr = (int) raw->len;
+ }
+ break;
case PARSING_DONE:
KMS_ERROR (parser, "Unexpected extra HTTP content");
return false;
diff --git a/src/third_party/scripts/kms_message_get_sources.sh b/src/third_party/scripts/kms_message_get_sources.sh
index 6ad2fbb0e67..9567d9dde2a 100755
--- a/src/third_party/scripts/kms_message_get_sources.sh
+++ b/src/third_party/scripts/kms_message_get_sources.sh
@@ -18,7 +18,7 @@ if grep -q Microsoft /proc/version; then
fi
NAME=libmongocrypt
-REVISION=59c8c17bbdfa1cf0fdec60cfdde73a437a868221
+REVISION=ebddc00f74874b8dcad8661f77d8ff51ded0318f
if grep -q Microsoft /proc/version; then
SRC_ROOT=$(wslpath -u $(powershell.exe -Command "Get-ChildItem Env:TEMP | Get-Content | Write-Host"))