commit 9e241f5866aa5e493af8641ade54df66eba4bfcd
parent c57beff4c4df73311fb9be9790097685af84d2c3
Author: Florian Dold <florian@dold.me>
Date: Wed, 14 Dec 2022 00:29:45 +0100
blind signatures
Diffstat:
| M | Makefile | | | 2 | +- |
| M | quickjs-libc.c | | | 644 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
2 files changed, 613 insertions(+), 33 deletions(-)
diff --git a/Makefile b/Makefile
@@ -87,7 +87,7 @@ ifdef CONFIG_CLANG
else
HOST_CC=gcc
CC=$(CROSS_PREFIX)gcc
- CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
+ CFLAGS=-ggdb -fno-omit-frame-pointer -Wall -MMD -MF $(OBJDIR)/$(@F).d
CFLAGS += -Wno-array-bounds -Wno-format-truncation
ifdef CONFIG_LTO
AR=$(CROSS_PREFIX)gcc-ar
diff --git a/quickjs-libc.c b/quickjs-libc.c
@@ -82,8 +82,11 @@ 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 {
struct list_head link;
int fd;
@@ -4250,18 +4253,106 @@ uint8_t *expect_fixed_buffer(JSContext *ctx,
JSValue val, size_t len,
const char *msg)
{
- uint8_t *buf;
- size_t sz;
+ 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;
+ 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)
@@ -4278,6 +4369,42 @@ static JSValue make_js_ta_copy(JSContext *ctx, uint8_t *data, size_t size)
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)
{
@@ -4414,6 +4541,40 @@ static JSValue js_talercrypto_eddsa_verify(JSContext *ctx, JSValue this_val,
}
/**
+ * 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,
@@ -4427,21 +4588,9 @@ static JSValue js_talercrypto_kdf(JSContext *ctx, JSValue this_val,
uint8_t *ikm;
uint8_t *info;
uint8_t *okm = NULL;
- 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];
uint32_t out_bytes;
JSValue ret_val;
-
- md_extract = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
- md_expand = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
- if (NULL == md_extract) {
- goto exception;
- }
- if (NULL == md_expand) {
- goto exception;
- }
+ int ret;
if (0 != JS_ToUint32(ctx, &out_bytes, argv[0])) {
goto exception;
@@ -4473,16 +4622,10 @@ static JSValue js_talercrypto_kdf(JSContext *ctx, JSValue this_val,
}
}
- ret = mbedtls_hkdf_extract(md_extract, salt, salt_len, ikm, ikm_len, prk);
-
- if (ret != 0) {
- return JS_EXCEPTION;
- }
-
okm = malloc(okm_len);
- ret = mbedtls_hkdf_expand(md_expand, prk, mbedtls_md_get_size(md_extract),
- info, info_len, okm, okm_len);
+ ret = kdf(okm, okm_len, ikm, ikm_len, salt, salt_len, info, info_len);
+
if (ret != 0) {
return JS_EXCEPTION;
}
@@ -4567,6 +4710,437 @@ 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)
@@ -4621,6 +5195,12 @@ void js_std_add_helpers(JSContext *ctx, int argc, char **argv)
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) {