tart_module.c (75198B)
1 /* 2 This file is part of GNU Taler 3 Copyright (C) 2022 Taler Systems SA 4 5 GNU Taler is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 #include "quickjs/cutils.h" 18 #include "quickjs/list.h" 19 #include "quickjs/quickjs-libc.h" 20 21 #include <sodium.h> 22 #include <mbedtls/hkdf.h> 23 #include <mbedtls/bignum.h> 24 #include <mbedtls/error.h> 25 26 #include <ctype.h> 27 #include <string.h> 28 #include <assert.h> 29 #include <pthread.h> 30 31 #include <arpa/inet.h> 32 33 #if defined(__APPLE__) 34 #include <sqlite3.h> 35 #else 36 #include "sqlite3/sqlite3.h" 37 #endif 38 39 static JSValue make_js_ta_copy(JSContext *ctx, uint8_t *data, size_t size); 40 41 #define TART_MAX_RANDOM_BYTES (64U * 1024U * 1024U) 42 #define TART_MAX_ARGON2_OUTPUT (64U * 1024U) 43 #define TART_MAX_RSA_MODULUS_BYTES 1024U 44 #define TART_MAX_RSA_EXPONENT_BYTES 8U 45 46 /* Increment when the JavaScript-facing native API gains new semantics. */ 47 #define TART_API_VERSION 1 48 49 static JSValue js_encode_utf8(JSContext *ctx, JSValueConst this_val, 50 int argc, JSValueConst *argv) 51 { 52 const char *str; 53 size_t len; 54 JSValue buf; 55 str = JS_ToCStringLen2(ctx, &len, argv[0], FALSE); 56 if (!str) { 57 return JS_EXCEPTION; 58 } 59 // FIXME: Don't copy buffer but pass destructor function 60 buf = JS_NewArrayBufferCopy(ctx, (const uint8_t*) str, len); 61 JS_FreeCString(ctx, str); 62 return buf; 63 } 64 65 static JSValue js_crash_for_debug(JSContext *ctx, JSValueConst this_val, 66 int argc, JSValueConst *argv) 67 { 68 abort(); 69 return JS_EXCEPTION; 70 } 71 72 static JSValue js_random_bytes(JSContext *ctx, JSValueConst this_val, 73 int argc, JSValueConst *argv) 74 { 75 uint32_t nbytes; 76 uint8_t *randbuf = NULL; 77 JSValue buf = JS_EXCEPTION; 78 79 if (0 != JS_ToUint32(ctx, &nbytes, argv[0])) { 80 return JS_EXCEPTION; 81 } 82 if (nbytes > TART_MAX_RANDOM_BYTES) { 83 return JS_ThrowRangeError(ctx, "random byte request is too large"); 84 } 85 if (nbytes != 0) { 86 randbuf = js_malloc(ctx, nbytes); 87 if (!randbuf) { 88 return JS_EXCEPTION; 89 } 90 randombytes_buf(randbuf, nbytes); 91 } 92 buf = JS_NewArrayBufferCopy(ctx, randbuf, nbytes); 93 if (randbuf) { 94 sodium_memzero(randbuf, nbytes); 95 js_free(ctx, randbuf); 96 } 97 return buf; 98 } 99 100 /** 101 * Get the decoded value corresponding to a character according to Crockford 102 * Base32 encoding. 103 * 104 * @param a a character 105 * @return corresponding numeric value 106 */ 107 static int 108 getValue__ (unsigned char a) 109 { 110 unsigned int dec; 111 112 switch (a) 113 { 114 case 'O': 115 case 'o': 116 a = '0'; 117 break; 118 119 case 'i': 120 case 'I': 121 case 'l': 122 case 'L': 123 a = '1'; 124 break; 125 126 /* also consider U to be V */ 127 case 'u': 128 case 'U': 129 a = 'V'; 130 break; 131 132 default: 133 break; 134 } 135 if ((a >= '0') && (a <= '9')) 136 return a - '0'; 137 if ((a >= 'a') && (a <= 'z')) 138 a = toupper (a); 139 /* return (a - 'a' + 10); */ 140 dec = 0; 141 if ((a >= 'A') && (a <= 'Z')) 142 { 143 if ('I' < a) 144 dec++; 145 if ('L' < a) 146 dec++; 147 if ('O' < a) 148 dec++; 149 if ('U' < a) 150 dec++; 151 return(a - 'A' + 10 - dec); 152 } 153 return -1; 154 } 155 156 static JSValue js_talercrypto_encode_crock(JSContext *ctx, JSValue this_val, 157 int argc, JSValueConst *argv) 158 { 159 size_t size; 160 uint8_t *buf; 161 uint8_t *out = NULL; 162 size_t out_size; 163 // 32 characters for encoding 164 static char *encTable__ = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; 165 unsigned int wpos; 166 unsigned int rpos; 167 unsigned int bits; 168 unsigned int vbit; 169 const unsigned char *udata; 170 JSValue ret_val; 171 172 ret_val = JS_UNDEFINED; 173 174 buf = JS_GetArrayBuffer(ctx, &size, argv[0]); 175 176 if (!buf) { 177 goto exception; 178 } 179 180 if (size > (SIZE_MAX - 4) / 8) { 181 return JS_ThrowRangeError(ctx, "input is too large"); 182 } 183 out_size = size * 8; 184 185 if (out_size % 5 > 0) 186 out_size += 5 - out_size % 5; 187 out_size /= 5; 188 189 out = malloc (out_size + 1); 190 if (!out) { 191 JS_ThrowOutOfMemory(ctx); 192 goto exception; 193 } 194 memset (out, 0, out_size + 1); 195 196 udata = buf; 197 if (out_size < (size * 8 + 4) / 5) { 198 goto exception; 199 } 200 vbit = 0; 201 wpos = 0; 202 rpos = 0; 203 bits = 0; 204 while ((rpos < size) || (vbit > 0)) 205 { 206 if ((rpos < size) && (vbit < 5)) 207 { 208 /* eat 8 more bits */ 209 bits = (bits << 8) | udata[rpos++]; 210 vbit += 8; 211 } 212 if (vbit < 5) 213 { 214 bits <<= (5 - vbit); /* zero-padding */ 215 assert (vbit == ((size * 8) % 5)); 216 vbit = 5; 217 } 218 if (wpos >= out_size) 219 { 220 goto exception; 221 } 222 out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31]; 223 vbit -= 5; 224 } 225 assert (0 == vbit); 226 if (wpos < out_size) { 227 out[wpos] = '\0'; 228 } 229 230 ret_val = JS_NewString(ctx, (char *) out); 231 232 done: 233 if (NULL != out) { 234 free(out); 235 } 236 return ret_val; 237 exception: 238 ret_val = JS_EXCEPTION; 239 goto done; 240 } 241 242 static JSValue js_talercrypto_decode_crock(JSContext *ctx, JSValue this_val, 243 int argc, JSValueConst *argv) 244 { 245 size_t rpos; 246 size_t wpos; 247 unsigned int bits; 248 unsigned int vbit; 249 int ret; 250 int shift; 251 size_t enclen; 252 size_t encoded_len; 253 const char *enc; 254 JSValue ret_val = JS_UNDEFINED; 255 unsigned char *uout = NULL; 256 size_t out_size; 257 JSValue abuf; 258 259 enc = JS_ToCStringLen2(ctx, &enclen, argv[0], FALSE); 260 if (!enc) { 261 goto exception; 262 } 263 264 if (enclen == 0) { 265 ret_val = make_js_ta_copy(ctx, NULL, 0); 266 goto done; 267 } 268 if (enclen > SIZE_MAX / 5) { 269 JS_ThrowRangeError(ctx, "encoded input is too large"); 270 goto exception; 271 } 272 out_size = (enclen * 5) / 8; 273 encoded_len = out_size * 8; 274 uout = malloc(out_size); 275 if (out_size != 0 && !uout) { 276 JS_ThrowOutOfMemory(ctx); 277 goto exception; 278 } 279 wpos = out_size; 280 rpos = enclen; 281 if ((encoded_len % 5) > 0) 282 { 283 vbit = encoded_len % 5; /* padding! */ 284 shift = 5 - vbit; 285 bits = (ret = getValue__ (enc[--rpos])) >> shift; 286 if (ret < 0 || (ret & ((1U << shift) - 1)) != 0) { 287 JS_ThrowTypeError(ctx, "invalid padding bits in encoding"); 288 goto exception; 289 } 290 } 291 else 292 { 293 vbit = 5; 294 shift = 0; 295 bits = (ret = getValue__ (enc[--rpos])); 296 } 297 if ((encoded_len + shift) / 5 != enclen) { 298 JS_ThrowTypeError(ctx, "wrong encoded length"); 299 goto exception; 300 } 301 302 if (-1 == ret) { 303 JS_ThrowTypeError(ctx, "invalid character in encoding"); 304 goto exception; 305 } 306 while (wpos > 0) 307 { 308 if (0 == rpos) 309 { 310 goto exception; 311 } 312 bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits; 313 if (-1 == ret) { 314 JS_ThrowTypeError(ctx, "invalid character in encoding"); 315 goto exception; 316 } 317 vbit += 5; 318 if (vbit >= 8) 319 { 320 uout[--wpos] = (unsigned char) bits; 321 bits >>= 8; 322 vbit -= 8; 323 } 324 } 325 if ((0 != rpos) || (0 != vbit)) { 326 JS_ThrowTypeError(ctx, "rpos or vbit not zero"); 327 goto exception; 328 } 329 abuf = JS_NewArrayBufferCopy(ctx, uout, out_size); 330 if (JS_IsException(abuf)) { 331 goto exception; 332 } 333 ret_val = JS_NewTypedArraySimple(ctx, abuf, 1); 334 done: 335 JS_FreeCString(ctx, enc); 336 if (uout) { 337 free(uout); 338 } 339 return ret_val; 340 exception: 341 ret_val = JS_EXCEPTION; 342 goto done; 343 } 344 345 uint8_t *expect_fixed_buffer(JSContext *ctx, 346 JSValue val, size_t len, 347 const char *msg) 348 { 349 uint8_t *buf; 350 size_t sz; 351 352 buf = JS_GetArrayBuffer(ctx, &sz, val); 353 if (!buf) { 354 return NULL; 355 } 356 if (sz != len) { 357 JS_ThrowTypeError(ctx, "invalid length for %s", msg); 358 return NULL; 359 } 360 return buf; 361 } 362 363 int 364 expect_mpi(JSContext *ctx, 365 JSValue val, 366 const char *msg, 367 mbedtls_mpi *ret_mpi) 368 { 369 uint8_t *buf; 370 size_t sz; 371 372 buf = JS_GetArrayBuffer(ctx, &sz, val); 373 if (!buf) { 374 return -1; 375 } 376 if (0 != mbedtls_mpi_read_binary(ret_mpi, buf, sz)) { 377 return -1; 378 } 379 return 0; 380 } 381 382 #define CHECK(x) do { if (!(x)) { abort(); } } while (0) 383 384 typedef struct { 385 mbedtls_mpi N; 386 mbedtls_mpi e; 387 } RsaPub; 388 389 typedef uint8_t BlindingKeySecret[32]; 390 typedef uint8_t HashCode[64]; 391 392 int 393 rsa_public_key_decode(RsaPub *pkey, uint8_t *inbuf, size_t inbuf_len) 394 { 395 size_t sz; 396 uint8_t *p; /* read pointer */ 397 size_t mod_len; 398 size_t exp_len; 399 int ret; 400 401 CHECK(NULL != pkey); 402 if (inbuf_len < 4) { 403 ret = -1; 404 goto cleanup; 405 } 406 p = inbuf; 407 uint16_t encoded_len; 408 409 memcpy(&encoded_len, p, sizeof encoded_len); 410 mod_len = ntohs(encoded_len); 411 p += sizeof(uint16_t); 412 memcpy(&encoded_len, p, sizeof encoded_len); 413 exp_len = ntohs(encoded_len); 414 sz = 4 + mod_len + exp_len; 415 if (sz != inbuf_len || 416 mod_len < 128 || mod_len > TART_MAX_RSA_MODULUS_BYTES || 417 exp_len == 0 || exp_len > TART_MAX_RSA_EXPONENT_BYTES) { 418 ret = -1; 419 goto cleanup; 420 } 421 p += sizeof(uint16_t); 422 MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&pkey->N, p, mod_len)); 423 p += mod_len; 424 MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&pkey->e, p, exp_len)); 425 if (mbedtls_mpi_cmp_int(&pkey->N, 3) <= 0 || 426 mbedtls_mpi_get_bit(&pkey->N, 0) == 0 || 427 mbedtls_mpi_cmp_int(&pkey->e, 3) < 0 || 428 mbedtls_mpi_get_bit(&pkey->e, 0) == 0 || 429 mbedtls_mpi_cmp_mpi(&pkey->e, &pkey->N) >= 0) { 430 ret = -1; 431 goto cleanup; 432 } 433 434 cleanup: 435 if (ret != 0) { 436 mbedtls_mpi_free(&pkey->N); 437 mbedtls_mpi_free(&pkey->e); 438 } 439 return ret; 440 } 441 442 int 443 expect_rsa_pub(JSContext *ctx, 444 JSValue val, 445 const char *msg, 446 RsaPub *ret_rsa_pub) 447 { 448 uint8_t *rsa_enc; 449 size_t rsa_enc_len; 450 int ret = -1; 451 452 rsa_enc = JS_GetArrayBuffer(ctx, &rsa_enc_len, val); 453 if (!rsa_enc) { 454 goto cleanup; 455 } 456 if (0 != rsa_public_key_decode(ret_rsa_pub, rsa_enc, rsa_enc_len)) { 457 JS_ThrowTypeError(ctx, "rsa pubkey"); 458 goto cleanup; 459 } 460 ret = 0; 461 cleanup: 462 return ret; 463 } 464 465 #define REQUIRE(cond, _label) do { if (!(cond)) { goto _label; } } while (0) 466 467 468 static JSValue make_js_ta_copy(JSContext *ctx, uint8_t *data, size_t size) 469 { 470 JSValue array_buf; 471 472 array_buf = JS_NewArrayBufferCopy(ctx, data, size); 473 if (JS_IsException(array_buf)) { 474 return JS_EXCEPTION; 475 } 476 return JS_NewTypedArraySimple(ctx, array_buf, 1); 477 } 478 479 /** 480 * Make a JS typed array from an mbedtls MPI. 481 */ 482 static JSValue make_js_ta_mpi(JSContext *ctx, const mbedtls_mpi *v) 483 { 484 JSValue array_buf; 485 size_t sz; 486 uint8_t *buf = NULL; 487 JSValue ret_val; 488 489 sz = mbedtls_mpi_size(v); 490 buf = malloc(sz); 491 if (!buf) { 492 ret_val = JS_EXCEPTION; 493 goto cleanup; 494 } 495 496 if (0 != mbedtls_mpi_write_binary(v, buf, sz)) { 497 ret_val = JS_EXCEPTION; 498 goto cleanup; 499 } 500 501 // FIXME(#perf): Don't copy 502 array_buf = JS_NewArrayBufferCopy(ctx, buf, sz); 503 if (JS_IsException(array_buf)) { 504 ret_val = JS_EXCEPTION; 505 goto cleanup; 506 } 507 ret_val = JS_NewTypedArraySimple(ctx, array_buf, 1); 508 cleanup: 509 if (buf) { 510 free(buf); 511 } 512 return ret_val; 513 } 514 515 static JSValue js_talercrypto_hash(JSContext *ctx, JSValue this_val, 516 int argc, JSValueConst *argv) 517 { 518 size_t size; 519 uint8_t *buf; 520 unsigned char h[crypto_hash_BYTES]; 521 522 buf = JS_GetArrayBuffer(ctx, &size, argv[0]); 523 if (!buf) { 524 return JS_EXCEPTION; 525 } 526 crypto_hash_sha512(h, buf, size); 527 528 return make_js_ta_copy(ctx, h, crypto_hash_BYTES); 529 } 530 531 static JSValue js_talercrypto_hash_argon2id(JSContext *ctx, JSValue this_val, 532 int argc, JSValueConst *argv) 533 { 534 size_t pw_len; 535 size_t salt_len; 536 uint32_t iters; 537 uint32_t mem_size; 538 uint32_t hash_len; 539 uint8_t *pw; 540 uint8_t *salt; 541 uint8_t *hash = NULL; 542 543 JSValue ret_val; 544 545 // password: Uint8Array 546 pw = JS_GetArrayBuffer(ctx, &pw_len, argv[0]); 547 if (!pw) { 548 goto exception; 549 } 550 551 // salt: Uint8Array 552 salt = JS_GetArrayBuffer(ctx, &salt_len, argv[1]); 553 if (!salt) { 554 goto exception; 555 } 556 if (salt_len != crypto_pwhash_SALTBYTES) { 557 JS_ThrowTypeError(ctx, "invalid salt size"); 558 goto exception; 559 } 560 561 // iterations: number 562 if (0 != JS_ToUint32(ctx, &iters, argv[2])) { 563 goto exception; 564 } 565 566 // memorySize: number (kibibytes) 567 if (0 != JS_ToUint32(ctx, &mem_size, argv[3])) { 568 goto exception; 569 } 570 571 // hashLength: number 572 if (0 != JS_ToUint32(ctx, &hash_len, argv[4])) { 573 goto exception; 574 } 575 576 if (iters < crypto_pwhash_OPSLIMIT_MIN || 577 iters > crypto_pwhash_OPSLIMIT_MAX || 578 iters > crypto_pwhash_OPSLIMIT_MODERATE) { 579 JS_ThrowRangeError(ctx, "iterations out of range"); 580 goto exception; 581 } 582 if (hash_len < crypto_pwhash_BYTES_MIN || 583 hash_len > crypto_pwhash_BYTES_MAX || 584 hash_len > TART_MAX_ARGON2_OUTPUT) { 585 JS_ThrowRangeError(ctx, "hash length out of range"); 586 goto exception; 587 } 588 589 // Check for overflow when converting memory size to bytes 590 if (((unsigned long long)mem_size * 1024) > UINT32_MAX) { 591 JS_ThrowTypeError(ctx, "mem_size too large"); 592 goto exception; 593 } 594 595 if ((size_t) mem_size * 1024 < crypto_pwhash_MEMLIMIT_MIN || 596 (size_t) mem_size * 1024 > crypto_pwhash_MEMLIMIT_MAX || 597 (size_t) mem_size * 1024 > crypto_pwhash_MEMLIMIT_MODERATE) { 598 JS_ThrowRangeError(ctx, "memory size out of range"); 599 goto exception; 600 } 601 602 hash = malloc(hash_len); 603 if (NULL == hash) { 604 JS_ThrowOutOfMemory(ctx); 605 goto exception; 606 } 607 608 if (crypto_pwhash(hash, 609 hash_len, 610 (const char*) pw, 611 pw_len, 612 salt, 613 iters, 614 mem_size * 1024, 615 crypto_pwhash_ALG_ARGON2ID13) != 0) { 616 JS_ThrowInternalError(ctx, "crypto_pwhash() call failed"); 617 goto exception; 618 } 619 ret_val = make_js_ta_copy(ctx, hash, hash_len); 620 done: 621 if (NULL != hash) { 622 sodium_memzero(hash, hash_len); 623 free(hash); 624 } 625 return ret_val; 626 exception: 627 ret_val = JS_EXCEPTION; 628 goto done; 629 } 630 631 static JSValue js_talercrypto_eddsa_key_get_public(JSContext *ctx, JSValue this_val, 632 int argc, JSValueConst *argv) 633 { 634 JSValue ret; 635 uint8_t *buf; 636 unsigned char pk[crypto_sign_PUBLICKEYBYTES]; 637 unsigned char sk[crypto_sign_SECRETKEYBYTES]; 638 639 buf = expect_fixed_buffer(ctx, argv[0], crypto_sign_SEEDBYTES, 640 "eddsa private key seed"); 641 642 if (!buf) { 643 goto exception; 644 } 645 646 crypto_sign_seed_keypair(pk, sk, buf); 647 648 ret = make_js_ta_copy(ctx, pk, crypto_sign_PUBLICKEYBYTES); 649 done: 650 sodium_memzero(sk, sizeof sk); 651 return ret; 652 exception: 653 ret = JS_EXCEPTION; 654 goto done; 655 } 656 657 static JSValue js_talercrypto_ecdhe_key_get_public(JSContext *ctx, JSValue this_val, 658 int argc, JSValueConst *argv) 659 { 660 JSValue ret; 661 uint8_t *buf; 662 unsigned char pk[crypto_scalarmult_BYTES]; 663 664 buf = expect_fixed_buffer(ctx, argv[0], crypto_sign_SEEDBYTES, "ecdh private key seed"); 665 666 if (!buf) { 667 goto exception; 668 } 669 670 if (0 != crypto_scalarmult_base(pk, buf)) { 671 goto exception; 672 } 673 674 ret = make_js_ta_copy(ctx, pk, crypto_sign_PUBLICKEYBYTES); 675 done: 676 buf = NULL; 677 return ret; 678 exception: 679 ret = JS_EXCEPTION; 680 goto done; 681 } 682 683 /** 684 * (msg, priv) => sig 685 */ 686 static JSValue js_talercrypto_eddsa_sign(JSContext *ctx, JSValue this_val, 687 int argc, JSValueConst *argv) 688 { 689 unsigned char *seed; 690 size_t seed_size; 691 unsigned char *data; 692 size_t data_size; 693 unsigned char sk[crypto_sign_SECRETKEYBYTES]; 694 unsigned char pk[crypto_sign_PUBLICKEYBYTES]; 695 unsigned char sig[64]; 696 int res; 697 JSValue ret = JS_EXCEPTION; 698 699 data = JS_GetArrayBuffer(ctx, &data_size, argv[0]); 700 if (!data) { 701 goto done; 702 } 703 704 seed = JS_GetArrayBuffer(ctx, &seed_size, argv[1]); 705 if (!seed) { 706 goto done; 707 } 708 if (seed_size != 32) { 709 ret = JS_ThrowTypeError(ctx, "invalid private key seed size"); 710 goto done; 711 } 712 713 if (0 != crypto_sign_seed_keypair(pk, sk, seed)) { 714 goto done; 715 } 716 717 res = crypto_sign_detached((uint8_t *)sig, 718 NULL, 719 (uint8_t *)data, 720 data_size, 721 sk); 722 if (res != 0) { 723 goto done; 724 } 725 ret = make_js_ta_copy(ctx, sig, 64); 726 done: 727 sodium_memzero(sk, sizeof sk); 728 return ret; 729 } 730 731 /** 732 * (msg, sig, pub) -> bool 733 */ 734 static JSValue js_talercrypto_eddsa_verify(JSContext *ctx, JSValue this_val, 735 int argc, JSValueConst *argv) 736 { 737 unsigned char *msg; 738 size_t msg_size; 739 unsigned char *sig; 740 size_t sig_size; 741 unsigned char *pub; 742 size_t pub_size; 743 int res; 744 745 msg = JS_GetArrayBuffer(ctx, &msg_size, argv[0]); 746 if (!msg) { 747 return JS_EXCEPTION; 748 } 749 sig = JS_GetArrayBuffer(ctx, &sig_size, argv[1]); 750 if (!sig) { 751 return JS_EXCEPTION; 752 } 753 if (sig_size != 64) { 754 return JS_ThrowTypeError(ctx, "invalid signature size"); 755 } 756 pub = JS_GetArrayBuffer(ctx, &pub_size, argv[2]); 757 if (!pub) { 758 return JS_EXCEPTION; 759 } 760 if (pub_size != 32) { 761 return JS_ThrowTypeError(ctx, "invalid public key size"); 762 } 763 764 res = crypto_sign_verify_detached (sig, msg, msg_size, pub); 765 return (res == 0) ? JS_TRUE : JS_FALSE; 766 } 767 768 /** 769 * Returns 0 on success. 770 */ 771 static int 772 kdf(void *okm, size_t okm_len, 773 const void *ikm, size_t ikm_len, 774 const void *salt, size_t salt_len, 775 const void *info, size_t info_len) 776 { 777 const mbedtls_md_info_t *md_extract; 778 const mbedtls_md_info_t *md_expand; 779 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 780 unsigned char prk[MBEDTLS_MD_MAX_SIZE]; 781 782 md_extract = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); 783 md_expand = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); 784 if (NULL == md_extract) { 785 return -1; 786 } 787 if (NULL == md_expand) { 788 return -1; 789 } 790 791 ret = mbedtls_hkdf_extract(md_extract, salt, salt_len, ikm, ikm_len, prk); 792 793 if (ret != 0) { 794 return ret; 795 } 796 797 ret = mbedtls_hkdf_expand(md_expand, prk, mbedtls_md_get_size(md_extract), 798 info, info_len, okm, okm_len); 799 return ret; 800 } 801 802 /** 803 * (outLen, ikm, salt?, info?) -> output 804 */ 805 static JSValue js_talercrypto_kdf(JSContext *ctx, JSValue this_val, 806 int argc, JSValueConst *argv) 807 { 808 size_t salt_len; 809 size_t ikm_len; 810 size_t info_len; 811 size_t okm_len; 812 uint8_t *salt; 813 uint8_t *ikm; 814 uint8_t *info; 815 uint8_t *okm = NULL; 816 uint8_t empty_okm; 817 uint32_t out_bytes; 818 JSValue ret_val; 819 int ret; 820 821 if (0 != JS_ToUint32(ctx, &out_bytes, argv[0])) { 822 goto exception; 823 } 824 okm_len = out_bytes; 825 if (okm_len > 255 * 32) { 826 JS_ThrowRangeError(ctx, "HKDF output is too large"); 827 goto exception; 828 } 829 830 ikm = JS_GetArrayBuffer(ctx, &ikm_len, argv[1]); 831 if (!ikm) { 832 goto exception; 833 } 834 835 if (JS_IsUndefined(argv[2])) { 836 salt = NULL; 837 salt_len = 0; 838 } else { 839 salt = JS_GetArrayBuffer(ctx, &salt_len, argv[2]); 840 if (!salt) { 841 goto exception; 842 } 843 } 844 845 if (JS_IsUndefined(argv[3])) { 846 info = NULL; 847 info_len = 0; 848 } else { 849 info = JS_GetArrayBuffer(ctx, &info_len, argv[3]); 850 if (!info) { 851 goto exception; 852 } 853 } 854 855 if (okm_len != 0) { 856 okm = malloc(okm_len); 857 if (!okm) { 858 JS_ThrowOutOfMemory(ctx); 859 goto exception; 860 } 861 } 862 863 ret = kdf(okm_len == 0 ? &empty_okm : okm, okm_len, 864 ikm, ikm_len, salt, salt_len, info, info_len); 865 866 if (ret != 0) { 867 JS_ThrowInternalError(ctx, "kdf() call failed"); 868 goto exception; 869 } 870 ret_val = make_js_ta_copy(ctx, okm, okm_len); 871 done: 872 if (NULL != okm) { 873 sodium_memzero(okm, okm_len); 874 free(okm); 875 } 876 return ret_val; 877 exception: 878 ret_val = JS_EXCEPTION; 879 goto done; 880 } 881 882 /** 883 * (ecdhePriv, eddsaPub) -> keyMaterial 884 */ 885 static JSValue js_talercrypto_kx_ecdh_eddsa(JSContext *ctx, JSValue this_val, 886 int argc, JSValueConst *argv) 887 { 888 JSValue ret_val; 889 uint8_t p[crypto_scalarmult_BYTES]; 890 uint8_t *ecdh_priv; 891 uint8_t *eddsa_pub; 892 uint8_t curve25510_pk[crypto_scalarmult_BYTES]; 893 uint8_t key_material[crypto_hash_BYTES]; 894 895 ecdh_priv = expect_fixed_buffer(ctx, argv[0], 32, "ecdhe priv"); 896 REQUIRE(ecdh_priv, exception); 897 898 eddsa_pub = expect_fixed_buffer(ctx, argv[1], 32, "eddsa pub"); 899 REQUIRE(eddsa_pub, exception); 900 901 if (0 != crypto_sign_ed25519_pk_to_curve25519(curve25510_pk, eddsa_pub)) { 902 goto exception; 903 } 904 if (0 != crypto_scalarmult(p, ecdh_priv, curve25510_pk)) { 905 goto exception; 906 } 907 if (0 != crypto_hash(key_material, p, 32)) { 908 JS_ThrowTypeError(ctx, "hashing failed"); 909 goto exception; 910 } 911 ret_val = make_js_ta_copy(ctx, key_material, crypto_hash_BYTES); 912 done: 913 sodium_memzero(p, sizeof p); 914 sodium_memzero(key_material, sizeof key_material); 915 return ret_val; 916 exception: 917 ret_val = JS_EXCEPTION; 918 goto done; 919 } 920 921 /** 922 * (eddsaPriv, ecdhePub) -> keyMaterial 923 */ 924 static JSValue js_talercrypto_kx_eddsa_ecdh(JSContext *ctx, JSValue this_val, 925 int argc, JSValueConst *argv) 926 { 927 JSValue ret_val; 928 uint8_t *priv; 929 uint8_t *pub; 930 uint8_t hc[crypto_hash_BYTES]; 931 uint8_t a[crypto_scalarmult_SCALARBYTES]; 932 uint8_t p[crypto_scalarmult_BYTES]; 933 uint8_t key_material[crypto_hash_BYTES]; 934 935 priv = expect_fixed_buffer(ctx, argv[0], 32, "eddsa priv"); 936 REQUIRE(priv, exception); 937 pub = expect_fixed_buffer(ctx, argv[1], 32, "ecdh pub"); 938 REQUIRE(pub, exception); 939 940 crypto_hash(hc, priv, 32); 941 memcpy (a, &hc, 32); 942 if (0 != crypto_scalarmult(p, a, pub)) { 943 goto exception; 944 } 945 crypto_hash(key_material, p, crypto_scalarmult_BYTES); 946 ret_val = make_js_ta_copy(ctx, key_material, crypto_hash_BYTES); 947 done: 948 sodium_memzero(hc, sizeof hc); 949 sodium_memzero(a, sizeof a); 950 sodium_memzero(p, sizeof p); 951 sodium_memzero(key_material, sizeof key_material); 952 return ret_val; 953 exception: 954 ret_val = JS_EXCEPTION; 955 goto done; 956 } 957 958 static int 959 kdf_mod_mpi(mbedtls_mpi *r, 960 const mbedtls_mpi *n, 961 const void *xts, size_t xts_len, 962 const void *skm, size_t skm_len, 963 const char *ctx) 964 { 965 int ret = -1; 966 size_t nbits; 967 uint32_t ctr; 968 size_t ctxlen = strlen(ctx); 969 size_t my_ctx_len = ctxlen + 2; 970 unsigned char *my_ctx = NULL; 971 uint8_t *buf = NULL; 972 size_t bsize; 973 974 my_ctx = malloc(my_ctx_len); 975 if (!my_ctx) { 976 goto cleanup; 977 } 978 memcpy(my_ctx, ctx, ctxlen); 979 980 nbits = mbedtls_mpi_bitlen(n); 981 if (nbits == 0) { 982 goto cleanup; 983 } 984 bsize = (nbits + 7) / 8; 985 buf = malloc(bsize); 986 if (!buf) { 987 goto cleanup; 988 } 989 for (ctr = 0; ctr <= UINT16_MAX; ctr++) { 990 uint16_t ctr_nbo = htons((uint16_t) ctr); 991 992 memcpy(my_ctx + ctxlen, &ctr_nbo, sizeof ctr_nbo); 993 if (0 != kdf(buf, bsize, 994 skm, skm_len, 995 xts, xts_len, 996 my_ctx, my_ctx_len)) { 997 goto cleanup; 998 } 999 if (0 != mbedtls_mpi_read_binary(r, buf, bsize)) { 1000 goto cleanup; 1001 } 1002 while (1) { 1003 size_t rlen = mbedtls_mpi_bitlen(r); 1004 if (rlen <= nbits) { 1005 break; 1006 } 1007 if (0 != mbedtls_mpi_set_bit(r, rlen - 1, 0)) { 1008 goto cleanup; 1009 } 1010 } 1011 /* We reject this FDH if either r > n and retry with another ctr */ 1012 if (0 > mbedtls_mpi_cmp_mpi (r, n)) { 1013 ret = 0; 1014 break; 1015 } 1016 mbedtls_mpi_free (r); 1017 mbedtls_mpi_init (r); 1018 } 1019 cleanup: 1020 if (buf) { 1021 sodium_memzero(buf, bsize); 1022 } 1023 free(buf); 1024 free(my_ctx); 1025 return ret; 1026 } 1027 1028 1029 /** 1030 * Test for malicious RSA key. 1031 * 1032 * Assuming n is an RSA modulous and r is generated using a call to 1033 * GNUNET_CRYPTO_kdf_mod_mpi, if gcd(r,n) != 1 then n must be a 1034 * malicious RSA key designed to deanomize the user. 1035 * 1036 * @param r KDF result 1037 * @param n RSA modulus 1038 * @return 0 if gcd(r,n) = 1, 1 means RSA key is malicious, negative is syserror 1039 */ 1040 static int 1041 rsa_gcd_validate (mbedtls_mpi *r, 1042 const mbedtls_mpi *n) 1043 { 1044 mbedtls_mpi g; 1045 int ret; 1046 1047 mbedtls_mpi_init(&g); 1048 MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(&g, r, n)); 1049 1050 if (mbedtls_mpi_cmp_int(&g, 1) == 0) { 1051 ret = 0; 1052 } else { 1053 ret = 1; 1054 } 1055 1056 cleanup: 1057 mbedtls_mpi_free(&g); 1058 return ret; 1059 } 1060 1061 1062 int 1063 rsa_blinding_key_derive(mbedtls_mpi *r, 1064 const RsaPub *pkey, 1065 const BlindingKeySecret *bks) 1066 { 1067 /* Trusts bks' randomness more */ 1068 const char *xts = "Blinding KDF extractor HMAC key"; 1069 1070 if (0 != kdf_mod_mpi(r, 1071 &pkey->N, 1072 xts, strlen(xts), 1073 bks, sizeof(*bks), 1074 "Blinding KDF")) { 1075 return -1; 1076 } 1077 1078 if (0 != rsa_gcd_validate (r, &pkey->N)) { 1079 return -1; 1080 } 1081 1082 return 0; 1083 } 1084 1085 int 1086 rsa_public_key_encode(const RsaPub *pkey, uint8_t **outbuf, size_t *outbuf_len) 1087 { 1088 size_t sz; 1089 uint8_t *buf; 1090 uint8_t *p; /* write pointer */ 1091 size_t mod_len; 1092 size_t exp_len; 1093 int ret; 1094 1095 *outbuf = NULL; 1096 *outbuf_len = 0; 1097 1098 mod_len = mbedtls_mpi_size(&pkey->N); 1099 exp_len = mbedtls_mpi_size(&pkey->e); 1100 if (mod_len > UINT16_MAX || exp_len > UINT16_MAX) { 1101 return -1; 1102 } 1103 sz = 2 + 2 + exp_len + mod_len; 1104 buf = malloc(sz); 1105 if (!buf) { 1106 return -1; 1107 } 1108 1109 p = buf; 1110 uint16_t encoded_len = htons((uint16_t) mod_len); 1111 memcpy(p, &encoded_len, sizeof encoded_len); 1112 p += sizeof (uint16_t); 1113 encoded_len = htons((uint16_t) exp_len); 1114 memcpy(p, &encoded_len, sizeof encoded_len); 1115 p += sizeof (uint16_t); 1116 MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&pkey->N, p, mod_len)); 1117 p += mod_len; 1118 MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&pkey->e, p, exp_len)); 1119 1120 *outbuf = buf; 1121 *outbuf_len = sz; 1122 1123 cleanup: 1124 if (0 != ret) { 1125 free(buf); 1126 } 1127 return ret; 1128 } 1129 1130 1131 void 1132 rsa_public_key_init(RsaPub *pkey) 1133 { 1134 CHECK(NULL != pkey); 1135 mbedtls_mpi_init(&pkey->e); 1136 mbedtls_mpi_init(&pkey->N); 1137 } 1138 1139 void 1140 rsa_public_key_free(RsaPub *pkey) 1141 { 1142 if (!pkey) { 1143 return; 1144 } 1145 mbedtls_mpi_free(&pkey->e); 1146 mbedtls_mpi_free(&pkey->N); 1147 } 1148 1149 1150 int 1151 rsa_full_domain_hash (mbedtls_mpi *r, const RsaPub *pkey, 1152 const HashCode *hash) 1153 { 1154 uint8_t *xts; 1155 size_t xts_len; 1156 1157 /* We key with the public denomination key as a homage to RSA-PSS by 1158 Mihir Bellare and Phillip Rogaway. Doing this lowers the degree 1159 of the hypothetical polyomial-time attack on RSA-KTI created by a 1160 polynomial-time one-more forgary attack. Yey seeding! */ 1161 if (0 != rsa_public_key_encode(pkey, &xts, &xts_len)) { 1162 return -1; 1163 } 1164 1165 if (0 != kdf_mod_mpi(r, 1166 &pkey->N, 1167 xts, xts_len, 1168 hash, sizeof(*hash), 1169 "RSA-FDA FTpsW!")) { 1170 free(xts); 1171 return -1; 1172 } 1173 free(xts); 1174 if (0 == rsa_gcd_validate (r, &pkey->N)) { 1175 return 0; 1176 } 1177 return 1; 1178 } 1179 1180 static int 1181 rsa_blind(const HashCode *hash, 1182 const BlindingKeySecret *bks, 1183 const RsaPub *pkey, 1184 uint8_t **buf, 1185 size_t *buf_size) 1186 { 1187 mbedtls_mpi bkey, data, r_e, data_r_e; 1188 size_t outsize; 1189 uint8_t *outbuf; 1190 int ret; 1191 1192 CHECK(buf != NULL); 1193 CHECK(buf_size != NULL); 1194 1195 *buf = NULL; 1196 *buf_size = 0; 1197 1198 mbedtls_mpi_init(&bkey); 1199 mbedtls_mpi_init(&data); 1200 mbedtls_mpi_init(&r_e); 1201 mbedtls_mpi_init(&data_r_e); 1202 1203 MBEDTLS_MPI_CHK(rsa_full_domain_hash (&data, pkey, hash)); 1204 MBEDTLS_MPI_CHK(rsa_blinding_key_derive (&bkey, pkey, bks)); 1205 MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&r_e, &bkey, &pkey->e, &pkey->N, NULL)); 1206 MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&data_r_e, &data, &r_e)); 1207 MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&data_r_e, &data_r_e, &pkey->N)); 1208 1209 outsize = (mbedtls_mpi_bitlen(&data_r_e) + 7) / 8; 1210 outbuf = malloc(outsize); 1211 if (outsize != 0 && !outbuf) { 1212 ret = -1; 1213 goto cleanup; 1214 } 1215 1216 MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&data_r_e, outbuf, outsize)); 1217 1218 *buf = outbuf; 1219 *buf_size = outsize; 1220 ret = 0; 1221 1222 cleanup: 1223 mbedtls_mpi_free(&data); 1224 mbedtls_mpi_free(&bkey); 1225 mbedtls_mpi_free(&r_e); 1226 mbedtls_mpi_free(&data_r_e); 1227 return ret; 1228 } 1229 1230 int 1231 rsa_unblind (const mbedtls_mpi *sig_blinded, 1232 const BlindingKeySecret *bks, 1233 const RsaPub *pkey, 1234 mbedtls_mpi *sig_ret) 1235 { 1236 mbedtls_mpi bkey, r_inv, ubsig; 1237 int ret; 1238 1239 mbedtls_mpi_init(&bkey); 1240 mbedtls_mpi_init(&r_inv); 1241 mbedtls_mpi_init(&ubsig); 1242 1243 MBEDTLS_MPI_CHK(rsa_blinding_key_derive (&bkey, pkey, bks)); 1244 MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&r_inv, &bkey, &pkey->N)); 1245 MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ubsig, sig_blinded, &r_inv)); 1246 MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(sig_ret, &ubsig, &pkey->N)); 1247 1248 cleanup: 1249 mbedtls_mpi_free(&bkey); 1250 mbedtls_mpi_free(&r_inv); 1251 mbedtls_mpi_free(&ubsig); 1252 return ret; 1253 } 1254 1255 1256 int 1257 rsa_verify(const HashCode *hash, 1258 const mbedtls_mpi *sig, 1259 const RsaPub *pkey) 1260 { 1261 mbedtls_mpi r; 1262 mbedtls_mpi sig_e; 1263 int ret; 1264 1265 mbedtls_mpi_init(&r); 1266 mbedtls_mpi_init(&sig_e); 1267 1268 if (mbedtls_mpi_cmp_int(sig, 0) < 0 || 1269 mbedtls_mpi_cmp_mpi(sig, &pkey->N) >= 0) { 1270 ret = -1; 1271 goto cleanup; 1272 } 1273 1274 /* Can fail if RSA key is malicious since rsa_gcd_validate failed here. 1275 * It should have failed during GNUNET_CRYPTO_rsa_blind too though, 1276 * so the exchange is being malicious in an unfamilair way, maybe 1277 * just trying to crash us. Arguably, we've only an internal error 1278 * though because we should've detected this in our previous call 1279 * to GNUNET_CRYPTO_rsa_unblind. */// 1280 MBEDTLS_MPI_CHK(rsa_full_domain_hash(&r, pkey, hash)); 1281 1282 MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&sig_e, sig, &pkey->e, &pkey->N, NULL)); 1283 1284 if (0 != mbedtls_mpi_cmp_mpi(&r, &sig_e)) { 1285 ret = -1; 1286 } else { 1287 ret = 0; 1288 } 1289 1290 cleanup: 1291 mbedtls_mpi_free(&r); 1292 mbedtls_mpi_free(&sig_e); 1293 return ret; 1294 } 1295 1296 1297 /** 1298 * (hmsg, bks, rsaPub) -> blinded 1299 */ 1300 static JSValue js_talercrypto_rsa_blind(JSContext *ctx, JSValueConst this_val, 1301 int argc, JSValueConst *argv) 1302 { 1303 HashCode *hmsg; 1304 BlindingKeySecret *bks; 1305 uint8_t *rsa_enc; 1306 size_t rsa_enc_len; 1307 RsaPub rsa_pub; 1308 JSValue ret_val = JS_UNDEFINED; 1309 uint8_t *out_buf = NULL; 1310 size_t out_len; 1311 1312 rsa_public_key_init(&rsa_pub); 1313 1314 hmsg = (HashCode *) expect_fixed_buffer(ctx, argv[0], 64, "hmsg"); 1315 if (!hmsg) { 1316 ret_val = JS_EXCEPTION; 1317 goto cleanup; 1318 } 1319 bks = (BlindingKeySecret *) expect_fixed_buffer(ctx, argv[1], 32, "bks"); 1320 if (!bks) { 1321 ret_val = JS_EXCEPTION; 1322 goto cleanup; 1323 } 1324 rsa_enc = JS_GetArrayBuffer(ctx, &rsa_enc_len, argv[2]); 1325 if (!rsa_enc) { 1326 ret_val = JS_EXCEPTION; 1327 goto cleanup; 1328 } 1329 if (0 != rsa_public_key_decode(&rsa_pub, rsa_enc, rsa_enc_len)) { 1330 ret_val = JS_ThrowTypeError(ctx, "rsa pubkey"); 1331 goto cleanup; 1332 } 1333 if (0 != rsa_blind(hmsg, bks, &rsa_pub, &out_buf, &out_len)) { 1334 ret_val = JS_ThrowInternalError(ctx, "blinding failed"); 1335 goto cleanup; 1336 } 1337 1338 ret_val = make_js_ta_copy(ctx, out_buf, out_len); 1339 cleanup: 1340 if (NULL != out_buf) { 1341 free(out_buf); 1342 out_buf = NULL; 1343 } 1344 rsa_public_key_free(&rsa_pub); 1345 return ret_val; 1346 } 1347 1348 /** 1349 * (blindSig, rsaPub, bks) -> ubsig 1350 */ 1351 static JSValue js_talercrypto_rsa_unblind(JSContext *ctx, JSValueConst this_val, 1352 int argc, JSValueConst *argv) 1353 { 1354 JSValue ret_val = JS_UNDEFINED; 1355 mbedtls_mpi bsig; 1356 mbedtls_mpi sig_ret; 1357 RsaPub rsa_pub; 1358 BlindingKeySecret *bks; 1359 1360 mbedtls_mpi_init(&bsig); 1361 mbedtls_mpi_init(&sig_ret); 1362 rsa_public_key_init(&rsa_pub); 1363 1364 if (0 != expect_mpi(ctx, argv[0], "blindSig", &bsig)) { 1365 ret_val = JS_EXCEPTION; 1366 goto cleanup; 1367 } 1368 if (0 != expect_rsa_pub(ctx, argv[1], "rsaPub", &rsa_pub)) { 1369 ret_val = JS_EXCEPTION; 1370 goto cleanup; 1371 } 1372 bks = (BlindingKeySecret *) expect_fixed_buffer(ctx, argv[2], 32, "bks"); 1373 if (!bks) { 1374 ret_val = JS_EXCEPTION; 1375 goto cleanup; 1376 } 1377 1378 if (0 != rsa_unblind(&bsig, bks, &rsa_pub, &sig_ret)) { 1379 ret_val = JS_ThrowInternalError(ctx, "unblinding failed"); 1380 goto cleanup; 1381 } 1382 1383 ret_val = make_js_ta_mpi(ctx, &sig_ret); 1384 1385 cleanup: 1386 mbedtls_mpi_free(&bsig); 1387 mbedtls_mpi_free(&sig_ret); 1388 rsa_public_key_free(&rsa_pub); 1389 return ret_val; 1390 } 1391 1392 /** 1393 * (hm, rsaSig, rsaPub) -> bool 1394 */ 1395 static JSValue js_talercrypto_rsa_verify(JSContext *ctx, JSValueConst this_val, 1396 int argc, JSValueConst *argv) 1397 { 1398 JSValue ret_val = JS_UNDEFINED; 1399 HashCode *hmsg; 1400 mbedtls_mpi sig; 1401 RsaPub rsa_pub; 1402 1403 mbedtls_mpi_init(&sig); 1404 rsa_public_key_init(&rsa_pub); 1405 1406 hmsg = (HashCode *) expect_fixed_buffer(ctx, argv[0], 64, "hmsg"); 1407 if (!hmsg) { 1408 ret_val = JS_EXCEPTION; 1409 goto cleanup; 1410 } 1411 1412 if (0 != expect_mpi(ctx, argv[1], "sig", &sig)) { 1413 ret_val = JS_EXCEPTION; 1414 goto cleanup; 1415 } 1416 1417 if (0 != expect_rsa_pub(ctx, argv[2], "rsaPub", &rsa_pub)) { 1418 ret_val = JS_EXCEPTION; 1419 goto cleanup; 1420 } 1421 1422 if (0 != rsa_verify(hmsg, &sig, &rsa_pub)) { 1423 ret_val = JS_FALSE; 1424 goto cleanup; 1425 } 1426 1427 ret_val = JS_TRUE; 1428 1429 cleanup: 1430 mbedtls_mpi_free(&sig); 1431 rsa_public_key_free(&rsa_pub); 1432 return ret_val; 1433 1434 } 1435 1436 // (ArrayBuffer | TypedArray) => string 1437 static JSValue js_decode_utf8(JSContext *ctx, JSValueConst this_val, 1438 int argc, JSValueConst *argv) 1439 { 1440 size_t psize; 1441 uint8_t *utf8_buf; 1442 JSValue ret_val; 1443 1444 utf8_buf = JS_GetArrayBuffer(ctx, &psize, argv[0]); 1445 if (NULL == utf8_buf) { 1446 goto exception; 1447 } 1448 return JS_NewStringLen(ctx, (char *) utf8_buf, psize); 1449 done: 1450 return ret_val; 1451 exception: 1452 ret_val = JS_EXCEPTION; 1453 goto done; 1454 } 1455 1456 static JSValue js_structured_clone(JSContext *ctx, JSValueConst this_val, 1457 int argc, JSValueConst *argv) 1458 { 1459 uint8_t *buf; 1460 size_t len; 1461 JSValue obj; 1462 1463 buf = JS_WriteObject(ctx, &len, argv[0], JS_WRITE_OBJ_REFERENCE); 1464 1465 if (NULL == buf) { 1466 return JS_EXCEPTION; 1467 } 1468 1469 obj = JS_ReadObject(ctx, buf, len, JS_WRITE_OBJ_REFERENCE); 1470 js_free(ctx, buf); 1471 return obj; 1472 } 1473 1474 static JSClassID js_hash_state_class_id; 1475 1476 typedef struct { 1477 crypto_hash_sha512_state h; 1478 int finalized; 1479 } TART_HashState; 1480 1481 1482 static void js_hash_state_finalizer(JSRuntime *rt, JSValue val) 1483 { 1484 TART_HashState *s = JS_GetOpaque(val, js_hash_state_class_id); 1485 /* Note: 's' can be NULL in case JS_SetOpaque() was not called */ 1486 js_free_rt(rt, s); 1487 } 1488 1489 static JSClassDef js_hash_state_class = { 1490 "HashState", 1491 .finalizer = js_hash_state_finalizer, 1492 }; 1493 1494 static JSValue js_talercrypto_hash_state_init(JSContext *ctx, JSValue this_val, 1495 int argc, JSValueConst *argv) 1496 { 1497 TART_HashState *hstate; 1498 JSValue obj; 1499 1500 hstate = js_malloc_rt(JS_GetRuntime(ctx), sizeof (TART_HashState)); 1501 if (!hstate) { 1502 obj = JS_EXCEPTION; 1503 goto done; 1504 } 1505 hstate->finalized = FALSE; 1506 obj = JS_NewObjectClass(ctx, js_hash_state_class_id); 1507 if (JS_IsException(obj)) { 1508 goto done; 1509 } 1510 crypto_hash_sha512_init(&hstate->h); 1511 JS_SetOpaque(obj, hstate); 1512 hstate = NULL; 1513 return obj; 1514 done: 1515 if (NULL != hstate) { 1516 js_free_rt(JS_GetRuntime(ctx), hstate); 1517 } 1518 return obj; 1519 } 1520 1521 static JSValue js_talercrypto_hash_state_update(JSContext *ctx, JSValue this_val, 1522 int argc, JSValueConst *argv) 1523 { 1524 JSValue state = argv[0]; 1525 JSValue data_val = argv[1]; 1526 TART_HashState *hstate; 1527 uint8_t *data; 1528 size_t data_len; 1529 1530 hstate = JS_GetOpaque(state, js_hash_state_class_id); 1531 1532 if (!hstate) { 1533 return JS_ThrowTypeError(ctx, "expected HashState"); 1534 } 1535 1536 if (hstate->finalized) { 1537 return JS_ThrowTypeError(ctx, "already finalized"); 1538 } 1539 1540 data = JS_GetArrayBuffer(ctx, &data_len, data_val); 1541 1542 if (!data) { 1543 return JS_EXCEPTION; 1544 } 1545 1546 if (0 != crypto_hash_sha512_update(&hstate->h, data, data_len)) { 1547 return JS_ThrowInternalError(ctx, "hashing failed"); 1548 } 1549 1550 return JS_UNDEFINED; 1551 } 1552 1553 static JSValue js_talercrypto_hash_state_finish(JSContext *ctx, JSValue this_val, 1554 int argc, JSValueConst *argv) 1555 { 1556 JSValue state = argv[0]; 1557 TART_HashState *hstate; 1558 uint8_t hashval[crypto_hash_sha512_BYTES]; 1559 1560 hstate = JS_GetOpaque(state, js_hash_state_class_id); 1561 1562 if (!hstate) { 1563 return JS_ThrowTypeError(ctx, "expected HashState"); 1564 } 1565 1566 if (hstate->finalized) { 1567 return JS_ThrowTypeError(ctx, "already finalized"); 1568 } 1569 1570 if (0 != crypto_hash_sha512_final(&hstate->h, hashval)) { 1571 return JS_ThrowInternalError(ctx, "hashing failed"); 1572 } 1573 1574 hstate->finalized = TRUE; 1575 1576 return make_js_ta_copy(ctx, hashval, crypto_hash_sha512_BYTES); 1577 } 1578 1579 static JSClassID js_sqlite3_database_class_id; 1580 static JSClassID js_sqlite3_statement_class_id; 1581 static JSClassID js_sqlite3_error_class_id; 1582 1583 static void js_sqlite3_database_finalizer(JSRuntime *rt, JSValue val) 1584 { 1585 sqlite3 *sqlite3_db; 1586 sqlite3_db = JS_GetOpaque(val, js_sqlite3_database_class_id); 1587 if (sqlite3_db) { 1588 (void)sqlite3_close_v2(sqlite3_db); 1589 } 1590 JS_SetOpaque(val, NULL); 1591 } 1592 1593 static void js_sqlite3_statement_finalizer(JSRuntime *rt, JSValue val) 1594 { 1595 sqlite3_stmt *stmt; 1596 1597 stmt = JS_GetOpaque(val, js_sqlite3_statement_class_id); 1598 if (stmt) { 1599 // The return code reports the last evaluation error, not whether the 1600 // statement resource was released, so there is nothing to retry here. 1601 (void)sqlite3_finalize(stmt); 1602 } 1603 JS_SetOpaque(val, NULL); 1604 } 1605 1606 static JSClassDef js_sqlite3_database_class = { 1607 .class_name = "Sqlite3Database", 1608 .finalizer = js_sqlite3_database_finalizer, 1609 }; 1610 1611 static JSClassDef js_sqlite3_statement_class = { 1612 .class_name = "Sqlite3Statement", 1613 .finalizer = js_sqlite3_statement_finalizer, 1614 }; 1615 1616 /* 1617 * Sqlite3Error instances use QuickJS' built-in Error class so that they get 1618 * normal Error behaviour and stack traces. This otherwise-unused class ID 1619 * gives each context a place to retain the Sqlite3Error prototype. 1620 */ 1621 static JSClassDef js_sqlite3_error_class = { 1622 .class_name = "Sqlite3Error", 1623 }; 1624 1625 #define ERRCASE(c) case c: return #c 1626 1627 const char *translate_sqlite3_err_to_string(int errcode) 1628 { 1629 switch (errcode) { 1630 ERRCASE(SQLITE_OK); 1631 ERRCASE(SQLITE_ERROR); 1632 ERRCASE(SQLITE_INTERNAL); 1633 ERRCASE(SQLITE_PERM); 1634 ERRCASE(SQLITE_ABORT); 1635 ERRCASE(SQLITE_BUSY); 1636 ERRCASE(SQLITE_LOCKED); 1637 ERRCASE(SQLITE_NOMEM); 1638 ERRCASE(SQLITE_READONLY); 1639 ERRCASE(SQLITE_INTERRUPT); 1640 ERRCASE(SQLITE_IOERR); 1641 ERRCASE(SQLITE_CORRUPT); 1642 ERRCASE(SQLITE_NOTFOUND); 1643 ERRCASE(SQLITE_FULL); 1644 ERRCASE(SQLITE_CANTOPEN); 1645 ERRCASE(SQLITE_PROTOCOL); 1646 ERRCASE(SQLITE_EMPTY); 1647 ERRCASE(SQLITE_SCHEMA); 1648 ERRCASE(SQLITE_TOOBIG); 1649 ERRCASE(SQLITE_CONSTRAINT); 1650 ERRCASE(SQLITE_MISMATCH); 1651 ERRCASE(SQLITE_MISUSE); 1652 ERRCASE(SQLITE_NOLFS); 1653 ERRCASE(SQLITE_AUTH); 1654 ERRCASE(SQLITE_FORMAT); 1655 ERRCASE(SQLITE_RANGE); 1656 ERRCASE(SQLITE_NOTADB); 1657 ERRCASE(SQLITE_NOTICE); 1658 ERRCASE(SQLITE_WARNING); 1659 ERRCASE(SQLITE_ROW); 1660 ERRCASE(SQLITE_DONE); 1661 ERRCASE(SQLITE_ERROR_MISSING_COLLSEQ); 1662 ERRCASE(SQLITE_ERROR_RETRY); 1663 ERRCASE(SQLITE_ERROR_SNAPSHOT); 1664 ERRCASE(SQLITE_IOERR_READ); 1665 ERRCASE(SQLITE_IOERR_SHORT_READ); 1666 ERRCASE(SQLITE_IOERR_WRITE); 1667 ERRCASE(SQLITE_IOERR_FSYNC); 1668 ERRCASE(SQLITE_IOERR_DIR_FSYNC); 1669 ERRCASE(SQLITE_IOERR_TRUNCATE); 1670 ERRCASE(SQLITE_IOERR_FSTAT); 1671 ERRCASE(SQLITE_IOERR_UNLOCK); 1672 ERRCASE(SQLITE_IOERR_RDLOCK); 1673 ERRCASE(SQLITE_IOERR_DELETE); 1674 ERRCASE(SQLITE_IOERR_BLOCKED); 1675 ERRCASE(SQLITE_IOERR_NOMEM); 1676 ERRCASE(SQLITE_IOERR_ACCESS); 1677 ERRCASE(SQLITE_IOERR_CHECKRESERVEDLOCK); 1678 ERRCASE(SQLITE_IOERR_LOCK); 1679 ERRCASE(SQLITE_IOERR_CLOSE); 1680 ERRCASE(SQLITE_IOERR_DIR_CLOSE); 1681 ERRCASE(SQLITE_IOERR_SHMOPEN); 1682 ERRCASE(SQLITE_IOERR_SHMSIZE); 1683 ERRCASE(SQLITE_IOERR_SHMLOCK); 1684 ERRCASE(SQLITE_IOERR_SHMMAP); 1685 ERRCASE(SQLITE_IOERR_SEEK); 1686 ERRCASE(SQLITE_IOERR_DELETE_NOENT); 1687 ERRCASE(SQLITE_IOERR_MMAP); 1688 ERRCASE(SQLITE_IOERR_GETTEMPPATH); 1689 ERRCASE(SQLITE_IOERR_CONVPATH); 1690 ERRCASE(SQLITE_IOERR_VNODE); 1691 ERRCASE(SQLITE_IOERR_AUTH); 1692 ERRCASE(SQLITE_IOERR_BEGIN_ATOMIC); 1693 ERRCASE(SQLITE_IOERR_COMMIT_ATOMIC); 1694 ERRCASE(SQLITE_IOERR_ROLLBACK_ATOMIC); 1695 ERRCASE(SQLITE_IOERR_DATA); 1696 ERRCASE(SQLITE_IOERR_CORRUPTFS); 1697 #ifdef SQLITE_IOERR_IN_PAGE 1698 ERRCASE(SQLITE_IOERR_IN_PAGE); 1699 #endif 1700 ERRCASE(SQLITE_LOCKED_SHAREDCACHE); 1701 ERRCASE(SQLITE_LOCKED_VTAB); 1702 ERRCASE(SQLITE_BUSY_RECOVERY); 1703 ERRCASE(SQLITE_BUSY_SNAPSHOT); 1704 ERRCASE(SQLITE_BUSY_TIMEOUT); 1705 ERRCASE(SQLITE_CANTOPEN_NOTEMPDIR); 1706 ERRCASE(SQLITE_CANTOPEN_ISDIR); 1707 ERRCASE(SQLITE_CANTOPEN_FULLPATH); 1708 ERRCASE(SQLITE_CANTOPEN_CONVPATH); 1709 ERRCASE(SQLITE_CANTOPEN_DIRTYWAL); 1710 ERRCASE(SQLITE_CANTOPEN_SYMLINK); 1711 ERRCASE(SQLITE_CORRUPT_VTAB); 1712 ERRCASE(SQLITE_CORRUPT_SEQUENCE); 1713 ERRCASE(SQLITE_CORRUPT_INDEX); 1714 ERRCASE(SQLITE_READONLY_RECOVERY); 1715 ERRCASE(SQLITE_READONLY_CANTLOCK); 1716 ERRCASE(SQLITE_READONLY_ROLLBACK); 1717 ERRCASE(SQLITE_READONLY_DBMOVED); 1718 ERRCASE(SQLITE_READONLY_CANTINIT); 1719 ERRCASE(SQLITE_READONLY_DIRECTORY); 1720 ERRCASE(SQLITE_ABORT_ROLLBACK); 1721 ERRCASE(SQLITE_CONSTRAINT_CHECK); 1722 ERRCASE(SQLITE_CONSTRAINT_COMMITHOOK); 1723 ERRCASE(SQLITE_CONSTRAINT_FOREIGNKEY); 1724 ERRCASE(SQLITE_CONSTRAINT_FUNCTION); 1725 ERRCASE(SQLITE_CONSTRAINT_NOTNULL); 1726 ERRCASE(SQLITE_CONSTRAINT_PRIMARYKEY); 1727 ERRCASE(SQLITE_CONSTRAINT_TRIGGER); 1728 ERRCASE(SQLITE_CONSTRAINT_UNIQUE); 1729 ERRCASE(SQLITE_CONSTRAINT_VTAB); 1730 ERRCASE(SQLITE_CONSTRAINT_ROWID); 1731 ERRCASE(SQLITE_CONSTRAINT_PINNED); 1732 ERRCASE(SQLITE_CONSTRAINT_DATATYPE); 1733 ERRCASE(SQLITE_NOTICE_RECOVER_WAL); 1734 ERRCASE(SQLITE_NOTICE_RECOVER_ROLLBACK); 1735 #ifdef SQLITE_NOTICE_RBU 1736 ERRCASE(SQLITE_NOTICE_RBU); 1737 #endif 1738 ERRCASE(SQLITE_WARNING_AUTOINDEX); 1739 ERRCASE(SQLITE_AUTH_USER); 1740 ERRCASE(SQLITE_OK_LOAD_PERMANENTLY); 1741 ERRCASE(SQLITE_OK_SYMLINK); 1742 default: 1743 return "SQLITE_UNKNOWN_ERRCODE"; 1744 } 1745 } 1746 1747 1748 static JSValue new_sqlite3_error_with_proto(JSContext *ctx, 1749 JSValueConst proto) 1750 { 1751 JSValue obj; 1752 1753 obj = JS_NewError(ctx); 1754 if (JS_IsException(obj)) { 1755 return obj; 1756 } 1757 if (JS_SetPrototype(ctx, obj, proto) < 0) { 1758 JS_FreeValue(ctx, obj); 1759 return JS_EXCEPTION; 1760 } 1761 return obj; 1762 } 1763 1764 static JSValue new_sqlite3_error(JSContext *ctx) 1765 { 1766 JSValue obj; 1767 JSValue proto; 1768 1769 proto = JS_GetClassProto(ctx, js_sqlite3_error_class_id); 1770 if (JS_IsException(proto)) { 1771 return proto; 1772 } 1773 obj = new_sqlite3_error_with_proto(ctx, proto); 1774 JS_FreeValue(ctx, proto); 1775 return obj; 1776 } 1777 1778 static JSValue js_sqlite3_error_constructor(JSContext *ctx, 1779 JSValueConst new_target, 1780 int argc, 1781 JSValueConst *argv) 1782 { 1783 JSValue obj; 1784 JSValue proto; 1785 1786 proto = JS_GetPropertyStr(ctx, new_target, "prototype"); 1787 if (JS_IsException(proto)) { 1788 return proto; 1789 } 1790 obj = new_sqlite3_error_with_proto(ctx, proto); 1791 JS_FreeValue(ctx, proto); 1792 if (JS_IsException(obj)) { 1793 return obj; 1794 } 1795 if (argc > 0 && !JS_IsUndefined(argv[0])) { 1796 JSValue message = JS_ToString(ctx, argv[0]); 1797 if (JS_IsException(message) || 1798 JS_DefinePropertyValueStr( 1799 ctx, 1800 obj, 1801 "message", 1802 message, 1803 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0) { 1804 JS_FreeValue(ctx, obj); 1805 return JS_EXCEPTION; 1806 } 1807 } 1808 return obj; 1809 } 1810 1811 static JSValue throw_sqlite3_error(JSContext *ctx, sqlite3 *db) 1812 { 1813 JSValue obj; 1814 int error_code; 1815 1816 error_code = sqlite3_extended_errcode(db); 1817 obj = new_sqlite3_error(ctx); 1818 if (JS_IsException(obj)) { 1819 return obj; 1820 } 1821 1822 if (JS_DefinePropertyValueStr( 1823 ctx, 1824 obj, 1825 "message", 1826 JS_NewString(ctx, sqlite3_errmsg(db)), 1827 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0 || 1828 JS_DefinePropertyValueStr( 1829 ctx, 1830 obj, 1831 "code", 1832 JS_NewString(ctx, translate_sqlite3_err_to_string(error_code)), 1833 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0 || 1834 JS_DefinePropertyValueStr( 1835 ctx, 1836 obj, 1837 "errno", 1838 JS_NewInt32(ctx, error_code), 1839 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0) { 1840 JS_FreeValue(ctx, obj); 1841 return JS_EXCEPTION; 1842 } 1843 return JS_Throw(ctx, obj); 1844 } 1845 1846 #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) 1847 #define MIN_SAFE_INTEGER (-(((int64_t)1 << 53) - 1)) 1848 1849 static int sqlite3_open_boolean_option(JSContext *ctx, 1850 JSValueConst options, 1851 const char *name, 1852 int *result) 1853 { 1854 JSValue value; 1855 1856 value = JS_GetPropertyStr(ctx, options, name); 1857 if (JS_IsException(value)) { 1858 return -1; 1859 } 1860 if (JS_IsUndefined(value)) { 1861 *result = 0; 1862 } else if (!JS_IsBool(value)) { 1863 JS_FreeValue(ctx, value); 1864 JS_ThrowTypeError(ctx, "sqlite3Open option '%s' must be a boolean", name); 1865 return -1; 1866 } else { 1867 *result = JS_ToBool(ctx, value); 1868 } 1869 JS_FreeValue(ctx, value); 1870 return 0; 1871 } 1872 1873 static int sqlite_uri_path_char(unsigned char c) 1874 { 1875 return (c >= 'a' && c <= 'z') || 1876 (c >= 'A' && c <= 'Z') || 1877 (c >= '0' && c <= '9') || 1878 c == '/' || c == '-' || c == '.' || c == '_' || c == '~'; 1879 } 1880 1881 static char *sqlite3_immutable_uri(const char *filename) 1882 { 1883 static const char hex[] = "0123456789ABCDEF"; 1884 static const char prefix[] = "file:"; 1885 static const char suffix[] = "?immutable=1"; 1886 size_t filename_len; 1887 size_t allocation_size; 1888 char *uri; 1889 char *p; 1890 1891 filename_len = strlen(filename); 1892 if (filename_len > (SIZE_MAX - sizeof(prefix) - sizeof(suffix)) / 3) { 1893 return NULL; 1894 } 1895 allocation_size = sizeof(prefix) - 1 + filename_len * 3 + sizeof(suffix); 1896 uri = sqlite3_malloc64(allocation_size); 1897 if (!uri) { 1898 return NULL; 1899 } 1900 p = uri; 1901 memcpy(p, prefix, sizeof(prefix) - 1); 1902 p += sizeof(prefix) - 1; 1903 for (size_t i = 0; i < filename_len; i++) { 1904 unsigned char c = (unsigned char)filename[i]; 1905 if (sqlite_uri_path_char(c)) { 1906 *p++ = (char)c; 1907 } else { 1908 *p++ = '%'; 1909 *p++ = hex[c >> 4]; 1910 *p++ = hex[c & 0x0f]; 1911 } 1912 } 1913 memcpy(p, suffix, sizeof(suffix)); 1914 return uri; 1915 } 1916 1917 // (path: string, options?: { readonly?: boolean, immutable?: boolean }) => Sqlite3Database 1918 static JSValue js_sqlite3_open(JSContext *ctx, JSValue this_val, 1919 int argc, JSValueConst *argv) 1920 { 1921 JSValue ret_val = JS_UNINITIALIZED; 1922 JSValue db_obj = JS_UNINITIALIZED; 1923 const char *filename = NULL; 1924 const char *open_filename; 1925 char *immutable_uri = NULL; 1926 sqlite3 *sqlite3_db = NULL; 1927 int readonly = 0; 1928 int immutable = 0; 1929 int open_flags; 1930 int ret; 1931 1932 if (argc < 1 || !JS_IsString(argv[0])) { 1933 ret_val = JS_ThrowTypeError(ctx, "filename argument required"); 1934 goto done; 1935 } 1936 1937 if (argc > 1 && !JS_IsUndefined(argv[1])) { 1938 if (!JS_IsObject(argv[1])) { 1939 ret_val = JS_ThrowTypeError(ctx, "sqlite3Open options must be an object"); 1940 goto done; 1941 } 1942 if (sqlite3_open_boolean_option(ctx, argv[1], "readonly", &readonly) < 0 || 1943 sqlite3_open_boolean_option(ctx, argv[1], "immutable", &immutable) < 0) { 1944 ret_val = JS_EXCEPTION; 1945 goto done; 1946 } 1947 } 1948 if (immutable) { 1949 readonly = 1; 1950 } 1951 1952 filename = JS_ToCString(ctx, argv[0]); 1953 1954 if (!filename) { 1955 ret_val = JS_ThrowTypeError(ctx, "filename argument required"); 1956 goto done; 1957 } 1958 1959 open_filename = filename; 1960 open_flags = readonly 1961 ? SQLITE_OPEN_READONLY 1962 : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; 1963 if (immutable) { 1964 immutable_uri = sqlite3_immutable_uri(filename); 1965 if (!immutable_uri) { 1966 ret_val = JS_ThrowOutOfMemory(ctx); 1967 goto done; 1968 } 1969 open_filename = immutable_uri; 1970 open_flags |= SQLITE_OPEN_URI; 1971 } 1972 1973 // fprintf(stderr, "opening sqlite3 db at %s\n", filename), 1974 ret = sqlite3_open_v2(open_filename, &sqlite3_db, open_flags, NULL); 1975 if (SQLITE_OK != ret) { 1976 if (NULL == sqlite3_db) { 1977 // sqlite3 was unable to even allocate memory 1978 ret_val = JS_ThrowInternalError(ctx, "unable to open database (OOM)"); 1979 } else { 1980 // get error details from DB before we close it 1981 fprintf(stderr, "sqlite3_open failed: %s / %s\n", 1982 sqlite3_errstr(ret), 1983 sqlite3_errmsg(sqlite3_db)); 1984 ret_val = throw_sqlite3_error(ctx, sqlite3_db); 1985 fprintf(stderr, "calling sqlite3 close on failed db\n"); 1986 (void)sqlite3_close_v2(sqlite3_db); 1987 } 1988 goto done; 1989 } 1990 db_obj = JS_NewObjectClass(ctx, js_sqlite3_database_class_id); 1991 if (JS_IsException(db_obj)) { 1992 (void) sqlite3_close_v2(sqlite3_db); 1993 ret_val = JS_EXCEPTION; 1994 goto done; 1995 } 1996 JS_SetOpaque(db_obj, sqlite3_db); 1997 ret_val = db_obj; 1998 done: 1999 sqlite3_free(immutable_uri); 2000 JS_FreeCString(ctx, filename); 2001 return ret_val; 2002 } 2003 2004 // (handle: Sqlite3Database) -> undefined 2005 static JSValue js_sqlite3_close(JSContext *ctx, JSValue this_val, 2006 int argc, JSValueConst *argv) 2007 { 2008 JSValue db_handle; 2009 sqlite3 *sqlite3_db; 2010 int ret; 2011 2012 if (argc < 1 || 2013 JS_GetClassID(argv[0]) != js_sqlite3_database_class_id) { 2014 return JS_ThrowTypeError(ctx, "expected sqlite3 database handle"); 2015 } 2016 db_handle = argv[0]; 2017 2018 sqlite3_db = JS_GetOpaque(db_handle, js_sqlite3_database_class_id); 2019 2020 if (!sqlite3_db) { 2021 return JS_ThrowTypeError(ctx, "invalid sqlite3 database handle"); 2022 } 2023 2024 /* 2025 * Unlike close_v2(), sqlite3_close() refuses to turn a connection with 2026 * live statements into a zombie. Keep the JS handle valid on SQLITE_BUSY 2027 * so the caller can finalize the statements and retry safely. 2028 */ 2029 ret = sqlite3_close(sqlite3_db); 2030 if (SQLITE_OK != ret) { 2031 return throw_sqlite3_error(ctx, sqlite3_db); 2032 } 2033 JS_SetOpaque(db_handle, NULL); 2034 return JS_UNDEFINED; 2035 } 2036 2037 // (handle: Sqlite3Database, stmt: string) => Sqlite3Statement 2038 static JSValue js_sqlite3_prepare(JSContext *ctx, JSValue this_val, 2039 int argc, JSValueConst *argv) 2040 { 2041 JSValue db_handle; 2042 JSValue stmt_str; 2043 JSValue ret_val = JS_UNDEFINED; 2044 JSValue stmt_obj = JS_UNDEFINED; 2045 int ret; 2046 sqlite3 *sqlite3_db; 2047 sqlite3_stmt *stmt; 2048 const char *stmt_cstr; 2049 const char *tail; 2050 2051 if (argc < 2 || 2052 JS_GetClassID(argv[0]) != js_sqlite3_database_class_id) { 2053 return JS_ThrowTypeError(ctx, "expected database handle and SQL string"); 2054 } 2055 db_handle = argv[0]; 2056 stmt_str = argv[1]; 2057 2058 sqlite3_db = JS_GetOpaque(db_handle, js_sqlite3_database_class_id); 2059 2060 if (!sqlite3_db) { 2061 return JS_ThrowTypeError(ctx, "invalid sqlite3 database handle"); 2062 } 2063 2064 stmt_cstr = JS_ToCString(ctx, stmt_str); 2065 2066 if (!stmt_cstr) { 2067 ret_val = JS_ThrowTypeError(ctx, "invalid prepared statement, string expected"); 2068 goto done; 2069 } 2070 2071 ret = sqlite3_prepare_v3(sqlite3_db, stmt_cstr, (int) strlen(stmt_cstr), 0, &stmt, &tail); 2072 if (SQLITE_OK != ret) { 2073 ret_val = throw_sqlite3_error(ctx, sqlite3_db); 2074 goto done; 2075 } 2076 2077 stmt_obj = JS_NewObjectClass(ctx, js_sqlite3_statement_class_id); 2078 if (JS_IsException(stmt_obj)) { 2079 sqlite3_finalize(stmt); 2080 ret_val = JS_EXCEPTION; 2081 goto done; 2082 } 2083 JS_SetOpaque(stmt_obj, stmt); 2084 /* 2085 * A statement must keep its database JS object alive. Otherwise QuickJS 2086 * could run the database finalizer while the sqlite3_stmt still refers to 2087 * that connection. 2088 */ 2089 if (JS_DefinePropertyValueStr( 2090 ctx, 2091 stmt_obj, 2092 "__sqlite3_database_owner", 2093 JS_DupValue(ctx, db_handle), 2094 0) < 0) { 2095 JS_FreeValue(ctx, stmt_obj); 2096 ret_val = JS_EXCEPTION; 2097 goto done; 2098 } 2099 ret_val = stmt_obj; 2100 done: 2101 JS_FreeCString(ctx, stmt_cstr); 2102 return ret_val; 2103 } 2104 2105 2106 static JSValue js_sqlite3_finalize(JSContext *ctx, JSValue this_val, 2107 int argc, JSValueConst *argv) 2108 { 2109 sqlite3_stmt *stmt; 2110 2111 if (argc < 1 || 2112 JS_GetClassID(argv[0]) != js_sqlite3_statement_class_id) { 2113 return JS_ThrowTypeError(ctx, "expected sqlite3 statement handle"); 2114 } 2115 stmt = JS_GetOpaque(argv[0], js_sqlite3_statement_class_id); 2116 if (!stmt) { 2117 return JS_UNDEFINED; 2118 } 2119 (void)sqlite3_finalize(stmt); 2120 JS_SetOpaque(argv[0], NULL); 2121 return JS_UNDEFINED; 2122 } 2123 2124 static int sql_exec_cb(void *cls, int numcol, char **res, char **colnames) { 2125 return 0; 2126 } 2127 2128 static JSValue js_sqlite3_exec(JSContext *ctx, JSValue this_val, 2129 int argc, JSValueConst *argv) 2130 { 2131 sqlite3 *sqlite3_db; 2132 JSValue db_handle = argv[0]; 2133 JSValue stmt_str = argv[1]; 2134 const char *stmt_cstr = NULL; 2135 int res; 2136 char *errmsg = NULL; 2137 JSValue ret_val = JS_UNDEFINED; 2138 2139 sqlite3_db = JS_GetOpaque(db_handle, js_sqlite3_database_class_id); 2140 if (!sqlite3_db) { 2141 JS_ThrowTypeError(ctx, "invalid sqlite3 database handle"); 2142 goto exception; 2143 } 2144 stmt_cstr = JS_ToCString(ctx, stmt_str); 2145 if (!stmt_cstr) { 2146 goto exception; 2147 } 2148 res = sqlite3_exec(sqlite3_db, stmt_cstr, &sql_exec_cb, NULL, &errmsg); 2149 if (SQLITE_OK != res) { 2150 throw_sqlite3_error(ctx, sqlite3_db); 2151 goto exception; 2152 } 2153 done: 2154 sqlite3_free(errmsg); 2155 JS_FreeCString(ctx, stmt_cstr); 2156 return ret_val; 2157 exception: 2158 ret_val = JS_EXCEPTION; 2159 goto done; 2160 } 2161 2162 static int find_param_index(JSContext *ctx, sqlite3_stmt *stmt, const char *name) 2163 { 2164 int param_index; 2165 char *prefixed_name = malloc(strlen(name) + 2); 2166 if (!prefixed_name) { 2167 JS_ThrowOutOfMemory(ctx); 2168 return -1; 2169 } 2170 memcpy(prefixed_name + 1, name, strlen(name) + 1); 2171 2172 prefixed_name[0] = '$'; 2173 param_index = sqlite3_bind_parameter_index(stmt, prefixed_name); 2174 if (param_index) { 2175 goto done; 2176 } 2177 prefixed_name[0] = ':'; 2178 param_index = sqlite3_bind_parameter_index(stmt, prefixed_name); 2179 if (param_index) { 2180 goto done; 2181 } 2182 prefixed_name[0] = '@'; 2183 param_index = sqlite3_bind_parameter_index(stmt, prefixed_name); 2184 if (param_index) { 2185 goto done; 2186 } 2187 done: 2188 free(prefixed_name); 2189 return param_index; 2190 } 2191 2192 static int bind_from_object(JSContext *ctx, sqlite3_stmt *stmt, JSValueConst obj) 2193 { 2194 JSValue val = JS_UNDEFINED; 2195 int i; 2196 uint32_t len = 0; /* len of property table */ 2197 JSPropertyEnum *tab; 2198 const char *key = NULL; 2199 int retval = 0; 2200 2201 if (JS_IsUndefined(obj)) { 2202 return 0; 2203 } 2204 2205 if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, 2206 JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) { 2207 JS_ThrowTypeError(ctx, "can't get property names"); 2208 return -1; 2209 } 2210 2211 for (i = 0; i < len; i++) { 2212 int param_index; 2213 val = JS_GetProperty(ctx, obj, tab[i].atom); 2214 if (JS_IsException(val)) 2215 goto fail; 2216 key = JS_AtomToCString(ctx, tab[i].atom); 2217 if (!key) { 2218 goto fail; 2219 } 2220 param_index = find_param_index(ctx, stmt, key); 2221 if (param_index < 0) { 2222 goto fail; 2223 } 2224 if (0 == param_index) { 2225 // JS_ThrowTypeError(ctx, "unable to bind, named param '%s' not found", key); 2226 //goto fail; 2227 // We ignore parameters that are bound but not used. 2228 goto next; 2229 } 2230 if (JS_IsNull(val)) { 2231 if (SQLITE_OK != sqlite3_bind_null(stmt, param_index)) { 2232 goto sqlite_fail; 2233 } 2234 goto next; 2235 } 2236 if (JS_IsString(val)) { 2237 const char *cstr; 2238 size_t cstr_len; 2239 cstr = JS_ToCStringLen(ctx, &cstr_len, val); 2240 if (!cstr) { 2241 goto fail; 2242 } 2243 if (SQLITE_OK != sqlite3_bind_text64(stmt, param_index, cstr, 2244 cstr_len, SQLITE_TRANSIENT, 2245 SQLITE_UTF8)) { 2246 JS_FreeCString(ctx, cstr); 2247 goto sqlite_fail; 2248 } 2249 JS_FreeCString(ctx, cstr); 2250 goto next; 2251 } 2252 if (JS_IsNumber(val)) { 2253 int64_t n; 2254 if (0 != JS_ToInt64(ctx, &n, val)) { 2255 goto fail; 2256 } 2257 if (SQLITE_OK != sqlite3_bind_int64(stmt, param_index, n)) { 2258 goto sqlite_fail; 2259 } 2260 goto next; 2261 } 2262 if (JS_IsArrayBuffer(val)) { 2263 uint8_t *data; 2264 size_t size; 2265 data = JS_GetArrayBuffer(ctx, &size, val); 2266 if (!data) { 2267 goto fail; 2268 } 2269 if (SQLITE_OK != sqlite3_bind_blob64(stmt, param_index, data, size, 2270 SQLITE_TRANSIENT)) { 2271 goto sqlite_fail; 2272 } 2273 goto next; 2274 } 2275 JS_ThrowTypeError(ctx, "unable to bind, unsupported type for arg %s", key); 2276 goto fail; 2277 next: 2278 JS_FreeCString(ctx, key); 2279 key = NULL; 2280 JS_FreeValue(ctx, val); 2281 val = JS_UNDEFINED; 2282 } 2283 done: 2284 for (i = 0; i < len; i++) { 2285 JS_FreeAtom(ctx, tab[i].atom); 2286 } 2287 js_free(ctx, tab); 2288 return retval; 2289 fail: 2290 JS_FreeCString(ctx, key); 2291 JS_FreeValue(ctx, val); 2292 retval = -1; 2293 goto done; 2294 sqlite_fail: 2295 throw_sqlite3_error(ctx, sqlite3_db_handle(stmt)); 2296 goto fail; 2297 } 2298 2299 2300 static JSValue js_sqlite3_stmt_run(JSContext *ctx, JSValue this_val, 2301 int argc, JSValueConst *argv) 2302 { 2303 JSValue ret_val = JS_UNDEFINED; 2304 JSValue stmt_handle; 2305 sqlite3_stmt *stmt; 2306 sqlite3 *db; 2307 int sqlret; 2308 2309 if (argc < 1 || 2310 JS_GetClassID(argv[0]) != js_sqlite3_statement_class_id) { 2311 return JS_ThrowTypeError(ctx, "expected sqlite3 statement handle"); 2312 } 2313 stmt_handle = argv[0]; 2314 2315 stmt = JS_GetOpaque(stmt_handle, js_sqlite3_statement_class_id); 2316 if (!stmt) { 2317 ret_val = JS_ThrowTypeError(ctx, "invalid sqlite3 statement handle"); 2318 goto done; 2319 } 2320 db = sqlite3_db_handle(stmt); 2321 // Reset is not strictly necessary, we do it anyway just to be safe 2322 sqlret = sqlite3_reset(stmt); 2323 if (SQLITE_OK != sqlret) { 2324 fprintf(stderr, "sqlite3_reset failed (in stmt_run): %s\n", sqlite3_errmsg(db)); 2325 ret_val = throw_sqlite3_error(ctx, db); 2326 goto done; 2327 } 2328 sqlret = sqlite3_clear_bindings(stmt); 2329 if (SQLITE_OK != sqlret) { 2330 ret_val = throw_sqlite3_error(ctx, db); 2331 goto done; 2332 } 2333 if (argc > 1) { 2334 if (0 != bind_from_object(ctx, stmt, argv[1])) { 2335 ret_val = JS_EXCEPTION; 2336 goto done; 2337 } 2338 } 2339 while (1) { 2340 sqlret = sqlite3_step(stmt); 2341 switch (sqlret) { 2342 case SQLITE_ROW: 2343 break; 2344 case SQLITE_DONE: { 2345 JSValue rowid_val; 2346 ret_val = JS_NewObject(ctx); 2347 sqlite3_int64 rowid = sqlite3_last_insert_rowid(db); 2348 if (rowid >= MIN_SAFE_INTEGER && rowid <= MAX_SAFE_INTEGER) { 2349 rowid_val = JS_NewInt64(ctx, rowid); 2350 } else { 2351 rowid_val = JS_NewBigInt64(ctx, rowid); 2352 } 2353 JS_SetPropertyStr(ctx, ret_val, "lastInsertRowid", rowid_val); 2354 sqlret = sqlite3_reset(stmt); 2355 if (SQLITE_OK != sqlret) { 2356 fprintf(stderr, "sqlite3_reset failed (in stmt_run after SQLITE_DONE): %s\n", sqlite3_errmsg(db)); 2357 JS_FreeValue(ctx, ret_val); 2358 ret_val = throw_sqlite3_error(ctx, db); 2359 goto done; 2360 } 2361 goto done; 2362 } 2363 default: 2364 ret_val = throw_sqlite3_error(ctx, db); 2365 sqlite3_reset(stmt); 2366 goto done; 2367 } 2368 } 2369 done: 2370 return ret_val; 2371 } 2372 2373 2374 static int extract_result_row(JSContext *ctx, sqlite3_stmt *stmt, JSValueConst target) 2375 { 2376 int colcount; 2377 int i; 2378 2379 colcount = sqlite3_column_count(stmt); 2380 2381 for (i = 0; i < colcount; i++) { 2382 const char *colname = sqlite3_column_name(stmt, i); 2383 int coltype = sqlite3_column_type(stmt, i); 2384 switch (coltype) { 2385 case SQLITE_INTEGER: { 2386 int64_t val = sqlite3_column_int64(stmt, i); 2387 if (val > MAX_SAFE_INTEGER || val < MIN_SAFE_INTEGER) { 2388 JS_SetPropertyStr(ctx, target, colname, JS_NewBigInt64(ctx, val)); 2389 } else { 2390 JS_SetPropertyStr(ctx, target, colname, JS_NewInt64(ctx, val)); 2391 } 2392 break; 2393 } 2394 case SQLITE_FLOAT: { 2395 double val = sqlite3_column_double(stmt, i); 2396 JS_SetPropertyStr(ctx, target, colname, JS_NewFloat64(ctx, val)); 2397 break; 2398 } 2399 case SQLITE_BLOB: { 2400 JSValue abuf; 2401 const uint8_t *blobdata = sqlite3_column_blob(stmt, i); 2402 size_t bloblen = sqlite3_column_bytes(stmt, i); 2403 abuf = JS_NewArrayBufferCopy(ctx, blobdata, bloblen); 2404 JS_SetPropertyStr(ctx, target, colname, JS_NewTypedArraySimple(ctx, abuf, 1)); 2405 break; 2406 } 2407 case SQLITE_NULL: 2408 JS_SetPropertyStr(ctx, target, colname, JS_NULL); 2409 break; 2410 case SQLITE_TEXT: { 2411 const char *text = (const char *) sqlite3_column_text(stmt, i); 2412 size_t text_len = sqlite3_column_bytes(stmt, i); 2413 JS_SetPropertyStr(ctx, target, colname, 2414 JS_NewStringLen(ctx, text, text_len)); 2415 break; 2416 } 2417 default: 2418 JS_ThrowInternalError(ctx, "unexpected type from DB"); 2419 return -1; 2420 } 2421 } 2422 return 0; 2423 } 2424 2425 2426 static JSValue js_sqlite3_stmt_get_all(JSContext *ctx, JSValue this_val, 2427 int argc, JSValueConst *argv) 2428 { 2429 JSValue ret_val = JS_UNDEFINED; 2430 JSValue stmt_handle; 2431 sqlite3_stmt *stmt; 2432 sqlite3 *db; 2433 int sqlret; 2434 JSValue rows_array = JS_UNDEFINED; 2435 2436 if (argc < 1 || 2437 JS_GetClassID(argv[0]) != js_sqlite3_statement_class_id) { 2438 return JS_ThrowTypeError(ctx, "expected sqlite3 statement handle"); 2439 } 2440 stmt_handle = argv[0]; 2441 2442 stmt = JS_GetOpaque(stmt_handle, js_sqlite3_statement_class_id); 2443 if (!stmt) { 2444 ret_val = JS_ThrowTypeError(ctx, "invalid sqlite3 statement handle"); 2445 goto done; 2446 } 2447 db = sqlite3_db_handle(stmt); 2448 sqlret = sqlite3_reset(stmt); 2449 if (SQLITE_OK != sqlret) { 2450 fprintf(stderr, "sqlite3_reset failed (in stmt_get_all): %s\n", sqlite3_errmsg(db)); 2451 ret_val = throw_sqlite3_error(ctx, db); 2452 goto done; 2453 } 2454 sqlret = sqlite3_clear_bindings(stmt); 2455 if (SQLITE_OK != sqlret) { 2456 ret_val = throw_sqlite3_error(ctx, db); 2457 goto done; 2458 } 2459 if (argc > 1) { 2460 if (0 != bind_from_object(ctx, stmt, argv[1])) { 2461 ret_val = JS_EXCEPTION; 2462 goto done; 2463 } 2464 } 2465 rows_array = JS_NewArray(ctx); 2466 while (1) { 2467 sqlret = sqlite3_step(stmt); 2468 switch (sqlret) { 2469 case SQLITE_ROW: { 2470 JSValue row_obj = JS_NewObject(ctx); 2471 if (0 != extract_result_row(ctx, stmt, row_obj)) { 2472 goto fail; 2473 } 2474 qjs_array_append_new(ctx, rows_array, row_obj); 2475 break; 2476 } 2477 case SQLITE_DONE: { 2478 sqlret = sqlite3_reset(stmt); 2479 if (SQLITE_OK != sqlret) { 2480 fprintf(stderr, "sqlite3_reset failed (in stmt_get_all after SQLITE_DONE): %s\n", sqlite3_errmsg(db)); 2481 ret_val = throw_sqlite3_error(ctx, db); 2482 } else { 2483 ret_val = JS_DupValue(ctx, rows_array); 2484 } 2485 goto done; 2486 } 2487 default: 2488 ret_val = throw_sqlite3_error(ctx, db); 2489 sqlite3_reset(stmt); 2490 goto done; 2491 } 2492 } 2493 done: 2494 JS_FreeValue(ctx, rows_array); 2495 return ret_val; 2496 fail: 2497 ret_val = JS_EXCEPTION; 2498 goto done; 2499 } 2500 2501 static JSValue js_sqlite3_stmt_get_first(JSContext *ctx, JSValue this_val, 2502 int argc, JSValueConst *argv) 2503 { 2504 JSValue ret_val = JS_UNDEFINED; 2505 JSValue stmt_handle; 2506 sqlite3_stmt *stmt; 2507 sqlite3 *db; 2508 int sqlret; 2509 2510 if (argc < 1 || 2511 JS_GetClassID(argv[0]) != js_sqlite3_statement_class_id) { 2512 return JS_ThrowTypeError(ctx, "expected sqlite3 statement handle"); 2513 } 2514 stmt_handle = argv[0]; 2515 2516 stmt = JS_GetOpaque(stmt_handle, js_sqlite3_statement_class_id); 2517 if (!stmt) { 2518 ret_val = JS_ThrowTypeError(ctx, "invalid sqlite3 statement handle"); 2519 goto done; 2520 } 2521 db = sqlite3_db_handle(stmt); 2522 sqlret = sqlite3_reset(stmt); 2523 if (SQLITE_OK != sqlret) { 2524 fprintf(stderr, "sqlite3_reset failed (in stmt_get_first): %s\n", sqlite3_errmsg(db)); 2525 ret_val = throw_sqlite3_error(ctx, db); 2526 goto done; 2527 } 2528 sqlret = sqlite3_clear_bindings(stmt); 2529 if (SQLITE_OK != sqlret) { 2530 ret_val = throw_sqlite3_error(ctx, db); 2531 goto done; 2532 } 2533 if (argc > 1) { 2534 if (0 != bind_from_object(ctx, stmt, argv[1])) { 2535 ret_val = JS_EXCEPTION; 2536 goto done; 2537 } 2538 } 2539 while (1) { 2540 sqlret = sqlite3_step(stmt); 2541 switch (sqlret) { 2542 case SQLITE_ROW: { 2543 JSValue row_obj = JS_NewObject(ctx); 2544 if (0 != extract_result_row(ctx, stmt, row_obj)) { 2545 goto fail; 2546 } 2547 ret_val = row_obj; 2548 goto reset; 2549 } 2550 case SQLITE_DONE: { 2551 ret_val = JS_UNDEFINED; 2552 goto reset; 2553 } 2554 default: 2555 ret_val = throw_sqlite3_error(ctx, db); 2556 sqlite3_reset(stmt); 2557 goto done; 2558 } 2559 } 2560 reset: 2561 sqlret = sqlite3_reset(stmt); 2562 if (SQLITE_OK != sqlret) { 2563 fprintf(stderr, "sqlite3_reset failed (in stmt_get_first): %s\n", sqlite3_errmsg(db)); 2564 JS_FreeValue(ctx, ret_val); 2565 ret_val = throw_sqlite3_error(ctx, db); 2566 } 2567 done: 2568 return ret_val; 2569 fail: 2570 ret_val = JS_EXCEPTION; 2571 goto reset; 2572 } 2573 2574 2575 static const JSCFunctionListEntry tart_talercrypto_funcs[] = { 2576 JS_PROP_INT32_DEF("apiVersion", TART_API_VERSION, 0), 2577 JS_CFUNC_DEF("structuredClone", 1, js_structured_clone), 2578 JS_CFUNC_DEF("encodeUtf8", 1, js_encode_utf8), 2579 JS_CFUNC_DEF("crashForDebug", 0, js_crash_for_debug), 2580 JS_CFUNC_DEF("decodeUtf8", 1, js_decode_utf8), 2581 JS_CFUNC_DEF("randomBytes", 1, js_random_bytes), 2582 JS_CFUNC_DEF("encodeCrock", 1, js_talercrypto_encode_crock), 2583 JS_CFUNC_DEF("decodeCrock", 1, js_talercrypto_decode_crock), 2584 JS_CFUNC_DEF("hash", 1, js_talercrypto_hash), 2585 JS_CFUNC_DEF("hashStateInit", 0, js_talercrypto_hash_state_init), 2586 JS_CFUNC_DEF("hashStateUpdate", 2, js_talercrypto_hash_state_update), 2587 JS_CFUNC_DEF("hashStateFinish", 1, js_talercrypto_hash_state_finish), 2588 JS_CFUNC_DEF("hashArgon2id", 5, js_talercrypto_hash_argon2id), 2589 JS_CFUNC_DEF("eddsaGetPublic", 1, js_talercrypto_eddsa_key_get_public), 2590 JS_CFUNC_DEF("ecdheGetPublic", 1, js_talercrypto_ecdhe_key_get_public), 2591 JS_CFUNC_DEF("eddsaSign", 2, js_talercrypto_eddsa_sign), 2592 JS_CFUNC_DEF("eddsaVerify", 3, js_talercrypto_eddsa_verify), 2593 JS_CFUNC_DEF("kdf", 3, js_talercrypto_kdf), 2594 JS_CFUNC_DEF("keyExchangeEcdhEddsa", 2, js_talercrypto_kx_ecdh_eddsa), 2595 JS_CFUNC_DEF("keyExchangeEddsaEcdh", 2, js_talercrypto_kx_eddsa_ecdh), 2596 JS_CFUNC_DEF("rsaBlind", 3, js_talercrypto_rsa_blind), 2597 JS_CFUNC_DEF("rsaUnblind", 3, js_talercrypto_rsa_unblind), 2598 JS_CFUNC_DEF("rsaVerify", 3, js_talercrypto_rsa_verify), 2599 JS_CFUNC_DEF("sqlite3Open", 2, js_sqlite3_open), 2600 JS_CFUNC_DEF("sqlite3Close", 1, js_sqlite3_close), 2601 JS_CFUNC_DEF("sqlite3Prepare", 2, js_sqlite3_prepare), 2602 JS_CFUNC_DEF("sqlite3Finalize", 1, js_sqlite3_finalize), 2603 JS_CFUNC_DEF("sqlite3Exec", 2, js_sqlite3_exec), 2604 JS_CFUNC_DEF("sqlite3StmtRun", 2, js_sqlite3_stmt_run), 2605 JS_CFUNC_DEF("sqlite3StmtGetFirst", 2, js_sqlite3_stmt_get_first), 2606 JS_CFUNC_DEF("sqlite3StmtGetAll", 2, js_sqlite3_stmt_get_all), 2607 }; 2608 2609 static pthread_once_t tart_class_ids_once = PTHREAD_ONCE_INIT; 2610 2611 static void tart_init_class_ids(void) 2612 { 2613 JS_NewClassID(&js_hash_state_class_id); 2614 JS_NewClassID(&js_sqlite3_database_class_id); 2615 JS_NewClassID(&js_sqlite3_statement_class_id); 2616 JS_NewClassID(&js_sqlite3_error_class_id); 2617 } 2618 2619 static int tart_talercrypto_init(JSContext *ctx, JSModuleDef *m) 2620 { 2621 JSValue error_class; 2622 JSValue error_base_proto; 2623 JSValue error_constructor; 2624 JSValue error_proto; 2625 JSValue global; 2626 2627 if (0 != pthread_once(&tart_class_ids_once, tart_init_class_ids)) { 2628 return -1; 2629 } 2630 if (0 != JS_NewClass(JS_GetRuntime(ctx), js_hash_state_class_id, 2631 &js_hash_state_class) || 2632 0 != JS_NewClass(JS_GetRuntime(ctx), js_sqlite3_database_class_id, 2633 &js_sqlite3_database_class) || 2634 0 != JS_NewClass(JS_GetRuntime(ctx), js_sqlite3_statement_class_id, 2635 &js_sqlite3_statement_class) || 2636 0 != JS_NewClass(JS_GetRuntime(ctx), js_sqlite3_error_class_id, 2637 &js_sqlite3_error_class)) { 2638 return -1; 2639 } 2640 2641 global = JS_GetGlobalObject(ctx); 2642 error_class = JS_GetPropertyStr(ctx, global, "Error"); 2643 JS_FreeValue(ctx, global); 2644 if (JS_IsException(error_class)) { 2645 return -1; 2646 } 2647 error_base_proto = JS_GetPropertyStr(ctx, error_class, "prototype"); 2648 JS_FreeValue(ctx, error_class); 2649 if (JS_IsException(error_base_proto)) { 2650 return -1; 2651 } 2652 error_proto = JS_NewObjectProto(ctx, error_base_proto); 2653 JS_FreeValue(ctx, error_base_proto); 2654 if (JS_IsException(error_proto)) { 2655 return -1; 2656 } 2657 if (JS_DefinePropertyValueStr( 2658 ctx, 2659 error_proto, 2660 "name", 2661 JS_NewString(ctx, "Sqlite3Error"), 2662 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0) { 2663 JS_FreeValue(ctx, error_proto); 2664 return -1; 2665 } 2666 2667 error_constructor = JS_NewCFunction2(ctx, 2668 js_sqlite3_error_constructor, 2669 "Sqlite3Error", 2670 1, 2671 JS_CFUNC_constructor, 2672 0); 2673 if (JS_IsException(error_constructor)) { 2674 JS_FreeValue(ctx, error_proto); 2675 return -1; 2676 } 2677 if (JS_SetConstructor(ctx, error_constructor, error_proto) < 0) { 2678 JS_FreeValue(ctx, error_constructor); 2679 JS_FreeValue(ctx, error_proto); 2680 return -1; 2681 } 2682 JS_SetClassProto(ctx, js_sqlite3_error_class_id, error_proto); 2683 2684 if (JS_SetModuleExportList(ctx, m, tart_talercrypto_funcs, 2685 countof(tart_talercrypto_funcs)) < 0) { 2686 JS_FreeValue(ctx, error_constructor); 2687 return -1; 2688 } 2689 return JS_SetModuleExport(ctx, m, "Sqlite3Error", error_constructor); 2690 } 2691 2692 JSModuleDef *tart_init_module_talercrypto(JSContext *ctx, const char *module_name) 2693 { 2694 JSModuleDef *m; 2695 m = JS_NewCModule(ctx, module_name, tart_talercrypto_init); 2696 if (!m) { 2697 return NULL; 2698 } 2699 JS_AddModuleExportList(ctx, m, tart_talercrypto_funcs, 2700 countof(tart_talercrypto_funcs)); 2701 JS_AddModuleExport(ctx, m, "Sqlite3Error"); 2702 return m; 2703 }