MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 664 lines 21 kB view raw
1#include <compat.h> // IWYU pragma: keep 2 3#include <string.h> 4#include <time.h> 5#include <limits.h> 6#include <openssl/evp.h> 7#include <openssl/rand.h> 8 9#pragma GCC diagnostic push 10#pragma GCC diagnostic ignored "-Wimplicit-int-conversion" 11#include <uuidv7.h> 12#pragma GCC diagnostic pop 13 14#include "ant.h" 15#include "base64.h" 16#include "errors.h" 17#include "runtime.h" 18#include "modules/crypto.h" 19#include "modules/buffer.h" 20#include "modules/symbol.h" 21 22typedef enum { 23 CRYPTO_TEXT_UTF8 = 0, 24 CRYPTO_TEXT_HEX, 25 CRYPTO_TEXT_BASE64, 26 CRYPTO_TEXT_LATIN1 27} crypto_text_encoding_t; 28 29typedef struct { 30 EVP_MD_CTX *ctx; 31 const EVP_MD *md; 32 unsigned char digest[EVP_MAX_MD_SIZE]; 33 unsigned int digest_len; 34 bool finalized; 35} ant_hash_state_t; 36 37int crypto_fill_random(void *buf, size_t len) { 38 if (len == 0) return 0; 39 if (len > (size_t)INT_MAX) return -1; 40 return RAND_bytes((uint8_t *)buf, (int)len) == 1 ? 0 : -1; 41} 42 43static inline ant_value_t crypto_random_error(ant_t *js) { 44 return js_mkerr(js, "secure random generation failed"); 45} 46 47static ant_value_t crypto_format_uuid_v4(ant_t *js, const uint8_t uuid[16]) { 48 static char lut[256][2]; 49 static bool lut_init = false; 50 char uuid_str[36]; 51 52 if (!lut_init) { 53 static const char hex[] = "0123456789abcdef"; 54 for (int i = 0; i < 256; i++) { 55 lut[i][0] = hex[(unsigned)i >> 4]; 56 lut[i][1] = hex[(unsigned)i & 0x0f]; 57 } 58 lut_init = true; 59 } 60 61 memcpy(uuid_str + 0, lut[uuid[0]], 2); 62 memcpy(uuid_str + 2, lut[uuid[1]], 2); 63 memcpy(uuid_str + 4, lut[uuid[2]], 2); 64 memcpy(uuid_str + 6, lut[uuid[3]], 2); 65 66 uuid_str[8] = '-'; 67 memcpy(uuid_str + 9, lut[uuid[4]], 2); 68 memcpy(uuid_str + 11, lut[uuid[5]], 2); 69 70 uuid_str[13] = '-'; 71 memcpy(uuid_str + 14, lut[uuid[6]], 2); 72 memcpy(uuid_str + 16, lut[uuid[7]], 2); 73 74 uuid_str[18] = '-'; 75 memcpy(uuid_str + 19, lut[uuid[8]], 2); 76 memcpy(uuid_str + 21, lut[uuid[9]], 2); 77 78 uuid_str[23] = '-'; 79 memcpy(uuid_str + 24, lut[uuid[10]], 2); 80 memcpy(uuid_str + 26, lut[uuid[11]], 2); 81 memcpy(uuid_str + 28, lut[uuid[12]], 2); 82 memcpy(uuid_str + 30, lut[uuid[13]], 2); 83 memcpy(uuid_str + 32, lut[uuid[14]], 2); 84 memcpy(uuid_str + 34, lut[uuid[15]], 2); 85 86 return js_mkstr(js, uuid_str, sizeof(uuid_str)); 87} 88 89static ant_value_t crypto_make_buffer(ant_t *js, const uint8_t *data, size_t len) { 90 ArrayBufferData *buffer = create_array_buffer_data(len); 91 if (!buffer) return js_mkerr(js, "Failed to allocate Buffer"); 92 if (len > 0 && data) memcpy(buffer->data, data, len); 93 return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, len, "Buffer"); 94} 95 96static bool crypto_get_mutable_bytes(ant_value_t value, uint8_t **out, size_t *len) { 97 TypedArrayData *ta = buffer_get_typedarray_data(value); 98 if (ta) { 99 if (!ta->buffer || ta->buffer->is_detached) return false; 100 *out = ta->buffer->data + ta->byte_offset; 101 *len = ta->byte_length; 102 return true; 103 } 104 105 ArrayBufferData *ab = buffer_get_arraybuffer_data(value); 106 if (ab) { 107 if (ab->is_detached) return false; 108 *out = ab->data; 109 *len = ab->length; 110 return true; 111 } 112 113 return false; 114} 115 116static crypto_text_encoding_t crypto_parse_encoding(const char *str, size_t len) { 117 if (len == 3 && strncasecmp(str, "hex", 3) == 0) return CRYPTO_TEXT_HEX; 118 if (len == 6 && strncasecmp(str, "base64", 6) == 0) return CRYPTO_TEXT_BASE64; 119 if (len == 9 && strncasecmp(str, "base64url", 9) == 0) return CRYPTO_TEXT_BASE64; 120 121 if ( 122 (len == 5 && strncasecmp(str, "ascii", 5) == 0) || 123 (len == 6 && strncasecmp(str, "latin1", 6) == 0) || 124 (len == 6 && strncasecmp(str, "binary", 6) == 0) 125 ) return CRYPTO_TEXT_LATIN1; 126 127 return CRYPTO_TEXT_UTF8; 128} 129 130static uint8_t crypto_hex_nibble(char ch) { 131 if (ch >= '0' && ch <= '9') return (uint8_t)(ch - '0'); 132 if (ch >= 'a' && ch <= 'f') return (uint8_t)(10 + ch - 'a'); 133 if (ch >= 'A' && ch <= 'F') return (uint8_t)(10 + ch - 'A'); 134 return 0xFFu; 135} 136 137static uint8_t *crypto_decode_hex(const char *str, size_t len, size_t *out_len) { 138 if ((len & 1u) != 0) return NULL; 139 140 uint8_t *buf = malloc(len / 2u); 141 if (!buf) return NULL; 142 143 for (size_t i = 0; i < len; i += 2u) { 144 uint8_t hi = crypto_hex_nibble(str[i]); 145 uint8_t lo = crypto_hex_nibble(str[i + 1u]); 146 if (hi == 0xFFu || lo == 0xFFu) { 147 free(buf); 148 return NULL; 149 } 150 buf[i / 2u] = (uint8_t)((hi << 4u) | lo); 151 } 152 153 *out_len = len / 2u; 154 return buf; 155} 156 157static ant_value_t crypto_get_input_bytes( 158 ant_t *js, ant_value_t value, ant_value_t encoding_val, 159 const uint8_t **out_bytes, size_t *out_len, uint8_t **owned 160) { 161 const uint8_t *bytes = NULL; 162 size_t len = 0; 163 uint8_t *buf = NULL; 164 165 ant_value_t string_val; 166 size_t str_len = 0; 167 168 const char *str = NULL; 169 crypto_text_encoding_t enc = CRYPTO_TEXT_UTF8; 170 171 if (buffer_source_get_bytes(js, value, &bytes, &len)) { 172 *out_bytes = bytes; 173 *out_len = len; 174 *owned = NULL; 175 return js_mkundef(); 176 } 177 178 string_val = js_tostring_val(js, value); 179 if (is_err(string_val)) return string_val; 180 181 str = js_getstr(js, string_val, &str_len); 182 if (!str) return js_mkerr(js, "Failed to convert hash input to string"); 183 184 if (vtype(encoding_val) == T_STR) { 185 size_t enc_len = 0; 186 const char *enc_str = js_getstr(js, encoding_val, &enc_len); 187 if (enc_str) enc = crypto_parse_encoding(enc_str, enc_len); 188 } 189 190 switch (enc) { 191 case CRYPTO_TEXT_HEX: 192 buf = crypto_decode_hex(str, str_len, &len); 193 if (!buf) return js_mkerr(js, "Invalid hex string"); 194 bytes = buf; 195 break; 196 case CRYPTO_TEXT_BASE64: 197 buf = ant_base64_decode(str, str_len, &len); 198 if (!buf) return js_mkerr(js, "Invalid base64 string"); 199 bytes = buf; 200 break; 201 case CRYPTO_TEXT_LATIN1: 202 buf = malloc(str_len); 203 if (!buf) return js_mkerr(js, "Out of memory"); 204 for (size_t i = 0; i < str_len; i++) buf[i] = (uint8_t)str[i]; 205 bytes = buf; 206 len = str_len; 207 break; 208 case CRYPTO_TEXT_UTF8: 209 default: 210 bytes = (const uint8_t *)str; 211 len = str_len; 212 break; 213 } 214 215 *out_bytes = bytes; 216 *out_len = len; 217 *owned = buf; 218 return js_mkundef(); 219} 220 221static ant_hash_state_t *crypto_get_hash_state(ant_value_t value) { 222 ant_value_t slot = js_get_slot(value, SLOT_DATA); 223 if (vtype(slot) != T_NUM) return NULL; 224 225 ant_hash_state_t *state = (ant_hash_state_t *)(uintptr_t)js_getnum(slot); 226 return (state && state->ctx) ? state : NULL; 227} 228 229static ant_value_t crypto_require_hash_state( 230 ant_t *js, ant_value_t value, ant_hash_state_t **out_state 231) { 232 ant_hash_state_t *state = crypto_get_hash_state(value); 233 if (!state) return js_mkerr(js, "Invalid Hash state"); 234 *out_state = state; 235 return js_mkundef(); 236} 237 238static ant_value_t crypto_digest_result( 239 ant_t *js, const uint8_t *digest, size_t digest_len, ant_value_t encoding_val 240) { 241 if (vtype(encoding_val) != T_STR) return crypto_make_buffer(js, digest, digest_len); 242 243 size_t enc_len = 0; 244 const char *enc = js_getstr(js, encoding_val, &enc_len); 245 if (!enc) return crypto_make_buffer(js, digest, digest_len); 246 247 crypto_text_encoding_t encoding = crypto_parse_encoding(enc, enc_len); 248 249 if (encoding == CRYPTO_TEXT_HEX) { 250 char *hex = malloc(digest_len * 2u + 1u); 251 if (!hex) return js_mkerr(js, "Out of memory"); 252 for (size_t i = 0; i < digest_len; i++) { 253 snprintf(hex + (i * 2u), 3, "%02x", digest[i]); 254 } 255 ant_value_t result = js_mkstr(js, hex, digest_len * 2u); 256 free(hex); 257 return result; 258 } 259 260 if (encoding == CRYPTO_TEXT_BASE64) { 261 size_t out_len = 0; 262 char *encoded = ant_base64_encode(digest, digest_len, &out_len); 263 if (!encoded) return js_mkerr(js, "Failed to encode base64"); 264 265 if (enc_len == 9 && strncasecmp(enc, "base64url", 9) == 0) { 266 for (size_t i = 0; i < out_len; i++) { 267 if (encoded[i] == '+') encoded[i] = '-'; 268 else if (encoded[i] == '/') encoded[i] = '_'; 269 } 270 while (out_len > 0 && encoded[out_len - 1u] == '=') out_len--; 271 } 272 273 ant_value_t result = js_mkstr(js, encoded, out_len); 274 free(encoded); 275 276 return result; 277 } 278 279 return crypto_make_buffer(js, digest, digest_len); 280} 281 282int uuidv7_new(uint8_t *uuid_out) { 283 static uint8_t uuid_prev[16] = {0}; 284 static uint8_t rand_bytes[256] = {0}; 285 static size_t n_rand_consumed = sizeof(rand_bytes); 286 287 struct timespec tp; 288 clock_gettime(CLOCK_REALTIME, &tp); 289 uint64_t unix_ts_ms = (uint64_t)tp.tv_sec * 1000 + tp.tv_nsec / 1000000; 290 291 if (n_rand_consumed > sizeof(rand_bytes) - 10) { 292 if (crypto_fill_random(rand_bytes, sizeof(rand_bytes)) < 0) return -1; 293 n_rand_consumed = 0; 294 } 295 296 int8_t status = uuidv7_generate(uuid_prev, unix_ts_ms, &rand_bytes[n_rand_consumed], uuid_prev); 297 n_rand_consumed += uuidv7_status_n_rand_consumed(status); 298 299 memcpy(uuid_out, uuid_prev, 16); 300 return status; 301} 302 303// crypto.random() 304static ant_value_t js_crypto_random(ant_t *js, ant_value_t *args, int nargs) { 305 unsigned int value = 0; 306 if (crypto_fill_random(&value, sizeof(value)) < 0) return crypto_random_error(js); 307 return js_mknum((double)value); 308} 309 310// crypto.randomBytes(length) 311static ant_value_t js_crypto_random_bytes(ant_t *js, ant_value_t *args, int nargs) { 312 if (nargs < 1) { 313 return js_mkerr(js, "randomBytes requires a length argument"); 314 } 315 316 int length = (int)js_getnum(args[0]); 317 if (length <= 0 || length > 65536) { 318 return js_mkerr(js, "invalid length"); 319 } 320 321 unsigned char *random_bytes = malloc(length); 322 if (random_bytes == NULL) { 323 return js_mkerr(js, "memory allocation failed"); 324 } 325 326 if (crypto_fill_random(random_bytes, (size_t)length) < 0) { 327 free(random_bytes); 328 return crypto_random_error(js); 329 } 330 331 ant_value_t array = crypto_make_buffer(js, random_bytes, (size_t)length); 332 free(random_bytes); 333 334 return array; 335} 336 337// crypto.randomUUID() 338static ant_value_t js_crypto_random_uuid(ant_t *js, ant_value_t *args, int nargs) { 339 uint8_t uuid[16]; 340 if (crypto_fill_random(uuid, sizeof(uuid)) < 0) return crypto_random_error(js); 341 342 uuid[6] = (uint8_t)((uuid[6] & 0x0f) | 0x40); 343 uuid[8] = (uint8_t)((uuid[8] & 0x3f) | 0x80); 344 345 return crypto_format_uuid_v4(js, uuid); 346} 347 348// crypto.randomUUIDv7() 349static ant_value_t js_crypto_random_uuidv7(ant_t *js, ant_value_t *args, int nargs) { 350 uint8_t uuid[16]; 351 char uuid_str[37]; 352 353 int result = uuidv7_new(uuid); 354 if (result < 0) return js_mkerr(js, "UUIDv7 generation failed"); 355 356 uuidv7_to_string(uuid, uuid_str); 357 return js_mkstr(js, uuid_str, strlen(uuid_str)); 358} 359 360// crypto.getRandomValues(typedArray) 361static ant_value_t js_crypto_get_random_values(ant_t *js, ant_value_t *args, int nargs) { 362 if (nargs < 1) { 363 return js_mkerr(js, "getRandomValues requires a TypedArray argument"); 364 } 365 366 TypedArrayData *ta_data = buffer_get_typedarray_data(args[0]); 367 if (!ta_data || !ta_data->buffer) { 368 return js_mkerr(js, "argument must be a TypedArray"); 369 } 370 371 if (ta_data->byte_length > 65536) { 372 return js_mkerr(js, "TypedArray byte length exceeds 65536"); 373 } 374 375 uint8_t *ptr = ta_data->buffer->data + ta_data->byte_offset; 376 if (crypto_fill_random(ptr, ta_data->byte_length) < 0) return crypto_random_error(js); 377 378 return args[0]; 379} 380 381static ant_value_t js_crypto_random_fill_sync(ant_t *js, ant_value_t *args, int nargs) { 382 if (nargs < 1) return js_mkerr(js, "randomFillSync requires a target"); 383 384 uint8_t *bytes = NULL; 385 size_t len = 0; 386 if (!crypto_get_mutable_bytes(args[0], &bytes, &len)) { 387 return js_mkerr(js, "randomFillSync target must be a Buffer, TypedArray, or ArrayBuffer"); 388 } 389 390 size_t offset = 0; 391 if (nargs >= 2 && vtype(args[1]) != T_UNDEF) { 392 double num = js_to_number(js, args[1]); 393 if (num < 0) return js_mkerr(js, "randomFillSync offset must be non-negative"); 394 offset = (size_t)num; 395 } 396 397 size_t size = len - offset; 398 if (nargs >= 3 && vtype(args[2]) != T_UNDEF) { 399 double num = js_to_number(js, args[2]); 400 if (num < 0) return js_mkerr(js, "randomFillSync size must be non-negative"); 401 size = (size_t)num; 402 } 403 404 if (offset > len || size > len - offset) { 405 return js_mkerr(js, "randomFillSync range is out of bounds"); 406 } 407 408 if (crypto_fill_random(bytes + offset, size) < 0) return crypto_random_error(js); 409 return args[0]; 410} 411 412// TODO: extend subtle 413static ant_value_t crypto_subtle_get_algorithm_name(ant_t *js, ant_value_t algorithm) { 414 if (vtype(algorithm) == T_STR) return js_tostring_val(js, algorithm); 415 if (is_object_type(algorithm)) { 416 ant_value_t name = js_get(js, algorithm, "name"); 417 if (is_err(name)) return name; 418 return js_tostring_val(js, name); 419 } 420 return js_mkerr_typed(js, JS_ERR_TYPE, "Algorithm must be a string or object with a name"); 421} 422 423static ant_value_t crypto_subtle_digest_impl( 424 ant_t *js, ant_value_t algorithm, ant_value_t data 425) { 426 ant_value_t algo_val = crypto_subtle_get_algorithm_name(js, algorithm); 427 if (is_err(algo_val)) return algo_val; 428 429 size_t algo_len = 0; 430 const char *algo = js_getstr(js, algo_val, &algo_len); 431 if (!algo || algo_len == 0) { 432 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid digest algorithm"); 433 } 434 435 char algo_name[16]; 436 if (algo_len >= sizeof(algo_name)) { 437 return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported digest algorithm"); 438 } 439 440 for (size_t i = 0; i < algo_len; i++) { 441 char ch = algo[i]; 442 if (ch >= 'a' && ch <= 'z') ch = (char)(ch - ('a' - 'A')); 443 algo_name[i] = ch; 444 } 445 algo_name[algo_len] = '\0'; 446 447 const char *evp_name = NULL; 448 if (strcmp(algo_name, "SHA-1") == 0 || strcmp(algo_name, "SHA1") == 0) evp_name = "sha1"; 449 else if (strcmp(algo_name, "SHA-256") == 0 || strcmp(algo_name, "SHA256") == 0) evp_name = "sha256"; 450 else if (strcmp(algo_name, "SHA-384") == 0 || strcmp(algo_name, "SHA384") == 0) evp_name = "sha384"; 451 else if (strcmp(algo_name, "SHA-512") == 0 || strcmp(algo_name, "SHA512") == 0) evp_name = "sha512"; 452 else return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported digest algorithm"); 453 454 const EVP_MD *md = EVP_get_digestbyname(evp_name); 455 if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported digest algorithm"); 456 457 const uint8_t *bytes = NULL; 458 size_t len = 0; 459 if (!buffer_source_get_bytes(js, data, &bytes, &len)) { 460 return js_mkerr_typed(js, JS_ERR_TYPE, "digest data must be an ArrayBuffer, TypedArray, DataView, or Buffer"); 461 } 462 463 unsigned char digest[EVP_MAX_MD_SIZE]; 464 unsigned int digest_len = 0; 465 if (EVP_Digest(bytes, len, digest, &digest_len, md, NULL) != 1) { 466 return js_mkerr(js, "Digest failed"); 467 } 468 469 ArrayBufferData *buffer = create_array_buffer_data(digest_len); 470 if (!buffer) return js_mkerr(js, "Out of memory"); 471 if (digest_len > 0) memcpy(buffer->data, digest, digest_len); 472 473 return create_arraybuffer_obj(js, buffer); 474} 475 476static ant_value_t js_crypto_subtle_digest(ant_t *js, ant_value_t *args, int nargs) { 477 ant_value_t promise = js_mkpromise(js); 478 if (nargs < 2) { 479 js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "subtle.digest requires algorithm and data")); 480 return promise; 481 } 482 483 ant_value_t result = crypto_subtle_digest_impl(js, args[0], args[1]); 484 if (is_err(result)) js_reject_promise(js, promise, result); 485 else js_resolve_promise(js, promise, result); 486 487 return promise; 488} 489 490static ant_value_t js_hash_update(ant_t *js, ant_value_t *args, int nargs) { 491 ant_value_t this_val = js_getthis(js); 492 ant_hash_state_t *state = NULL; 493 494 const uint8_t *bytes = NULL; 495 size_t len = 0; 496 497 uint8_t *owned = NULL; 498 ant_value_t err; 499 500 err = crypto_require_hash_state(js, this_val, &state); 501 if (is_err(err)) return err; 502 if (state->finalized) return js_mkerr(js, "Hash digest already called"); 503 if (nargs < 1) return js_mkerr(js, "Hash.update requires data"); 504 505 ant_value_t encoding = (nargs >= 2) ? args[1] : js_mkundef(); 506 err = crypto_get_input_bytes(js, args[0], encoding, &bytes, &len, &owned); 507 if (is_err(err)) goto cleanup; 508 509 if (EVP_DigestUpdate(state->ctx, bytes, len) != 1) { 510 err = js_mkerr(js, "Hash update failed"); 511 goto cleanup; 512 } 513 514 err = this_val; 515 516cleanup: 517 if (owned) free(owned); 518 return err; 519} 520 521static ant_value_t js_hash_digest(ant_t *js, ant_value_t *args, int nargs) { 522 ant_hash_state_t *state = NULL; 523 ant_value_t err = crypto_require_hash_state(js, js_getthis(js), &state); 524 525 if (is_err(err)) return err; 526 if (state->finalized) return js_mkerr(js, "Hash digest already called"); 527 528 if (EVP_DigestFinal_ex(state->ctx, state->digest, &state->digest_len) != 1) { 529 return js_mkerr(js, "Hash digest failed"); 530 } 531 532 state->finalized = true; 533 EVP_MD_CTX_free(state->ctx); 534 state->ctx = NULL; 535 536 return crypto_digest_result(js, state->digest, state->digest_len, nargs >= 1 ? args[0] : js_mkundef()); 537} 538 539static ant_value_t js_crypto_create_hash(ant_t *js, ant_value_t *args, int nargs) { 540 if (nargs < 1) return js_mkerr(js, "createHash requires an algorithm"); 541 542 ant_value_t algo_val = js_tostring_val(js, args[0]); 543 if (is_err(algo_val)) return algo_val; 544 545 size_t algo_len = 0; 546 const char *algo = js_getstr(js, algo_val, &algo_len); 547 if (!algo || algo_len == 0) return js_mkerr(js, "Invalid hash algorithm"); 548 549 char *algo_name = strndup(algo, algo_len); 550 if (!algo_name) return js_mkerr(js, "Out of memory"); 551 552 const EVP_MD *md = EVP_get_digestbyname(algo_name); 553 free(algo_name); 554 if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported hash algorithm"); 555 556 ant_hash_state_t *state = calloc(1, sizeof(*state)); 557 if (!state) return js_mkerr(js, "Out of memory"); 558 559 state->ctx = EVP_MD_CTX_new(); 560 state->md = md; 561 if (!state->ctx || EVP_DigestInit_ex(state->ctx, md, NULL) != 1) { 562 if (state->ctx) EVP_MD_CTX_free(state->ctx); 563 free(state); 564 return js_mkerr(js, "Failed to initialize hash"); 565 } 566 567 ant_value_t obj = js_mkobj(js); 568 js_set(js, obj, "update", js_mkfun(js_hash_update)); 569 js_set(js, obj, "digest", js_mkfun(js_hash_digest)); 570 571 js_set_slot(obj, SLOT_DATA, ANT_PTR(state)); 572 js_set_sym(js, obj, get_toStringTag_sym(), js_mkstr(js, "Hash", 4)); 573 574 return obj; 575} 576 577static ant_value_t js_crypto_hash(ant_t *js, ant_value_t *args, int nargs) { 578 ant_value_t algo_val; 579 ant_value_t output_encoding; 580 581 const char *algo; 582 size_t algo_len = 0; 583 char *algo_name = NULL; 584 585 const EVP_MD *md = NULL; 586 const uint8_t *bytes = NULL; 587 588 size_t len = 0; 589 uint8_t *owned = NULL; 590 591 unsigned char digest[EVP_MAX_MD_SIZE]; 592 unsigned int digest_len = 0; 593 ant_value_t err; 594 595 if (nargs < 2) return js_mkerr(js, "hash requires algorithm and data"); 596 output_encoding = nargs >= 3 ? args[2] : js_mkundef(); 597 598 algo_val = js_tostring_val(js, args[0]); 599 if (is_err(algo_val)) return algo_val; 600 601 algo = js_getstr(js, algo_val, &algo_len); 602 if (!algo || algo_len == 0) return js_mkerr(js, "Invalid hash algorithm"); 603 604 algo_name = strndup(algo, algo_len); 605 if (!algo_name) return js_mkerr(js, "Out of memory"); 606 607 md = EVP_get_digestbyname(algo_name); 608 free(algo_name); 609 if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported hash algorithm"); 610 611 err = crypto_get_input_bytes(js, args[1], js_mkundef(), &bytes, &len, &owned); 612 if (is_err(err)) goto cleanup; 613 614 if (EVP_Digest(bytes, len, digest, &digest_len, md, NULL) != 1) { 615 err = js_mkerr(js, "Hash failed"); 616 goto cleanup; 617 } 618 619 err = crypto_digest_result(js, digest, digest_len, output_encoding); 620 621cleanup: 622 if (owned) free(owned); 623 return err; 624} 625 626static ant_value_t create_crypto_obj(ant_t *js) { 627 ant_value_t crypto_obj = js_mkobj(js); 628 ant_value_t subtle_obj = js_mkobj(js); 629 630 js_set(js, crypto_obj, "random", js_mkfun(js_crypto_random)); 631 js_set(js, crypto_obj, "randomBytes", js_mkfun(js_crypto_random_bytes)); 632 js_set(js, crypto_obj, "randomFillSync", js_mkfun(js_crypto_random_fill_sync)); 633 js_set(js, crypto_obj, "randomUUID", js_mkfun(js_crypto_random_uuid)); 634 js_set(js, crypto_obj, "randomUUIDv7", js_mkfun(js_crypto_random_uuidv7)); 635 js_set(js, crypto_obj, "getRandomValues", js_mkfun(js_crypto_get_random_values)); 636 js_set(js, subtle_obj, "digest", js_mkfun(js_crypto_subtle_digest)); 637 js_set_sym(js, subtle_obj, get_toStringTag_sym(), js_mkstr(js, "SubtleCrypto", 12)); 638 js_set(js, crypto_obj, "subtle", subtle_obj); 639 640 js_set_sym(js, crypto_obj, get_toStringTag_sym(), js_mkstr(js, "Crypto", 6)); 641 return crypto_obj; 642} 643 644void init_crypto_module() { 645 ant_t *js = rt->js; 646 ant_value_t crypto_obj = create_crypto_obj(js); 647 js_set(js, js_glob(js), "crypto", crypto_obj); 648} 649 650ant_value_t crypto_library(ant_t *js) { 651 ant_value_t lib = js_mkobj(js); 652 ant_value_t webcrypto = create_crypto_obj(js); 653 654 js_set(js, lib, "webcrypto", webcrypto); 655 js_set(js, lib, "hash", js_mkfun(js_crypto_hash)); 656 js_set(js, lib, "createHash", js_mkfun(js_crypto_create_hash)); 657 js_set(js, lib, "randomBytes", js_mkfun(js_crypto_random_bytes)); 658 js_set(js, lib, "randomFillSync", js_mkfun(js_crypto_random_fill_sync)); 659 js_set(js, lib, "randomUUID", js_mkfun(js_crypto_random_uuid)); 660 js_set(js, lib, "getRandomValues", js_mkfun(js_crypto_get_random_values)); 661 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "crypto", 6)); 662 663 return lib; 664}