MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
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}