diff options
author | Florian Dold <florian@dold.me> | 2022-12-21 19:09:36 +0100 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2022-12-21 19:09:36 +0100 |
commit | c9e2023e15ce70d6754e3e2aedad9e8c8093d071 (patch) | |
tree | 0c02a80d873f118671fa176075391aeb61c3b29c | |
parent | 67e1d6709faa82837ae8dbfce914466c9c373d18 (diff) | |
download | quickjs-tart-c9e2023e15ce70d6754e3e2aedad9e8c8093d071.tar.gz quickjs-tart-c9e2023e15ce70d6754e3e2aedad9e8c8093d071.tar.bz2 quickjs-tart-c9e2023e15ce70d6754e3e2aedad9e8c8093d071.zip |
factor out taler-specific code
-rw-r--r-- | CMakeLists.txt | 55 | ||||
-rw-r--r-- | README.md | 12 | ||||
-rw-r--r-- | libtalerwalletcore.c | 7 | ||||
-rw-r--r-- | meson.build | 70 | ||||
-rw-r--r-- | prelude.js | 9 | ||||
-rw-r--r-- | qtart.c | 527 | ||||
-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-x | quickjs/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-x | quickjs/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.md | 3 | ||||
-rw-r--r-- | tart_module.c | 1208 | ||||
-rw-r--r-- | tart_module.h | 8 |
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]) @@ -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; @@ -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/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/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 */ |