Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300#include <compat.h> // IWYU pragma: keep
#include <string.h>#include <time.h>#include <limits.h>#include <math.h>#include <openssl/ec.h>#include <openssl/evp.h>#include <openssl/hmac.h>#include <openssl/mem.h>#include <openssl/obj.h>#include <openssl/rand.h>
#pragma GCC diagnostic push#pragma GCC diagnostic ignored "-Wimplicit-int-conversion"#include <uuidv7.h>#pragma GCC diagnostic pop
#include "ant.h"#include "cfunc.h"#include "ascii.h"#include "ptr.h"#include "gc.h"#include "utf8.h"#include "base64.h"#include "errors.h"#include "gc/roots.h"#include "sync.h"#include "gc/strings.h"#include "modules/crypto.h"#include "modules/buffer.h"#include "modules/domexception.h"
typedef enum { CRYPTO_TEXT_UTF8 = 0, CRYPTO_TEXT_HEX, CRYPTO_TEXT_BASE64, CRYPTO_TEXT_LATIN1} crypto_text_encoding_t;
typedef struct { EVP_MD_CTX *ctx; const EVP_MD *md; unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_len; bool finalized;} ant_hash_state_t;
typedef struct { HMAC_CTX *ctx; const EVP_MD *md; unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_len; bool finalized;} ant_hmac_state_t;
typedef enum { CRYPTO_KEY_HMAC = 1, CRYPTO_KEY_AES_GCM, CRYPTO_KEY_PBKDF2,} ant_crypto_key_kind_t;
typedef enum { CRYPTO_KEY_USAGE_ENCRYPT = 1u << 0, CRYPTO_KEY_USAGE_DECRYPT = 1u << 1, CRYPTO_KEY_USAGE_SIGN = 1u << 2, CRYPTO_KEY_USAGE_VERIFY = 1u << 3, CRYPTO_KEY_USAGE_DERIVE_KEY = 1u << 4, CRYPTO_KEY_USAGE_DERIVE_BITS = 1u << 5, CRYPTO_KEY_USAGE_WRAP_KEY = 1u << 6, CRYPTO_KEY_USAGE_UNWRAP_KEY = 1u << 7,} ant_crypto_key_usage_t;
typedef struct { ant_crypto_key_kind_t kind; const EVP_MD *md; uint8_t *key; size_t key_len; uint8_t usages; bool extractable;} ant_crypto_key_t;
typedef struct { EVP_CIPHER_CTX *ctx; bool encrypt; bool finalized; bool is_gcm; uint8_t auth_tag[16]; size_t auth_tag_len;} ant_cipher_state_t;
static constexpr uint32_t CRYPTO_HASH_NATIVE_TAG = Ant_NativeTag("HASH");static constexpr uint32_t CRYPTO_HMAC_NATIVE_TAG = Ant_NativeTag("HMAC");static constexpr uint32_t CRYPTO_KEY_NATIVE_TAG = Ant_NativeTag("CKEY");static constexpr uint32_t CRYPTO_CIPHER_NATIVE_TAG = Ant_NativeTag("CIPH");
static const char *const k_crypto_cipher_names[] = { "aes-128-cbc", "aes-128-ctr", "aes-128-ecb", "aes-128-gcm", "aes-128-ofb", "aes-192-cbc", "aes-192-ctr", "aes-192-ecb", "aes-192-gcm", "aes-192-ofb", "aes-256-cbc", "aes-256-ctr", "aes-256-ecb", "aes-256-gcm", "aes-256-ofb", "des-cbc", "des-ecb", "des-ede", "des-ede-cbc", "des-ede3-cbc", "rc2-cbc", "rc4",};
static const char *const k_crypto_hash_names[] = { "DSA-SHA", "DSA-SHA1", "MD4", "MD5", "MD5-SHA1", "RSA-MD5", "RSA-SHA1", "RSA-SHA224", "RSA-SHA256", "RSA-SHA384", "RSA-SHA512", "dsaWithSHA", "dsaWithSHA1", "ecdsa-with-SHA1", "md4", "md5", "md5-sha1", "md5WithRSAEncryption", "sha1", "sha1WithRSAEncryption", "sha224", "sha224WithRSAEncryption", "sha256", "sha256WithRSAEncryption", "sha384", "sha384WithRSAEncryption", "sha512", "sha512-256", "sha512WithRSAEncryption",};
static const char *const k_crypto_curve_names[] = { "prime256v1", "secp224r1", "secp384r1", "secp521r1",};
typedef enum { CRYPTO_NAME_CIPHER = 0, CRYPTO_NAME_HASH, CRYPTO_NAME_CURVE} crypto_name_kind_t;
static ant_value_t crypto_webcrypto_string(ant_t *js, ant_value_t value) { if (vtype(value) == kTypeString) return value; if (is_object_type(value) || vtype(value) == kTypeBuiltin) value = js_to_primitive(js, value, 1); if (is_err(value)) return value; if (vtype(value) == kTypeSymbol) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert a Symbol to a string"); return js_tostring_val(js, value);}
static ant_value_t crypto_algorithm_member(ant_t *js, ant_value_t object, const char *interned_name) { if (!interned_name) return js_mkerr(js, "oom"); if (vtype(object) != kTypeObject) return js_get(js, object, interned_name);
ant_object_t *obj = js_obj_ptr(object); if (obj->flags.is_exotic) return js_get(js, object, interned_name);
int32_t slot = ant_shape_lookup_interned(obj->shape, interned_name); if (slot >= 0) { const ant_shape_prop_t *prop = ant_shape_prop_at(obj->shape, (uint32_t)slot); if (prop->has_getter || prop->has_setter) return js_get(js, object, interned_name); return ant_object_prop_get_unchecked(obj, (uint32_t)slot); }
if (js_get_proto(js, object) != js->sym.object_proto) return js_get(js, object, interned_name); ant_object_t *proto = js_obj_ptr(js->sym.object_proto); if (proto->flags.is_exotic || ant_shape_lookup_interned(proto->shape, interned_name) >= 0 || vtype(js_get_proto(js, js->sym.object_proto)) != kTypeNull) return js_get(js, object, interned_name);
return js_mkundef();}
static ant_value_t crypto_subtle_get_algorithm_name(ant_t *js, ant_value_t algorithm) { if (vtype(algorithm) == kTypeString) return algorithm; if (is_object_type(algorithm)) { ant_value_t name = crypto_algorithm_member(js, algorithm, js->intern.name); if (is_err(name)) return name; if (vtype(name) == kTypeUndefined) return js_mkerr_typed(js, JS_ERR_TYPE, "Algorithm.name is required"); return crypto_webcrypto_string(js, name); } return js_mkerr_typed(js, JS_ERR_TYPE, "Algorithm must be a string or object with a name");}
static void crypto_hash_state_free(ant_hash_state_t *state) { if (!state) return; if (state->ctx) EVP_MD_CTX_free(state->ctx); free(state);}
static void crypto_hash_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); ant_hash_state_t *state = (ant_hash_state_t *)js_get_native(value, CRYPTO_HASH_NATIVE_TAG);
if (!state) return; js_clear_native(value, CRYPTO_HASH_NATIVE_TAG); crypto_hash_state_free(state);}
static void crypto_hmac_state_free(ant_hmac_state_t *state) { if (!state) return; if (state->ctx) HMAC_CTX_free(state->ctx); free(state);}
static void crypto_hmac_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); ant_hmac_state_t *state = (ant_hmac_state_t *)js_get_native(value, CRYPTO_HMAC_NATIVE_TAG);
if (!state) return; js_clear_native(value, CRYPTO_HMAC_NATIVE_TAG); crypto_hmac_state_free(state);}
static void crypto_key_free(ant_crypto_key_t *key) { if (!key) return; OPENSSL_cleanse(key->key, key->key_len); free(key);}
static void crypto_key_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); ant_crypto_key_t *key = (ant_crypto_key_t *)js_get_native(value, CRYPTO_KEY_NATIVE_TAG);
if (!key) return; js_clear_native(value, CRYPTO_KEY_NATIVE_TAG); crypto_key_free(key);}
static void crypto_cipher_state_free(ant_cipher_state_t *state) { if (!state) return; if (state->ctx) EVP_CIPHER_CTX_free(state->ctx); free(state);}
static void crypto_cipher_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); ant_cipher_state_t *state = (ant_cipher_state_t *)js_get_native(value, CRYPTO_CIPHER_NATIVE_TAG);
if (!state) return; js_clear_native(value, CRYPTO_CIPHER_NATIVE_TAG); crypto_cipher_state_free(state);}
int crypto_fill_random(void *buf, size_t len) { if (len == 0) return 0; if (len > (size_t)INT_MAX) return -1; return RAND_bytes((uint8_t *)buf, (int)len) == 1 ? 0 : -1;}
static inline ant_value_t crypto_random_error(ant_t *js) { return js_mkerr(js, "secure random generation failed");}
#define UUID_HEX_DIGIT(n) ((char)((n) < 10 ? '0' + (n) : 'a' - 10 + (n)))#define UUID_HEX2(i) { UUID_HEX_DIGIT((i) >> 4), UUID_HEX_DIGIT((i) & 15) }#define UUID_HEX16(h) \ UUID_HEX2((h) * 16 + 0), UUID_HEX2((h) * 16 + 1), UUID_HEX2((h) * 16 + 2), UUID_HEX2((h) * 16 + 3), \ UUID_HEX2((h) * 16 + 4), UUID_HEX2((h) * 16 + 5), UUID_HEX2((h) * 16 + 6), UUID_HEX2((h) * 16 + 7), \ UUID_HEX2((h) * 16 + 8), UUID_HEX2((h) * 16 + 9), UUID_HEX2((h) * 16 + 10), UUID_HEX2((h) * 16 + 11), \ UUID_HEX2((h) * 16 + 12), UUID_HEX2((h) * 16 + 13), UUID_HEX2((h) * 16 + 14), UUID_HEX2((h) * 16 + 15)
static const char uuid_hex_lut[256][2] = { UUID_HEX16(0), UUID_HEX16(1), UUID_HEX16(2), UUID_HEX16(3), UUID_HEX16(4), UUID_HEX16(5), UUID_HEX16(6), UUID_HEX16(7), UUID_HEX16(8), UUID_HEX16(9), UUID_HEX16(10), UUID_HEX16(11), UUID_HEX16(12), UUID_HEX16(13), UUID_HEX16(14), UUID_HEX16(15),};
static inline void crypto_format_uuid_v4(const uint8_t uuid[16], char out[37]) { const char (*lut)[2] = uuid_hex_lut;
memcpy(out + 0, lut[uuid[0]], 2); memcpy(out + 2, lut[uuid[1]], 2); memcpy(out + 4, lut[uuid[2]], 2); memcpy(out + 6, lut[uuid[3]], 2); out[8] = '-'; memcpy(out + 9, lut[uuid[4]], 2); memcpy(out + 11, lut[uuid[5]], 2); out[13] = '-'; memcpy(out + 14, lut[uuid[6]], 2); memcpy(out + 16, lut[uuid[7]], 2); out[18] = '-'; memcpy(out + 19, lut[uuid[8]], 2); memcpy(out + 21, lut[uuid[9]], 2); out[23] = '-'; memcpy(out + 24, lut[uuid[10]], 2); memcpy(out + 26, lut[uuid[11]], 2); memcpy(out + 28, lut[uuid[12]], 2); memcpy(out + 30, lut[uuid[13]], 2); memcpy(out + 32, lut[uuid[14]], 2); memcpy(out + 34, lut[uuid[15]], 2); out[36] = '\0';}
int crypto_random_uuid(char out[37]) { uint8_t uuid[16]; if (!out || crypto_fill_random(uuid, sizeof(uuid)) < 0) return -1;
uuid[6] = (uint8_t)((uuid[6] & 0x0f) | 0x40); uuid[8] = (uint8_t)((uuid[8] & 0x3f) | 0x80);
crypto_format_uuid_v4(uuid, out); return 0;}
static ant_value_t crypto_make_buffer(ant_t *js, const uint8_t *data, size_t len) { ArrayBufferData *buffer = create_array_buffer_data(js, len); if (!buffer) return js_mkerr(js, "Failed to allocate Buffer"); if (len > 0 && data) memcpy(buffer->data, data, len); return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, len, js->builtins.buffer_proto);}
static bool crypto_get_mutable_bytes(ant_value_t value, uint8_t **out, size_t *len) { TypedArrayData *ta = buffer_get_typedarray_data(value); if (ta) { Ant_Buffer_GetViewBytes(ta, out, len); return true; }
ArrayBufferData *ab = buffer_get_arraybuffer_data(value); if (!ab) return false; bool live = Ant_Buffer_IsLive(ab); *out = live ? ab->data : NULL; *len = live ? ab->length : 0; return true;}
static crypto_text_encoding_t crypto_parse_encoding(const char *str, size_t len) { if (len == 3 && strncasecmp(str, "hex", 3) == 0) return CRYPTO_TEXT_HEX; if (len == 6 && strncasecmp(str, "base64", 6) == 0) return CRYPTO_TEXT_BASE64; if (len == 9 && strncasecmp(str, "base64url", 9) == 0) return CRYPTO_TEXT_BASE64; if ( (len == 5 && strncasecmp(str, "ascii", 5) == 0) || (len == 6 && strncasecmp(str, "latin1", 6) == 0) || (len == 6 && strncasecmp(str, "binary", 6) == 0) ) return CRYPTO_TEXT_LATIN1; return CRYPTO_TEXT_UTF8;}
static uint8_t *crypto_decode_hex(const char *str, size_t len, size_t *out_len) { if ((len & 1u) != 0) return NULL;
uint8_t *buf = malloc(len / 2u); if (!buf) return NULL;
for (size_t i = 0; i < len; i += 2u) { int hi = ant_ascii_hex_value((unsigned char)str[i]); int lo = ant_ascii_hex_value((unsigned char)str[i + 1u]); if (hi < 0 || lo < 0) { free(buf); return NULL; } buf[i / 2u] = (uint8_t)((hi << 4u) | lo); }
*out_len = len / 2u; return buf;}
static ant_value_t crypto_get_input_bytes( ant_t *js, ant_value_t value, ant_value_t encoding_val, const uint8_t **out_bytes, size_t *out_len, uint8_t **owned) { const uint8_t *bytes = NULL; size_t len = 0; uint8_t *buf = NULL; ant_value_t string_val; size_t str_len = 0; const char *str = NULL; crypto_text_encoding_t enc = CRYPTO_TEXT_UTF8;
if (buffer_source_get_bytes(js, value, &bytes, &len)) { *out_bytes = bytes; *out_len = len; *owned = NULL; return js_mkundef(); }
string_val = js_tostring_val(js, value); if (is_err(string_val)) return string_val;
str = js_getstr(js, string_val, &str_len); if (!str) return js_mkerr(js, "Failed to convert hash input to string");
if (vtype(encoding_val) == kTypeString) { size_t enc_len = 0; const char *enc_str = js_getstr(js, encoding_val, &enc_len); if (enc_str) enc = crypto_parse_encoding(enc_str, enc_len); }
switch (enc) { case CRYPTO_TEXT_HEX: buf = crypto_decode_hex(str, str_len, &len); if (!buf) return js_mkerr(js, "Invalid hex string"); bytes = buf; break; case CRYPTO_TEXT_BASE64: buf = ant_base64_decode_loose(str, str_len, &len); if (!buf) return js_mkerr(js, "Out of memory"); bytes = buf; break; case CRYPTO_TEXT_LATIN1: { size_t units = (size_t)str_utf16_len(js, string_val); buf = malloc(units == 0 ? 1 : units); if (!buf) return js_mkerr(js, "Out of memory"); for (size_t i = 0; i < units; i++) buf[i] = (uint8_t)utf16_code_unit_at(str, str_len, i); bytes = buf; len = units; break; } case CRYPTO_TEXT_UTF8: default: bytes = (const uint8_t *)str; len = str_len; break; }
*out_bytes = bytes; *out_len = len; *owned = buf; return js_mkundef();}
static const EVP_MD *crypto_digest_from_name(const char *algo, size_t algo_len) { if (!algo || algo_len == 0) return NULL; char *algo_name = strndup(algo, algo_len); if (!algo_name) return NULL; const EVP_MD *md = EVP_get_digestbyname(algo_name); free(algo_name); return md;}
static const EVP_MD *crypto_digest_from_algorithm(ant_t *js, ant_value_t algorithm) { ant_value_t algo_val = crypto_subtle_get_algorithm_name(js, algorithm); if (is_err(algo_val)) return NULL;
size_t algo_len = 0; const char *algo = js_getstr(js, algo_val, &algo_len); if (!algo || algo_len == 0) return NULL;
if ( (algo_len == 5 && strncasecmp(algo, "SHA-1", 5) == 0) || (algo_len == 4 && strncasecmp(algo, "SHA1", 4) == 0) ) return EVP_get_digestbyname("sha1"); if ( (algo_len == 7 && strncasecmp(algo, "SHA-256", 7) == 0) || (algo_len == 6 && strncasecmp(algo, "SHA256", 6) == 0) ) return EVP_get_digestbyname("sha256"); if ( (algo_len == 7 && strncasecmp(algo, "SHA-384", 7) == 0) || (algo_len == 6 && strncasecmp(algo, "SHA384", 6) == 0) ) return EVP_get_digestbyname("sha384"); if ( (algo_len == 7 && strncasecmp(algo, "SHA-512", 7) == 0) || (algo_len == 6 && strncasecmp(algo, "SHA512", 6) == 0) ) return EVP_get_digestbyname("sha512");
return crypto_digest_from_name(algo, algo_len);}
static unsigned crypto_hash_record_index(const EVP_MD *md) { if (!md) return 0; switch (EVP_MD_type(md)) { case NID_sha1: return 4; case NID_sha256: return 5; case NID_sha384: return 6; case NID_sha512: return 7; default: return 0; }}
static ant_value_t crypto_dom_exception_error(ant_t *js, const char *message, const char *name) { return js_throw(js, make_dom_exception(js, message, name));}
static ant_value_t crypto_read_key_length( ant_t *js, ant_value_t object, bool required, uint32_t maximum, bool *present, uint32_t *result) { ant_value_t value = crypto_algorithm_member(js, object, js->intern.length); if (is_err(value)) return value; if (vtype(value) == kTypeUndefined) { *present = false; if (required) return js_mkerr_typed(js, JS_ERR_TYPE, "Algorithm.length is required"); return js_mkundef(); }
if (is_object_type(value) || vtype(value) == kTypeBuiltin) value = js_to_primitive(js, value, 2); if (is_err(value)) return value; if (vtype(value) == kTypeBigInt || vtype(value) == kTypeSymbol) return js_mkerr_typed(js, JS_ERR_TYPE, "Algorithm.length must be a number");
double number = trunc(js_to_number(js, value)); if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js);
if (!isfinite(number) || number < 0 || number > (double)maximum) return js_mkerr_typed(js, JS_ERR_TYPE, "Algorithm.length is outside its accepted range");
*present = true; *result = (uint32_t)number;
return js_mkundef();}
static ant_value_t crypto_get_hmac_digest( ant_t *js, ant_value_t algorithm, const EVP_MD **result) { if (!is_object_type(algorithm)) return js_mkerr_typed(js, JS_ERR_TYPE, "HMAC algorithm must be an object");
ant_value_t hash = crypto_algorithm_member(js, algorithm, intern_string(js, "hash", 4)); if (is_err(hash)) return hash; if (vtype(hash) == kTypeUndefined) return js_mkerr_typed(js, JS_ERR_TYPE, "HMAC algorithm.hash is required");
ant_value_t name_value = crypto_subtle_get_algorithm_name(js, hash); if (is_err(name_value)) return name_value; size_t len = 0; const char *name = js_getstr(js, name_value, &len); const EVP_MD *md = NULL; if (len == 5 && strncasecmp(name, "SHA-1", len) == 0) md = EVP_sha1(); else if (len == 7 && strncasecmp(name, "SHA-256", len) == 0) md = EVP_sha256(); else if (len == 7 && strncasecmp(name, "SHA-384", len) == 0) md = EVP_sha384(); else if (len == 7 && strncasecmp(name, "SHA-512", len) == 0) md = EVP_sha512(); if (!md) return crypto_dom_exception_error(js, "Unsupported HMAC hash algorithm", "NotSupportedError");
*result = md; return js_mkundef();}
static bool crypto_algorithm_is(ant_t *js, ant_value_t algorithm, const char *expected) { ant_value_t name_val = crypto_subtle_get_algorithm_name(js, algorithm); if (is_err(name_val)) return false; size_t len = 0; const char *name = js_getstr(js, name_val, &len); return name && strlen(expected) == len && strncasecmp(name, expected, len) == 0;}
static const EVP_CIPHER *crypto_aes_gcm_cipher_for_key(size_t key_len) { switch (key_len) { case 16: return EVP_aes_128_gcm(); case 24: return EVP_aes_192_gcm(); case 32: return EVP_aes_256_gcm(); default: return NULL; }}
static const EVP_CIPHER *crypto_cipher_from_name(const char *name, size_t len) { char *copy = strndup(name, len); if (!copy) return NULL; const EVP_CIPHER *cipher = EVP_get_cipherbyname(copy); free(copy); return cipher;}
static ant_hash_state_t *crypto_get_hash_state(ant_value_t value) { ant_hash_state_t *state = (ant_hash_state_t *)js_get_native(value, CRYPTO_HASH_NATIVE_TAG); return (state && state->ctx) ? state : NULL;}
static ant_value_t crypto_require_hash_state( ant_t *js, ant_value_t value, ant_hash_state_t **out_state) { ant_hash_state_t *state = crypto_get_hash_state(value); if (!state) return js_mkerr(js, "Invalid Hash state"); *out_state = state; return js_mkundef();}
static ant_value_t crypto_digest_result( ant_t *js, const uint8_t *digest, size_t digest_len, ant_value_t encoding_val) { if (vtype(encoding_val) != kTypeString) return crypto_make_buffer(js, digest, digest_len);
size_t enc_len = 0; const char *enc = js_getstr(js, encoding_val, &enc_len); if (!enc) return crypto_make_buffer(js, digest, digest_len);
ant_value_t result = js_mkundef(); if (buffer_bytes_to_string(js, digest, digest_len, enc, enc_len, &result)) return result;
return crypto_make_buffer(js, digest, digest_len);}
static ant_mutex_t uuidv7_lock = ANT_MUTEX_INIT;static uint8_t uuidv7_prev[16] = {0};static uint8_t uuidv7_rand[256] = {0};static size_t uuidv7_rand_consumed = sizeof(uuidv7_rand);
int uuidv7_new(uint8_t *uuid_out) { struct timespec tp; clock_gettime(CLOCK_REALTIME, &tp); uint64_t unix_ts_ms = (uint64_t)tp.tv_sec * 1000 + tp.tv_nsec / 1000000; ant_mutex_lock(&uuidv7_lock); if (uuidv7_rand_consumed > sizeof(uuidv7_rand) - 10) { if (crypto_fill_random(uuidv7_rand, sizeof(uuidv7_rand)) < 0) { ant_mutex_unlock(&uuidv7_lock); return -1; } uuidv7_rand_consumed = 0; } int8_t status = uuidv7_generate(uuidv7_prev, unix_ts_ms, &uuidv7_rand[uuidv7_rand_consumed], uuidv7_prev); uuidv7_rand_consumed += uuidv7_status_n_rand_consumed(status); memcpy(uuid_out, uuidv7_prev, 16); ant_mutex_unlock(&uuidv7_lock); return status;}
// crypto.random()static ant_value_t js_crypto_random(ant_params_t) { unsigned int value = 0; if (crypto_fill_random(&value, sizeof(value)) < 0) return crypto_random_error(js); return js_mknum((double)value);}
// crypto.randomBytes(length)static ant_value_t js_crypto_random_bytes(ant_params_t) { if (nargs < 1) { return js_mkerr(js, "randomBytes requires a length argument"); }
int length = (int)js_getnum(args[0]); if (length <= 0 || length > 65536) { return js_mkerr(js, "invalid length"); } unsigned char *random_bytes = malloc(length); if (random_bytes == NULL) { return js_mkerr(js, "memory allocation failed"); } if (crypto_fill_random(random_bytes, (size_t)length) < 0) { free(random_bytes); return crypto_random_error(js); } ant_value_t array = crypto_make_buffer(js, random_bytes, (size_t)length); free(random_bytes); return array;}
// crypto.randomUUID()static ant_value_t js_crypto_random_uuid(ant_params_t) { char uuid[37]; if (crypto_random_uuid(uuid) < 0) return crypto_random_error(js); return js_mkstr(js, uuid, 36);}
// crypto.randomUUIDv7()static ant_value_t js_crypto_random_uuidv7(ant_params_t) { uint8_t uuid[16]; char uuid_str[37]; int result = uuidv7_new(uuid); if (result < 0) return js_mkerr(js, "UUIDv7 generation failed"); uuidv7_to_string(uuid, uuid_str); return js_mkstr(js, uuid_str, strlen(uuid_str));}
// crypto.getRandomValues(typedArray)static ant_value_t js_crypto_get_random_values(ant_params_t) { if (nargs < 1) return js_mkerr(js, "getRandomValues requires a TypedArray argument");
TypedArrayData *ta = buffer_get_typedarray_data(args[0]); if (!ta) return js_mkerr_typed(js, JS_ERR_TYPE, "getRandomValues requires a TypedArray argument");
uint8_t *ptr; size_t len; Ant_Buffer_GetViewBytes(ta, &ptr, &len); if (len > 65536) return js_mkerr(js, "TypedArray byte length exceeds 65536"); if (crypto_fill_random(ptr, len) < 0) return crypto_random_error(js); return args[0];}
static ant_value_t js_crypto_random_fill_sync(ant_params_t) { if (nargs < 1) return js_mkerr(js, "randomFillSync requires a target");
uint8_t *bytes = NULL; size_t len = 0; if (!crypto_get_mutable_bytes(args[0], &bytes, &len)) { return js_mkerr(js, "randomFillSync target must be a Buffer, TypedArray, or ArrayBuffer"); }
size_t offset = 0; if (nargs >= 2 && vtype(args[1]) != kTypeUndefined) { double num = js_to_number(js, args[1]); if (num < 0) return js_mkerr(js, "randomFillSync offset must be non-negative"); offset = (size_t)num; }
size_t size = len - offset; if (nargs >= 3 && vtype(args[2]) != kTypeUndefined) { double num = js_to_number(js, args[2]); if (num < 0) return js_mkerr(js, "randomFillSync size must be non-negative"); size = (size_t)num; }
if (offset > len || size > len - offset) { return js_mkerr(js, "randomFillSync range is out of bounds"); }
if (crypto_fill_random(bytes + offset, size) < 0) return crypto_random_error(js); return args[0];}
static ant_value_t crypto_make_filtered_name_array( ant_t *js, const char *const *names, size_t count, crypto_name_kind_t kind) { ant_value_t result = js_mkarr(js); if (is_err(result)) return result;
GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, result);
for (size_t i = 0; i < count; i++) { const char *name = names[i]; bool supported = false; switch (kind) { case CRYPTO_NAME_CIPHER: supported = EVP_get_cipherbyname(name) != NULL; break; case CRYPTO_NAME_HASH: supported = EVP_get_digestbyname(name) != NULL; break; case CRYPTO_NAME_CURVE: { int nid = OBJ_sn2nid(name); if (nid == NID_undef) break; EC_GROUP *group = EC_GROUP_new_by_curve_name(nid); if (!group) break; EC_GROUP_free(group); supported = true; break; }} if (!supported) continue; ant_value_t name_val = js_mkstr(js, name, strlen(name)); if (is_err(name_val)) { GC_ROOT_RESTORE(js, root_mark); return name_val; } js_arr_push(js, result, name_val); }
GC_ROOT_RESTORE(js, root_mark); return result;}
static ant_value_t js_crypto_get_ciphers(ant_params_t) {return crypto_make_filtered_name_array( js, k_crypto_cipher_names, sizeof(k_crypto_cipher_names) / sizeof(k_crypto_cipher_names[0]), CRYPTO_NAME_CIPHER);}
static ant_value_t js_crypto_get_hashes(ant_params_t) {return crypto_make_filtered_name_array( js, k_crypto_hash_names, sizeof(k_crypto_hash_names) / sizeof(k_crypto_hash_names[0]), CRYPTO_NAME_HASH);}
static ant_value_t js_crypto_get_curves(ant_params_t) {return crypto_make_filtered_name_array( js, k_crypto_curve_names, sizeof(k_crypto_curve_names) / sizeof(k_crypto_curve_names[0]), CRYPTO_NAME_CURVE);}
static ant_value_t js_crypto_timing_safe_equal(ant_params_t) { const uint8_t *left = NULL; const uint8_t *right = NULL; size_t left_len = 0; size_t right_len = 0;
if ( nargs < 2 || !buffer_source_get_bytes(js, args[0], &left, &left_len) || !buffer_source_get_bytes(js, args[1], &right, &right_len) ) return js_mkerr_typed( js, JS_ERR_TYPE, "timingSafeEqual arguments must be Buffer, TypedArray, DataView, or ArrayBuffer" );
if (left_len != right_len) return js_mkerr_typed(js, JS_ERR_RANGE, "Input buffers must have the same byte length");
return js_bool(CRYPTO_memcmp(left, right, left_len) == 0);}
static ant_value_t crypto_subtle_digest_impl( ant_t *js, ant_value_t algorithm, ant_value_t data) { ant_value_t algo_val = crypto_subtle_get_algorithm_name(js, algorithm); if (is_err(algo_val)) return algo_val;
size_t algo_len = 0; const char *algo = js_getstr(js, algo_val, &algo_len); if (!algo || algo_len == 0) { return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid digest algorithm"); }
char algo_name[16]; if (algo_len >= sizeof(algo_name)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported digest algorithm"); }
for (size_t i = 0; i < algo_len; i++) { char ch = algo[i]; if (ch >= 'a' && ch <= 'z') ch = (char)(ch - ('a' - 'A')); algo_name[i] = ch; } algo_name[algo_len] = '\0';
const char *evp_name = NULL; if (strcmp(algo_name, "SHA-1") == 0 || strcmp(algo_name, "SHA1") == 0) evp_name = "sha1"; else if (strcmp(algo_name, "SHA-256") == 0 || strcmp(algo_name, "SHA256") == 0) evp_name = "sha256"; else if (strcmp(algo_name, "SHA-384") == 0 || strcmp(algo_name, "SHA384") == 0) evp_name = "sha384"; else if (strcmp(algo_name, "SHA-512") == 0 || strcmp(algo_name, "SHA512") == 0) evp_name = "sha512"; else return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported digest algorithm");
const EVP_MD *md = EVP_get_digestbyname(evp_name); if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported digest algorithm");
const uint8_t *bytes = NULL; size_t len = 0; if (!buffer_source_get_bytes(js, data, &bytes, &len)) { return js_mkerr_typed(js, JS_ERR_TYPE, "digest data must be an ArrayBuffer, TypedArray, DataView, or Buffer"); }
unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_len = 0; if (EVP_Digest(bytes, len, digest, &digest_len, md, NULL) != 1) { return js_mkerr(js, "Digest failed"); }
return create_arraybuffer_copy(js, digest, digest_len);}
static ant_value_t crypto_subtle_call( ant_native_params_t, ant_cfunc_t impl) { GC_ROOT_SAVE(root_mark, js); ant_value_t promise = js_mkpromise(js); if (is_err(promise)) return promise; GC_ROOT_PIN(js, promise); ant_value_t result = impl(js, args, nargs, js_mkundef()); if (is_err(result) || Ant_Exception_Pending(js)) { ant_value_t reason = js_take_thrown(js, result); js_reject_promise(js, promise, reason); } else js_resolve_promise(js, promise, result); GC_ROOT_RESTORE(js, root_mark); return promise;}
static ant_value_t crypto_subtle_digest_args(ant_params_t) { if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "subtle.digest requires algorithm and data"); return crypto_subtle_digest_impl(js, args[0], args[1]);}
static ant_value_t js_crypto_subtle_digest(ant_params_t) { return crypto_subtle_call(js, args, nargs, crypto_subtle_digest_args);}
static const char *const crypto_key_usage_names[] = { "encrypt", "decrypt", "sign", "verify", "deriveKey", "deriveBits", "wrapKey", "unwrapKey"};
static uint8_t crypto_key_usage_bit(ant_t *js, ant_value_t string) { size_t len = 0; const char *name = js_getstr(js, string, &len); for (unsigned i = 0; i < 8; i++) if ( strlen(crypto_key_usage_names[i]) == len && memcmp(name, crypto_key_usage_names[i], len) == 0 ) return (uint8_t)(1u << i); return 0;}
static bool crypto_read_key_usage(ant_t *js, ant_value_t value, void *ctx) { ant_value_t string = crypto_webcrypto_string(js, value); if (is_err(string)) return false; uint8_t bit = crypto_key_usage_bit(js, string); if (bit) { *(uint8_t *)ctx |= bit; return true; } js_mkerr_typed(js, JS_ERR_TYPE, "Invalid WebCrypto key usage"); return false;}
static bool crypto_read_dense_key_usages(ant_t *js, ant_value_t input, uint8_t *mask) { if (vtype(input) != kTypeArray) return false; ant_object_t *array = js_obj_ptr(input); if (!array->flags.fast_array || array->flags.is_exotic || array->flags.may_have_holes || js_get_proto(js, input) != js->sym.array_proto) return false;
ant_offset_t symbol = (ant_offset_t)vdata(js->sym.iterator_sym); if (lkp_sym(input, symbol).obj) return false; ant_prop_loc_t method = lkp_sym(js->sym.array_proto, symbol); if (!method.obj || js_prop_load(method) != js->sym.array_values_fn) return false; const ant_shape_prop_t *prop = ant_shape_prop_at(method.obj->shape, method.slot); if (!prop || prop->has_getter || prop->has_setter || method.obj->flags.is_exotic) return false; ant_prop_loc_t next = lkp(js, js->sym.array_iterator_proto, "next", 4); if (!next.obj || js_prop_load(next) != js->mutable_roots.crypto_array_iterator_next) return false; prop = ant_shape_prop_at(next.obj->shape, next.slot); if (!prop || prop->has_getter || prop->has_setter || next.obj->flags.is_exotic) return false;
uint8_t usages = 0; for (uint32_t i = 0; i < array->u.array.len; i++) { ant_value_t value = array->u.array.data[i]; if (vtype(value) != kTypeString) return false; uint8_t bit = crypto_key_usage_bit(js, value); if (!bit) return false; usages |= bit; } *mask = usages; return true;}
static ant_value_t crypto_read_key_usages(ant_t *js, ant_value_t input, uint8_t *mask) { if (!is_object_type(input)) return js_mkerr_typed(js, JS_ERR_TYPE, "keyUsages must be an iterable sequence"); *mask = 0; if (crypto_read_dense_key_usages(js, input, mask)) return js_mkundef(); // TODO: align js_iter with WebCrypto sequence semantics (getters, cached next, // TypeError validation, and preserving conversion exceptions during IteratorClose). bool iterated = js_iter(js, input, crypto_read_key_usage, mask); if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); if (!iterated) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to iterate keyUsages"); return js_mkundef();}
static ant_value_t crypto_check_key_usages( ant_t *js, ant_crypto_key_kind_t kind, uint8_t usages) { uint8_t allowed; switch (kind) { case CRYPTO_KEY_AES_GCM: allowed = CRYPTO_KEY_USAGE_ENCRYPT | CRYPTO_KEY_USAGE_DECRYPT | CRYPTO_KEY_USAGE_WRAP_KEY | CRYPTO_KEY_USAGE_UNWRAP_KEY; break; case CRYPTO_KEY_HMAC: allowed = CRYPTO_KEY_USAGE_SIGN | CRYPTO_KEY_USAGE_VERIFY; break; default: allowed = CRYPTO_KEY_USAGE_DERIVE_KEY | CRYPTO_KEY_USAGE_DERIVE_BITS; break; } return (usages & ~allowed) ? crypto_dom_exception_error(js, "Unsupported key usage for algorithm", "SyntaxError") : js_mkundef();}
static ant_value_t crypto_require_nonempty_secret_usages( ant_t *js, uint8_t usage_mask) { return usage_mask == 0 ? crypto_dom_exception_error( js, "Usages cannot be empty when creating a secret key", "SyntaxError" ) : js_mkundef();}
static ant_value_t crypto_get_key_algorithm_kind( ant_t *js, ant_value_t algorithm, ant_crypto_key_kind_t *kind) { ant_value_t name_value = crypto_subtle_get_algorithm_name(js, algorithm); if (is_err(name_value)) return name_value;
size_t len = 0; const char *value = js_getstr(js, name_value, &len); if (value && len == 7 && strncasecmp(value, "AES-GCM", 7) == 0) { *kind = CRYPTO_KEY_AES_GCM; return js_mkundef(); } if (value && len == 4 && strncasecmp(value, "HMAC", 4) == 0) { *kind = CRYPTO_KEY_HMAC; return js_mkundef(); } if (value && len == 6 && strncasecmp(value, "PBKDF2", 6) == 0) { *kind = CRYPTO_KEY_PBKDF2; return js_mkundef(); } return crypto_dom_exception_error(js, "Unsupported key algorithm", "NotSupportedError");}
static ant_value_t crypto_read_key_format(ant_t *js, ant_value_t value, bool *raw) { ant_value_t string = crypto_webcrypto_string(js, value); if (is_err(string)) return string; size_t len = 0; const char *format = js_getstr(js, string, &len); *raw = len == 3 && memcmp(format, "raw", 3) == 0; if (*raw || (len == 3 && memcmp(format, "jwk", 3) == 0) || (len == 4 && memcmp(format, "spki", 4) == 0) || (len == 5 && memcmp(format, "pkcs8", 5) == 0)) return js_mkundef(); return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid CryptoKey format");}
static ant_value_t crypto_new_key_record(ant_t *js, unsigned index) { ant_value_t seed = js->mutable_roots.crypto_key_templates[index]; if (vtype(seed) != kTypeObject) { seed = Ant_Object_CreateBare(js); if (is_err(seed)) return seed; GC_ROOT_SAVE(mark, js); GC_ROOT_PIN(js, seed); if (index == 0) { mkprop(js, seed, ANT_STRING("type"), ANT_STRING("secret"), ANT_PROP_ATTR_ENUMERABLE); mkprop(js, seed, ANT_STRING("extractable"), js_false, ANT_PROP_ATTR_ENUMERABLE); mkprop(js, seed, ANT_STRING("algorithm"), js_mkundef(), ANT_PROP_ATTR_ENUMERABLE); mkprop(js, seed, ANT_STRING("usages"), js_mkundef(), ANT_PROP_ATTR_ENUMERABLE); js_set_tostringtag(js, seed, ANT_STRING("CryptoKey")); } else { static const char *const hashes[] = { "SHA-1", "SHA-256", "SHA-384", "SHA-512" }; ant_value_t name = index == CRYPTO_KEY_AES_GCM ? ANT_STRING("AES-GCM") : index == CRYPTO_KEY_HMAC ? ANT_STRING("HMAC") : index == CRYPTO_KEY_PBKDF2 ? ANT_STRING("PBKDF2") : js_mkstr(js, hashes[index - 4], strlen(hashes[index - 4])); js_mkprop_fast(js, seed, "name", 4, name); if (index == CRYPTO_KEY_AES_GCM || index == CRYPTO_KEY_HMAC) js_mkprop_fast(js, seed, "length", 6, js_mknum(0)); if (index == CRYPTO_KEY_HMAC) js_mkprop_fast(js, seed, "hash", 4, js_mkundef()); } GC_ROOT_RESTORE(js, mark); if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); js->mutable_roots.crypto_key_templates[index] = seed; }
return js_mkobj_from_template(js, seed);}
static inline void crypto_key_record_set(ant_t *js, ant_value_t obj, uint32_t slot, ant_value_t value) { ant_object_t *target = js_obj_ptr(obj); ant_object_prop_set_unchecked(target, slot, value); gc_write_barrier(js, target, value);}
static ant_value_t crypto_make_key_algorithm( ant_t *js, ant_crypto_key_kind_t kind, const EVP_MD *md, uint32_t key_bits) { ant_value_t algorithm = crypto_new_key_record(js, (unsigned)kind), hash = js_mkundef(); if (is_err(algorithm)) return algorithm; GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, algorithm); GC_ROOT_PIN(js, hash);
if (kind == CRYPTO_KEY_AES_GCM) { crypto_key_record_set(js, algorithm, 1, js_mknum((double)key_bits)); } else if (kind == CRYPTO_KEY_HMAC) { unsigned hash_index = crypto_hash_record_index(md); if (!hash_index) { ant_value_t error = crypto_dom_exception_error(js, "Unsupported HMAC hash algorithm", "NotSupportedError"); GC_ROOT_RESTORE(js, root_mark); return error; } hash = crypto_new_key_record(js, hash_index); if (is_err(hash)) { GC_ROOT_RESTORE(js, root_mark); return hash; } crypto_key_record_set(js, algorithm, 1, js_mknum((double)key_bits)); crypto_key_record_set(js, algorithm, 2, hash); }
GC_ROOT_RESTORE(js, root_mark); return algorithm;}
static ant_crypto_key_t *crypto_key_alloc(size_t len) { size_t capacity = len ? len : 1; if (capacity > SIZE_MAX - sizeof(ant_crypto_key_t)) return NULL; ant_crypto_key_t *key = malloc(sizeof(*key) + capacity); if (!key) return NULL; *key = (ant_crypto_key_t){ .key = (uint8_t *)(key + 1), .key_len = len }; return key;}
static ant_value_t crypto_make_key_object( ant_t *js, ant_crypto_key_t *key, uint32_t bits) { if (key->kind == CRYPTO_KEY_HMAC && bits % 8) key->key[key->key_len - 1] &= (uint8_t)(0xffu << (8 - bits % 8));
GC_ROOT_SAVE(root_mark, js); ant_value_t algorithm = js_mkundef(), usages = js_mkundef(), obj = js_mkundef(); GC_ROOT_PIN(js, algorithm); GC_ROOT_PIN(js, usages); GC_ROOT_PIN(js, obj); algorithm = crypto_make_key_algorithm(js, key->kind, key->md, bits); if (is_err(algorithm)) { obj = algorithm; goto cleanup; } ant_value_t usage_values[8]; uint32_t usage_count = 0; for (unsigned i = 0; i < 8; i++) { if (key->usages & (1u << i)) usage_values[usage_count++] = js->mutable_roots.crypto_usage_strings[i]; } usages = js_mkarr_dense_literal(js, usage_values, usage_count); if (is_err(usages)) { obj = usages; goto cleanup; } obj = crypto_new_key_record(js, 0); if (is_err(obj)) goto cleanup; crypto_key_record_set(js, obj, 1, js_bool(key->extractable)); crypto_key_record_set(js, obj, 2, algorithm); crypto_key_record_set(js, obj, 3, usages); if (Ant_Exception_Pending(js)) { obj = Ant_Exception_Current(js); goto cleanup; } js_set_native(obj, key, CRYPTO_KEY_NATIVE_TAG); js_set_finalizer(obj, crypto_key_finalize); key = NULL;
cleanup: crypto_key_free(key); GC_ROOT_RESTORE(js, root_mark); return obj;}
static ant_crypto_key_t *crypto_require_key(ant_value_t value) { return (ant_crypto_key_t *)js_get_native(value, CRYPTO_KEY_NATIVE_TAG);}
static ant_value_t crypto_authorize_key( ant_t *js, ant_value_t value, ant_crypto_key_kind_t kind, uint8_t usage, ant_crypto_key_t **key) { *key = crypto_require_key(value); if (!*key) return js_mkerr_typed(js, JS_ERR_TYPE, "Operation requires a CryptoKey"); if ((*key)->kind != kind || !((*key)->usages & usage)) return crypto_dom_exception_error(js, "CryptoKey does not permit this operation", "InvalidAccessError"); return js_mkundef();}
static ant_value_t crypto_subtle_import_key_impl(ant_params_t) { if (nargs < 5) return js_mkerr_typed(js, JS_ERR_TYPE, "subtle.importKey requires format, keyData, algorithm, extractable, and keyUsages");
bool raw = false; ant_value_t result = crypto_read_key_format(js, args[0], &raw); if (is_err(result)) return result; if (!raw) return crypto_dom_exception_error(js, "Only raw key import is supported", "NotSupportedError");
uint8_t usages = 0; result = crypto_read_key_usages(js, args[4], &usages); if (is_err(result)) return result;
const uint8_t *bytes = NULL; size_t len = 0; if (!buffer_source_get_bytes(js, args[1], &bytes, &len)) return js_mkerr_typed(js, JS_ERR_TYPE, "keyData must be a BufferSource");
ant_crypto_key_t *key = crypto_key_alloc(len); if (!key) return js_mkerr(js, "Out of memory"); if (len) memcpy(key->key, bytes, len); key->extractable = js_truthy(js, args[3]); key->usages = usages; uint32_t bits = 0;
result = crypto_get_key_algorithm_kind(js, args[2], &key->kind); if (is_err(result)) goto cleanup; bool length_present = false; if (key->kind == CRYPTO_KEY_HMAC) { result = crypto_get_hmac_digest(js, args[2], &key->md); if (is_err(result)) goto cleanup; result = crypto_read_key_length( js, args[2], false, UINT32_MAX, &length_present, &bits); if (is_err(result)) goto cleanup; } result = crypto_check_key_usages(js, key->kind, usages); if (is_err(result)) goto cleanup;
if (key->kind == CRYPTO_KEY_AES_GCM) { if (!crypto_aes_gcm_cipher_for_key(len)) { result = crypto_dom_exception_error(js, "Invalid AES-GCM key length", "DataError"); goto cleanup; } bits = (uint32_t)(len * 8); } else if (key->kind == CRYPTO_KEY_HMAC) { if (!len || len > UINT32_MAX / 8u) { result = crypto_dom_exception_error(js, "Invalid HMAC key length", "DataError"); goto cleanup; } uint32_t data_bits = (uint32_t)(len * 8); if (!length_present) bits = data_bits; if (bits > data_bits || bits <= data_bits - 8) { result = crypto_dom_exception_error(js, "Invalid HMAC key length", "DataError"); goto cleanup; } } else if (key->extractable) { result = crypto_dom_exception_error(js, "PBKDF2 keys must not be extractable", "SyntaxError"); goto cleanup; }
result = crypto_require_nonempty_secret_usages(js, usages); if (is_err(result)) goto cleanup; return crypto_make_key_object(js, key, bits);
cleanup: crypto_key_free(key); return result;}
static ant_value_t js_crypto_subtle_import_key(ant_params_t) { return crypto_subtle_call(js, args, nargs, crypto_subtle_import_key_impl);}
static ant_value_t crypto_subtle_generate_key_impl(ant_params_t) { if (nargs < 3) return js_mkerr_typed(js, JS_ERR_TYPE, "subtle.generateKey requires algorithm, extractable, and keyUsages");
uint8_t usages = 0; ant_value_t result = crypto_read_key_usages(js, args[2], &usages); if (is_err(result)) return result; ant_crypto_key_kind_t kind = 0; result = crypto_get_key_algorithm_kind(js, args[0], &kind); if (is_err(result)) return result; if (kind == CRYPTO_KEY_PBKDF2) return crypto_dom_exception_error(js, "Unsupported generateKey algorithm", "NotSupportedError");
const EVP_MD *md = NULL; if (kind == CRYPTO_KEY_HMAC) { result = crypto_get_hmac_digest(js, args[0], &md); if (is_err(result)) return result; } bool length_present = false; uint32_t bits = 0; result = crypto_read_key_length( js, args[0], kind == CRYPTO_KEY_AES_GCM, kind == CRYPTO_KEY_AES_GCM ? UINT16_MAX : UINT32_MAX, &length_present, &bits); if (is_err(result)) return result; result = crypto_check_key_usages(js, kind, usages); if (is_err(result)) return result;
if (kind == CRYPTO_KEY_AES_GCM) { if (bits != 128 && bits != 192 && bits != 256) return crypto_dom_exception_error(js, "AES-GCM key length must be 128, 192, or 256", "OperationError"); } else { if (!length_present) bits = (uint32_t)EVP_MD_block_size(md) * 8u; if (!bits) return crypto_dom_exception_error(js, "HMAC key length must be non-zero", "OperationError"); } result = crypto_require_nonempty_secret_usages(js, usages); if (is_err(result)) return result;
ant_crypto_key_t *key = crypto_key_alloc(bits / 8u + (bits % 8u != 0)); if (!key) return js_mkerr(js, "Out of memory"); key->kind = kind; key->md = md; key->usages = usages; key->extractable = js_truthy(js, args[1]); if (crypto_fill_random(key->key, key->key_len) != 0) { crypto_key_free(key); return crypto_dom_exception_error(js, "Key generation failed", "OperationError"); } return crypto_make_key_object(js, key, bits);}
static ant_value_t js_crypto_subtle_generate_key(ant_params_t) { return crypto_subtle_call(js, args, nargs, crypto_subtle_generate_key_impl);}
static ant_value_t crypto_subtle_export_key_impl(ant_params_t) { if (nargs < 2) { return js_mkerr_typed(js, JS_ERR_TYPE, "subtle.exportKey requires format and key"); }
bool raw = false; ant_value_t result = crypto_read_key_format(js, args[0], &raw); if (is_err(result)) return result; ant_crypto_key_t *key = crypto_require_key(args[1]); if (!key) { return js_mkerr_typed(js, JS_ERR_TYPE, "subtle.exportKey requires a CryptoKey"); } if (key->kind != CRYPTO_KEY_AES_GCM && key->kind != CRYPTO_KEY_HMAC) { return crypto_dom_exception_error(js, "CryptoKey algorithm cannot be exported", "NotSupportedError"); } if (!key->extractable) { return crypto_dom_exception_error(js, "CryptoKey is not extractable", "InvalidAccessError"); }
if (!raw) return crypto_dom_exception_error(js, "Only raw key export is supported", "NotSupportedError"); return create_arraybuffer_copy(js, key->key, key->key_len);}
static ant_value_t js_crypto_subtle_export_key(ant_params_t) { return crypto_subtle_call(js, args, nargs, crypto_subtle_export_key_impl);}
static ant_value_t crypto_subtle_derive_bits_impl(ant_params_t) { if (nargs < 3) return js_mkerr_typed(js, JS_ERR_TYPE, "subtle.deriveBits requires algorithm, baseKey, and length"); if (!crypto_algorithm_is(js, args[0], "PBKDF2")) { return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported deriveBits algorithm"); }
ant_crypto_key_t *key = crypto_require_key(args[1]); if (!key || key->kind != CRYPTO_KEY_PBKDF2) { return js_mkerr_typed(js, JS_ERR_TYPE, "PBKDF2 deriveBits requires a PBKDF2 CryptoKey"); } if ((key->usages & CRYPTO_KEY_USAGE_DERIVE_BITS) == 0) { return crypto_dom_exception_error( js, "CryptoKey does not permit deriveBits", "InvalidAccessError" ); }
double length_num = js_getnum(args[2]); if (!isfinite(length_num) || length_num < 0 || length_num > (double)INT_MAX * 8.0 || fmod(length_num, 8) != 0) { return js_mkerr_typed(js, JS_ERR_TYPE, "deriveBits length must be a non-negative multiple of 8"); } int keylen = (int)(length_num / 8);
ant_value_t salt_val = js_get(js, args[0], "salt"); const uint8_t *salt = NULL; size_t salt_len = 0; if (!buffer_source_get_bytes(js, salt_val, &salt, &salt_len)) { return js_mkerr_typed(js, JS_ERR_TYPE, "PBKDF2 salt must be an ArrayBuffer, TypedArray, DataView, or Buffer"); } if (salt_len > (size_t)INT_MAX || key->key_len > (size_t)INT_MAX) { return js_mkerr_typed(js, JS_ERR_TYPE, "PBKDF2 input is too large"); }
ant_value_t iterations_val = js_get(js, args[0], "iterations"); double iterations_num = js_getnum(iterations_val); if (!isfinite(iterations_num) || iterations_num <= 0 || iterations_num > INT_MAX || floor(iterations_num) != iterations_num) { return js_mkerr_typed(js, JS_ERR_TYPE, "PBKDF2 iterations must be a positive integer"); } int iterations = (int)iterations_num;
ant_value_t hash_val = js_get(js, args[0], "hash"); const EVP_MD *md = crypto_digest_from_algorithm(js, hash_val); if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported PBKDF2 hash algorithm");
uint8_t *out = malloc((size_t)keylen ? (size_t)keylen : 1); if (!out) return js_mkerr(js, "Out of memory"); if (PKCS5_PBKDF2_HMAC((const char *)key->key, (int)key->key_len, salt, (int)salt_len, iterations, md, keylen, out) != 1) { free(out); return js_mkerr(js, "PBKDF2 failed"); }
ant_value_t result = create_arraybuffer_copy(js, out, (size_t)keylen); free(out); return result;}
static ant_value_t js_crypto_subtle_derive_bits(ant_params_t) { return crypto_subtle_call(js, args, nargs, crypto_subtle_derive_bits_impl);}
static ant_value_t crypto_hmac_once( ant_t *js, ant_crypto_key_t *key, ant_value_t data, ant_value_t encoding, bool arraybuffer_result) { const uint8_t *bytes = NULL; size_t len = 0; uint8_t *owned = NULL; ant_value_t err = crypto_get_input_bytes(js, data, encoding, &bytes, &len, &owned); if (is_err(err)) return err;
unsigned char out[EVP_MAX_MD_SIZE]; unsigned int out_len = 0; unsigned char *res = HMAC(key->md, key->key, (int)key->key_len, bytes, len, out, &out_len); if (owned) free(owned); if (!res) return js_mkerr(js, "HMAC failed"); return arraybuffer_result ? create_arraybuffer_copy(js, out, out_len) : crypto_make_buffer(js, out, out_len);}
static ant_value_t crypto_subtle_sign_impl(ant_params_t) { if (nargs < 3) return js_mkerr_typed(js, JS_ERR_TYPE, "subtle.sign requires algorithm, key, and data"); if (!crypto_algorithm_is(js, args[0], "HMAC")) { if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); return crypto_dom_exception_error(js, "Unsupported signing algorithm", "NotSupportedError"); } ant_crypto_key_t *key = NULL; ant_value_t error = crypto_authorize_key(js, args[1], CRYPTO_KEY_HMAC, CRYPTO_KEY_USAGE_SIGN, &key); if (is_err(error)) return error; return crypto_hmac_once(js, key, args[2], js_mkundef(), true);}
static ant_value_t js_crypto_subtle_sign(ant_params_t) { return crypto_subtle_call(js, args, nargs, crypto_subtle_sign_impl);}
static ant_value_t crypto_subtle_verify_impl(ant_params_t) { if (nargs < 4) return js_mkerr_typed(js, JS_ERR_TYPE, "subtle.verify requires algorithm, key, signature, and data"); if (!crypto_algorithm_is(js, args[0], "HMAC")) { if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); return crypto_dom_exception_error(js, "Unsupported verification algorithm", "NotSupportedError"); } ant_crypto_key_t *key = NULL; ant_value_t error = crypto_authorize_key(js, args[1], CRYPTO_KEY_HMAC, CRYPTO_KEY_USAGE_VERIFY, &key); if (is_err(error)) return error;
const uint8_t *sig = NULL; size_t sig_len = 0; if (!buffer_source_get_bytes(js, args[2], &sig, &sig_len)) { return js_mkerr_typed(js, JS_ERR_TYPE, "signature must be an ArrayBuffer, TypedArray, DataView, or Buffer"); }
ant_value_t expected = crypto_hmac_once(js, key, args[3], js_mkundef(), false); if (is_err(expected)) return expected;
const uint8_t *expected_bytes = NULL; size_t expected_len = 0; if (!buffer_source_get_bytes(js, expected, &expected_bytes, &expected_len)) return js_false; if (sig_len != expected_len) return js_false; return js_bool(CRYPTO_memcmp(sig, expected_bytes, sig_len) == 0);}
static ant_value_t js_crypto_subtle_verify(ant_params_t) { return crypto_subtle_call(js, args, nargs, crypto_subtle_verify_impl);}
static ant_value_t crypto_aes_gcm_crypt( ant_t *js, bool encrypt, ant_value_t algorithm, ant_crypto_key_t *key, ant_value_t data) { if (!key || key->kind != CRYPTO_KEY_AES_GCM) { return js_mkerr_typed(js, JS_ERR_TYPE, "AES-GCM requires an AES CryptoKey"); }
ant_value_t iv_val = js_get(js, algorithm, "iv"); const uint8_t *iv = NULL; size_t iv_len = 0; if (!buffer_source_get_bytes(js, iv_val, &iv, &iv_len)) { return js_mkerr_typed(js, JS_ERR_TYPE, "AES-GCM iv must be an ArrayBuffer, TypedArray, DataView, or Buffer"); }
const uint8_t *input = NULL; size_t input_len = 0; if (!buffer_source_get_bytes(js, data, &input, &input_len)) { return js_mkerr_typed(js, JS_ERR_TYPE, "AES-GCM data must be an ArrayBuffer, TypedArray, DataView, or Buffer"); }
int tag_len = 16; ant_value_t tag_len_val = js_get(js, algorithm, "tagLength"); if (vtype(tag_len_val) == kTypeNumber) { tag_len = (int)(js_getnum(tag_len_val) / 8.0); if (tag_len <= 0 || tag_len > 16) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid AES-GCM tagLength"); }
const uint8_t *aad = NULL; size_t aad_len = 0; ant_value_t aad_val = js_get(js, algorithm, "additionalData"); bool has_aad = buffer_source_get_bytes(js, aad_val, &aad, &aad_len);
if (!encrypt && input_len < (size_t)tag_len) { return js_mkerr_typed(js, JS_ERR_TYPE, "AES-GCM ciphertext is shorter than tagLength"); }
const EVP_CIPHER *cipher = crypto_aes_gcm_cipher_for_key(key->key_len); if (!cipher) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid AES-GCM key length");
size_t text_len = encrypt ? input_len : input_len - (size_t)tag_len; size_t out_cap = encrypt ? input_len + (size_t)tag_len : text_len; uint8_t *out = malloc(out_cap ? out_cap : 1); if (!out) return js_mkerr(js, "Out of memory");
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) { free(out); return js_mkerr(js, "AES-GCM initialization failed"); }
int ok = encrypt ? EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) : EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL); ok = ok && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, (int)iv_len, NULL); ok = ok && (encrypt ? EVP_EncryptInit_ex(ctx, NULL, NULL, key->key, iv) : EVP_DecryptInit_ex(ctx, NULL, NULL, key->key, iv));
int out_len = 0; int total = 0; if (ok && has_aad && aad_len > 0) { ok = encrypt ? EVP_EncryptUpdate(ctx, NULL, &out_len, aad, (int)aad_len) : EVP_DecryptUpdate(ctx, NULL, &out_len, aad, (int)aad_len); }
if (ok && text_len > 0) { ok = encrypt ? EVP_EncryptUpdate(ctx, out, &out_len, input, (int)text_len) : EVP_DecryptUpdate(ctx, out, &out_len, input, (int)text_len); total += out_len; }
if (!encrypt && ok) { ok = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len, (void *)(input + text_len)); }
if (ok) { ok = encrypt ? EVP_EncryptFinal_ex(ctx, out + total, &out_len) : EVP_DecryptFinal_ex(ctx, out + total, &out_len); total += out_len; }
if (ok && encrypt) { ok = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_len, out + total); total += tag_len; }
EVP_CIPHER_CTX_free(ctx); if (!ok) { free(out); return js_mkerr_typed(js, JS_ERR_TYPE, "AES-GCM operation failed"); }
ant_value_t result = create_arraybuffer_copy(js, out, (size_t)total); free(out); return result;}
static ant_value_t crypto_subtle_encrypt_impl(ant_params_t) { if (nargs < 3) return js_mkerr_typed(js, JS_ERR_TYPE, "subtle.encrypt requires algorithm, key, and data"); if (!crypto_algorithm_is(js, args[0], "AES-GCM")) { if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); return crypto_dom_exception_error(js, "Unsupported encryption algorithm", "NotSupportedError"); } ant_crypto_key_t *key = NULL; ant_value_t error = crypto_authorize_key(js, args[1], CRYPTO_KEY_AES_GCM, CRYPTO_KEY_USAGE_ENCRYPT, &key); if (is_err(error)) return error; return crypto_aes_gcm_crypt(js, true, args[0], key, args[2]);}
static ant_value_t crypto_subtle_decrypt_impl(ant_params_t) { if (nargs < 3) return js_mkerr_typed(js, JS_ERR_TYPE, "subtle.decrypt requires algorithm, key, and data"); if (!crypto_algorithm_is(js, args[0], "AES-GCM")) { if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); return crypto_dom_exception_error(js, "Unsupported decryption algorithm", "NotSupportedError"); } ant_crypto_key_t *key = NULL; ant_value_t error = crypto_authorize_key(js, args[1], CRYPTO_KEY_AES_GCM, CRYPTO_KEY_USAGE_DECRYPT, &key); if (is_err(error)) return error; return crypto_aes_gcm_crypt(js, false, args[0], key, args[2]);}
static ant_value_t js_crypto_subtle_encrypt(ant_params_t) { return crypto_subtle_call(js, args, nargs, crypto_subtle_encrypt_impl);}
static ant_value_t js_crypto_subtle_decrypt(ant_params_t) { return crypto_subtle_call(js, args, nargs, crypto_subtle_decrypt_impl);}
static ant_value_t js_hash_update(ant_params_t) { ant_value_t this_val = js_getthis(js); ant_hash_state_t *state = NULL; const uint8_t *bytes = NULL; size_t len = 0; uint8_t *owned = NULL; ant_value_t err;
err = crypto_require_hash_state(js, this_val, &state); if (is_err(err)) return err; if (state->finalized) return js_mkerr(js, "Hash digest already called"); if (nargs < 1) return js_mkerr(js, "Hash.update requires data");
ant_value_t encoding = (nargs >= 2) ? args[1] : js_mkundef(); err = crypto_get_input_bytes(js, args[0], encoding, &bytes, &len, &owned); if (is_err(err)) goto cleanup;
if (EVP_DigestUpdate(state->ctx, bytes, len) != 1) { err = js_mkerr(js, "Hash update failed"); goto cleanup; }
err = this_val;
cleanup: if (owned) free(owned); return err;}
static ant_value_t js_hash_digest(ant_params_t) { ant_hash_state_t *state = NULL; ant_value_t err = crypto_require_hash_state(js, js_getthis(js), &state); if (is_err(err)) return err; if (state->finalized) return js_mkerr(js, "Hash digest already called");
if (EVP_DigestFinal_ex(state->ctx, state->digest, &state->digest_len) != 1) { return js_mkerr(js, "Hash digest failed"); }
state->finalized = true; EVP_MD_CTX_free(state->ctx); state->ctx = NULL;
return crypto_digest_result(js, state->digest, state->digest_len, nargs >= 1 ? args[0] : js_mkundef());}
static ant_value_t js_crypto_create_hash(ant_params_t) { if (nargs < 1) return js_mkerr(js, "createHash requires an algorithm");
ant_value_t algo_val = js_tostring_val(js, args[0]); if (is_err(algo_val)) return algo_val;
size_t algo_len = 0; const char *algo = js_getstr(js, algo_val, &algo_len); if (!algo || algo_len == 0) return js_mkerr(js, "Invalid hash algorithm");
char *algo_name = strndup(algo, algo_len); if (!algo_name) return js_mkerr(js, "Out of memory");
const EVP_MD *md = EVP_get_digestbyname(algo_name); free(algo_name); if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported hash algorithm");
ant_hash_state_t *state = calloc(1, sizeof(*state)); if (!state) return js_mkerr(js, "Out of memory");
state->ctx = EVP_MD_CTX_new(); state->md = md; if (!state->ctx || EVP_DigestInit_ex(state->ctx, md, NULL) != 1) { crypto_hash_state_free(state); return js_mkerr(js, "Failed to initialize hash"); }
ant_value_t obj = Ant_Object_CreateBare(js); js_set(js, obj, "update", Ant_Function_Create(js_hash_update)); js_set(js, obj, "digest", Ant_Function_Create(js_hash_digest)); js_set_native(obj, state, CRYPTO_HASH_NATIVE_TAG); js_set_finalizer(obj, crypto_hash_finalize); js_set_tostringtag(js, obj, js_mkstr(js, "Hash", 4)); return obj;}
static ant_hmac_state_t *crypto_get_hmac_state(ant_value_t value) { ant_hmac_state_t *state = (ant_hmac_state_t *)js_get_native(value, CRYPTO_HMAC_NATIVE_TAG); return (state && state->ctx) ? state : NULL;}
static ant_value_t crypto_require_hmac_state( ant_t *js, ant_value_t value, ant_hmac_state_t **out_state) { ant_hmac_state_t *state = crypto_get_hmac_state(value); if (!state) return js_mkerr(js, "Invalid Hmac state"); *out_state = state; return js_mkundef();}
static ant_value_t js_hmac_update(ant_params_t) { ant_hmac_state_t *state = NULL; const uint8_t *bytes = NULL; size_t len = 0; uint8_t *owned = NULL;
ant_value_t err = crypto_require_hmac_state(js, js_getthis(js), &state); if (is_err(err)) return err; if (state->finalized) return js_mkerr(js, "Hmac digest already called"); if (nargs < 1) return js_mkerr(js, "Hmac.update requires data");
ant_value_t encoding = (nargs >= 2) ? args[1] : js_mkundef(); err = crypto_get_input_bytes(js, args[0], encoding, &bytes, &len, &owned); if (is_err(err)) goto cleanup;
if (HMAC_Update(state->ctx, bytes, len) != 1) { err = js_mkerr(js, "Hmac update failed"); goto cleanup; }
err = js_getthis(js);
cleanup: if (owned) free(owned); return err;}
static ant_value_t js_hmac_digest(ant_params_t) { ant_hmac_state_t *state = NULL; ant_value_t err = crypto_require_hmac_state(js, js_getthis(js), &state); if (is_err(err)) return err; if (state->finalized) return js_mkerr(js, "Hmac digest already called");
if (HMAC_Final(state->ctx, state->digest, &state->digest_len) != 1) { return js_mkerr(js, "Hmac digest failed"); }
state->finalized = true; HMAC_CTX_free(state->ctx); state->ctx = NULL;
return crypto_digest_result(js, state->digest, state->digest_len, nargs >= 1 ? args[0] : js_mkundef());}
static ant_value_t js_crypto_create_hmac(ant_params_t) { if (nargs < 2) return js_mkerr(js, "createHmac requires an algorithm and key");
ant_value_t algo_val = js_tostring_val(js, args[0]); if (is_err(algo_val)) return algo_val; size_t algo_len = 0; const char *algo = js_getstr(js, algo_val, &algo_len); const EVP_MD *md = crypto_digest_from_name(algo, algo_len); if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported HMAC algorithm");
const uint8_t *key = NULL; size_t key_len = 0; uint8_t *owned = NULL; ant_value_t err = crypto_get_input_bytes(js, args[1], js_mkundef(), &key, &key_len, &owned); if (is_err(err)) return err;
ant_hmac_state_t *state = calloc(1, sizeof(*state)); if (!state) { if (owned) free(owned); return js_mkerr(js, "Out of memory"); }
state->ctx = HMAC_CTX_new(); state->md = md; if (!state->ctx || HMAC_Init_ex(state->ctx, key, (int)key_len, md, NULL) != 1) { if (owned) free(owned); crypto_hmac_state_free(state); return js_mkerr(js, "Failed to initialize Hmac"); } if (owned) free(owned);
ant_value_t obj = Ant_Object_CreateBare(js); js_set(js, obj, "update", Ant_Function_Create(js_hmac_update)); js_set(js, obj, "digest", Ant_Function_Create(js_hmac_digest)); js_set_native(obj, state, CRYPTO_HMAC_NATIVE_TAG); js_set_finalizer(obj, crypto_hmac_finalize); js_set_tostringtag(js, obj, js_mkstr(js, "Hmac", 4)); return obj;}
static ant_value_t js_crypto_hash(ant_params_t) { ant_value_t algo_val; ant_value_t output_encoding; const char *algo; size_t algo_len = 0; char *algo_name = NULL; const EVP_MD *md = NULL; const uint8_t *bytes = NULL; size_t len = 0; uint8_t *owned = NULL; unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_len = 0; ant_value_t err;
if (nargs < 2) return js_mkerr(js, "hash requires algorithm and data"); output_encoding = nargs >= 3 ? args[2] : js_mkundef();
algo_val = js_tostring_val(js, args[0]); if (is_err(algo_val)) return algo_val;
algo = js_getstr(js, algo_val, &algo_len); if (!algo || algo_len == 0) return js_mkerr(js, "Invalid hash algorithm");
algo_name = strndup(algo, algo_len); if (!algo_name) return js_mkerr(js, "Out of memory");
md = EVP_get_digestbyname(algo_name); free(algo_name); if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported hash algorithm");
err = crypto_get_input_bytes(js, args[1], js_mkundef(), &bytes, &len, &owned); if (is_err(err)) goto cleanup;
if (EVP_Digest(bytes, len, digest, &digest_len, md, NULL) != 1) { err = js_mkerr(js, "Hash failed"); goto cleanup; }
err = crypto_digest_result(js, digest, digest_len, output_encoding);
cleanup: if (owned) free(owned); return err;}
static ant_cipher_state_t *crypto_get_cipher_state(ant_value_t value) { ant_cipher_state_t *state = (ant_cipher_state_t *)js_get_native(value, CRYPTO_CIPHER_NATIVE_TAG); return (state && state->ctx) ? state : NULL;}
static ant_value_t crypto_require_cipher_state( ant_t *js, ant_value_t value, ant_cipher_state_t **out_state) { ant_cipher_state_t *state = crypto_get_cipher_state(value); if (!state) return js_mkerr(js, "Invalid Cipher state"); *out_state = state; return js_mkundef();}
static ant_value_t crypto_cipher_bytes_result( ant_t *js, uint8_t *bytes, size_t len, ant_value_t encoding) { if (vtype(encoding) == kTypeString) return crypto_digest_result(js, bytes, len, encoding); return crypto_make_buffer(js, bytes, len);}
static ant_value_t js_cipher_update(ant_params_t) { ant_cipher_state_t *state = NULL; ant_value_t err = crypto_require_cipher_state(js, js_getthis(js), &state); if (is_err(err)) return err; if (state->finalized) return js_mkerr(js, "Cipher already finalized"); if (nargs < 1) return js_mkerr(js, "Cipher.update requires data");
const uint8_t *input = NULL; size_t input_len = 0; uint8_t *owned = NULL; ant_value_t input_encoding = nargs >= 2 ? args[1] : js_mkundef(); err = crypto_get_input_bytes(js, args[0], input_encoding, &input, &input_len, &owned); if (is_err(err)) return err;
int block_size = EVP_CIPHER_CTX_block_size(state->ctx); size_t out_cap = input_len + (block_size > 0 ? (size_t)block_size : 16u); uint8_t *out = malloc(out_cap ? out_cap : 1); if (!out) { if (owned) free(owned); return js_mkerr(js, "Out of memory"); }
int out_len = 0; int ok = state->encrypt ? EVP_EncryptUpdate(state->ctx, out, &out_len, input, (int)input_len) : EVP_DecryptUpdate(state->ctx, out, &out_len, input, (int)input_len);
if (owned) free(owned); if (!ok) { free(out); return js_mkerr(js, "Cipher update failed"); }
ant_value_t result = crypto_cipher_bytes_result(js, out, (size_t)out_len, nargs >= 3 ? args[2] : js_mkundef()); free(out); return result;}
static ant_value_t js_cipher_final(ant_params_t) { ant_cipher_state_t *state = NULL; ant_value_t err = crypto_require_cipher_state(js, js_getthis(js), &state); if (is_err(err)) return err; if (state->finalized) return js_mkerr(js, "Cipher already finalized");
int block_size = EVP_CIPHER_CTX_block_size(state->ctx); size_t out_cap = block_size > 0 ? (size_t)block_size : 16u; uint8_t out[EVP_MAX_BLOCK_LENGTH + 16]; int out_len = 0;
int ok = state->encrypt ? EVP_EncryptFinal_ex(state->ctx, out, &out_len) : EVP_DecryptFinal_ex(state->ctx, out, &out_len); (void)out_cap;
state->finalized = true; if (!ok) return js_mkerr(js, "Cipher final failed");
if (state->encrypt && state->is_gcm) { state->auth_tag_len = 16; EVP_CIPHER_CTX_ctrl(state->ctx, EVP_CTRL_GCM_GET_TAG, 16, state->auth_tag); }
return crypto_cipher_bytes_result(js, out, (size_t)out_len, nargs >= 1 ? args[0] : js_mkundef());}
static ant_value_t js_cipher_set_aad(ant_params_t) { ant_cipher_state_t *state = NULL; ant_value_t err = crypto_require_cipher_state(js, js_getthis(js), &state); if (is_err(err)) return err; if (nargs < 1) return js_mkerr(js, "setAAD requires data");
const uint8_t *aad = NULL; size_t aad_len = 0; if (!buffer_source_get_bytes(js, args[0], &aad, &aad_len)) { return js_mkerr(js, "setAAD data must be a Buffer, TypedArray, DataView, or ArrayBuffer"); }
int out_len = 0; int ok = state->encrypt ? EVP_EncryptUpdate(state->ctx, NULL, &out_len, aad, (int)aad_len) : EVP_DecryptUpdate(state->ctx, NULL, &out_len, aad, (int)aad_len); if (!ok) return js_mkerr(js, "setAAD failed"); return js_getthis(js);}
static ant_value_t js_cipher_get_auth_tag(ant_params_t) { ant_cipher_state_t *state = NULL; ant_value_t err = crypto_require_cipher_state(js, js_getthis(js), &state); if (is_err(err)) return err; if (!state->is_gcm || !state->encrypt || state->auth_tag_len == 0) { return js_mkerr(js, "Auth tag is not available"); } return crypto_make_buffer(js, state->auth_tag, state->auth_tag_len);}
static ant_value_t js_cipher_set_auth_tag(ant_params_t) { ant_cipher_state_t *state = NULL; ant_value_t err = crypto_require_cipher_state(js, js_getthis(js), &state); if (is_err(err)) return err; if (nargs < 1) return js_mkerr(js, "setAuthTag requires a tag");
const uint8_t *tag = NULL; size_t tag_len = 0; if (!buffer_source_get_bytes(js, args[0], &tag, &tag_len) || tag_len > sizeof(state->auth_tag)) { return js_mkerr(js, "Invalid authentication tag"); }
memcpy(state->auth_tag, tag, tag_len); state->auth_tag_len = tag_len; if (!state->encrypt && state->is_gcm) { EVP_CIPHER_CTX_ctrl(state->ctx, EVP_CTRL_GCM_SET_TAG, (int)tag_len, state->auth_tag); } return js_getthis(js);}
static ant_value_t js_cipher_set_auto_padding(ant_params_t) { ant_cipher_state_t *state = NULL; ant_value_t err = crypto_require_cipher_state(js, js_getthis(js), &state); if (is_err(err)) return err; int padding = nargs < 1 || js_truthy(js, args[0]); EVP_CIPHER_CTX_set_padding(state->ctx, padding); return js_getthis(js);}
static ant_value_t crypto_create_cipheriv(ant_native_params_t, bool encrypt) { if (nargs < 3) return js_mkerr(js, "createCipheriv requires algorithm, key, and iv");
ant_value_t algo_val = js_tostring_val(js, args[0]); if (is_err(algo_val)) return algo_val; size_t algo_len = 0; const char *algo = js_getstr(js, algo_val, &algo_len); const EVP_CIPHER *cipher = crypto_cipher_from_name(algo, algo_len); if (!cipher) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported cipher algorithm");
const uint8_t *key = NULL; size_t key_len = 0; uint8_t *key_owned = NULL; ant_value_t err = crypto_get_input_bytes(js, args[1], js_mkundef(), &key, &key_len, &key_owned); if (is_err(err)) return err;
const uint8_t *iv = NULL; size_t iv_len = 0; uint8_t *iv_owned = NULL; if (vtype(args[2]) != kTypeNull && vtype(args[2]) != kTypeUndefined) { err = crypto_get_input_bytes(js, args[2], js_mkundef(), &iv, &iv_len, &iv_owned); if (is_err(err)) { if (key_owned) free(key_owned); return err; } }
ant_cipher_state_t *state = calloc(1, sizeof(*state)); if (!state) { if (key_owned) free(key_owned); if (iv_owned) free(iv_owned); return js_mkerr(js, "Out of memory"); }
state->ctx = EVP_CIPHER_CTX_new(); state->encrypt = encrypt; state->is_gcm = EVP_CIPHER_mode(cipher) == EVP_CIPH_GCM_MODE; if (!state->ctx) { crypto_cipher_state_free(state); if (key_owned) free(key_owned); if (iv_owned) free(iv_owned); return js_mkerr(js, "Cipher initialization failed"); }
int ok = encrypt ? EVP_EncryptInit_ex(state->ctx, cipher, NULL, NULL, NULL) : EVP_DecryptInit_ex(state->ctx, cipher, NULL, NULL, NULL); if (ok && state->is_gcm && iv) { ok = EVP_CIPHER_CTX_ctrl(state->ctx, EVP_CTRL_GCM_SET_IVLEN, (int)iv_len, NULL); } ok = ok && (encrypt ? EVP_EncryptInit_ex(state->ctx, NULL, NULL, key, iv) : EVP_DecryptInit_ex(state->ctx, NULL, NULL, key, iv));
if (key_owned) free(key_owned); if (iv_owned) free(iv_owned); if (!ok) { crypto_cipher_state_free(state); return js_mkerr(js, "Cipher initialization failed"); }
ant_value_t obj = Ant_Object_CreateBare(js); js_set(js, obj, "update", Ant_Function_Create(js_cipher_update)); js_set(js, obj, "final", Ant_Function_Create(js_cipher_final)); js_set(js, obj, "setAAD", Ant_Function_Create(js_cipher_set_aad)); js_set(js, obj, "getAuthTag", Ant_Function_Create(js_cipher_get_auth_tag)); js_set(js, obj, "setAuthTag", Ant_Function_Create(js_cipher_set_auth_tag)); js_set(js, obj, "setAutoPadding", Ant_Function_Create(js_cipher_set_auto_padding)); js_set_native(obj, state, CRYPTO_CIPHER_NATIVE_TAG); js_set_finalizer(obj, crypto_cipher_finalize); return obj;}
static ant_value_t js_crypto_create_cipheriv(ant_params_t) { return crypto_create_cipheriv(js, args, nargs, true);}
static ant_value_t js_crypto_create_decipheriv(ant_params_t) { return crypto_create_cipheriv(js, args, nargs, false);}
static ant_value_t crypto_pbkdf2_result( ant_t *js, ant_value_t password_val, ant_value_t salt_val, ant_value_t iterations_val, ant_value_t keylen_val, ant_value_t digest_val) { const uint8_t *password = NULL, *salt = NULL; size_t password_len = 0, salt_len = 0; uint8_t *password_owned = NULL, *salt_owned = NULL; ant_value_t err = crypto_get_input_bytes(js, password_val, js_mkundef(), &password, &password_len, &password_owned); if (is_err(err)) return err; err = crypto_get_input_bytes(js, salt_val, js_mkundef(), &salt, &salt_len, &salt_owned); if (is_err(err)) goto cleanup_err;
int iterations = (int)js_getnum(iterations_val); int keylen = (int)js_getnum(keylen_val); if (iterations <= 0 || keylen < 0) { err = js_mkerr(js, "Invalid PBKDF2 parameters"); goto cleanup_err; }
ant_value_t digest_str = js_tostring_val(js, digest_val); if (is_err(digest_str)) { err = digest_str; goto cleanup_err; } size_t digest_len = 0; const char *digest = js_getstr(js, digest_str, &digest_len); const EVP_MD *md = crypto_digest_from_name(digest, digest_len); if (!md) { err = js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported PBKDF2 digest"); goto cleanup_err; }
uint8_t *out = malloc((size_t)keylen ? (size_t)keylen : 1); if (!out) { err = js_mkerr(js, "Out of memory"); goto cleanup_err; } if (PKCS5_PBKDF2_HMAC((const char *)password, (int)password_len, salt, (int)salt_len, iterations, md, keylen, out) != 1) { free(out); err = js_mkerr(js, "PBKDF2 failed"); goto cleanup_err; }
ant_value_t result = crypto_make_buffer(js, out, (size_t)keylen); free(out); if (password_owned) free(password_owned); if (salt_owned) free(salt_owned); return result;
cleanup_err: if (password_owned) free(password_owned); if (salt_owned) free(salt_owned); return err;}
static ant_value_t js_crypto_pbkdf2_sync(ant_params_t) { if (nargs < 5) return js_mkerr(js, "pbkdf2Sync requires password, salt, iterations, keylen, and digest"); return crypto_pbkdf2_result(js, args[0], args[1], args[2], args[3], args[4]);}
static ant_value_t js_crypto_pbkdf2(ant_params_t) { if (nargs < 6 || (vtype(args[5]) != kTypeFunction && vtype(args[5]) != kTypeBuiltin)) { return js_mkerr(js, "pbkdf2 requires a callback"); } ant_value_t result = crypto_pbkdf2_result(js, args[0], args[1], args[2], args[3], args[4]); ant_value_t cb_args[2] = { is_err(result) ? result : js_mknull(), is_err(result) ? js_mkundef() : result }; ant_value_t cb_result = Ant_Error_CallCallback(js, args[5], js_mkundef(), cb_args, 2); return is_err(cb_result) ? cb_result : js_mkundef();}
static ant_value_t crypto_scrypt_result(ant_native_params_t, int options_index) { const uint8_t *password = NULL, *salt = NULL; size_t password_len = 0, salt_len = 0; uint8_t *password_owned = NULL, *salt_owned = NULL; ant_value_t err = crypto_get_input_bytes(js, args[0], js_mkundef(), &password, &password_len, &password_owned); if (is_err(err)) return err; err = crypto_get_input_bytes(js, args[1], js_mkundef(), &salt, &salt_len, &salt_owned); if (is_err(err)) goto cleanup_err;
int keylen = (int)js_getnum(args[2]); if (keylen < 0) { err = js_mkerr(js, "Invalid scrypt key length"); goto cleanup_err; }
uint64_t N = 16384, r = 8, p = 1, maxmem = 32 * 1024 * 1024; if (options_index >= 0 && options_index < nargs && is_object_type(args[options_index])) { ant_value_t v = js_get(js, args[options_index], "N"); if (vtype(v) == kTypeUndefined) v = js_get(js, args[options_index], "cost"); if (is_err(v)) { err = v; goto cleanup_err; } if (vtype(v) == kTypeNumber) N = (uint64_t)js_getnum(v); v = js_get(js, args[options_index], "r");
if (vtype(v) == kTypeUndefined) v = js_get(js, args[options_index], "blockSize"); if (is_err(v)) { err = v; goto cleanup_err; } if (vtype(v) == kTypeNumber) r = (uint64_t)js_getnum(v); v = js_get(js, args[options_index], "p");
if (vtype(v) == kTypeUndefined) v = js_get(js, args[options_index], "parallelization"); if (is_err(v)) { err = v; goto cleanup_err; } if (vtype(v) == kTypeNumber) p = (uint64_t)js_getnum(v); v = js_get(js, args[options_index], "maxmem");
if (is_err(v)) { err = v; goto cleanup_err; } if (vtype(v) == kTypeNumber) maxmem = (uint64_t)js_getnum(v); }
uint8_t *out = malloc((size_t)keylen ? (size_t)keylen : 1); if (!out) { err = js_mkerr(js, "Out of memory"); goto cleanup_err; }
if (EVP_PBE_scrypt( (const char *)password, password_len, salt, salt_len, N, r, p, maxmem, out, (size_t)keylen) != 1 ) { free(out); err = js_mkerr(js, "scrypt failed"); goto cleanup_err; }
ant_value_t result = crypto_make_buffer(js, out, (size_t)keylen); free(out); if (password_owned) free(password_owned); if (salt_owned) free(salt_owned); return result;
cleanup_err: if (password_owned) free(password_owned); if (salt_owned) free(salt_owned); return err;}
static ant_value_t js_crypto_scrypt_sync(ant_params_t) { if (nargs < 3) return js_mkerr(js, "scryptSync requires password, salt, and keylen"); return crypto_scrypt_result(js, args, nargs, 3);}
static ant_value_t js_crypto_scrypt(ant_params_t) { if (nargs < 4) return js_mkerr(js, "scrypt requires a callback"); int callback_index = (vtype(args[3]) == kTypeFunction || vtype(args[3]) == kTypeBuiltin) ? 3 : 4; if (callback_index >= nargs || (vtype(args[callback_index]) != kTypeFunction && vtype(args[callback_index]) != kTypeBuiltin)) return js_mkerr(js, "scrypt requires a callback");
ant_value_t result = crypto_scrypt_result(js, args, nargs, callback_index == 3 ? -1 : 3); ant_value_t cb_args[2] = { is_err(result) ? result : js_mknull(), is_err(result) ? js_mkundef() : result }; ant_value_t cb_result = Ant_Error_CallCallback(js, args[callback_index], js_mkundef(), cb_args, 2);
return is_err(cb_result) ? cb_result : js_mkundef();}
static ant_value_t create_crypto_obj(ant_t *js) { ant_value_t crypto_obj = Ant_Object_Create(js); ant_value_t subtle_obj = Ant_Object_Create(js); js_set(js, crypto_obj, "random", Ant_Function_Create(js_crypto_random)); js_set(js, crypto_obj, "randomBytes", Ant_Function_CreateWithArity(js_crypto_random_bytes, 2)); js_set(js, crypto_obj, "randomFillSync", Ant_Function_CreateWithArity(js_crypto_random_fill_sync, 1)); js_set(js, crypto_obj, "randomUUID", Ant_Function_CreateWithArity(js_crypto_random_uuid, 1)); js_set(js, crypto_obj, "randomUUIDv7", Ant_Function_Create(js_crypto_random_uuidv7)); js_set(js, crypto_obj, "getRandomValues", Ant_Function_Create(js_crypto_get_random_values)); // TODO: add remaining WebCrypto key formats, key derivation, wrapping, and asymmetric algorithms. js_set(js, subtle_obj, "digest", Ant_Function_Create(js_crypto_subtle_digest)); js_set(js, subtle_obj, "importKey", Ant_Function_Create(js_crypto_subtle_import_key)); js_set(js, subtle_obj, "generateKey", Ant_Function_Create(js_crypto_subtle_generate_key)); js_set(js, subtle_obj, "exportKey", Ant_Function_Create(js_crypto_subtle_export_key)); js_set(js, subtle_obj, "deriveBits", Ant_Function_Create(js_crypto_subtle_derive_bits)); js_set(js, subtle_obj, "sign", Ant_Function_Create(js_crypto_subtle_sign)); js_set(js, subtle_obj, "verify", Ant_Function_Create(js_crypto_subtle_verify)); js_set(js, subtle_obj, "encrypt", Ant_Function_Create(js_crypto_subtle_encrypt)); js_set(js, subtle_obj, "decrypt", Ant_Function_Create(js_crypto_subtle_decrypt)); js_set(js, subtle_obj, "timingSafeEqual", Ant_Function_Create(js_crypto_timing_safe_equal)); js_set_tostringtag(js, subtle_obj, js_mkstr(js, "SubtleCrypto", 12)); js_set(js, crypto_obj, "subtle", subtle_obj); js_set_tostringtag(js, crypto_obj, js_mkstr(js, "Crypto", 6)); return crypto_obj;}
void init_crypto_module(ant_t *js) { js->mutable_roots.crypto_array_iterator_next = js_get(js, js->sym.array_iterator_proto, "next"); for (unsigned i = 0; i < 8; i++) js->mutable_roots.crypto_usage_strings[i] = js_mkstr(js, crypto_key_usage_names[i], strlen(crypto_key_usage_names[i])); ant_value_t crypto_obj = create_crypto_obj(js); js_set(js, js_glob(js), "crypto", crypto_obj);}
ant_value_t crypto_library(ant_t *js) { ant_value_t lib = Ant_Object_CreateBare(js); ant_value_t webcrypto = create_crypto_obj(js); js_set(js, lib, "webcrypto", webcrypto); js_set(js, lib, "subtle", js_get(js, webcrypto, "subtle")); js_set(js, lib, "hash", Ant_Function_CreateWithArity(js_crypto_hash, 3)); js_set(js, lib, "createHash", Ant_Function_CreateWithArity(js_crypto_create_hash, 2)); js_set(js, lib, "createHmac", Ant_Function_CreateWithArity(js_crypto_create_hmac, 3)); js_set(js, lib, "createCipheriv", Ant_Function_CreateWithArity(js_crypto_create_cipheriv, 4)); js_set(js, lib, "createDecipheriv", Ant_Function_CreateWithArity(js_crypto_create_decipheriv, 4)); js_set(js, lib, "pbkdf2", Ant_Function_CreateWithArity(js_crypto_pbkdf2, 6)); js_set(js, lib, "pbkdf2Sync", Ant_Function_CreateWithArity(js_crypto_pbkdf2_sync, 5)); js_set(js, lib, "scrypt", Ant_Function_CreateWithArity(js_crypto_scrypt, 4)); js_set(js, lib, "scryptSync", Ant_Function_CreateWithArity(js_crypto_scrypt_sync, 3)); js_set(js, lib, "randomBytes", Ant_Function_CreateWithArity(js_crypto_random_bytes, 2)); js_set(js, lib, "randomFillSync", Ant_Function_CreateWithArity(js_crypto_random_fill_sync, 1)); js_set(js, lib, "randomUUID", Ant_Function_CreateWithArity(js_crypto_random_uuid, 1)); js_set(js, lib, "getRandomValues", Ant_Function_Create(js_crypto_get_random_values)); js_set(js, lib, "getCiphers", Ant_Function_Create(js_crypto_get_ciphers)); js_set(js, lib, "getCurves", Ant_Function_Create(js_crypto_get_curves)); js_set(js, lib, "getHashes", Ant_Function_Create(js_crypto_get_hashes)); js_set(js, lib, "timingSafeEqual", Ant_Function_Create(js_crypto_timing_safe_equal)); js_set_tostringtag(js, lib, js_mkstr(js, "crypto", 6));
return lib;}