summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt14
-rw-r--r--lib/crypto-mbedtls.c412
2 files changed, 426 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4564818..3ceb0d3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,6 +28,7 @@ find_library(libuci NAMES uci)
find_library(libubox NAMES ubox)
find_library(libubus NAMES ubus)
find_library(libblobmsg_json NAMES blobmsg_json)
+find_library(libmbedtls NAMES mbedtls)
find_package(ZLIB)
if(LINUX)
@@ -54,6 +55,10 @@ if(ZLIB_FOUND)
set(DEFAULT_ZLIB_SUPPORT ON)
endif()
+if(libmbedtls)
+ set(DEFAULT_CRYPTO_MBEDTLS_SUPPORT ON)
+endif()
+
option(DEBUG_SUPPORT "Debug plugin support" ON)
option(FS_SUPPORT "Filesystem plugin support" ON)
option(MATH_SUPPORT "Math plugin support" ON)
@@ -67,6 +72,7 @@ option(ULOOP_SUPPORT "Uloop plugin support" ${DEFAULT_ULOOP_SUPPORT})
option(LOG_SUPPORT "Log plugin support" ON)
option(SOCKET_SUPPORT "Socket plugin support" ON)
option(ZLIB_SUPPORT "Zlib plugin support" ${DEFAULT_ZLIB_SUPPORT})
+option(CRYPTO_MBEDTLS_SUPPORT "Crypto Mbed-TLS plugin support" ${DEFAULT_CRYPTO_MBEDTLS_SUPPORT})
set(LIB_SEARCH_PATH "${CMAKE_INSTALL_PREFIX}/lib/ucode/*.so:${CMAKE_INSTALL_PREFIX}/share/ucode/*.uc:./*.so:./*.uc" CACHE STRING "Default library search path")
string(REPLACE ":" "\", \"" LIB_SEARCH_DEFINE "${LIB_SEARCH_PATH}")
@@ -290,6 +296,14 @@ if(ZLIB_SUPPORT)
target_link_libraries(zlib_lib ZLIB::ZLIB)
endif()
+if(CRYPTO_MBEDTLS_SUPPORT)
+ set(LIBRARIES ${LIBRARIES} crypto_mbedtls_lib)
+ add_library(crypto_mbedtls_lib MODULE lib/crypto-mbedtls.c)
+ set_target_properties(crypto_mbedtls_lib PROPERTIES OUTPUT_NAME crypto_mbedtls PREFIX "")
+ target_link_options(crypto_mbedtls_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS} -Wl,-no-as-needed)
+ target_link_libraries(crypto_mbedtls_lib ${libmbedtls})
+endif()
+
if(UNIT_TESTING)
enable_testing()
add_definitions(-DUNIT_TESTING)
diff --git a/lib/crypto-mbedtls.c b/lib/crypto-mbedtls.c
new file mode 100644
index 0000000..3ab9f5f
--- /dev/null
+++ b/lib/crypto-mbedtls.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2024 Mikael Magnusson <mikma@users.sourceforge.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <alloca.h>
+
+#include <ucode/module.h>
+
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/error.h>
+#include <mbedtls/md.h>
+#include <mbedtls/pk.h>
+
+#define PK_TYPE "crypto_mbedtls.pk"
+
+#define TRUE ucv_boolean_new(true)
+#define FALSE ucv_boolean_new(false)
+
+const char *personalization = "ucode-crypto-mbedtls";
+
+struct context {
+ mbedtls_pk_context pk;
+ mbedtls_ctr_drbg_context ctr_drbg;
+};
+
+static uc_resource_type_t *pk_type;
+static mbedtls_entropy_context entropy;
+
+static void __attribute__((constructor)) load();
+static void __attribute__((destructor)) unload();
+
+static uc_value_t *
+md_digest(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *alg = uc_fn_arg(0);
+ uc_value_t *input = uc_fn_arg(1);
+
+ if (ucv_type(alg) != UC_STRING) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "alg not a string");
+ return NULL;
+ }
+
+ if (ucv_type(input) != UC_STRING) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "input not a string");
+ return NULL;
+ }
+
+ const mbedtls_md_info_t *info = mbedtls_md_info_from_string(ucv_string_get(alg));
+
+ if (!info) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "unknown MD algorithm");
+ return NULL;
+ }
+
+ unsigned char size = mbedtls_md_get_size(info);
+ char *output = alloca(size);
+
+ if (mbedtls_md(info, (const unsigned char*)ucv_string_get(input), ucv_string_length(input), (unsigned char*)output)) {
+ uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "bad input data");
+ return NULL;
+ }
+
+ uc_value_t *rv = ucv_string_new_length(output, size);
+ return rv;
+}
+
+static uc_value_t *
+pk_init(uc_vm_t *vm, size_t nargs)
+{
+ struct context *ctx = calloc(1, sizeof(struct context));
+
+ mbedtls_pk_init(&ctx->pk);
+ mbedtls_ctr_drbg_init(&ctx->ctr_drbg);
+
+ int ret = mbedtls_ctr_drbg_seed(&ctx->ctr_drbg, mbedtls_entropy_func, &entropy,
+ (const unsigned char *) personalization,
+ strlen(personalization) );
+ if( ret != 0 ) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Problem with random generator");
+ return NULL;
+ }
+
+ return uc_resource_new(pk_type, ctx);
+}
+
+static void
+pk_free(void *ptr)
+{
+ struct context *ctx = ptr;
+
+ mbedtls_pk_free(&ctx->pk);
+ mbedtls_ctr_drbg_free(&ctx->ctr_drbg);
+ free(ctx);
+}
+
+struct alias {
+ const char *alias;
+ const char *name;
+};
+
+static struct alias curve_aliases[] = {
+ { alias: "P-192", name: "secp192r1"},
+ { alias: "P-224", name: "secp224r1"},
+ { alias: "P-256", name: "secp256r1"},
+ { alias: "P-384", name: "secp384r1"},
+ { alias: "P-521", name: "secp521r1"},
+ { alias: NULL, name: NULL},
+};
+
+static const char *translate_curve(const char *type)
+{
+ if (!type)
+ return NULL;
+
+ for (int i=0; curve_aliases[i].alias; i++) {
+ if (!strcmp(type, curve_aliases[i].alias))
+ return curve_aliases[i].name;
+ }
+
+ return type;
+};
+
+static uc_value_t *
+pk_keygen(uc_vm_t *vm, size_t nargs)
+{
+ struct context *ctx = uc_fn_thisval(PK_TYPE);
+
+ if (!ctx) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "invalid " PK_TYPE " object");
+ return NULL;
+ }
+
+ uc_value_t *type = uc_fn_arg(0);
+ if (ucv_type(type) != UC_STRING) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "type is not a string");
+ return NULL;
+ }
+
+ const char *type_str = ucv_string_get(type);
+
+ if (!strcasecmp(type_str, "EC")) {
+ if (nargs != 2) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Expected 2 arguments got %d", nargs);
+ return NULL;
+ }
+
+ uc_value_t *curve_v = uc_fn_arg(1);
+ if (ucv_type(curve_v) != UC_STRING) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "curve is not a string");
+ return NULL;
+ }
+
+ const char *curve = translate_curve(ucv_string_get(curve_v));
+ const mbedtls_ecp_curve_info *curve_info = mbedtls_ecp_curve_info_from_name(curve);
+
+ if (!curve_info) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Unsupported curve: %s", curve);
+ return NULL;
+ }
+
+ const mbedtls_pk_info_t *info = mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
+ if (mbedtls_pk_setup(&ctx->pk, info)) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "PK setup failed");
+ return NULL;
+ }
+ int err = mbedtls_ecp_gen_key(curve_info->grp_id,
+ mbedtls_pk_ec(ctx->pk),
+ mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
+ if (err != 0) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "EC gen key failed");
+ return NULL;
+ }
+ } else if (!strcasecmp(type_str, "RSA")) {
+ if (nargs != 2) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Expected 2 arguments got %d", nargs);
+ return NULL;
+ }
+
+ uc_value_t *size_v = uc_fn_arg(1);
+ if (ucv_type(size_v) != UC_INTEGER) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "size is not a number");
+ return NULL;
+ }
+
+ size_t size = ucv_to_integer(size_v);
+ const mbedtls_pk_info_t *info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA);
+ if (mbedtls_pk_setup(&ctx->pk, info)) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "PK setup failed");
+ return NULL;
+ }
+
+ int err = mbedtls_rsa_gen_key(mbedtls_pk_rsa(ctx->pk),
+ mbedtls_ctr_drbg_random, &ctx->ctr_drbg,
+ size, 65537);
+ if (err != 0) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "RSA gen key failed");
+ return NULL;
+ }
+ } else {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Unsupported key type %s", type_str);
+ return NULL;
+ }
+
+ return TRUE;
+}
+
+static uc_value_t *
+pk_get_public_key(uc_vm_t *vm, size_t nargs)
+{
+ struct context *ctx = uc_fn_thisval(PK_TYPE);
+
+ if (!ctx) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "invalid " PK_TYPE " object");
+ return NULL;
+ }
+
+ if (!mbedtls_pk_get_len(&ctx->pk)) {
+ // No key
+ return NULL;
+ }
+
+ unsigned char buf[16000];
+ int res = mbedtls_pk_write_pubkey_der(&ctx->pk, buf, sizeof(buf));
+ if (res <= 0) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "failed to get public key");
+ return NULL;
+ }
+
+ uc_value_t *rv = ucv_string_new_length((const char*)(buf + sizeof(buf) - res), res);
+ return rv;
+}
+
+static uc_value_t *
+pk_set_public_key(uc_vm_t *vm, size_t nargs)
+{
+ struct context *ctx = uc_fn_thisval(PK_TYPE);
+
+ if (!ctx) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "invalid " PK_TYPE " object");
+ return NULL;
+ }
+
+ uc_value_t *key = uc_fn_arg(0);
+ if (ucv_type(key) == UC_NULL) {
+ mbedtls_pk_free(&ctx->pk);
+ return NULL;
+ }
+
+ if (ucv_type(key) != UC_STRING) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "key is not a string");
+ return NULL;
+ }
+
+ int err = mbedtls_pk_parse_public_key(&ctx->pk, (const unsigned char*)ucv_string_get(key), ucv_string_length(key));
+ if (err)
+ uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "not a valid DER key %s: %s",
+ mbedtls_high_level_strerr(err),
+ mbedtls_low_level_strerr(err));
+ return NULL;
+}
+
+static uc_value_t *
+pk_sign(uc_vm_t *vm, size_t nargs)
+{
+ struct context *ctx = uc_fn_thisval(PK_TYPE);
+
+ if (!ctx) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "invalid " PK_TYPE " object");
+ return NULL;
+ }
+
+ uc_value_t *md_alg = uc_fn_arg(0);
+ uc_value_t *input = uc_fn_arg(1);
+
+ if (ucv_type(md_alg) != UC_STRING) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "md_alg is not a string");
+ return NULL;
+ }
+
+ if (ucv_type(input) != UC_STRING)
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "input is not a string");
+
+ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_string(ucv_string_get(md_alg));
+ if (!md_info) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "unknown MD algorithm");
+ return NULL;
+ }
+
+ unsigned char hash_len = mbedtls_md_get_size(md_info);
+ unsigned char *hash = alloca(hash_len);
+
+ if (mbedtls_md(md_info, (const unsigned char*)ucv_string_get(input), ucv_string_length(input), (unsigned char*)hash)) {
+ uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "bad input data");
+ return NULL;
+ }
+
+ const mbedtls_md_type_t md_type = mbedtls_md_get_type(md_info);
+ unsigned char sig[MBEDTLS_PK_SIGNATURE_MAX_SIZE];
+ size_t sig_len = sizeof(sig);
+
+ if (mbedtls_pk_sign(&ctx->pk, md_type, hash, hash_len,
+ sig, &sig_len,
+ mbedtls_ctr_drbg_random, &ctx->ctr_drbg)) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "pk sign failed");
+ return NULL;
+ }
+
+ uc_value_t *rv = ucv_string_new_length((const char*)sig, sig_len);
+ return rv;
+}
+
+static uc_value_t *
+pk_verify(uc_vm_t *vm, size_t nargs)
+{
+ struct context *ctx = uc_fn_thisval(PK_TYPE);
+
+ if (!ctx) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "invalid " PK_TYPE " object");
+ return NULL;
+ }
+
+ uc_value_t *md_alg = uc_fn_arg(0);
+ uc_value_t *input = uc_fn_arg(1);
+ uc_value_t *sig = uc_fn_arg(2);
+
+ if (ucv_type(md_alg) != UC_STRING) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "md_alg is not a string");
+ return NULL;
+ }
+
+ if (ucv_type(input) != UC_STRING) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "input is not a string");
+ return NULL;
+ }
+
+ if (ucv_type(sig) != UC_STRING) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "sig is not a string");
+ return NULL;
+ }
+
+ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_string(ucv_string_get(md_alg));
+
+ if (!md_info) {
+ uc_vm_raise_exception(vm, EXCEPTION_TYPE, "unknown MD algorithm");
+ return NULL;
+ }
+
+ unsigned char hash_size = mbedtls_md_get_size(md_info);
+ unsigned char *hash = alloca(hash_size);
+
+ if (mbedtls_md(md_info, (const unsigned char*)ucv_string_get(input), ucv_string_length(input), (unsigned char*)hash))
+ uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "bad input data");
+
+ const mbedtls_md_type_t md_type = mbedtls_md_get_type(md_info);
+
+ int err = mbedtls_pk_verify(&ctx->pk, md_type,
+ hash, hash_size,
+ (const unsigned char*)ucv_string_get(sig), ucv_string_length(sig));
+ if (err) {
+ uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "validation failed: %s: %s",
+ mbedtls_high_level_strerr(err),
+ mbedtls_low_level_strerr(err));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static const uc_function_list_t global_fns[] = {
+ { "md_digest", md_digest },
+ { "pk", pk_init },
+};
+
+static const uc_function_list_t pk_fns[] = {
+ { "keygen", pk_keygen },
+ { "get_public_key", pk_get_public_key },
+ { "set_public_key", pk_set_public_key },
+ { "sign", pk_sign },
+ { "verify", pk_verify },
+};
+
+void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
+{
+ uc_function_list_register(scope, global_fns);
+
+ pk_type = uc_type_declare(vm, PK_TYPE, pk_fns, pk_free);
+}
+
+static void load()
+{
+ mbedtls_entropy_init(&entropy);
+}
+
+static void unload()
+{
+ mbedtls_entropy_free(&entropy);
+}