summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-12-21 19:09:36 +0100
committerFlorian Dold <florian@dold.me>2022-12-21 19:09:36 +0100
commitc9e2023e15ce70d6754e3e2aedad9e8c8093d071 (patch)
tree0c02a80d873f118671fa176075391aeb61c3b29c
parent67e1d6709faa82837ae8dbfce914466c9c373d18 (diff)
downloadquickjs-tart-c9e2023e15ce70d6754e3e2aedad9e8c8093d071.tar.gz
quickjs-tart-c9e2023e15ce70d6754e3e2aedad9e8c8093d071.tar.bz2
quickjs-tart-c9e2023e15ce70d6754e3e2aedad9e8c8093d071.zip
factor out taler-specific code
-rw-r--r--CMakeLists.txt55
-rw-r--r--README.md12
-rw-r--r--libtalerwalletcore.c7
-rw-r--r--meson.build70
-rw-r--r--prelude.js9
-rw-r--r--qtart.c527
-rw-r--r--quickjs/.gitignore (renamed from .gitignore)0
-rw-r--r--quickjs/Changelog (renamed from Changelog)0
-rw-r--r--quickjs/LICENSE (renamed from LICENSE)0
-rw-r--r--quickjs/Makefile (renamed from Makefile)0
-rw-r--r--quickjs/TODO (renamed from TODO)0
-rw-r--r--quickjs/VERSION (renamed from VERSION)0
-rw-r--r--quickjs/cutils.c (renamed from cutils.c)0
-rw-r--r--quickjs/cutils.h (renamed from cutils.h)0
-rw-r--r--quickjs/doc/jsbignum.texi (renamed from doc/jsbignum.texi)0
-rw-r--r--quickjs/doc/quickjs.texi (renamed from doc/quickjs.texi)0
-rw-r--r--quickjs/examples/fib.c (renamed from examples/fib.c)0
-rw-r--r--quickjs/examples/fib_module.js (renamed from examples/fib_module.js)0
-rw-r--r--quickjs/examples/hello.js (renamed from examples/hello.js)0
-rw-r--r--quickjs/examples/hello_module.js (renamed from examples/hello_module.js)0
-rw-r--r--quickjs/examples/pi_bigdecimal.js (renamed from examples/pi_bigdecimal.js)0
-rw-r--r--quickjs/examples/pi_bigfloat.js (renamed from examples/pi_bigfloat.js)0
-rw-r--r--quickjs/examples/pi_bigint.js (renamed from examples/pi_bigint.js)0
-rw-r--r--quickjs/examples/point.c (renamed from examples/point.c)0
-rw-r--r--quickjs/examples/test_fib.js (renamed from examples/test_fib.js)0
-rw-r--r--quickjs/examples/test_point.js (renamed from examples/test_point.js)0
-rw-r--r--quickjs/libbf.c (renamed from libbf.c)0
-rw-r--r--quickjs/libbf.h (renamed from libbf.h)0
-rw-r--r--quickjs/libregexp-opcode.h (renamed from libregexp-opcode.h)0
-rw-r--r--quickjs/libregexp.c (renamed from libregexp.c)0
-rw-r--r--quickjs/libregexp.h (renamed from libregexp.h)0
-rw-r--r--quickjs/libunicode-table.h (renamed from libunicode-table.h)0
-rw-r--r--quickjs/libunicode.c (renamed from libunicode.c)0
-rw-r--r--quickjs/libunicode.h (renamed from libunicode.h)0
-rw-r--r--quickjs/list.h (renamed from list.h)0
-rw-r--r--quickjs/qjs.c (renamed from qjs.c)0
-rw-r--r--quickjs/qjsc.c (renamed from qjsc.c)0
-rw-r--r--quickjs/qjscalc.js (renamed from qjscalc.js)0
-rw-r--r--quickjs/quickjs-atom.h (renamed from quickjs-atom.h)0
-rw-r--r--quickjs/quickjs-libc.c (renamed from quickjs-libc.c)1200
-rw-r--r--quickjs/quickjs-libc.h (renamed from quickjs-libc.h)0
-rw-r--r--quickjs/quickjs-opcode.h (renamed from quickjs-opcode.h)0
-rw-r--r--quickjs/quickjs.c (renamed from quickjs.c)0
-rw-r--r--quickjs/quickjs.h (renamed from quickjs.h)0
-rw-r--r--quickjs/readme.txt (renamed from readme.txt)0
-rwxr-xr-xquickjs/release.sh (renamed from release.sh)0
-rw-r--r--quickjs/repl.js (renamed from repl.js)0
-rw-r--r--quickjs/run-test262.c (renamed from run-test262.c)0
-rw-r--r--quickjs/test262.conf (renamed from test262.conf)0
-rw-r--r--quickjs/test262_errors.txt (renamed from test262_errors.txt)0
-rw-r--r--quickjs/test262o.conf (renamed from test262o.conf)0
-rw-r--r--quickjs/test262o_errors.txt (renamed from test262o_errors.txt)0
-rw-r--r--quickjs/tests/bjson.c (renamed from tests/bjson.c)0
-rw-r--r--quickjs/tests/microbench.js (renamed from tests/microbench.js)0
-rw-r--r--quickjs/tests/test262.patch (renamed from tests/test262.patch)0
-rw-r--r--quickjs/tests/test_bignum.js (renamed from tests/test_bignum.js)0
-rw-r--r--quickjs/tests/test_bjson.js (renamed from tests/test_bjson.js)0
-rw-r--r--quickjs/tests/test_builtin.js (renamed from tests/test_builtin.js)0
-rw-r--r--quickjs/tests/test_closure.js (renamed from tests/test_closure.js)0
-rw-r--r--quickjs/tests/test_language.js (renamed from tests/test_language.js)0
-rw-r--r--quickjs/tests/test_loop.js (renamed from tests/test_loop.js)0
-rw-r--r--quickjs/tests/test_op_overloading.js (renamed from tests/test_op_overloading.js)0
-rw-r--r--quickjs/tests/test_qjscalc.js (renamed from tests/test_qjscalc.js)0
-rw-r--r--quickjs/tests/test_std.js (renamed from tests/test_std.js)0
-rw-r--r--quickjs/tests/test_worker.js (renamed from tests/test_worker.js)0
-rw-r--r--quickjs/tests/test_worker_module.js (renamed from tests/test_worker_module.js)0
-rwxr-xr-xquickjs/unicode_download.sh (renamed from unicode_download.sh)0
-rw-r--r--quickjs/unicode_gen.c (renamed from unicode_gen.c)0
-rw-r--r--quickjs/unicode_gen_def.h (renamed from unicode_gen_def.h)0
-rw-r--r--readme-wallet.md3
-rw-r--r--tart_module.c1208
-rw-r--r--tart_module.h8
72 files changed, 1805 insertions, 1294 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644
index 0ed0575..0000000
--- a/CMakeLists.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-cmake_minimum_required(VERSION 3.10)
-
-project(quickjs-tart
- DESCRIPTION "Taler Runtime based on QuickJS"
- LANGUAGES C)
-
-#add_compile_options(-fsanitize=address)
-#add_link_options(-fsanitize=address)
-
-add_compile_definitions(_GNU_SOURCE)
-#add_compile_definitions(CONFIG_BIGNUM)
-
-add_library(quickjs STATIC quickjs.c)
-target_compile_definitions(quickjs PRIVATE CONFIG_VERSION="0.0.1")
-target_link_libraries(quickjs PRIVATE m libregexp libbf)
-
-add_library(libregexp STATIC libregexp.c)
-target_link_libraries(quickjs PRIVATE cutils libunicode)
-
-add_library(libunicode STATIC libunicode.c)
-add_library(libbf STATIC libbf.c)
-
-add_library(cutils STATIC cutils.c)
-
-
-add_library(quickjs-libc STATIC quickjs-libc.c)
-target_link_libraries(quickjs-libc PRIVATE quickjs libregexp libunicode cutils MbedTLS::mbedcrypto sodium CURL::libcurl)
-
-add_executable(qjsc qjsc.c)
-target_compile_definitions(qjsc PRIVATE CONFIG_VERSION="0.0.1")
-target_link_libraries(qjsc PRIVATE libregexp libunicode cutils quickjs-libc)
-
-add_custom_command(OUTPUT repl.c
- COMMAND qjsc ARGS -c -o repl.c -m ${CMAKE_SOURCE_DIR}/repl.js
- MAIN_DEPENDENCY repl.js
- DEPENDS qjsc
- COMMENT compiling repl
- VERBATIM)
-
-add_executable(qjs qjs.c repl.c)
-target_compile_definitions(qjs PRIVATE CONFIG_VERSION="0.0.1")
-target_link_libraries(qjs PRIVATE quickjs quickjs-libc)
-
-add_library(talerwalletcore SHARED libtalerwalletcore.c)
-target_link_libraries(talerwalletcore "-pie -Wl,-E")
-set_property(TARGET talerwalletcore PROPERTY POSITION_INDEPENDENT_CODE TRUE)
-
-# options for libcurl
-#option(BUILD_SHARED_LIBS "" OFF)
-option(HTTP_ONLY "" ON)
-option(CURL_USE_MBEDTLS "" ON)
-
-add_subdirectory(thirdparty/zlib)
-add_subdirectory(thirdparty/mbedtls)
-add_subdirectory(thirdparty/curl)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c11a8e5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+This repository contains qtart, a JavaScript runtime for the GNU Taler based on
+Fabrice Bellard's QuickJS.
+
+# Project Structure
+
+# Usage / Examples
+
+Run test:
+
+./qtart -I prelude.js -I ~/repos/taler/wallet-core/packages/taler-wallet-embedded/dist/taler-wallet-core-qjs.mjs -e 'testWithLocal()'
+
+./qtart -I prelude.js -I ~/repos/taler/wallet-core/packages/taler-wallet-embedded/dist/taler-wallet-core-qjs.mjs -I wallet-setup.js
diff --git a/libtalerwalletcore.c b/libtalerwalletcore.c
deleted file mode 100644
index e050c22..0000000
--- a/libtalerwalletcore.c
+++ /dev/null
@@ -1,7 +0,0 @@
-
-#include <stdio.h>
-
-int main()
-{
- printf("hello, world\n");
-}
diff --git a/meson.build b/meson.build
index 53998dd..fe60375 100644
--- a/meson.build
+++ b/meson.build
@@ -29,40 +29,58 @@ sodium_proj = subproject('libsodium', required : true)
#sodium_dep = cc.find_library('sodium', required : true)
sodium_dep = sodium_proj.get_variable('sodium_dep')
+libbf = static_library('bf', 'quickjs/libbf.c')
+libregexp = static_library('regexp', 'quickjs/libregexp.c')
+libunicode = static_library('unicode', 'quickjs/libunicode.c')
+cutils = static_library('cutils', 'quickjs/cutils.c')
+quickjs_libc = static_library('quickjs-libc', 'quickjs/quickjs-libc.c')
+quickjs = static_library('quickjs', 'quickjs/quickjs.c')
+
qjsc_exe = executable('qjsc', [
- 'qjsc.c',
- 'libbf.c',
- 'libregexp.c',
- 'libunicode.c',
- 'cutils.c',
- 'quickjs-libc.c',
- 'quickjs.c',
+ 'quickjs/qjsc.c',
+ ],
+ link_with: [
+ libbf,
+ libregexp,
+ libunicode ,
+ cutils,
+ quickjs_libc,
+ quickjs,
],
- dependencies: [m_dep, mbedcrypto_dep, mbedtls_dep, mbedx509_dep, curl_dep, sodium_dep])
+ dependencies: [
+ m_dep,
+ mbedcrypto_dep,
+ mbedtls_dep,
+ mbedx509_dep,
+ curl_dep,
+ sodium_dep])
repl_c = custom_target('repl',
- input : ['repl.js'],
+ input : ['quickjs/repl.js'],
output : ['repl.c'],
command : [qjsc_exe, '-c', '-m',
'-o', '@OUTPUT@',
'@INPUT@'])
-qjscalc_c = custom_target('qjscalc',
- input : ['qjscalc.js'],
- output : ['qjscalc.c'],
- command : [qjsc_exe, '-c', '-m',
- '-o', '@OUTPUT@',
- '@INPUT@'])
+repl = static_library('repl', repl_c)
-qjs_exe = executable('qjs', [
- 'qjs.c',
- 'libbf.c',
- 'libregexp.c',
- 'libunicode.c',
- 'cutils.c',
- 'quickjs-libc.c',
- 'quickjs.c',
- repl_c,
- qjscalc_c,
+qtart_exe = executable('qtart', [
+ 'qtart.c',
+ 'tart_module.c',
+ ],
+ link_with: [
+ libbf,
+ libregexp,
+ libunicode ,
+ cutils,
+ quickjs_libc,
+ quickjs,
+ repl,
],
- dependencies: [m_dep, mbedcrypto_dep, mbedtls_dep, mbedx509_dep, curl_dep, sodium_dep])
+ dependencies: [
+ m_dep,
+ mbedcrypto_dep,
+ mbedtls_dep,
+ mbedx509_dep,
+ curl_dep,
+ sodium_dep])
diff --git a/prelude.js b/prelude.js
index 9549083..9bab08b 100644
--- a/prelude.js
+++ b/prelude.js
@@ -1,17 +1,18 @@
import * as os from "os";
+import * as tart from "tart";
class TextEncoder {
encode(str) {
- return new Uint8Array(_encodeUtf8(str));
+ return new Uint8Array(tart.encodeUtf8(str));
}
}
class TextDecoder {
decode(bytes) {
if (ArrayBuffer.isView(bytes)) {
- return _decodeUtf8(bytes.buffer);
+ return tart.decodeUtf8(bytes.buffer);
}
- return _decodeUtf8(bytes);
+ return tart.decodeUtf8(bytes);
}
}
@@ -26,3 +27,5 @@ console.info = (...args) => { console.log(...args); };
console.warn = (...args) => { console.log(...args); };
console.error = (...args) => { console.log(...args); };
console.assert = (b) => { if (!b) throw Error("assertion failed") };
+
+globalThis._tart = tart;
diff --git a/qtart.c b/qtart.c
new file mode 100644
index 0000000..2e71767
--- /dev/null
+++ b/qtart.c
@@ -0,0 +1,527 @@
+/*
+ * Taler JS runtime based on the QuickJS stand alone interpreter
+ *
+ * Copyright (c) 2022 Florian Dold
+ * Copyright (c) 2017-2021 Fabrice Bellard
+ * Copyright (c) 2017-2021 Charlie Gordon
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#if defined(__APPLE__)
+#include <malloc/malloc.h>
+#elif defined(__linux__)
+#include <malloc.h>
+#endif
+
+#include "tart_module.h"
+
+#include "quickjs/cutils.h"
+
+extern const uint8_t qjsc_repl[];
+extern const uint32_t qjsc_repl_size;
+
+static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
+ const char *filename, int eval_flags)
+{
+ JSValue val;
+ int ret;
+
+ if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) {
+ /* for the modules, we compile then run to be able to set
+ import.meta */
+ val = JS_Eval(ctx, buf, buf_len, filename,
+ eval_flags | JS_EVAL_FLAG_COMPILE_ONLY);
+ if (!JS_IsException(val)) {
+ js_module_set_import_meta(ctx, val, TRUE, TRUE);
+ val = JS_EvalFunction(ctx, val);
+ }
+ } else {
+ val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
+ }
+ if (JS_IsException(val)) {
+ js_std_dump_error(ctx);
+ ret = -1;
+ } else {
+ ret = 0;
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static int eval_file(JSContext *ctx, const char *filename, int module)
+{
+ uint8_t *buf;
+ int ret, eval_flags;
+ size_t buf_len;
+
+ buf = js_load_file(ctx, &buf_len, filename);
+ if (!buf) {
+ perror(filename);
+ exit(1);
+ }
+
+ if (module < 0) {
+ module = (has_suffix(filename, ".mjs") ||
+ JS_DetectModule((const char *)buf, buf_len));
+ }
+ if (module)
+ eval_flags = JS_EVAL_TYPE_MODULE;
+ else
+ eval_flags = JS_EVAL_TYPE_GLOBAL;
+ ret = eval_buf(ctx, buf, buf_len, filename, eval_flags);
+ js_free(ctx, buf);
+ return ret;
+}
+
+/* also used to initialize the worker context */
+static JSContext *JS_NewCustomContext(JSRuntime *rt)
+{
+ JSContext *ctx;
+ ctx = JS_NewContext(rt);
+ if (!ctx)
+ return NULL;
+ /* system modules */
+ js_init_module_std(ctx, "std");
+ js_init_module_os(ctx, "os");
+ /* taler runtime */
+ tart_init_module_talercrypto(ctx, "tart");
+ return ctx;
+}
+
+#if defined(__APPLE__)
+#define MALLOC_OVERHEAD 0
+#else
+#define MALLOC_OVERHEAD 8
+#endif
+
+struct trace_malloc_data {
+ uint8_t *base;
+};
+
+static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr,
+ struct trace_malloc_data *dp)
+{
+ return ptr - dp->base;
+}
+
+/* default memory allocation functions with memory limitation */
+static inline size_t js_trace_malloc_usable_size(void *ptr)
+{
+#if defined(__APPLE__)
+ return malloc_size(ptr);
+#elif defined(_WIN32)
+ return _msize(ptr);
+#elif defined(EMSCRIPTEN)
+ return 0;
+#elif defined(__linux__)
+ return malloc_usable_size(ptr);
+#else
+ /* change this to `return 0;` if compilation fails */
+ return malloc_usable_size(ptr);
+#endif
+}
+
+static void
+#ifdef _WIN32
+/* mingw printf is used */
+__attribute__((format(gnu_printf, 2, 3)))
+#else
+__attribute__((format(printf, 2, 3)))
+#endif
+ js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...)
+{
+ va_list ap;
+ int c;
+
+ va_start(ap, fmt);
+ while ((c = *fmt++) != '\0') {
+ if (c == '%') {
+ /* only handle %p and %zd */
+ if (*fmt == 'p') {
+ uint8_t *ptr = va_arg(ap, void *);
+ if (ptr == NULL) {
+ printf("NULL");
+ } else {
+ printf("H%+06lld.%zd",
+ js_trace_malloc_ptr_offset(ptr, s->opaque),
+ js_trace_malloc_usable_size(ptr));
+ }
+ fmt++;
+ continue;
+ }
+ if (fmt[0] == 'z' && fmt[1] == 'd') {
+ size_t sz = va_arg(ap, size_t);
+ printf("%zd", sz);
+ fmt += 2;
+ continue;
+ }
+ }
+ putc(c, stdout);
+ }
+ va_end(ap);
+}
+
+static void js_trace_malloc_init(struct trace_malloc_data *s)
+{
+ free(s->base = malloc(8));
+}
+
+static void *js_trace_malloc(JSMallocState *s, size_t size)
+{
+ void *ptr;
+
+ /* Do not allocate zero bytes: behavior is platform dependent */
+ assert(size != 0);
+
+ if (unlikely(s->malloc_size + size > s->malloc_limit))
+ return NULL;
+ ptr = malloc(size);
+ js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr);
+ if (ptr) {
+ s->malloc_count++;
+ s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
+ }
+ return ptr;
+}
+
+static void js_trace_free(JSMallocState *s, void *ptr)
+{
+ if (!ptr)
+ return;
+
+ js_trace_malloc_printf(s, "F %p\n", ptr);
+ s->malloc_count--;
+ s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
+ free(ptr);
+}
+
+static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size)
+{
+ size_t old_size;
+
+ if (!ptr) {
+ if (size == 0)
+ return NULL;
+ return js_trace_malloc(s, size);
+ }
+ old_size = js_trace_malloc_usable_size(ptr);
+ if (size == 0) {
+ js_trace_malloc_printf(s, "R %zd %p\n", size, ptr);
+ s->malloc_count--;
+ s->malloc_size -= old_size + MALLOC_OVERHEAD;
+ free(ptr);
+ return NULL;
+ }
+ if (s->malloc_size + size - old_size > s->malloc_limit)
+ return NULL;
+
+ js_trace_malloc_printf(s, "R %zd %p", size, ptr);
+
+ ptr = realloc(ptr, size);
+ js_trace_malloc_printf(s, " -> %p\n", ptr);
+ if (ptr) {
+ s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size;
+ }
+ return ptr;
+}
+
+static const JSMallocFunctions trace_mf = {
+ js_trace_malloc,
+ js_trace_free,
+ js_trace_realloc,
+#if defined(__APPLE__)
+ malloc_size,
+#elif defined(_WIN32)
+ (size_t (*)(const void *))_msize,
+#elif defined(EMSCRIPTEN)
+ NULL,
+#elif defined(__linux__)
+ (size_t (*)(const void *))malloc_usable_size,
+#else
+ /* change this to `NULL,` if compilation fails */
+ malloc_usable_size,
+#endif
+};
+
+#define PROG_NAME "qjs"
+
+void help(void)
+{
+ printf("QuickJS version " CONFIG_VERSION "\n"
+ "usage: " PROG_NAME " [options] [file [args]]\n"
+ "-h --help list options\n"
+ "-e --eval EXPR evaluate EXPR\n"
+ "-i --interactive go to interactive mode\n"
+ "-m --module load as ES6 module (default=autodetect)\n"
+ " --script load as ES6 script (default=autodetect)\n"
+ "-I --include file include an additional file\n"
+ " --std make 'std' and 'os' available to the loaded script\n"
+#ifdef CONFIG_BIGNUM
+ " --bignum enable the bignum extensions (BigFloat, BigDecimal)\n"
+#endif
+ "-T --trace trace memory allocation\n"
+ "-d --dump dump the memory usage stats\n"
+ " --memory-limit n limit the memory usage to 'n' bytes\n"
+ " --stack-size n limit the stack size to 'n' bytes\n"
+ " --unhandled-rejection dump unhandled promise rejections\n"
+ "-q --quit just instantiate the interpreter and quit\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ JSRuntime *rt;
+ JSContext *ctx;
+ struct trace_malloc_data trace_data = { NULL };
+ int optind;
+ char *expr = NULL;
+ int interactive = 0;
+ int dump_memory = 0;
+ int trace_memory = 0;
+ int empty_run = 0;
+ int module = -1;
+ int load_std = 0;
+ int dump_unhandled_promise_rejection = 0;
+ size_t memory_limit = 0;
+ char *include_list[32];
+ int i, include_count = 0;
+ size_t stack_size = 0;
+
+ /* cannot use getopt because we want to pass the command line to
+ the script */
+ optind = 1;
+ while (optind < argc && *argv[optind] == '-') {
+ char *arg = argv[optind] + 1;
+ const char *longopt = "";
+ /* a single - is not an option, it also stops argument scanning */
+ if (!*arg)
+ break;
+ optind++;
+ if (*arg == '-') {
+ longopt = arg + 1;
+ arg += strlen(arg);
+ /* -- stops argument scanning */
+ if (!*longopt)
+ break;
+ }
+ for (; *arg || *longopt; longopt = "") {
+ char opt = *arg;
+ if (opt)
+ arg++;
+ if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) {
+ help();
+ continue;
+ }
+ if (opt == 'e' || !strcmp(longopt, "eval")) {
+ if (*arg) {
+ expr = arg;
+ break;
+ }
+ if (optind < argc) {
+ expr = argv[optind++];
+ break;
+ }
+ fprintf(stderr, "qjs: missing expression for -e\n");
+ exit(2);
+ }
+ if (opt == 'I' || !strcmp(longopt, "include")) {
+ if (optind >= argc) {
+ fprintf(stderr, "expecting filename");
+ exit(1);
+ }
+ if (include_count >= countof(include_list)) {
+ fprintf(stderr, "too many included files");
+ exit(1);
+ }
+ include_list[include_count++] = argv[optind++];
+ continue;
+ }
+ if (opt == 'i' || !strcmp(longopt, "interactive")) {
+ interactive++;
+ continue;
+ }
+ if (opt == 'm' || !strcmp(longopt, "module")) {
+ module = 1;
+ continue;
+ }
+ if (!strcmp(longopt, "script")) {
+ module = 0;
+ continue;
+ }
+ if (opt == 'd' || !strcmp(longopt, "dump")) {
+ dump_memory++;
+ continue;
+ }
+ if (opt == 'T' || !strcmp(longopt, "trace")) {
+ trace_memory++;
+ continue;
+ }
+ if (!strcmp(longopt, "std")) {
+ load_std = 1;
+ continue;
+ }
+ if (!strcmp(longopt, "unhandled-rejection")) {
+ dump_unhandled_promise_rejection = 1;
+ continue;
+ }
+ if (opt == 'q' || !strcmp(longopt, "quit")) {
+ empty_run++;
+ continue;
+ }
+ if (!strcmp(longopt, "memory-limit")) {
+ if (optind >= argc) {
+ fprintf(stderr, "expecting memory limit");
+ exit(1);
+ }
+ memory_limit = (size_t)strtod(argv[optind++], NULL);
+ continue;
+ }
+ if (!strcmp(longopt, "stack-size")) {
+ if (optind >= argc) {
+ fprintf(stderr, "expecting stack size");
+ exit(1);
+ }
+ stack_size = (size_t)strtod(argv[optind++], NULL);
+ continue;
+ }
+ if (opt) {
+ fprintf(stderr, "qjs: unknown option '-%c'\n", opt);
+ } else {
+ fprintf(stderr, "qjs: unknown option '--%s'\n", longopt);
+ }
+ help();
+ }
+ }
+
+ if (trace_memory) {
+ js_trace_malloc_init(&trace_data);
+ rt = JS_NewRuntime2(&trace_mf, &trace_data);
+ } else {
+ rt = JS_NewRuntime();
+ }
+ if (!rt) {
+ fprintf(stderr, "qjs: cannot allocate JS runtime\n");
+ exit(2);
+ }
+ if (memory_limit != 0)
+ JS_SetMemoryLimit(rt, memory_limit);
+ if (stack_size != 0)
+ JS_SetMaxStackSize(rt, stack_size);
+ js_std_set_worker_new_context_func(JS_NewCustomContext);
+ js_std_init_handlers(rt);
+ ctx = JS_NewCustomContext(rt);
+ if (!ctx) {
+ fprintf(stderr, "qjs: cannot allocate JS context\n");
+ exit(2);
+ }
+
+ /* loader for ES6 modules */
+ JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
+
+ if (dump_unhandled_promise_rejection) {
+ JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker,
+ NULL);
+ }
+
+ if (!empty_run) {
+ js_std_add_helpers(ctx, argc - optind, argv + optind);
+
+ /* make 'std' and 'os' visible to non module code */
+ if (load_std) {
+ const char *str = "import * as std from 'std';\n"
+ "import * as os from 'os';\n"
+ "globalThis.std = std;\n"
+ "globalThis.os = os;\n";
+ eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE);
+ }
+
+ for(i = 0; i < include_count; i++) {
+ if (eval_file(ctx, include_list[i], module))
+ goto fail;
+ }
+
+ if (expr) {
+ if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0))
+ goto fail;
+ } else
+ if (optind >= argc) {
+ /* interactive mode */
+ interactive = 1;
+ } else {
+ const char *filename;
+ filename = argv[optind];
+ if (eval_file(ctx, filename, module))
+ goto fail;
+ }
+ if (interactive) {
+ js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0);
+ }
+ js_std_loop(ctx);
+ }
+
+ if (dump_memory) {
+ JSMemoryUsage stats;
+ JS_ComputeMemoryUsage(rt, &stats);
+ JS_DumpMemoryUsage(stdout, &stats, rt);
+ }
+ js_std_free_handlers(rt);
+ JS_FreeContext(ctx);
+ JS_FreeRuntime(rt);
+
+ if (empty_run && dump_memory) {
+ clock_t t[5];
+ double best[5];
+ int i, j;
+ for (i = 0; i < 100; i++) {
+ t[0] = clock();
+ rt = JS_NewRuntime();
+ t[1] = clock();
+ ctx = JS_NewContext(rt);
+ t[2] = clock();
+ JS_FreeContext(ctx);
+ t[3] = clock();
+ JS_FreeRuntime(rt);
+ t[4] = clock();
+ for (j = 4; j > 0; j--) {
+ double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC;
+ if (i == 0 || best[j] > ms)
+ best[j] = ms;
+ }
+ }
+ printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n",
+ best[1] + best[2] + best[3] + best[4],
+ best[1], best[2], best[3], best[4]);
+ }
+ return 0;
+ fail:
+ js_std_free_handlers(rt);
+ JS_FreeContext(ctx);
+ JS_FreeRuntime(rt);
+ return 1;
+}
diff --git a/.gitignore b/quickjs/.gitignore
index 5a1620d..5a1620d 100644
--- a/.gitignore
+++ b/quickjs/.gitignore
diff --git a/Changelog b/quickjs/Changelog
index c09af91..c09af91 100644
--- a/Changelog
+++ b/quickjs/Changelog
diff --git a/LICENSE b/quickjs/LICENSE
index 2c8fdeb..2c8fdeb 100644
--- a/LICENSE
+++ b/quickjs/LICENSE
diff --git a/Makefile b/quickjs/Makefile
index 5783a31..5783a31 100644
--- a/Makefile
+++ b/quickjs/Makefile
diff --git a/TODO b/quickjs/TODO
index 2a3b3c3..2a3b3c3 100644
--- a/TODO
+++ b/quickjs/TODO
diff --git a/VERSION b/quickjs/VERSION
index 22ffec1..22ffec1 100644
--- a/VERSION
+++ b/quickjs/VERSION
diff --git a/cutils.c b/quickjs/cutils.c
index a02fb76..a02fb76 100644
--- a/cutils.c
+++ b/quickjs/cutils.c
diff --git a/cutils.h b/quickjs/cutils.h
index 31f7cd8..31f7cd8 100644
--- a/cutils.h
+++ b/quickjs/cutils.h
diff --git a/doc/jsbignum.texi b/quickjs/doc/jsbignum.texi
index 079d920..079d920 100644
--- a/doc/jsbignum.texi
+++ b/quickjs/doc/jsbignum.texi
diff --git a/doc/quickjs.texi b/quickjs/doc/quickjs.texi
index 9eb6354..9eb6354 100644
--- a/doc/quickjs.texi
+++ b/quickjs/doc/quickjs.texi
diff --git a/examples/fib.c b/quickjs/examples/fib.c
index c77b705..c77b705 100644
--- a/examples/fib.c
+++ b/quickjs/examples/fib.c
diff --git a/examples/fib_module.js b/quickjs/examples/fib_module.js
index 6a81071..6a81071 100644
--- a/examples/fib_module.js
+++ b/quickjs/examples/fib_module.js
diff --git a/examples/hello.js b/quickjs/examples/hello.js
index accefce..accefce 100644
--- a/examples/hello.js
+++ b/quickjs/examples/hello.js
diff --git a/examples/hello_module.js b/quickjs/examples/hello_module.js
index 463660f..463660f 100644
--- a/examples/hello_module.js
+++ b/quickjs/examples/hello_module.js
diff --git a/examples/pi_bigdecimal.js b/quickjs/examples/pi_bigdecimal.js
index 6a416b7..6a416b7 100644
--- a/examples/pi_bigdecimal.js
+++ b/quickjs/examples/pi_bigdecimal.js
diff --git a/examples/pi_bigfloat.js b/quickjs/examples/pi_bigfloat.js
index 2bcda22..2bcda22 100644
--- a/examples/pi_bigfloat.js
+++ b/quickjs/examples/pi_bigfloat.js
diff --git a/examples/pi_bigint.js b/quickjs/examples/pi_bigint.js
index cbbb2c4..cbbb2c4 100644
--- a/examples/pi_bigint.js
+++ b/quickjs/examples/pi_bigint.js
diff --git a/examples/point.c b/quickjs/examples/point.c
index fbe2ce1..fbe2ce1 100644
--- a/examples/point.c
+++ b/quickjs/examples/point.c
diff --git a/examples/test_fib.js b/quickjs/examples/test_fib.js
index 70d26bd..70d26bd 100644
--- a/examples/test_fib.js
+++ b/quickjs/examples/test_fib.js
diff --git a/examples/test_point.js b/quickjs/examples/test_point.js
index 0659bc3..0659bc3 100644
--- a/examples/test_point.js
+++ b/quickjs/examples/test_point.js
diff --git a/libbf.c b/quickjs/libbf.c
index fe1628e..fe1628e 100644
--- a/libbf.c
+++ b/quickjs/libbf.c
diff --git a/libbf.h b/quickjs/libbf.h
index 48e9d95..48e9d95 100644
--- a/libbf.h
+++ b/quickjs/libbf.h
diff --git a/libregexp-opcode.h b/quickjs/libregexp-opcode.h
index f90c23b..f90c23b 100644
--- a/libregexp-opcode.h
+++ b/quickjs/libregexp-opcode.h
diff --git a/libregexp.c b/quickjs/libregexp.c
index 379bfc7..379bfc7 100644
--- a/libregexp.c
+++ b/quickjs/libregexp.c
diff --git a/libregexp.h b/quickjs/libregexp.h
index 9aedb7e..9aedb7e 100644
--- a/libregexp.h
+++ b/quickjs/libregexp.h
diff --git a/libunicode-table.h b/quickjs/libunicode-table.h
index 1727525..1727525 100644
--- a/libunicode-table.h
+++ b/quickjs/libunicode-table.h
diff --git a/libunicode.c b/quickjs/libunicode.c
index 63c12a0..63c12a0 100644
--- a/libunicode.c
+++ b/quickjs/libunicode.c
diff --git a/libunicode.h b/quickjs/libunicode.h
index cfa600a..cfa600a 100644
--- a/libunicode.h
+++ b/quickjs/libunicode.h
diff --git a/list.h b/quickjs/list.h
index 0a1bc5a..0a1bc5a 100644
--- a/list.h
+++ b/quickjs/list.h
diff --git a/qjs.c b/quickjs/qjs.c
index 085018e..085018e 100644
--- a/qjs.c
+++ b/quickjs/qjs.c
diff --git a/qjsc.c b/quickjs/qjsc.c
index d9f1a84..d9f1a84 100644
--- a/qjsc.c
+++ b/quickjs/qjsc.c
diff --git a/qjscalc.js b/quickjs/qjscalc.js
index b1ad1e8..b1ad1e8 100644
--- a/qjscalc.js
+++ b/quickjs/qjscalc.js
diff --git a/quickjs-atom.h b/quickjs/quickjs-atom.h
index 4c22794..4c22794 100644
--- a/quickjs-atom.h
+++ b/quickjs/quickjs-atom.h
diff --git a/quickjs-libc.c b/quickjs/quickjs-libc.c
index 77ff022..af01cd3 100644
--- a/quickjs-libc.c
+++ b/quickjs/quickjs-libc.c
@@ -80,11 +80,6 @@ typedef sig_t sighandler_t;
*/
#include <curl/curl.h>
-#include <sodium.h>
-#include <mbedtls/hkdf.h>
-#include <mbedtls/bignum.h>
-#include <mbedtls/error.h>
-
#include <arpa/inet.h>
typedef struct {
@@ -3995,1168 +3990,6 @@ static JSValue js_print(JSContext *ctx, JSValueConst this_val,
return JS_UNDEFINED;
}
-// === begin Taler stuff here ===
-
-static JSValue js_encode_utf8(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- const char *str;
- size_t len;
- JSValue buf;
- str = JS_ToCStringLen2(ctx, &len, argv[0], FALSE);
- // FIXME: Don't copy buffer but pass destructor function
- buf = JS_NewArrayBufferCopy(ctx, (const uint8_t*) str, len);
- JS_FreeCString(ctx, str);
- return buf;
-}
-
-static JSValue js_random_bytes(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- uint32_t nbytes;
- JSValue buf;
- if (0 != JS_ToUint32(ctx, &nbytes, argv[0])) {
- return JS_EXCEPTION;
- }
- {
- uint8_t randbuf[nbytes];
- randombytes_buf (randbuf, nbytes);
- buf = JS_NewArrayBufferCopy(ctx, randbuf, nbytes);
- }
- return buf;
-}
-
-/**
- * Get the decoded value corresponding to a character according to Crockford
- * Base32 encoding.
- *
- * @param a a character
- * @return corresponding numeric value
- */
-static unsigned int
-getValue__ (unsigned char a)
-{
- unsigned int dec;
-
- switch (a)
- {
- case 'O':
- case 'o':
- a = '0';
- break;
-
- case 'i':
- case 'I':
- case 'l':
- case 'L':
- a = '1';
- break;
-
- /* also consider U to be V */
- case 'u':
- case 'U':
- a = 'V';
- break;
-
- default:
- break;
- }
- if ((a >= '0') && (a <= '9'))
- return a - '0';
- if ((a >= 'a') && (a <= 'z'))
- a = toupper (a);
- /* return (a - 'a' + 10); */
- dec = 0;
- if ((a >= 'A') && (a <= 'Z'))
- {
- if ('I' < a)
- dec++;
- if ('L' < a)
- dec++;
- if ('O' < a)
- dec++;
- if ('U' < a)
- dec++;
- return(a - 'A' + 10 - dec);
- }
- return -1;
-}
-
-static JSValue js_talercrypto_encode_crock(JSContext *ctx, JSValue this_val,
- int argc, JSValueConst *argv)
-{
- size_t size;
- uint8_t *buf;
- uint8_t *out = NULL;
- size_t out_size;
- // 32 characters for encoding
- static char *encTable__ = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
- unsigned int wpos;
- unsigned int rpos;
- unsigned int bits;
- unsigned int vbit;
- const unsigned char *udata;
- JSValue ret_val;
-
- ret_val = JS_UNDEFINED;
-
- buf = JS_GetArrayBuffer(ctx, &size, argv[0]);
-
- if (!buf) {
- goto exception;
- }
-
- assert (size < SIZE_MAX / 8 - 4);
- out_size = size * 8;
-
- if (out_size % 5 > 0)
- out_size += 5 - out_size % 5;
- out_size /= 5;
-
- out = malloc (out_size + 1);
- memset (out, 0, out_size + 1);
- if (!out) {
- goto exception;
- }
-
- udata = buf;
- if (out_size < (size * 8 + 4) / 5) {
- goto exception;
- }
- vbit = 0;
- wpos = 0;
- rpos = 0;
- bits = 0;
- while ((rpos < size) || (vbit > 0))
- {
- if ((rpos < size) && (vbit < 5))
- {
- bits = (bits << 8) | udata[rpos++]; /* eat 8 more bits */
- vbit += 8;
- }
- if (vbit < 5)
- {
- bits <<= (5 - vbit); /* zero-padding */
- assert (vbit == ((size * 8) % 5));
- vbit = 5;
- }
- if (wpos >= out_size)
- {
- goto exception;
- }
- out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31];
- vbit -= 5;
- }
- assert (0 == vbit);
- if (wpos < out_size)
- out[wpos] = '\0';
-
- ret_val = JS_NewString(ctx, (char *) out);
-
-done:
- if (NULL != out) {
- free(out);
- }
- return ret_val;
-exception:
- ret_val = JS_EXCEPTION;
- goto done;
-}
-
-static JSValue js_talercrypto_decode_crock(JSContext *ctx, JSValue this_val,
- int argc, JSValueConst *argv)
-{
- size_t rpos;
- size_t wpos;
- unsigned int bits;
- unsigned int vbit;
- int ret;
- int shift;
- size_t enclen;
- size_t encoded_len;
- const char *enc;
- JSValue ret_val = JS_UNDEFINED;
- unsigned char *uout = NULL;
- size_t out_size;
- JSValue abuf;
-
- enc = JS_ToCStringLen2(ctx, &enclen, argv[0], FALSE);
- if (!enc) {
- goto exception;
- }
-
- out_size = (enclen * 5) / 8;
- encoded_len = out_size * 8;
- uout = malloc(out_size);
- assert (out_size < SIZE_MAX / 8);
- wpos = out_size;
- rpos = enclen;
- if ((encoded_len % 5) > 0)
- {
- vbit = encoded_len % 5; /* padding! */
- shift = 5 - vbit;
- bits = (ret = getValue__ (enc[--rpos])) >> shift;
- }
- else
- {
- vbit = 5;
- shift = 0;
- bits = (ret = getValue__ (enc[--rpos]));
- }
- if ((encoded_len + shift) / 5 != enclen) {
- JS_ThrowTypeError(ctx, "wrong encoded length");
- goto exception;
- }
-
- if (-1 == ret) {
- JS_ThrowTypeError(ctx, "invalid character in encoding");
- goto exception;
- }
- while (wpos > 0)
- {
- if (0 == rpos)
- {
- goto exception;
- }
- bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits;
- if (-1 == ret) {
- goto exception;
- }
- vbit += 5;
- if (vbit >= 8)
- {
- uout[--wpos] = (unsigned char) bits;
- bits >>= 8;
- vbit -= 8;
- }
- }
- if ((0 != rpos) || (0 != vbit)) {
- JS_ThrowTypeError(ctx, "rpos or vbit not zero");
- goto exception;
- }
- abuf = JS_NewArrayBufferCopy(ctx, uout, out_size);
- if (JS_IsException(abuf)) {
- goto exception;
- }
- ret_val = JS_NewTypedArray(ctx, abuf, 1);
-done:
- JS_FreeCString(ctx, enc);
- if (uout) {
- free(uout);
- }
- return ret_val;
-exception:
- ret_val = JS_EXCEPTION;
- goto done;
-}
-
-uint8_t *expect_fixed_buffer(JSContext *ctx,
- JSValue val, size_t len,
- const char *msg)
-{
- uint8_t *buf;
- size_t sz;
-
- buf = JS_GetArrayBuffer(ctx, &sz, val);
- if (!buf) {
- return NULL;
- }
- if (sz != len) {
- JS_ThrowTypeError(ctx, "invalid length for %s", msg);
- return NULL;
- }
- return buf;
-}
-
-int
-expect_mpi(JSContext *ctx,
- JSValue val,
- const char *msg,
- mbedtls_mpi *ret_mpi)
-{
- uint8_t *buf;
- size_t sz;
-
- buf = JS_GetArrayBuffer(ctx, &sz, val);
- if (!buf) {
- return -1;
- }
- if (0 != mbedtls_mpi_read_binary(ret_mpi, buf, sz)) {
- return -1;
- }
- return 0;
-}
-
-#define CHECK(x) do { if (!(x)) { abort(); } } while (0)
-
-typedef struct {
- mbedtls_mpi N;
- mbedtls_mpi e;
-} RsaPub;
-
-typedef uint8_t BlindingKeySecret[32];
-typedef uint8_t HashCode[64];
-
-int
-rsa_public_key_decode(RsaPub *pkey, uint8_t *inbuf, size_t inbuf_len)
-{
- size_t sz;
- uint8_t *p; /* read pointer */
- size_t mod_len;
- size_t exp_len;
- int ret;
-
- CHECK(NULL != pkey);
- if (inbuf_len < 4) {
- ret = -1;
- goto cleanup;
- }
- p = inbuf;
- mod_len = ntohs(*((uint16_t *) p));
- p += sizeof(uint16_t);
- exp_len = ntohs(*((uint16_t *) p));
- sz = 4 + mod_len + exp_len;
- if (sz != inbuf_len) {
- ret = -1;
- goto cleanup;
- }
- p += sizeof(uint16_t);
- MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&pkey->N, p, mod_len));
- p += mod_len;
- MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&pkey->e, p, exp_len));
-
-cleanup:
- if (ret != 0) {
- mbedtls_mpi_free(&pkey->N);
- mbedtls_mpi_free(&pkey->e);
- }
- return ret;
-}
-
-int
-expect_rsa_pub(JSContext *ctx,
- JSValue val,
- const char *msg,
- RsaPub *ret_rsa_pub)
-{
- uint8_t *rsa_enc;
- size_t rsa_enc_len;
- int ret = -1;
-
- rsa_enc = JS_GetArrayBuffer(ctx, &rsa_enc_len, val);
- if (!rsa_enc) {
- goto cleanup;
- }
- if (0 != rsa_public_key_decode(ret_rsa_pub, rsa_enc, rsa_enc_len)) {
- JS_ThrowTypeError(ctx, "rsa pubkey");
- goto cleanup;
- }
- ret = 0;
-cleanup:
- return ret;
-}
-
-#define REQUIRE(cond, _label) do { if (!(cond)) { goto _label; } } while (0)
-
-
-static JSValue make_js_ta_copy(JSContext *ctx, uint8_t *data, size_t size)
-{
- JSValue array_buf;
-
- array_buf = JS_NewArrayBufferCopy(ctx, data, size);
- if (JS_IsException(array_buf)) {
- return JS_EXCEPTION;
- }
- return JS_NewTypedArray(ctx, array_buf, 1);
-}
-
-/**
- * Make a JS typed array from an mbedtls MPI.
-*/
-static JSValue make_js_ta_mpi(JSContext *ctx, const mbedtls_mpi *v)
-{
- JSValue array_buf;
- size_t sz;
- uint8_t *buf = NULL;
- JSValue ret_val;
-
- sz = mbedtls_mpi_size(v);
- buf = malloc(sz);
- if (!buf) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
-
- if (0 != mbedtls_mpi_write_binary(v, buf, sz)) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
-
- // FIXME(#perf): Don't copy
- array_buf = JS_NewArrayBufferCopy(ctx, buf, sz);
- if (JS_IsException(array_buf)) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
- ret_val = JS_NewTypedArray(ctx, array_buf, 1);
-cleanup:
- if (buf) {
- free(buf);
- }
- return ret_val;
-}
-
-static JSValue js_talercrypto_hash(JSContext *ctx, JSValue this_val,
- int argc, JSValueConst *argv)
-{
- size_t size;
- uint8_t *buf;
- unsigned char h[crypto_hash_BYTES];
-
- buf = JS_GetArrayBuffer(ctx, &size, argv[0]);
- if (!buf) {
- return JS_EXCEPTION;
- }
- crypto_hash(h, buf, size);
-
- return make_js_ta_copy(ctx, h, crypto_hash_BYTES);
-}
-
-static JSValue js_talercrypto_eddsa_key_get_public(JSContext *ctx, JSValue this_val,
- int argc, JSValueConst *argv)
-{
- uint8_t *buf;
- unsigned char pk[crypto_sign_PUBLICKEYBYTES];
- unsigned char sk[crypto_sign_SECRETKEYBYTES];
-
- buf = expect_fixed_buffer(ctx, argv[0], 32, "eddsa private key");
-
- if (!buf) {
- return JS_EXCEPTION;
- }
-
- crypto_sign_seed_keypair(pk, sk, buf);
- // FIXME: clean up stack!
-
- return make_js_ta_copy(ctx, pk, crypto_sign_PUBLICKEYBYTES);
-}
-
-static JSValue js_talercrypto_ecdhe_key_get_public(JSContext *ctx, JSValue this_val,
- int argc, JSValueConst *argv)
-{
- uint8_t *buf;
- unsigned char pk[crypto_scalarmult_BYTES];
-
- buf = expect_fixed_buffer(ctx, argv[0], 32, "ecdh private key");
-
- if (!buf) {
- return JS_EXCEPTION;
- }
-
- if (0 != crypto_scalarmult_base(pk, buf)) {
- return JS_EXCEPTION;
- }
- // FIXME: clean up stack!
-
- return make_js_ta_copy(ctx, pk, crypto_sign_PUBLICKEYBYTES);
-}
-
-/**
- * (msg, priv) => sig
- */
-static JSValue js_talercrypto_eddsa_sign(JSContext *ctx, JSValue this_val,
- int argc, JSValueConst *argv)
-{
- unsigned char *seed;
- size_t seed_size;
- unsigned char *data;
- size_t data_size;
- unsigned char sk[crypto_sign_SECRETKEYBYTES];
- unsigned char pk[crypto_sign_PUBLICKEYBYTES];
- unsigned char sig[64];
- int res;
-
- data = JS_GetArrayBuffer(ctx, &data_size, argv[0]);
- if (!data) {
- return JS_EXCEPTION;
- }
-
- seed = JS_GetArrayBuffer(ctx, &seed_size, argv[1]);
- if (!seed) {
- return JS_EXCEPTION;
- }
- if (seed_size != 32) {
- return JS_ThrowTypeError(ctx, "invalid private key size");
- }
-
- if (0 != crypto_sign_seed_keypair(pk, sk, seed)) {
- return JS_EXCEPTION;
- }
-
- res = crypto_sign_detached((uint8_t *)sig,
- NULL,
- (uint8_t *)data,
- data_size,
- sk);
- if (res != 0) {
- return JS_EXCEPTION;
- }
- return make_js_ta_copy(ctx, sig, 64);
-}
-
-/**
- * (msg, sig, pub) -> bool
- */
-static JSValue js_talercrypto_eddsa_verify(JSContext *ctx, JSValue this_val,
- int argc, JSValueConst *argv)
-{
- unsigned char *msg;
- size_t msg_size;
- unsigned char *sig;
- size_t sig_size;
- unsigned char *pub;
- size_t pub_size;
- int res;
-
- msg = JS_GetArrayBuffer(ctx, &msg_size, argv[0]);
- if (!msg) {
- return JS_EXCEPTION;
- }
- sig = JS_GetArrayBuffer(ctx, &sig_size, argv[1]);
- if (!sig) {
- return JS_EXCEPTION;
- }
- if (sig_size != 64) {
- return JS_ThrowTypeError(ctx, "invalid signature size");
- }
- pub = JS_GetArrayBuffer(ctx, &pub_size, argv[2]);
- if (!pub) {
- return JS_EXCEPTION;
- }
- if (pub_size != 32) {
- return JS_ThrowTypeError(ctx, "invalid public key size");
- }
-
- res = crypto_sign_verify_detached (sig, msg, msg_size, pub);
- return (res == 0) ? JS_TRUE : JS_FALSE;
-}
-
-/**
- * Returns 0 on success.
- */
-static int
-kdf(void *okm, size_t okm_len,
- const void *ikm, size_t ikm_len,
- const void *salt, size_t salt_len,
- const void *info, size_t info_len)
-{
- const mbedtls_md_info_t *md_extract;
- const mbedtls_md_info_t *md_expand;
- int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
- unsigned char prk[MBEDTLS_MD_MAX_SIZE];
-
- md_extract = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
- md_expand = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
- if (NULL == md_extract) {
- return -1;
- }
- if (NULL == md_expand) {
- return -1;
- }
-
- ret = mbedtls_hkdf_extract(md_extract, salt, salt_len, ikm, ikm_len, prk);
-
- if (ret != 0) {
- return -1;
- }
-
- ret = mbedtls_hkdf_expand(md_expand, prk, mbedtls_md_get_size(md_extract),
- info, info_len, okm, okm_len);
- return ret;
-}
-
-/**
- * (outLen, ikm, salt?, info?) -> output
- */
-static JSValue js_talercrypto_kdf(JSContext *ctx, JSValue this_val,
- int argc, JSValueConst *argv)
-{
- size_t salt_len;
- size_t ikm_len;
- size_t info_len;
- size_t okm_len;
- uint8_t *salt;
- uint8_t *ikm;
- uint8_t *info;
- uint8_t *okm = NULL;
- uint32_t out_bytes;
- JSValue ret_val;
- int ret;
-
- if (0 != JS_ToUint32(ctx, &out_bytes, argv[0])) {
- goto exception;
- }
- okm_len = out_bytes;
-
- ikm = JS_GetArrayBuffer(ctx, &ikm_len, argv[1]);
- if (!ikm) {
- goto exception;
- }
-
- if (JS_IsUndefined(argv[2])) {
- salt = NULL;
- salt_len = 0;
- } else {
- salt = JS_GetArrayBuffer(ctx, &salt_len, argv[2]);
- if (!salt) {
- goto exception;
- }
- }
-
- if (JS_IsUndefined(argv[3])) {
- info = NULL;
- info_len = 0;
- } else {
- info = JS_GetArrayBuffer(ctx, &info_len, argv[3]);
- if (!info) {
- goto exception;
- }
- }
-
- okm = malloc(okm_len);
-
- ret = kdf(okm, okm_len, ikm, ikm_len, salt, salt_len, info, info_len);
-
- if (ret != 0) {
- return JS_EXCEPTION;
- }
- ret_val = make_js_ta_copy(ctx, okm, okm_len);
-done:
- if (NULL != okm) {
- free(okm);
- }
- return ret_val;
-exception:
- ret_val = JS_EXCEPTION;
- goto done;
-}
-
-/**
- * (ecdhePriv, eddsaPub) -> keyMaterial
- */
-static JSValue js_talercrypto_kx_ecdh_eddsa(JSContext *ctx, JSValue this_val,
- int argc, JSValueConst *argv)
-{
- JSValue ret_val;
- uint8_t p[crypto_scalarmult_BYTES];
- uint8_t *ecdh_priv;
- uint8_t *eddsa_pub;
- uint8_t curve25510_pk[crypto_scalarmult_BYTES];
- uint8_t key_material[crypto_hash_BYTES];
-
- ecdh_priv = expect_fixed_buffer(ctx, argv[0], 32, "ecdhe priv");
- REQUIRE(ecdh_priv, exception);
-
- eddsa_pub = expect_fixed_buffer(ctx, argv[1], 32, "eddsa pub");
- REQUIRE(eddsa_pub, exception);
-
- if (0 != crypto_sign_ed25519_pk_to_curve25519(curve25510_pk, eddsa_pub)) {
- goto exception;
- }
- if (0 != crypto_scalarmult(p, ecdh_priv, curve25510_pk)) {
- goto exception;
- }
- if (0 != crypto_hash(key_material, p, 32)) {
- JS_ThrowTypeError(ctx, "hashing failed");
- goto exception;
- }
- ret_val = make_js_ta_copy(ctx, key_material, crypto_hash_BYTES);
-done:
- return ret_val;
-exception:
- ret_val = JS_EXCEPTION;
- goto done;
-}
-
-/**
- * (eddsaPriv, ecdhePub) -> keyMaterial
- */
-static JSValue js_talercrypto_kx_eddsa_ecdh(JSContext *ctx, JSValue this_val,
- int argc, JSValueConst *argv)
-{
- JSValue ret_val;
- uint8_t *priv;
- uint8_t *pub;
- uint8_t hc[crypto_hash_BYTES];
- uint8_t a[crypto_scalarmult_SCALARBYTES];
- uint8_t p[crypto_scalarmult_BYTES];
- uint8_t key_material[crypto_hash_BYTES];
-
- priv = expect_fixed_buffer(ctx, argv[0], 32, "eddsa priv");
- REQUIRE(priv, exception);
- pub = expect_fixed_buffer(ctx, argv[1], 32, "ecdh pub");
- REQUIRE(pub, exception);
-
- crypto_hash(hc, priv, 32);
- memcpy (a, &hc, 32);
- if (0 != crypto_scalarmult(p, a, pub)) {
- goto exception;
- }
- crypto_hash(key_material, p, crypto_scalarmult_BYTES);
- ret_val = make_js_ta_copy(ctx, key_material, crypto_hash_BYTES);
-done:
- return ret_val;
-exception:
- ret_val = JS_EXCEPTION;
- goto done;
-}
-
-/** FIXME: Should return int */
-void
-kdf_mod_mpi(mbedtls_mpi *r,
- const mbedtls_mpi *n,
- const void *xts, size_t xts_len,
- const void *skm, size_t skm_len,
- const char *ctx)
-{
- int rc;
- unsigned int nbits;
- uint16_t ctr;
- size_t ctxlen = strlen(ctx);
- size_t my_ctx_len = ctxlen + 2;
- unsigned char *my_ctx = malloc(my_ctx_len);
- uint16_t *ctr_nbo_p = (uint16_t *) (my_ctx + ctxlen);
-
- memcpy(my_ctx, ctx, ctxlen);
-
- nbits = mbedtls_mpi_bitlen(n);
- ctr = 0;
- while (1) {
- /* Not clear if n is always divisible by 8 */
- size_t bsize = (nbits - 1) / 8 + 1;
- uint8_t buf[bsize];
-
- *ctr_nbo_p = htons (ctr);
-
- rc = kdf (buf, bsize,
- skm, skm_len,
- xts, xts_len,
- my_ctx, my_ctx_len);
- CHECK(0 == rc);
- rc = mbedtls_mpi_read_binary(r, buf, bsize);
- CHECK(0 == rc);
- while (1) {
- size_t rlen = mbedtls_mpi_bitlen(r);
- if (rlen <= nbits) {
- break;
- }
- mbedtls_mpi_set_bit(r, rlen - 1, 0);
- }
- ++ctr;
- /* We reject this FDH if either r > n and retry with another ctr */
- if (0 > mbedtls_mpi_cmp_mpi (r, n)) {
- break;
- }
- mbedtls_mpi_free (r);
- }
-}
-
-
-/**
- * Test for malicious RSA key.
- *
- * Assuming n is an RSA modulous and r is generated using a call to
- * GNUNET_CRYPTO_kdf_mod_mpi, if gcd(r,n) != 1 then n must be a
- * malicious RSA key designed to deanomize the user.
- *
- * @param r KDF result
- * @param n RSA modulus
- * @return 0 if gcd(r,n) = 1, 1 means RSA key is malicious, negative is syserror
- */
-static int
-rsa_gcd_validate (mbedtls_mpi *r,
- const mbedtls_mpi *n)
-{
- mbedtls_mpi g;
- int ret;
-
- mbedtls_mpi_init(&g);
- MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(&g, r, n));
-
- if (mbedtls_mpi_cmp_int(&g, 1) == 0) {
- ret = 0;
- } else {
- goto cleanup;
- }
-
-cleanup:
- mbedtls_mpi_free(&g);
- return ret;
-}
-
-
-int
-rsa_blinding_key_derive(mbedtls_mpi *r,
- const RsaPub *pkey,
- const BlindingKeySecret *bks)
-{
- /* Trusts bks' randomness more */
- const char *xts = "Blinding KDF extractor HMAC key";
-
- kdf_mod_mpi(r,
- &pkey->N,
- xts, strlen(xts),
- bks, sizeof(*bks),
- "Blinding KDF");
-
- if (0 != rsa_gcd_validate (r, &pkey->N)) {
- return -1;
- }
-
- return 0;
-}
-
-int
-rsa_public_key_encode(const RsaPub *pkey, uint8_t **outbuf, size_t *outbuf_len)
-{
- size_t sz;
- uint8_t *buf;
- uint8_t *p; /* write pointer */
- size_t mod_len;
- size_t exp_len;
- int ret;
-
- *outbuf = NULL;
- *outbuf_len = 0;
-
- mod_len = mbedtls_mpi_size(&pkey->N);
- exp_len = mbedtls_mpi_size(&pkey->e);
- sz = 2 + 2 + exp_len + mod_len;
- buf = malloc(sz);
- if (!buf) {
- return -1;
- }
-
- p = buf;
- *((uint16_t *) p) = htons(mod_len);
- p += sizeof (uint16_t);
- *((uint16_t *) p) = htons(exp_len);
- p += sizeof (uint16_t);
- MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&pkey->N, p, mod_len));
- p += mod_len;
- MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&pkey->e, p, exp_len));
-
- *outbuf = buf;
- *outbuf_len = sz;
-
-cleanup:
- if (0 != ret) {
- free(buf);
- }
- return ret;
-}
-
-
-void
-rsa_public_key_init(RsaPub *pkey)
-{
- CHECK(NULL != pkey);
- mbedtls_mpi_init(&pkey->e);
- mbedtls_mpi_init(&pkey->N);
-}
-
-void
-rsa_public_key_free(RsaPub *pkey)
-{
- if (!pkey) {
- return;
- }
- mbedtls_mpi_free(&pkey->e);
- mbedtls_mpi_free(&pkey->N);
-}
-
-
-int
-rsa_full_domain_hash (mbedtls_mpi *r, const RsaPub *pkey,
- const HashCode *hash)
-{
- uint8_t *xts;
- size_t xts_len;
-
- /* We key with the public denomination key as a homage to RSA-PSS by
- Mihir Bellare and Phillip Rogaway. Doing this lowers the degree
- of the hypothetical polyomial-time attack on RSA-KTI created by a
- polynomial-time one-more forgary attack. Yey seeding! */
- rsa_public_key_encode(pkey, &xts, &xts_len);
-
- kdf_mod_mpi(r,
- &pkey->N,
- xts, xts_len,
- hash, sizeof(*hash),
- "RSA-FDA FTpsW!");
- free(xts);
- if (0 == rsa_gcd_validate (r, &pkey->N)) {
- return 0;
- }
- return 1;
-}
-
-int
-rsa_blind(const HashCode *hash,
- const BlindingKeySecret *bks,
- const RsaPub *pkey,
- uint8_t **buf,
- size_t *buf_size)
-{
- mbedtls_mpi bkey, data, r_e, data_r_e;
- size_t outsize;
- uint8_t *outbuf;
- int ret;
-
- CHECK(buf != NULL);
- CHECK(buf_size != NULL);
-
- *buf = NULL;
- *buf_size = 0;
-
- mbedtls_mpi_init(&bkey);
- mbedtls_mpi_init(&data);
- mbedtls_mpi_init(&r_e);
- mbedtls_mpi_init(&data_r_e);
-
- MBEDTLS_MPI_CHK(rsa_full_domain_hash (&data, pkey, hash));
- MBEDTLS_MPI_CHK(rsa_blinding_key_derive (&bkey, pkey, bks));
- MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&r_e, &bkey, &pkey->e, &pkey->N, NULL));
- MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&data_r_e, &data, &r_e));
- MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&data_r_e, &data_r_e, &pkey->N));
-
- outsize = (mbedtls_mpi_bitlen(&data_r_e) + 7) / 8;
- outbuf = malloc(outsize);
-
- MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&data_r_e, outbuf, outsize));
-
- *buf = outbuf;
- *buf_size = outsize;
- ret = 0;
-
-cleanup:
- mbedtls_mpi_free(&data);
- mbedtls_mpi_free(&bkey);
- mbedtls_mpi_free(&r_e);
- mbedtls_mpi_free(&data_r_e);
- return ret;
-}
-
-int
-rsa_unblind (const mbedtls_mpi *sig_blinded,
- const BlindingKeySecret *bks,
- const RsaPub *pkey,
- mbedtls_mpi *sig_ret)
-{
- mbedtls_mpi bkey, r_inv, ubsig;
- int ret;
-
- mbedtls_mpi_init(&bkey);
- mbedtls_mpi_init(&r_inv);
- mbedtls_mpi_init(&ubsig);
-
- MBEDTLS_MPI_CHK(rsa_blinding_key_derive (&bkey, pkey, bks));
- MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&r_inv, &bkey, &pkey->N));
- MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ubsig, sig_blinded, &r_inv));
- MBEDTLS_MPI_CHK(mbedtls_mpi_copy(sig_ret, &ubsig));
-
-cleanup:
- mbedtls_mpi_free(&bkey);
- mbedtls_mpi_free(&r_inv);
- mbedtls_mpi_free(&ubsig);
- return ret;
-}
-
-
-int
-rsa_verify(const HashCode *hash,
- const mbedtls_mpi *sig,
- const RsaPub *pkey)
-{
- mbedtls_mpi r;
- mbedtls_mpi sig_2;
- int ret;
-
- mbedtls_mpi_init(&r);
- mbedtls_mpi_init(&sig_2);
-
- /* Can fail if RSA key is malicious since rsa_gcd_validate failed here.
- * It should have failed during GNUNET_CRYPTO_rsa_blind too though,
- * so the exchange is being malicious in an unfamilair way, maybe
- * just trying to crash us. Arguably, we've only an internal error
- * though because we should've detected this in our previous call
- * to GNUNET_CRYPTO_rsa_unblind. *///
- MBEDTLS_MPI_CHK(rsa_full_domain_hash(&r, pkey, hash));
-
- MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&sig_2, sig, &pkey->e, &pkey->N, NULL));
-
- if (0 != mbedtls_mpi_cmp_mpi(sig, &sig_2)) {
- ret = -1;
- } else {
- ret = 0;
- }
-
-cleanup:
- mbedtls_mpi_init(&r);
- mbedtls_mpi_init(&sig_2);
- return ret;
-}
-
-
-/**
- * (hmsg, bks, rsaPub) -> blinded
- */
-static JSValue js_talercrypto_rsa_blind(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- HashCode *hmsg;
- BlindingKeySecret *bks;
- uint8_t *rsa_enc;
- size_t rsa_enc_len;
- RsaPub rsa_pub;
- JSValue ret_val = JS_UNDEFINED;
- uint8_t *out_buf;
- size_t out_len;
-
- rsa_public_key_init(&rsa_pub);
-
- hmsg = (HashCode *) expect_fixed_buffer(ctx, argv[0], 64, "hmsg");
- if (!hmsg) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
- bks = (BlindingKeySecret *) expect_fixed_buffer(ctx, argv[1], 32, "bks");
- if (!bks) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
- rsa_enc = JS_GetArrayBuffer(ctx, &rsa_enc_len, argv[2]);
- if (!rsa_enc) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
- if (0 != rsa_public_key_decode(&rsa_pub, rsa_enc, rsa_enc_len)) {
- ret_val = JS_ThrowTypeError(ctx, "rsa pubkey");
- goto cleanup;
- }
- if (0 != rsa_blind(hmsg, bks, &rsa_pub, &out_buf, &out_len)) {
- ret_val = JS_ThrowInternalError(ctx, "blinding failed");
- goto cleanup;
- }
-
- ret_val = make_js_ta_copy(ctx, out_buf, out_len);
-cleanup:
- rsa_public_key_free(&rsa_pub);
- return ret_val;
-}
-
-/**
- * (blindSig, rsaPub, bks) -> ubsig
- */
-static JSValue js_talercrypto_rsa_unblind(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue ret_val = JS_UNDEFINED;
- mbedtls_mpi bsig;
- mbedtls_mpi sig_ret;
- RsaPub rsa_pub;
- BlindingKeySecret *bks;
-
- mbedtls_mpi_init(&bsig);
- mbedtls_mpi_init(&sig_ret);
- rsa_public_key_init(&rsa_pub);
-
- if (0 != expect_mpi(ctx, argv[0], "blindSig", &bsig)) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
- if (0 != expect_rsa_pub(ctx, argv[1], "rsaPub", &rsa_pub)) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
- bks = (BlindingKeySecret *) expect_fixed_buffer(ctx, argv[2], 32, "bks");
- if (!bks) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
-
- if (0 != rsa_unblind(&bsig, bks, &rsa_pub, &sig_ret)) {
- ret_val = JS_ThrowInternalError(ctx, "unblinding failed");
- goto cleanup;
- }
-
- ret_val = make_js_ta_mpi(ctx, &sig_ret);
-
-cleanup:
- mbedtls_mpi_free(&bsig);
- mbedtls_mpi_free(&sig_ret);
- rsa_public_key_free(&rsa_pub);
- return ret_val;
-}
-
-/**
- * (hm, rsaSig, rsaPub) -> ubsig
- */
-static JSValue js_talercrypto_rsa_verify(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue ret_val = JS_UNDEFINED;
- HashCode *hmsg;
- mbedtls_mpi sig;
- RsaPub rsa_pub;
-
- mbedtls_mpi_init(&sig);
- rsa_public_key_init(&rsa_pub);
-
- hmsg = (HashCode *) expect_fixed_buffer(ctx, argv[0], 64, "hmsg");
- if (!hmsg) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
-
- if (0 != expect_mpi(ctx, argv[1], "sig", &sig)) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
-
- if (0 != expect_rsa_pub(ctx, argv[2], "rsaPub", &rsa_pub)) {
- ret_val = JS_EXCEPTION;
- goto cleanup;
- }
-
- if (0 != rsa_verify(hmsg, &sig, &rsa_pub)) {
- ret_val = JS_FALSE;
- goto cleanup;
- }
-
- ret_val = JS_TRUE;
-
-cleanup:
- mbedtls_mpi_free(&sig);
- rsa_public_key_free(&rsa_pub);
- return ret_val;
-
-}
-
-static JSValue js_decode_utf8(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- size_t psize;
- uint8_t *utf8_buf;
- utf8_buf = JS_GetArrayBuffer(ctx, &psize, argv[0]);
- if (NULL == utf8_buf) {
- return JS_EXCEPTION;
- }
- return JS_NewStringLen(ctx, (char *) utf8_buf, psize);
-}
-
-// === end Taler stuff ===
-
void js_std_add_helpers(JSContext *ctx, int argc, char **argv)
{
JSValue global_obj, console, args;
@@ -5170,39 +4003,6 @@ void js_std_add_helpers(JSContext *ctx, int argc, char **argv)
JS_NewCFunction(ctx, js_print, "log", 1));
JS_SetPropertyStr(ctx, global_obj, "console", console);
- JS_SetPropertyStr(ctx, global_obj, "_encodeUtf8",
- JS_NewCFunction(ctx, js_encode_utf8, "_encodeUtf8", 1));
- JS_SetPropertyStr(ctx, global_obj, "_decodeUtf8",
- JS_NewCFunction(ctx, js_decode_utf8, "_decodeUtf8", 1));
- JS_SetPropertyStr(ctx, global_obj, "_randomBytes",
- JS_NewCFunction(ctx, js_random_bytes, "_randomBytes", 1));
- JS_SetPropertyStr(ctx, global_obj, "_encodeCrock",
- JS_NewCFunction(ctx, js_talercrypto_encode_crock, "_encodeCrock", 1));
- JS_SetPropertyStr(ctx, global_obj, "_decodeCrock",
- JS_NewCFunction(ctx, js_talercrypto_decode_crock, "_decodeCrock", 1));
- JS_SetPropertyStr(ctx, global_obj, "_hash",
- JS_NewCFunction(ctx, js_talercrypto_hash, "_hash", 1));
- JS_SetPropertyStr(ctx, global_obj, "_eddsaGetPublic",
- JS_NewCFunction(ctx, js_talercrypto_eddsa_key_get_public, "_eddsaGetPublic", 1));
- JS_SetPropertyStr(ctx, global_obj, "_ecdheGetPublic",
- JS_NewCFunction(ctx, js_talercrypto_ecdhe_key_get_public, "_ecdheGetPublic", 1));
- JS_SetPropertyStr(ctx, global_obj, "_eddsaSign",
- JS_NewCFunction(ctx, js_talercrypto_eddsa_sign, "_eddsaSign", 2));
- JS_SetPropertyStr(ctx, global_obj, "_eddsaVerify",
- JS_NewCFunction(ctx, js_talercrypto_eddsa_verify, "_eddsaVerify", 3));
- JS_SetPropertyStr(ctx, global_obj, "_kdf",
- JS_NewCFunction(ctx, js_talercrypto_kdf, "_kdf", 4));
- JS_SetPropertyStr(ctx, global_obj, "_keyExchangeEddsaEcdh",
- JS_NewCFunction(ctx, js_talercrypto_kx_eddsa_ecdh, "_keyExchangeEddsaEcdh", 2));
- JS_SetPropertyStr(ctx, global_obj, "_keyExchangeEcdhEddsa",
- JS_NewCFunction(ctx, js_talercrypto_kx_ecdh_eddsa, "_keyExchangeEcdhEddsa", 2));
- JS_SetPropertyStr(ctx, global_obj, "_rsaBlind",
- JS_NewCFunction(ctx, js_talercrypto_rsa_blind, "_rsaBlind", 3));
- JS_SetPropertyStr(ctx, global_obj, "_rsaUnblind",
- JS_NewCFunction(ctx, js_talercrypto_rsa_unblind, "_rsaUnblind", 3));
- JS_SetPropertyStr(ctx, global_obj, "_rsaVerify",
- JS_NewCFunction(ctx, js_talercrypto_rsa_verify, "_rsaVerify", 3));
-
/* same methods as the mozilla JS shell */
if (argc >= 0) {
args = JS_NewArray(ctx);
diff --git a/quickjs-libc.h b/quickjs/quickjs-libc.h
index fbbe5b0..fbbe5b0 100644
--- a/quickjs-libc.h
+++ b/quickjs/quickjs-libc.h
diff --git a/quickjs-opcode.h b/quickjs/quickjs-opcode.h
index c731a14..c731a14 100644
--- a/quickjs-opcode.h
+++ b/quickjs/quickjs-opcode.h
diff --git a/quickjs.c b/quickjs/quickjs.c
index c18e12e..c18e12e 100644
--- a/quickjs.c
+++ b/quickjs/quickjs.c
diff --git a/quickjs.h b/quickjs/quickjs.h
index 4791541..4791541 100644
--- a/quickjs.h
+++ b/quickjs/quickjs.h
diff --git a/readme.txt b/quickjs/readme.txt
index 789521d..789521d 100644
--- a/readme.txt
+++ b/quickjs/readme.txt
diff --git a/release.sh b/quickjs/release.sh
index 26fba1b..26fba1b 100755
--- a/release.sh
+++ b/quickjs/release.sh
diff --git a/repl.js b/quickjs/repl.js
index 67a121e..67a121e 100644
--- a/repl.js
+++ b/quickjs/repl.js
diff --git a/run-test262.c b/quickjs/run-test262.c
index 2092cac..2092cac 100644
--- a/run-test262.c
+++ b/quickjs/run-test262.c
diff --git a/test262.conf b/quickjs/test262.conf
index bb66cf4..bb66cf4 100644
--- a/test262.conf
+++ b/quickjs/test262.conf
diff --git a/test262_errors.txt b/quickjs/test262_errors.txt
index b7f6aef..b7f6aef 100644
--- a/test262_errors.txt
+++ b/quickjs/test262_errors.txt
diff --git a/test262o.conf b/quickjs/test262o.conf
index 669dead..669dead 100644
--- a/test262o.conf
+++ b/quickjs/test262o.conf
diff --git a/test262o_errors.txt b/quickjs/test262o_errors.txt
index e69de29..e69de29 100644
--- a/test262o_errors.txt
+++ b/quickjs/test262o_errors.txt
diff --git a/tests/bjson.c b/quickjs/tests/bjson.c
index 8e52741..8e52741 100644
--- a/tests/bjson.c
+++ b/quickjs/tests/bjson.c
diff --git a/tests/microbench.js b/quickjs/tests/microbench.js
index 1c5f52d..1c5f52d 100644
--- a/tests/microbench.js
+++ b/quickjs/tests/microbench.js
diff --git a/tests/test262.patch b/quickjs/tests/test262.patch
index 6576bdc..6576bdc 100644
--- a/tests/test262.patch
+++ b/quickjs/tests/test262.patch
diff --git a/tests/test_bignum.js b/quickjs/tests/test_bignum.js
index f4f72a0..f4f72a0 100644
--- a/tests/test_bignum.js
+++ b/quickjs/tests/test_bignum.js
diff --git a/tests/test_bjson.js b/quickjs/tests/test_bjson.js
index fcbb8e6..fcbb8e6 100644
--- a/tests/test_bjson.js
+++ b/quickjs/tests/test_bjson.js
diff --git a/tests/test_builtin.js b/quickjs/tests/test_builtin.js
index c5c0f83..c5c0f83 100644
--- a/tests/test_builtin.js
+++ b/quickjs/tests/test_builtin.js
diff --git a/tests/test_closure.js b/quickjs/tests/test_closure.js
index aa1d17e..aa1d17e 100644
--- a/tests/test_closure.js
+++ b/quickjs/tests/test_closure.js
diff --git a/tests/test_language.js b/quickjs/tests/test_language.js
index 8d13deb..8d13deb 100644
--- a/tests/test_language.js
+++ b/quickjs/tests/test_language.js
diff --git a/tests/test_loop.js b/quickjs/tests/test_loop.js
index 5fda9d8..5fda9d8 100644
--- a/tests/test_loop.js
+++ b/quickjs/tests/test_loop.js
diff --git a/tests/test_op_overloading.js b/quickjs/tests/test_op_overloading.js
index d08a85e..d08a85e 100644
--- a/tests/test_op_overloading.js
+++ b/quickjs/tests/test_op_overloading.js
diff --git a/tests/test_qjscalc.js b/quickjs/tests/test_qjscalc.js
index 1483466..1483466 100644
--- a/tests/test_qjscalc.js
+++ b/quickjs/tests/test_qjscalc.js
diff --git a/tests/test_std.js b/quickjs/tests/test_std.js
index 3ea6e34..3ea6e34 100644
--- a/tests/test_std.js
+++ b/quickjs/tests/test_std.js
diff --git a/tests/test_worker.js b/quickjs/tests/test_worker.js
index 4b52bf8..4b52bf8 100644
--- a/tests/test_worker.js
+++ b/quickjs/tests/test_worker.js
diff --git a/tests/test_worker_module.js b/quickjs/tests/test_worker_module.js
index c783e1f..c783e1f 100644
--- a/tests/test_worker_module.js
+++ b/quickjs/tests/test_worker_module.js
diff --git a/unicode_download.sh b/quickjs/unicode_download.sh
index 2ed3289..2ed3289 100755
--- a/unicode_download.sh
+++ b/quickjs/unicode_download.sh
diff --git a/unicode_gen.c b/quickjs/unicode_gen.c
index f18aaa0..f18aaa0 100644
--- a/unicode_gen.c
+++ b/quickjs/unicode_gen.c
diff --git a/unicode_gen_def.h b/quickjs/unicode_gen_def.h
index 55f6790..55f6790 100644
--- a/unicode_gen_def.h
+++ b/quickjs/unicode_gen_def.h
diff --git a/readme-wallet.md b/readme-wallet.md
deleted file mode 100644
index 2d24753..0000000
--- a/readme-wallet.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Run test:
-
-./qjs -I prelude.js -I ~/repos/taler/wallet-core/packages/taler-wallet-embedded/dist/taler-wallet-core-qjs.mjs -I wallet-setup.js
diff --git a/tart_module.c b/tart_module.c
new file mode 100644
index 0000000..1e2e837
--- /dev/null
+++ b/tart_module.c
@@ -0,0 +1,1208 @@
+#include "quickjs/cutils.h"
+#include "quickjs/list.h"
+#include "quickjs/quickjs-libc.h"
+
+#include <sodium.h>
+#include <mbedtls/hkdf.h>
+#include <mbedtls/bignum.h>
+#include <mbedtls/error.h>
+
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+
+#include <arpa/inet.h>
+
+static JSValue js_encode_utf8(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *str;
+ size_t len;
+ JSValue buf;
+ str = JS_ToCStringLen2(ctx, &len, argv[0], FALSE);
+ // FIXME: Don't copy buffer but pass destructor function
+ buf = JS_NewArrayBufferCopy(ctx, (const uint8_t*) str, len);
+ JS_FreeCString(ctx, str);
+ return buf;
+}
+
+static JSValue js_random_bytes(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ uint32_t nbytes;
+ JSValue buf;
+ if (0 != JS_ToUint32(ctx, &nbytes, argv[0])) {
+ return JS_EXCEPTION;
+ }
+ {
+ uint8_t randbuf[nbytes];
+ randombytes_buf (randbuf, nbytes);
+ buf = JS_NewArrayBufferCopy(ctx, randbuf, nbytes);
+ }
+ return buf;
+}
+
+/**
+ * Get the decoded value corresponding to a character according to Crockford
+ * Base32 encoding.
+ *
+ * @param a a character
+ * @return corresponding numeric value
+ */
+static unsigned int
+getValue__ (unsigned char a)
+{
+ unsigned int dec;
+
+ switch (a)
+ {
+ case 'O':
+ case 'o':
+ a = '0';
+ break;
+
+ case 'i':
+ case 'I':
+ case 'l':
+ case 'L':
+ a = '1';
+ break;
+
+ /* also consider U to be V */
+ case 'u':
+ case 'U':
+ a = 'V';
+ break;
+
+ default:
+ break;
+ }
+ if ((a >= '0') && (a <= '9'))
+ return a - '0';
+ if ((a >= 'a') && (a <= 'z'))
+ a = toupper (a);
+ /* return (a - 'a' + 10); */
+ dec = 0;
+ if ((a >= 'A') && (a <= 'Z'))
+ {
+ if ('I' < a)
+ dec++;
+ if ('L' < a)
+ dec++;
+ if ('O' < a)
+ dec++;
+ if ('U' < a)
+ dec++;
+ return(a - 'A' + 10 - dec);
+ }
+ return -1;
+}
+
+static JSValue js_talercrypto_encode_crock(JSContext *ctx, JSValue this_val,
+ int argc, JSValueConst *argv)
+{
+ size_t size;
+ uint8_t *buf;
+ uint8_t *out = NULL;
+ size_t out_size;
+ // 32 characters for encoding
+ static char *encTable__ = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
+ unsigned int wpos;
+ unsigned int rpos;
+ unsigned int bits;
+ unsigned int vbit;
+ const unsigned char *udata;
+ JSValue ret_val;
+
+ ret_val = JS_UNDEFINED;
+
+ buf = JS_GetArrayBuffer(ctx, &size, argv[0]);
+
+ if (!buf) {
+ goto exception;
+ }
+
+ assert (size < SIZE_MAX / 8 - 4);
+ out_size = size * 8;
+
+ if (out_size % 5 > 0)
+ out_size += 5 - out_size % 5;
+ out_size /= 5;
+
+ out = malloc (out_size + 1);
+ memset (out, 0, out_size + 1);
+ if (!out) {
+ goto exception;
+ }
+
+ udata = buf;
+ if (out_size < (size * 8 + 4) / 5) {
+ goto exception;
+ }
+ vbit = 0;
+ wpos = 0;
+ rpos = 0;
+ bits = 0;
+ while ((rpos < size) || (vbit > 0))
+ {
+ if ((rpos < size) && (vbit < 5))
+ {
+ bits = (bits << 8) | udata[rpos++]; /* eat 8 more bits */
+ vbit += 8;
+ }
+ if (vbit < 5)
+ {
+ bits <<= (5 - vbit); /* zero-padding */
+ assert (vbit == ((size * 8) % 5));
+ vbit = 5;
+ }
+ if (wpos >= out_size)
+ {
+ goto exception;
+ }
+ out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31];
+ vbit -= 5;
+ }
+ assert (0 == vbit);
+ if (wpos < out_size)
+ out[wpos] = '\0';
+
+ ret_val = JS_NewString(ctx, (char *) out);
+
+done:
+ if (NULL != out) {
+ free(out);
+ }
+ return ret_val;
+exception:
+ ret_val = JS_EXCEPTION;
+ goto done;
+}
+
+static JSValue js_talercrypto_decode_crock(JSContext *ctx, JSValue this_val,
+ int argc, JSValueConst *argv)
+{
+ size_t rpos;
+ size_t wpos;
+ unsigned int bits;
+ unsigned int vbit;
+ int ret;
+ int shift;
+ size_t enclen;
+ size_t encoded_len;
+ const char *enc;
+ JSValue ret_val = JS_UNDEFINED;
+ unsigned char *uout = NULL;
+ size_t out_size;
+ JSValue abuf;
+
+ enc = JS_ToCStringLen2(ctx, &enclen, argv[0], FALSE);
+ if (!enc) {
+ goto exception;
+ }
+
+ out_size = (enclen * 5) / 8;
+ encoded_len = out_size * 8;
+ uout = malloc(out_size);
+ assert (out_size < SIZE_MAX / 8);
+ wpos = out_size;
+ rpos = enclen;
+ if ((encoded_len % 5) > 0)
+ {
+ vbit = encoded_len % 5; /* padding! */
+ shift = 5 - vbit;
+ bits = (ret = getValue__ (enc[--rpos])) >> shift;
+ }
+ else
+ {
+ vbit = 5;
+ shift = 0;
+ bits = (ret = getValue__ (enc[--rpos]));
+ }
+ if ((encoded_len + shift) / 5 != enclen) {
+ JS_ThrowTypeError(ctx, "wrong encoded length");
+ goto exception;
+ }
+
+ if (-1 == ret) {
+ JS_ThrowTypeError(ctx, "invalid character in encoding");
+ goto exception;
+ }
+ while (wpos > 0)
+ {
+ if (0 == rpos)
+ {
+ goto exception;
+ }
+ bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits;
+ if (-1 == ret) {
+ goto exception;
+ }
+ vbit += 5;
+ if (vbit >= 8)
+ {
+ uout[--wpos] = (unsigned char) bits;
+ bits >>= 8;
+ vbit -= 8;
+ }
+ }
+ if ((0 != rpos) || (0 != vbit)) {
+ JS_ThrowTypeError(ctx, "rpos or vbit not zero");
+ goto exception;
+ }
+ abuf = JS_NewArrayBufferCopy(ctx, uout, out_size);
+ if (JS_IsException(abuf)) {
+ goto exception;
+ }
+ ret_val = JS_NewTypedArray(ctx, abuf, 1);
+done:
+ JS_FreeCString(ctx, enc);
+ if (uout) {
+ free(uout);
+ }
+ return ret_val;
+exception:
+ ret_val = JS_EXCEPTION;
+ goto done;
+}
+
+uint8_t *expect_fixed_buffer(JSContext *ctx,
+ JSValue val, size_t len,
+ const char *msg)
+{
+ uint8_t *buf;
+ size_t sz;
+
+ buf = JS_GetArrayBuffer(ctx, &sz, val);
+ if (!buf) {
+ return NULL;
+ }
+ if (sz != len) {
+ JS_ThrowTypeError(ctx, "invalid length for %s", msg);
+ return NULL;
+ }
+ return buf;
+}
+
+int
+expect_mpi(JSContext *ctx,
+ JSValue val,
+ const char *msg,
+ mbedtls_mpi *ret_mpi)
+{
+ uint8_t *buf;
+ size_t sz;
+
+ buf = JS_GetArrayBuffer(ctx, &sz, val);
+ if (!buf) {
+ return -1;
+ }
+ if (0 != mbedtls_mpi_read_binary(ret_mpi, buf, sz)) {
+ return -1;
+ }
+ return 0;
+}
+
+#define CHECK(x) do { if (!(x)) { abort(); } } while (0)
+
+typedef struct {
+ mbedtls_mpi N;
+ mbedtls_mpi e;
+} RsaPub;
+
+typedef uint8_t BlindingKeySecret[32];
+typedef uint8_t HashCode[64];
+
+int
+rsa_public_key_decode(RsaPub *pkey, uint8_t *inbuf, size_t inbuf_len)
+{
+ size_t sz;
+ uint8_t *p; /* read pointer */
+ size_t mod_len;
+ size_t exp_len;
+ int ret;
+
+ CHECK(NULL != pkey);
+ if (inbuf_len < 4) {
+ ret = -1;
+ goto cleanup;
+ }
+ p = inbuf;
+ mod_len = ntohs(*((uint16_t *) p));
+ p += sizeof(uint16_t);
+ exp_len = ntohs(*((uint16_t *) p));
+ sz = 4 + mod_len + exp_len;
+ if (sz != inbuf_len) {
+ ret = -1;
+ goto cleanup;
+ }
+ p += sizeof(uint16_t);
+ MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&pkey->N, p, mod_len));
+ p += mod_len;
+ MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&pkey->e, p, exp_len));
+
+cleanup:
+ if (ret != 0) {
+ mbedtls_mpi_free(&pkey->N);
+ mbedtls_mpi_free(&pkey->e);
+ }
+ return ret;
+}
+
+int
+expect_rsa_pub(JSContext *ctx,
+ JSValue val,
+ const char *msg,
+ RsaPub *ret_rsa_pub)
+{
+ uint8_t *rsa_enc;
+ size_t rsa_enc_len;
+ int ret = -1;
+
+ rsa_enc = JS_GetArrayBuffer(ctx, &rsa_enc_len, val);
+ if (!rsa_enc) {
+ goto cleanup;
+ }
+ if (0 != rsa_public_key_decode(ret_rsa_pub, rsa_enc, rsa_enc_len)) {
+ JS_ThrowTypeError(ctx, "rsa pubkey");
+ goto cleanup;
+ }
+ ret = 0;
+cleanup:
+ return ret;
+}
+
+#define REQUIRE(cond, _label) do { if (!(cond)) { goto _label; } } while (0)
+
+
+static JSValue make_js_ta_copy(JSContext *ctx, uint8_t *data, size_t size)
+{
+ JSValue array_buf;
+
+ array_buf = JS_NewArrayBufferCopy(ctx, data, size);
+ if (JS_IsException(array_buf)) {
+ return JS_EXCEPTION;
+ }
+ return JS_NewTypedArray(ctx, array_buf, 1);
+}
+
+/**
+ * Make a JS typed array from an mbedtls MPI.
+*/
+static JSValue make_js_ta_mpi(JSContext *ctx, const mbedtls_mpi *v)
+{
+ JSValue array_buf;
+ size_t sz;
+ uint8_t *buf = NULL;
+ JSValue ret_val;
+
+ sz = mbedtls_mpi_size(v);
+ buf = malloc(sz);
+ if (!buf) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+
+ if (0 != mbedtls_mpi_write_binary(v, buf, sz)) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+
+ // FIXME(#perf): Don't copy
+ array_buf = JS_NewArrayBufferCopy(ctx, buf, sz);
+ if (JS_IsException(array_buf)) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+ ret_val = JS_NewTypedArray(ctx, array_buf, 1);
+cleanup:
+ if (buf) {
+ free(buf);
+ }
+ return ret_val;
+}
+
+static JSValue js_talercrypto_hash(JSContext *ctx, JSValue this_val,
+ int argc, JSValueConst *argv)
+{
+ size_t size;
+ uint8_t *buf;
+ unsigned char h[crypto_hash_BYTES];
+
+ buf = JS_GetArrayBuffer(ctx, &size, argv[0]);
+ if (!buf) {
+ return JS_EXCEPTION;
+ }
+ crypto_hash(h, buf, size);
+
+ return make_js_ta_copy(ctx, h, crypto_hash_BYTES);
+}
+
+static JSValue js_talercrypto_eddsa_key_get_public(JSContext *ctx, JSValue this_val,
+ int argc, JSValueConst *argv)
+{
+ uint8_t *buf;
+ unsigned char pk[crypto_sign_PUBLICKEYBYTES];
+ unsigned char sk[crypto_sign_SECRETKEYBYTES];
+
+ buf = expect_fixed_buffer(ctx, argv[0], 32, "eddsa private key");
+
+ if (!buf) {
+ return JS_EXCEPTION;
+ }
+
+ crypto_sign_seed_keypair(pk, sk, buf);
+ // FIXME: clean up stack!
+
+ return make_js_ta_copy(ctx, pk, crypto_sign_PUBLICKEYBYTES);
+}
+
+static JSValue js_talercrypto_ecdhe_key_get_public(JSContext *ctx, JSValue this_val,
+ int argc, JSValueConst *argv)
+{
+ uint8_t *buf;
+ unsigned char pk[crypto_scalarmult_BYTES];
+
+ buf = expect_fixed_buffer(ctx, argv[0], 32, "ecdh private key");
+
+ if (!buf) {
+ return JS_EXCEPTION;
+ }
+
+ if (0 != crypto_scalarmult_base(pk, buf)) {
+ return JS_EXCEPTION;
+ }
+ // FIXME: clean up stack!
+
+ return make_js_ta_copy(ctx, pk, crypto_sign_PUBLICKEYBYTES);
+}
+
+/**
+ * (msg, priv) => sig
+ */
+static JSValue js_talercrypto_eddsa_sign(JSContext *ctx, JSValue this_val,
+ int argc, JSValueConst *argv)
+{
+ unsigned char *seed;
+ size_t seed_size;
+ unsigned char *data;
+ size_t data_size;
+ unsigned char sk[crypto_sign_SECRETKEYBYTES];
+ unsigned char pk[crypto_sign_PUBLICKEYBYTES];
+ unsigned char sig[64];
+ int res;
+
+ data = JS_GetArrayBuffer(ctx, &data_size, argv[0]);
+ if (!data) {
+ return JS_EXCEPTION;
+ }
+
+ seed = JS_GetArrayBuffer(ctx, &seed_size, argv[1]);
+ if (!seed) {
+ return JS_EXCEPTION;
+ }
+ if (seed_size != 32) {
+ return JS_ThrowTypeError(ctx, "invalid private key size");
+ }
+
+ if (0 != crypto_sign_seed_keypair(pk, sk, seed)) {
+ return JS_EXCEPTION;
+ }
+
+ res = crypto_sign_detached((uint8_t *)sig,
+ NULL,
+ (uint8_t *)data,
+ data_size,
+ sk);
+ if (res != 0) {
+ return JS_EXCEPTION;
+ }
+ return make_js_ta_copy(ctx, sig, 64);
+}
+
+/**
+ * (msg, sig, pub) -> bool
+ */
+static JSValue js_talercrypto_eddsa_verify(JSContext *ctx, JSValue this_val,
+ int argc, JSValueConst *argv)
+{
+ unsigned char *msg;
+ size_t msg_size;
+ unsigned char *sig;
+ size_t sig_size;
+ unsigned char *pub;
+ size_t pub_size;
+ int res;
+
+ msg = JS_GetArrayBuffer(ctx, &msg_size, argv[0]);
+ if (!msg) {
+ return JS_EXCEPTION;
+ }
+ sig = JS_GetArrayBuffer(ctx, &sig_size, argv[1]);
+ if (!sig) {
+ return JS_EXCEPTION;
+ }
+ if (sig_size != 64) {
+ return JS_ThrowTypeError(ctx, "invalid signature size");
+ }
+ pub = JS_GetArrayBuffer(ctx, &pub_size, argv[2]);
+ if (!pub) {
+ return JS_EXCEPTION;
+ }
+ if (pub_size != 32) {
+ return JS_ThrowTypeError(ctx, "invalid public key size");
+ }
+
+ res = crypto_sign_verify_detached (sig, msg, msg_size, pub);
+ return (res == 0) ? JS_TRUE : JS_FALSE;
+}
+
+/**
+ * Returns 0 on success.
+ */
+static int
+kdf(void *okm, size_t okm_len,
+ const void *ikm, size_t ikm_len,
+ const void *salt, size_t salt_len,
+ const void *info, size_t info_len)
+{
+ const mbedtls_md_info_t *md_extract;
+ const mbedtls_md_info_t *md_expand;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ unsigned char prk[MBEDTLS_MD_MAX_SIZE];
+
+ md_extract = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
+ md_expand = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+ if (NULL == md_extract) {
+ return -1;
+ }
+ if (NULL == md_expand) {
+ return -1;
+ }
+
+ ret = mbedtls_hkdf_extract(md_extract, salt, salt_len, ikm, ikm_len, prk);
+
+ if (ret != 0) {
+ return -1;
+ }
+
+ ret = mbedtls_hkdf_expand(md_expand, prk, mbedtls_md_get_size(md_extract),
+ info, info_len, okm, okm_len);
+ return ret;
+}
+
+/**
+ * (outLen, ikm, salt?, info?) -> output
+ */
+static JSValue js_talercrypto_kdf(JSContext *ctx, JSValue this_val,
+ int argc, JSValueConst *argv)
+{
+ size_t salt_len;
+ size_t ikm_len;
+ size_t info_len;
+ size_t okm_len;
+ uint8_t *salt;
+ uint8_t *ikm;
+ uint8_t *info;
+ uint8_t *okm = NULL;
+ uint32_t out_bytes;
+ JSValue ret_val;
+ int ret;
+
+ if (0 != JS_ToUint32(ctx, &out_bytes, argv[0])) {
+ goto exception;
+ }
+ okm_len = out_bytes;
+
+ ikm = JS_GetArrayBuffer(ctx, &ikm_len, argv[1]);
+ if (!ikm) {
+ goto exception;
+ }
+
+ if (JS_IsUndefined(argv[2])) {
+ salt = NULL;
+ salt_len = 0;
+ } else {
+ salt = JS_GetArrayBuffer(ctx, &salt_len, argv[2]);
+ if (!salt) {
+ goto exception;
+ }
+ }
+
+ if (JS_IsUndefined(argv[3])) {
+ info = NULL;
+ info_len = 0;
+ } else {
+ info = JS_GetArrayBuffer(ctx, &info_len, argv[3]);
+ if (!info) {
+ goto exception;
+ }
+ }
+
+ okm = malloc(okm_len);
+
+ ret = kdf(okm, okm_len, ikm, ikm_len, salt, salt_len, info, info_len);
+
+ if (ret != 0) {
+ return JS_EXCEPTION;
+ }
+ ret_val = make_js_ta_copy(ctx, okm, okm_len);
+done:
+ if (NULL != okm) {
+ free(okm);
+ }
+ return ret_val;
+exception:
+ ret_val = JS_EXCEPTION;
+ goto done;
+}
+
+/**
+ * (ecdhePriv, eddsaPub) -> keyMaterial
+ */
+static JSValue js_talercrypto_kx_ecdh_eddsa(JSContext *ctx, JSValue this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue ret_val;
+ uint8_t p[crypto_scalarmult_BYTES];
+ uint8_t *ecdh_priv;
+ uint8_t *eddsa_pub;
+ uint8_t curve25510_pk[crypto_scalarmult_BYTES];
+ uint8_t key_material[crypto_hash_BYTES];
+
+ ecdh_priv = expect_fixed_buffer(ctx, argv[0], 32, "ecdhe priv");
+ REQUIRE(ecdh_priv, exception);
+
+ eddsa_pub = expect_fixed_buffer(ctx, argv[1], 32, "eddsa pub");
+ REQUIRE(eddsa_pub, exception);
+
+ if (0 != crypto_sign_ed25519_pk_to_curve25519(curve25510_pk, eddsa_pub)) {
+ goto exception;
+ }
+ if (0 != crypto_scalarmult(p, ecdh_priv, curve25510_pk)) {
+ goto exception;
+ }
+ if (0 != crypto_hash(key_material, p, 32)) {
+ JS_ThrowTypeError(ctx, "hashing failed");
+ goto exception;
+ }
+ ret_val = make_js_ta_copy(ctx, key_material, crypto_hash_BYTES);
+done:
+ return ret_val;
+exception:
+ ret_val = JS_EXCEPTION;
+ goto done;
+}
+
+/**
+ * (eddsaPriv, ecdhePub) -> keyMaterial
+ */
+static JSValue js_talercrypto_kx_eddsa_ecdh(JSContext *ctx, JSValue this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue ret_val;
+ uint8_t *priv;
+ uint8_t *pub;
+ uint8_t hc[crypto_hash_BYTES];
+ uint8_t a[crypto_scalarmult_SCALARBYTES];
+ uint8_t p[crypto_scalarmult_BYTES];
+ uint8_t key_material[crypto_hash_BYTES];
+
+ priv = expect_fixed_buffer(ctx, argv[0], 32, "eddsa priv");
+ REQUIRE(priv, exception);
+ pub = expect_fixed_buffer(ctx, argv[1], 32, "ecdh pub");
+ REQUIRE(pub, exception);
+
+ crypto_hash(hc, priv, 32);
+ memcpy (a, &hc, 32);
+ if (0 != crypto_scalarmult(p, a, pub)) {
+ goto exception;
+ }
+ crypto_hash(key_material, p, crypto_scalarmult_BYTES);
+ ret_val = make_js_ta_copy(ctx, key_material, crypto_hash_BYTES);
+done:
+ return ret_val;
+exception:
+ ret_val = JS_EXCEPTION;
+ goto done;
+}
+
+/** FIXME: Should return int */
+void
+kdf_mod_mpi(mbedtls_mpi *r,
+ const mbedtls_mpi *n,
+ const void *xts, size_t xts_len,
+ const void *skm, size_t skm_len,
+ const char *ctx)
+{
+ int rc;
+ unsigned int nbits;
+ uint16_t ctr;
+ size_t ctxlen = strlen(ctx);
+ size_t my_ctx_len = ctxlen + 2;
+ unsigned char *my_ctx = malloc(my_ctx_len);
+ uint16_t *ctr_nbo_p = (uint16_t *) (my_ctx + ctxlen);
+
+ memcpy(my_ctx, ctx, ctxlen);
+
+ nbits = mbedtls_mpi_bitlen(n);
+ ctr = 0;
+ while (1) {
+ /* Not clear if n is always divisible by 8 */
+ size_t bsize = (nbits - 1) / 8 + 1;
+ uint8_t buf[bsize];
+
+ *ctr_nbo_p = htons (ctr);
+
+ rc = kdf (buf, bsize,
+ skm, skm_len,
+ xts, xts_len,
+ my_ctx, my_ctx_len);
+ CHECK(0 == rc);
+ rc = mbedtls_mpi_read_binary(r, buf, bsize);
+ CHECK(0 == rc);
+ while (1) {
+ size_t rlen = mbedtls_mpi_bitlen(r);
+ if (rlen <= nbits) {
+ break;
+ }
+ mbedtls_mpi_set_bit(r, rlen - 1, 0);
+ }
+ ++ctr;
+ /* We reject this FDH if either r > n and retry with another ctr */
+ if (0 > mbedtls_mpi_cmp_mpi (r, n)) {
+ break;
+ }
+ mbedtls_mpi_free (r);
+ }
+}
+
+
+/**
+ * Test for malicious RSA key.
+ *
+ * Assuming n is an RSA modulous and r is generated using a call to
+ * GNUNET_CRYPTO_kdf_mod_mpi, if gcd(r,n) != 1 then n must be a
+ * malicious RSA key designed to deanomize the user.
+ *
+ * @param r KDF result
+ * @param n RSA modulus
+ * @return 0 if gcd(r,n) = 1, 1 means RSA key is malicious, negative is syserror
+ */
+static int
+rsa_gcd_validate (mbedtls_mpi *r,
+ const mbedtls_mpi *n)
+{
+ mbedtls_mpi g;
+ int ret;
+
+ mbedtls_mpi_init(&g);
+ MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(&g, r, n));
+
+ if (mbedtls_mpi_cmp_int(&g, 1) == 0) {
+ ret = 0;
+ } else {
+ goto cleanup;
+ }
+
+cleanup:
+ mbedtls_mpi_free(&g);
+ return ret;
+}
+
+
+int
+rsa_blinding_key_derive(mbedtls_mpi *r,
+ const RsaPub *pkey,
+ const BlindingKeySecret *bks)
+{
+ /* Trusts bks' randomness more */
+ const char *xts = "Blinding KDF extractor HMAC key";
+
+ kdf_mod_mpi(r,
+ &pkey->N,
+ xts, strlen(xts),
+ bks, sizeof(*bks),
+ "Blinding KDF");
+
+ if (0 != rsa_gcd_validate (r, &pkey->N)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+rsa_public_key_encode(const RsaPub *pkey, uint8_t **outbuf, size_t *outbuf_len)
+{
+ size_t sz;
+ uint8_t *buf;
+ uint8_t *p; /* write pointer */
+ size_t mod_len;
+ size_t exp_len;
+ int ret;
+
+ *outbuf = NULL;
+ *outbuf_len = 0;
+
+ mod_len = mbedtls_mpi_size(&pkey->N);
+ exp_len = mbedtls_mpi_size(&pkey->e);
+ sz = 2 + 2 + exp_len + mod_len;
+ buf = malloc(sz);
+ if (!buf) {
+ return -1;
+ }
+
+ p = buf;
+ *((uint16_t *) p) = htons(mod_len);
+ p += sizeof (uint16_t);
+ *((uint16_t *) p) = htons(exp_len);
+ p += sizeof (uint16_t);
+ MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&pkey->N, p, mod_len));
+ p += mod_len;
+ MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&pkey->e, p, exp_len));
+
+ *outbuf = buf;
+ *outbuf_len = sz;
+
+cleanup:
+ if (0 != ret) {
+ free(buf);
+ }
+ return ret;
+}
+
+
+void
+rsa_public_key_init(RsaPub *pkey)
+{
+ CHECK(NULL != pkey);
+ mbedtls_mpi_init(&pkey->e);
+ mbedtls_mpi_init(&pkey->N);
+}
+
+void
+rsa_public_key_free(RsaPub *pkey)
+{
+ if (!pkey) {
+ return;
+ }
+ mbedtls_mpi_free(&pkey->e);
+ mbedtls_mpi_free(&pkey->N);
+}
+
+
+int
+rsa_full_domain_hash (mbedtls_mpi *r, const RsaPub *pkey,
+ const HashCode *hash)
+{
+ uint8_t *xts;
+ size_t xts_len;
+
+ /* We key with the public denomination key as a homage to RSA-PSS by
+ Mihir Bellare and Phillip Rogaway. Doing this lowers the degree
+ of the hypothetical polyomial-time attack on RSA-KTI created by a
+ polynomial-time one-more forgary attack. Yey seeding! */
+ rsa_public_key_encode(pkey, &xts, &xts_len);
+
+ kdf_mod_mpi(r,
+ &pkey->N,
+ xts, xts_len,
+ hash, sizeof(*hash),
+ "RSA-FDA FTpsW!");
+ free(xts);
+ if (0 == rsa_gcd_validate (r, &pkey->N)) {
+ return 0;
+ }
+ return 1;
+}
+
+int
+rsa_blind(const HashCode *hash,
+ const BlindingKeySecret *bks,
+ const RsaPub *pkey,
+ uint8_t **buf,
+ size_t *buf_size)
+{
+ mbedtls_mpi bkey, data, r_e, data_r_e;
+ size_t outsize;
+ uint8_t *outbuf;
+ int ret;
+
+ CHECK(buf != NULL);
+ CHECK(buf_size != NULL);
+
+ *buf = NULL;
+ *buf_size = 0;
+
+ mbedtls_mpi_init(&bkey);
+ mbedtls_mpi_init(&data);
+ mbedtls_mpi_init(&r_e);
+ mbedtls_mpi_init(&data_r_e);
+
+ MBEDTLS_MPI_CHK(rsa_full_domain_hash (&data, pkey, hash));
+ MBEDTLS_MPI_CHK(rsa_blinding_key_derive (&bkey, pkey, bks));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&r_e, &bkey, &pkey->e, &pkey->N, NULL));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&data_r_e, &data, &r_e));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&data_r_e, &data_r_e, &pkey->N));
+
+ outsize = (mbedtls_mpi_bitlen(&data_r_e) + 7) / 8;
+ outbuf = malloc(outsize);
+
+ MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&data_r_e, outbuf, outsize));
+
+ *buf = outbuf;
+ *buf_size = outsize;
+ ret = 0;
+
+cleanup:
+ mbedtls_mpi_free(&data);
+ mbedtls_mpi_free(&bkey);
+ mbedtls_mpi_free(&r_e);
+ mbedtls_mpi_free(&data_r_e);
+ return ret;
+}
+
+int
+rsa_unblind (const mbedtls_mpi *sig_blinded,
+ const BlindingKeySecret *bks,
+ const RsaPub *pkey,
+ mbedtls_mpi *sig_ret)
+{
+ mbedtls_mpi bkey, r_inv, ubsig;
+ int ret;
+
+ mbedtls_mpi_init(&bkey);
+ mbedtls_mpi_init(&r_inv);
+ mbedtls_mpi_init(&ubsig);
+
+ MBEDTLS_MPI_CHK(rsa_blinding_key_derive (&bkey, pkey, bks));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&r_inv, &bkey, &pkey->N));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ubsig, sig_blinded, &r_inv));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_copy(sig_ret, &ubsig));
+
+cleanup:
+ mbedtls_mpi_free(&bkey);
+ mbedtls_mpi_free(&r_inv);
+ mbedtls_mpi_free(&ubsig);
+ return ret;
+}
+
+
+int
+rsa_verify(const HashCode *hash,
+ const mbedtls_mpi *sig,
+ const RsaPub *pkey)
+{
+ mbedtls_mpi r;
+ mbedtls_mpi sig_2;
+ int ret;
+
+ mbedtls_mpi_init(&r);
+ mbedtls_mpi_init(&sig_2);
+
+ /* Can fail if RSA key is malicious since rsa_gcd_validate failed here.
+ * It should have failed during GNUNET_CRYPTO_rsa_blind too though,
+ * so the exchange is being malicious in an unfamilair way, maybe
+ * just trying to crash us. Arguably, we've only an internal error
+ * though because we should've detected this in our previous call
+ * to GNUNET_CRYPTO_rsa_unblind. *///
+ MBEDTLS_MPI_CHK(rsa_full_domain_hash(&r, pkey, hash));
+
+ MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&sig_2, sig, &pkey->e, &pkey->N, NULL));
+
+ if (0 != mbedtls_mpi_cmp_mpi(sig, &sig_2)) {
+ ret = -1;
+ } else {
+ ret = 0;
+ }
+
+cleanup:
+ mbedtls_mpi_init(&r);
+ mbedtls_mpi_init(&sig_2);
+ return ret;
+}
+
+
+/**
+ * (hmsg, bks, rsaPub) -> blinded
+ */
+static JSValue js_talercrypto_rsa_blind(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ HashCode *hmsg;
+ BlindingKeySecret *bks;
+ uint8_t *rsa_enc;
+ size_t rsa_enc_len;
+ RsaPub rsa_pub;
+ JSValue ret_val = JS_UNDEFINED;
+ uint8_t *out_buf;
+ size_t out_len;
+
+ rsa_public_key_init(&rsa_pub);
+
+ hmsg = (HashCode *) expect_fixed_buffer(ctx, argv[0], 64, "hmsg");
+ if (!hmsg) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+ bks = (BlindingKeySecret *) expect_fixed_buffer(ctx, argv[1], 32, "bks");
+ if (!bks) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+ rsa_enc = JS_GetArrayBuffer(ctx, &rsa_enc_len, argv[2]);
+ if (!rsa_enc) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+ if (0 != rsa_public_key_decode(&rsa_pub, rsa_enc, rsa_enc_len)) {
+ ret_val = JS_ThrowTypeError(ctx, "rsa pubkey");
+ goto cleanup;
+ }
+ if (0 != rsa_blind(hmsg, bks, &rsa_pub, &out_buf, &out_len)) {
+ ret_val = JS_ThrowInternalError(ctx, "blinding failed");
+ goto cleanup;
+ }
+
+ ret_val = make_js_ta_copy(ctx, out_buf, out_len);
+cleanup:
+ rsa_public_key_free(&rsa_pub);
+ return ret_val;
+}
+
+/**
+ * (blindSig, rsaPub, bks) -> ubsig
+ */
+static JSValue js_talercrypto_rsa_unblind(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue ret_val = JS_UNDEFINED;
+ mbedtls_mpi bsig;
+ mbedtls_mpi sig_ret;
+ RsaPub rsa_pub;
+ BlindingKeySecret *bks;
+
+ mbedtls_mpi_init(&bsig);
+ mbedtls_mpi_init(&sig_ret);
+ rsa_public_key_init(&rsa_pub);
+
+ if (0 != expect_mpi(ctx, argv[0], "blindSig", &bsig)) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+ if (0 != expect_rsa_pub(ctx, argv[1], "rsaPub", &rsa_pub)) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+ bks = (BlindingKeySecret *) expect_fixed_buffer(ctx, argv[2], 32, "bks");
+ if (!bks) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+
+ if (0 != rsa_unblind(&bsig, bks, &rsa_pub, &sig_ret)) {
+ ret_val = JS_ThrowInternalError(ctx, "unblinding failed");
+ goto cleanup;
+ }
+
+ ret_val = make_js_ta_mpi(ctx, &sig_ret);
+
+cleanup:
+ mbedtls_mpi_free(&bsig);
+ mbedtls_mpi_free(&sig_ret);
+ rsa_public_key_free(&rsa_pub);
+ return ret_val;
+}
+
+/**
+ * (hm, rsaSig, rsaPub) -> bool
+ */
+static JSValue js_talercrypto_rsa_verify(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue ret_val = JS_UNDEFINED;
+ HashCode *hmsg;
+ mbedtls_mpi sig;
+ RsaPub rsa_pub;
+
+ mbedtls_mpi_init(&sig);
+ rsa_public_key_init(&rsa_pub);
+
+ hmsg = (HashCode *) expect_fixed_buffer(ctx, argv[0], 64, "hmsg");
+ if (!hmsg) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+
+ if (0 != expect_mpi(ctx, argv[1], "sig", &sig)) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+
+ if (0 != expect_rsa_pub(ctx, argv[2], "rsaPub", &rsa_pub)) {
+ ret_val = JS_EXCEPTION;
+ goto cleanup;
+ }
+
+ if (0 != rsa_verify(hmsg, &sig, &rsa_pub)) {
+ ret_val = JS_FALSE;
+ goto cleanup;
+ }
+
+ ret_val = JS_TRUE;
+
+cleanup:
+ mbedtls_mpi_free(&sig);
+ rsa_public_key_free(&rsa_pub);
+ return ret_val;
+
+}
+
+static JSValue js_decode_utf8(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ size_t psize;
+ uint8_t *utf8_buf;
+ utf8_buf = JS_GetArrayBuffer(ctx, &psize, argv[0]);
+ if (NULL == utf8_buf) {
+ return JS_EXCEPTION;
+ }
+ return JS_NewStringLen(ctx, (char *) utf8_buf, psize);
+}
+
+static const JSCFunctionListEntry tart_talercrypto_funcs[] = {
+ JS_CFUNC_DEF("encodeUtf8", 1, js_encode_utf8),
+ JS_CFUNC_DEF("decodeUtf8", 1, js_decode_utf8),
+ JS_CFUNC_DEF("randomBytes", 1, js_random_bytes),
+ JS_CFUNC_DEF("encodeCrock", 1, js_talercrypto_encode_crock),
+ JS_CFUNC_DEF("decodeCrock", 1, js_talercrypto_decode_crock),
+ JS_CFUNC_DEF("hash", 1, js_talercrypto_hash),
+ JS_CFUNC_DEF("eddsaGetPublic", 1, js_talercrypto_eddsa_key_get_public),
+ JS_CFUNC_DEF("ecdheGetPublic", 1, js_talercrypto_ecdhe_key_get_public),
+ JS_CFUNC_DEF("eddsaSign", 2, js_talercrypto_eddsa_sign),
+ JS_CFUNC_DEF("eddsaVerify", 3, js_talercrypto_eddsa_verify),
+ JS_CFUNC_DEF("kdf", 3, js_talercrypto_kdf),
+ JS_CFUNC_DEF("keyExchangeEcdhEddsa", 2, js_talercrypto_kx_ecdh_eddsa),
+ JS_CFUNC_DEF("keyExchangeEddsaEcdh", 2, js_talercrypto_kx_eddsa_ecdh),
+ JS_CFUNC_DEF("rsaBlind", 3, js_talercrypto_rsa_blind),
+ JS_CFUNC_DEF("rsaUnblind", 3, js_talercrypto_rsa_unblind),
+ JS_CFUNC_DEF("rsaVerify", 3, js_talercrypto_rsa_verify),
+};
+
+static int tart_talercrypto_init(JSContext *ctx, JSModuleDef *m)
+{
+ return JS_SetModuleExportList(ctx, m, tart_talercrypto_funcs,
+ countof(tart_talercrypto_funcs));
+}
+
+JSModuleDef *tart_init_module_talercrypto(JSContext *ctx, const char *module_name)
+{
+ JSModuleDef *m;
+ m = JS_NewCModule(ctx, module_name, tart_talercrypto_init);
+ if (!m)
+ return NULL;
+ JS_AddModuleExportList(ctx, m, tart_talercrypto_funcs,
+ countof(tart_talercrypto_funcs));
+ return m;
+}
diff --git a/tart_module.h b/tart_module.h
new file mode 100644
index 0000000..abbbeae
--- /dev/null
+++ b/tart_module.h
@@ -0,0 +1,8 @@
+#ifndef _TALERCRYPTO_H
+#define _TALERCRYPTO_H
+
+#include "quickjs/quickjs-libc.h"
+
+JSModuleDef *tart_init_module_talercrypto(JSContext *ctx, const char *module_name);
+
+#endif /* _TALERCRYPTO_H */