From 9f696a03423383118fc439d1ec34d86b7fd539b9 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sat, 29 Nov 2025 16:43:03 -0600 Subject: [PATCH] random --- meson.build | 1 + scripts/crypto.c | 358 +++++++++++++++++++++++++++++---------------- scripts/mersenne.c | 148 +++++++++++++++++++ scripts/os.c | 10 +- source/cell.h | 10 +- source/jsffi.c | 83 ----------- source/qjs_blob.c | 10 ++ 7 files changed, 400 insertions(+), 220 deletions(-) create mode 100644 scripts/mersenne.c diff --git a/meson.build b/meson.build index 8a09b1f1..e86f04ad 100644 --- a/meson.build +++ b/meson.build @@ -238,6 +238,7 @@ src += ['quickjs.c', 'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c'] scripts = [ 'nota.c', 'js.c', + 'mersenne.c', 'qop.c', 'wildstar.c', 'fit.c', diff --git a/scripts/crypto.c b/scripts/crypto.c index c19f4ed6..154e4803 100644 --- a/scripts/crypto.c +++ b/scripts/crypto.c @@ -5,176 +5,284 @@ #include "monocypher.h" -static inline void to_hex(const uint8_t *in, size_t in_len, char *out) -{ - static const char hexchars[] = "0123456789abcdef"; - for (size_t i = 0; i < in_len; i++) { - out[2*i ] = hexchars[(in[i] >> 4) & 0x0F]; - out[2*i + 1] = hexchars[ in[i] & 0x0F]; +// External declaration if not in cell.h +void *js_get_blob_data_bits(JSContext *js, size_t *bits, JSValue v); + +/* + Crypto Module Documentation + + This module provides cryptographic functions using the Monocypher library. + All inputs and outputs are Blobs. + + Functions: + + - keypair() -> { public: Blob(256 bits), private: Blob(256 bits) } + Generates a new random X25519 keypair. + + - shared(public_key, private_key) -> Blob(256 bits) + Computes a shared secret from your private key and another's public key (X25519). + Input keys must be 256 bits (32 bytes). + + - blake2(data, [hash_size_bytes=32]) -> Blob + Computes the BLAKE2b hash of the data. + Default hash size is 32 bytes (256 bits). Supports 1-64 bytes. + + - sign(secret_key, message) -> Blob(512 bits) + Signs a message using EdDSA. + secret_key must be 512 bits (64 bytes). + (Note: If you have a 32-byte seed, extend it first or use appropriate key generation). + Returns a 64-byte signature. + + - verify(signature, public_key, message) -> bool + Verifies an EdDSA signature. + signature: 512 bits (64 bytes). + public_key: 256 bits (32 bytes). + Returns true if valid, false otherwise. + + - lock(key, nonce, message, [ad]) -> Blob + Encrypts and authenticates a message using XChaCha20-Poly1305. + key: 256 bits (32 bytes). + nonce: 192 bits (24 bytes). + ad: Optional associated data (Blob). + Returns a blob containing the ciphertext followed by the 16-byte MAC. + + - unlock(key, nonce, ciphertext_with_mac, [ad]) -> Blob or null + Decrypts and verifies a message. + key: 256 bits (32 bytes). + nonce: 192 bits (24 bytes). + ciphertext_with_mac: Must include the 16-byte MAC at the end. + ad: Optional associated data (Blob). + Returns the plaintext Blob if successful, or null if verification fails. +*/ + +// Helper to get blob data and check exact bit length +static void *get_blob_check_bits(JSContext *js, JSValue val, size_t expected_bits, const char *name) { + size_t bits; + void *data = js_get_blob_data_bits(js, &bits, val); + if (!data) { + JS_ThrowTypeError(js, "%s: expected a blob", name); + return NULL; } - out[2 * in_len] = '\0'; // null-terminate + if (bits != expected_bits) { + JS_ThrowTypeError(js, "%s: expected %zu bits, got %zu", name, expected_bits, bits); + return NULL; + } + return data; } -static inline int nibble_from_char(char c, uint8_t *nibble) -{ - if (c >= '0' && c <= '9') { *nibble = (uint8_t)(c - '0'); return 0; } - if (c >= 'a' && c <= 'f') { *nibble = (uint8_t)(c - 'a' + 10); return 0; } - if (c >= 'A' && c <= 'F') { *nibble = (uint8_t)(c - 'A' + 10); return 0; } - return -1; // invalid char -} - -static inline int from_hex(const char *hex, uint8_t *out, size_t out_len) -{ - for (size_t i = 0; i < out_len; i++) { - uint8_t hi, lo; - if (nibble_from_char(hex[2*i], &hi) < 0) return -1; - if (nibble_from_char(hex[2*i + 1], &lo) < 0) return -1; - out[i] = (uint8_t)((hi << 4) | lo); +// Helper to get any blob data (checking it is a stoned blob) +static void *get_blob_any(JSContext *js, JSValue val, size_t *out_bits, const char *name) { + void *data = js_get_blob_data_bits(js, out_bits, val); + if (!data) { + JS_ThrowTypeError(js, "%s: expected a blob", name); + return NULL; } - return 0; -} - -// Convert a JSValue containing a 64-character hex string into a 32-byte array. -static inline void js2crypto(JSContext *js, JSValue v, uint8_t *crypto) -{ - size_t hex_len; - const char *hex_str = JS_ToCStringLen(js, &hex_len, v); - if (!hex_str) - return; - - if (hex_len != 64) { - JS_FreeCString(js, hex_str); - JS_ThrowTypeError(js, "js2crypto: expected 64-hex-char string"); - return; - } - - if (from_hex(hex_str, crypto, 32) < 0) { - JS_FreeCString(js, hex_str); - JS_ThrowTypeError(js, "js2crypto: invalid hex encoding"); - return; - } - - JS_FreeCString(js, hex_str); -} - -static inline JSValue crypto2js(JSContext *js, const uint8_t *crypto) -{ - char hex[65]; // 32*2 + 1 for null terminator - to_hex(crypto, 32, hex); - return JS_NewString(js, hex); + return data; } JSValue js_crypto_keypair(JSContext *js, JSValue self, int argc, JSValue *argv) { - JSValue ret = JS_NewObject(js); + JSValue ret = JS_NewObject(js); - uint8_t public[32]; - uint8_t private[32]; - - JSValue global = JS_GetGlobalObject(js); - JSValue os = JS_GetPropertyStr(js, global, "os"); - JSValue random_blob = JS_GetPropertyStr(js, os, "random_blob"); - JSValue size_val = JS_NewInt32(js, 32); - JSValue blob = JS_Call(js, random_blob, os, 1, &size_val); - size_t len; - uint8_t *data = js_get_blob_data(js, &len, blob); - if (!data || len != 32) { - JS_FreeValue(js, blob); + // Generate 32 random bytes using os.random_blob + JSValue global = JS_GetGlobalObject(js); + JSValue os = JS_GetPropertyStr(js, global, "os"); + JSValue random_blob = JS_GetPropertyStr(js, os, "random_blob"); + JSValue size_val = JS_NewInt32(js, 32); // 32 bytes + JSValue blob = JS_Call(js, random_blob, os, 1, &size_val); + JS_FreeValue(js, size_val); JS_FreeValue(js, random_blob); JS_FreeValue(js, os); JS_FreeValue(js, global); - return JS_ThrowInternalError(js, "failed to get random bytes"); - } - memcpy(private, data, 32); - JS_FreeValue(js, blob); - JS_FreeValue(js, size_val); - JS_FreeValue(js, random_blob); - JS_FreeValue(js, os); - JS_FreeValue(js, global); + + if (JS_IsException(blob)) return blob; - private[0] &= 248; - private[31] &= 127; - private[31] |= 64; + size_t bits; + uint8_t *data = js_get_blob_data_bits(js, &bits, blob); + if (!data || bits != 256) { + JS_FreeValue(js, blob); + return JS_ThrowInternalError(js, "failed to get 256 bits of random bytes"); + } - crypto_x25519_public_key(public,private); + uint8_t priv[32]; + uint8_t pub[32]; - JS_SetPropertyStr(js, ret, "public", crypto2js(js, public)); - JS_SetPropertyStr(js, ret, "private", crypto2js(js,private)); - return ret; + memcpy(priv, data, 32); + JS_FreeValue(js, blob); // Done with random blob + + // Clamp the private key + priv[0] &= 248; + priv[31] &= 127; + priv[31] |= 64; + + crypto_x25519_public_key(pub, priv); + + JS_SetPropertyStr(js, ret, "public", js_new_blob_stoned_copy(js, pub, 32)); + JS_SetPropertyStr(js, ret, "private", js_new_blob_stoned_copy(js, priv, 32)); + return ret; } JSValue js_crypto_shared(JSContext *js, JSValue self, int argc, JSValue *argv) { - if (argc < 1 || !JS_IsObject(argv[0])) { - return JS_ThrowTypeError(js, "crypto.shared: expected an object argument"); + if (argc < 2) { + return JS_ThrowTypeError(js, "crypto.shared: expected public_key, private_key"); } - JSValue obj = argv[0]; + uint8_t *pub = get_blob_check_bits(js, argv[0], 256, "crypto.shared public_key"); + if (!pub) return JS_EXCEPTION; - JSValue val_pub = JS_GetPropertyStr(js, obj, "public"); - if (JS_IsException(val_pub)) { - JS_FreeValue(js, val_pub); - return JS_EXCEPTION; - } - - JSValue val_priv = JS_GetPropertyStr(js, obj, "private"); - if (JS_IsException(val_priv)) { - JS_FreeValue(js, val_pub); - JS_FreeValue(js, val_priv); - return JS_EXCEPTION; - } - - uint8_t pub[32], priv[32]; - js2crypto(js, val_pub, pub); - js2crypto(js, val_priv, priv); - - JS_FreeValue(js, val_pub); - JS_FreeValue(js, val_priv); + uint8_t *priv = get_blob_check_bits(js, argv[1], 256, "crypto.shared private_key"); + if (!priv) return JS_EXCEPTION; uint8_t shared[32]; crypto_x25519(shared, priv, pub); - return crypto2js(js, shared); + return js_new_blob_stoned_copy(js, shared, 32); } -JSValue js_crypto_hash(JSContext *js, JSValue self, int argc, JSValue *argv) +JSValue js_crypto_blake2(JSContext *js, JSValue self, int argc, JSValue *argv) { if (argc < 1) - return JS_ThrowTypeError(js, "hash requires at least one argument"); + return JS_ThrowTypeError(js, "crypto.blake2: expected data blob"); - // Get input data - size_t data_len; - void *data = js_get_blob_data(js, &data_len, argv[0]); - if (!data) - return JS_ThrowTypeError(js, "hash: first argument must be an ArrayBuffer"); + size_t data_bits; + uint8_t *data = get_blob_any(js, argv[0], &data_bits, "crypto.blake2 data"); + if (!data) return JS_EXCEPTION; - // Get hash length (default 32) int32_t hash_len = 32; if (argc > 1) { if (JS_ToInt32(js, &hash_len, argv[1])) return JS_EXCEPTION; if (hash_len < 1 || hash_len > 64) - return JS_ThrowRangeError(js, "hash length must be between 1 and 64"); + return JS_ThrowRangeError(js, "crypto.blake2: hash length must be between 1 and 64 bytes"); } - // Allocate output buffer - uint8_t *hash = js_malloc(js, hash_len); - if (!hash) - return JS_EXCEPTION; + uint8_t hash[64]; + // Use (bits + 7) / 8 to get byte length covering all bits + crypto_blake2b(hash, hash_len, data, (data_bits + 7) / 8); - // Compute BLAKE2b hash - crypto_blake2b(hash, hash_len, data, data_len); + return js_new_blob_stoned_copy(js, hash, hash_len); +} + +JSValue js_crypto_sign(JSContext *js, JSValue self, int argc, JSValue *argv) { + if (argc < 2) return JS_ThrowTypeError(js, "crypto.sign: expected secret_key, message"); - // Return as blob - JSValue result = js_new_blob_stoned_copy(js, hash, hash_len); - js_free(js, hash); - return result; + uint8_t *sk = get_blob_check_bits(js, argv[0], 512, "crypto.sign secret_key"); + if (!sk) return JS_EXCEPTION; + + size_t msg_bits; + uint8_t *msg = get_blob_any(js, argv[1], &msg_bits, "crypto.sign message"); + if (!msg) return JS_EXCEPTION; + + uint8_t sig[64]; + crypto_eddsa_sign(sig, sk, msg, (msg_bits + 7) / 8); + + return js_new_blob_stoned_copy(js, sig, 64); +} + +JSValue js_crypto_verify(JSContext *js, JSValue self, int argc, JSValue *argv) { + if (argc < 3) return JS_ThrowTypeError(js, "crypto.verify: expected signature, public_key, message"); + + uint8_t *sig = get_blob_check_bits(js, argv[0], 512, "crypto.verify signature"); + if (!sig) return JS_EXCEPTION; + + uint8_t *pk = get_blob_check_bits(js, argv[1], 256, "crypto.verify public_key"); + if (!pk) return JS_EXCEPTION; + + size_t msg_bits; + uint8_t *msg = get_blob_any(js, argv[2], &msg_bits, "crypto.verify message"); + if (!msg) return JS_EXCEPTION; + + int ret = crypto_eddsa_check(sig, pk, msg, (msg_bits + 7) / 8); + return JS_NewBool(js, ret == 0); +} + +JSValue js_crypto_lock(JSContext *js, JSValue self, int argc, JSValue *argv) { + if (argc < 3) return JS_ThrowTypeError(js, "crypto.lock: expected key, nonce, message, [ad]"); + + uint8_t *key = get_blob_check_bits(js, argv[0], 256, "crypto.lock key"); + if (!key) return JS_EXCEPTION; + + uint8_t *nonce = get_blob_check_bits(js, argv[1], 192, "crypto.lock nonce"); + if (!nonce) return JS_EXCEPTION; + + size_t msg_bits; + uint8_t *msg = get_blob_any(js, argv[2], &msg_bits, "crypto.lock message"); + if (!msg) return JS_EXCEPTION; + size_t msg_len = (msg_bits + 7) / 8; + + size_t ad_len = 0; + uint8_t *ad = NULL; + if (argc > 3 && !JS_IsNull(argv[3])) { + size_t ad_bits; + ad = get_blob_any(js, argv[3], &ad_bits, "crypto.lock ad"); + if (!ad) return JS_EXCEPTION; + ad_len = (ad_bits + 7) / 8; + } + + size_t out_len = msg_len + 16; + uint8_t *out = malloc(out_len); + if (!out) return JS_ThrowOutOfMemory(js); + + // Output: [Ciphertext (msg_len)] [MAC (16)] + crypto_aead_lock(out, out + msg_len, key, nonce, ad, ad_len, msg, msg_len); + + JSValue ret = js_new_blob_stoned_copy(js, out, out_len); + free(out); + return ret; +} + +JSValue js_crypto_unlock(JSContext *js, JSValue self, int argc, JSValue *argv) { + if (argc < 3) return JS_ThrowTypeError(js, "crypto.unlock: expected key, nonce, ciphertext, [ad]"); + + uint8_t *key = get_blob_check_bits(js, argv[0], 256, "crypto.unlock key"); + if (!key) return JS_EXCEPTION; + + uint8_t *nonce = get_blob_check_bits(js, argv[1], 192, "crypto.unlock nonce"); + if (!nonce) return JS_EXCEPTION; + + size_t cipher_bits; + uint8_t *cipher = get_blob_any(js, argv[2], &cipher_bits, "crypto.unlock ciphertext"); + if (!cipher) return JS_EXCEPTION; + + size_t cipher_len = (cipher_bits + 7) / 8; + if (cipher_len < 16) return JS_ThrowTypeError(js, "crypto.unlock: ciphertext too short (min 16 bytes)"); + + size_t msg_len = cipher_len - 16; + + size_t ad_len = 0; + uint8_t *ad = NULL; + if (argc > 3 && !JS_IsNull(argv[3])) { + size_t ad_bits; + ad = get_blob_any(js, argv[3], &ad_bits, "crypto.unlock ad"); + if (!ad) return JS_EXCEPTION; + ad_len = (ad_bits + 7) / 8; + } + + uint8_t *out = malloc(msg_len > 0 ? msg_len : 1); + if (!out) return JS_ThrowOutOfMemory(js); + + // MAC is at cipher + msg_len + const uint8_t *mac = cipher + msg_len; + + if (crypto_aead_unlock(out, mac, key, nonce, ad, ad_len, cipher, msg_len) != 0) { + free(out); + return JS_NULL; + } + + JSValue ret = js_new_blob_stoned_copy(js, out, msg_len); + free(out); + return ret; } static const JSCFunctionListEntry js_crypto_funcs[] = { JS_CFUNC_DEF("keypair", 0, js_crypto_keypair), - JS_CFUNC_DEF("shared", 1, js_crypto_shared), - JS_CFUNC_DEF("hash", 2, js_crypto_hash), + JS_CFUNC_DEF("shared", 2, js_crypto_shared), + JS_CFUNC_DEF("blake2", 1, js_crypto_blake2), + JS_CFUNC_DEF("sign", 2, js_crypto_sign), + JS_CFUNC_DEF("verify", 3, js_crypto_verify), + JS_CFUNC_DEF("lock", 3, js_crypto_lock), + JS_CFUNC_DEF("unlock", 3, js_crypto_unlock), }; JSValue js_crypto_use(JSContext *js) diff --git a/scripts/mersenne.c b/scripts/mersenne.c new file mode 100644 index 00000000..6c522c22 --- /dev/null +++ b/scripts/mersenne.c @@ -0,0 +1,148 @@ +#include "quickjs.h" +#include "cell.h" +#include +#include "qjs_macros.h" + +// Random number generation constants for MT19937-64 +#define STATE_VECTOR_LENGTH 312 +#define STATE_VECTOR_M 156 +#define NN STATE_VECTOR_LENGTH +#define MM STATE_VECTOR_M +#define MATRIX_A 0xB5026F5AA96619E9ULL +#define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */ +#define LM 0x7FFFFFFFULL /* Least significant 31 bits */ + +typedef struct tagMTRand { + uint64_t mt[STATE_VECTOR_LENGTH]; + int32_t index; +} MTRand; + +// Random number generation functions +static void m_seedRand(MTRand* rand, uint64_t seed) { + rand->mt[0] = seed; + for(rand->index = 1; rand->index < NN; rand->index++) { + rand->mt[rand->index] = (6364136223846793005ULL * (rand->mt[rand->index-1] ^ (rand->mt[rand->index-1] >> 62)) + rand->index); + } +} + +static int64_t genRandLong(MTRand* rand) { + int i; + uint64_t x; + static uint64_t mag01[2] = {0ULL, MATRIX_A}; + + if (rand->index >= NN) { /* generate NN words at one time */ + /* if init_genrand64() has not been called, */ + /* a default initial seed is used */ + if (rand->index == NN+1) + m_seedRand(rand, 5489ULL); + + for (i = 0; i < NN-MM; i++) { + x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM); + rand->mt[i] = rand->mt[i+MM] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; + } + for (; i < NN-1; i++) { + x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM); + rand->mt[i] = rand->mt[i+(MM-NN)] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; + } + x = (rand->mt[NN-1] & UM) | (rand->mt[0] & LM); + rand->mt[NN-1] = rand->mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; + + rand->index = 0; + } + + x = rand->mt[rand->index++]; + + x ^= (x >> 29) & 0x5555555555555555ULL; + x ^= (x << 17) & 0x71D67FFFEDA60000ULL; + x ^= (x << 37) & 0xFFF7EEE000000000ULL; + x ^= (x >> 43); + + return (int64_t)(x & 0x000FFFFFFFFFFFFFULL); /* return 52-bit value safe for JS */ +} + +static double genRand(MTRand* rand) { + /* generates a random number on [0,1)-real-interval */ + return (genRandLong(rand) >> 11) * (1.0/9007199254740992.0); +} + +/* JS Class Definition */ +static JSClassID js_mersenne_class_id; + +static void js_mersenne_finalizer(JSRuntime *rt, JSValue val) { + MTRand *mrand = JS_GetOpaque(val, js_mersenne_class_id); + js_free_rt(rt, mrand); +} + +static JSClassDef js_mersenne_class = { + "Mersenne", + .finalizer = js_mersenne_finalizer, +}; + +static MTRand *js2mersenne(JSContext *js, JSValue v) { + return JS_GetOpaque(v, js_mersenne_class_id); +} + +/* Methods */ +JSC_CCALL(mersenne_get, + MTRand *mrand = js2mersenne(js, self); + if (!mrand) return JS_ThrowTypeError(js, "Invalid mersenne context"); + return JS_NewFloat64(js, genRand(mrand)); +) + +static const JSCFunctionListEntry js_mersenne_funcs[] = { + JS_CFUNC_DEF("get", 0, js_mersenne_get), +}; + +/* Factory Function */ +static JSValue js_mersenne_use_call(JSContext *js, JSValueConst func_obj, + JSValueConst this_val, int argc, JSValueConst *argv) +{ + uint64_t seed; + + if (argc == 0 || JS_IsNull(argv[0])) { + // Use OS random + extern int randombytes(void *buf, size_t n); + randombytes(&seed, 8); + } else { + if (JS_ToFloat64(js, (double*)&seed, argv[0])) { + // Fallback to number if bigint fails or is not provided as bigint + double d; + if (JS_ToFloat64(js, &d, argv[0])) return JS_EXCEPTION; + seed = (uint64_t)d; + } + } + + MTRand *mrand = js_malloc(js, sizeof(MTRand)); + if (!mrand) return JS_ThrowOutOfMemory(js); + + m_seedRand(mrand, seed); + + JSValue obj = JS_NewObjectClass(js, js_mersenne_class_id); + if (JS_IsException(obj)) { + js_free(js, mrand); + return obj; + } + + JS_SetOpaque(obj, mrand); + + // Store seed as a read-only property + JS_DefinePropertyValueStr(js, obj, "seed", + JS_NewFloat64(js, seed), + JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE // Read-only (no WRITABLE) + ); + + return obj; +} + +JSValue js_mersenne_use(JSContext *js) +{ + JS_NewClassID(&js_mersenne_class_id); + JS_NewClass(JS_GetRuntime(js), js_mersenne_class_id, &js_mersenne_class); + + JSValue proto = JS_NewObject(js); + JS_SetPropertyFunctionList(js, proto, js_mersenne_funcs, sizeof(js_mersenne_funcs)/sizeof(JSCFunctionListEntry)); + JS_SetClassProto(js, js_mersenne_class_id, proto); + + // Return the factory function + return JS_NewCFunction2(js, js_mersenne_use_call, "mersenne", 1, JS_CFUNC_generic, 0); +} diff --git a/scripts/os.c b/scripts/os.c index 653becd0..8e82bcf4 100644 --- a/scripts/os.c +++ b/scripts/os.c @@ -476,11 +476,13 @@ int randombytes(void *buf, size_t n) { #else // ------- Other Unix: read from /dev/urandom ------- +static int rand_fd = -1; int randombytes(void *buf, size_t n) { - int fd = open("/dev/urandom", O_RDONLY); - if (fd < 0) return -1; - ssize_t r = read(fd, buf, n); - close(fd); + if (rand_fd < 0) { + rand_fd = open("/dev/urandom", O_RDONLY); + if (rand_fd < 0) return -1; + } + ssize_t r = read(rand_fd, buf, n); return (r == (ssize_t)n) ? 0 : -1; } #endif diff --git a/source/cell.h b/source/cell.h index 77b02f2c..3270ab0b 100644 --- a/source/cell.h +++ b/source/cell.h @@ -34,8 +34,6 @@ typedef struct letter { }; } letter; -#define STATE_VECTOR_LENGTH 312 -#define STATE_VECTOR_M 156 #define ACTOR_IDLE 0 // Actor not doing anything #define ACTOR_READY 1 // Actor ready for a turn @@ -46,11 +44,6 @@ typedef struct letter { extern int tracy_profiling_enabled; -typedef struct tagMTRand { - uint64_t mt[STATE_VECTOR_LENGTH]; - int32_t index; -} MTRand; - typedef struct cell_rt { JSContext *context; #ifdef HAVE_MIMALLOC @@ -67,7 +60,6 @@ mi_heap_t *heap; SDL_Mutex *msg_mutex; /* For message queue and timers queue */ char *id; - MTRand mrand; int idx_count; @@ -116,6 +108,8 @@ JSValue bool2js(JSContext *js, int b); double js2number(JSContext *js, JSValue v); JSValue number2js(JSContext *js, double g); +double cell_random(); + #ifdef __cplusplus } #endif diff --git a/source/jsffi.c b/source/jsffi.c index 9c985c36..f33a86c3 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -20,67 +20,6 @@ #include #endif -// Random number generation constants for MT19937-64 -#define NN STATE_VECTOR_LENGTH -#define MM STATE_VECTOR_M -#define MATRIX_A 0xB5026F5AA96619E9ULL -#define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */ -#define LM 0x7FFFFFFFULL /* Least significant 31 bits */ - -// Random number generation functions -void m_seedRand(MTRand* rand, uint64_t seed) { - rand->mt[0] = seed; - for(rand->index = 1; rand->index < NN; rand->index++) { - rand->mt[rand->index] = (6364136223846793005ULL * (rand->mt[rand->index-1] ^ (rand->mt[rand->index-1] >> 62)) + rand->index); - } -} - -int64_t genRandLong(MTRand* rand) { - int i; - uint64_t x; - static uint64_t mag01[2] = {0ULL, MATRIX_A}; - - if (rand->index >= NN) { /* generate NN words at one time */ - /* if init_genrand64() has not been called, */ - /* a default initial seed is used */ - if (rand->index == NN+1) - m_seedRand(rand, 5489ULL); - - for (i = 0; i < NN-MM; i++) { - x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM); - rand->mt[i] = rand->mt[i+MM] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; - } - for (; i < NN-1; i++) { - x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM); - rand->mt[i] = rand->mt[i+(MM-NN)] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; - } - x = (rand->mt[NN-1] & UM) | (rand->mt[0] & LM); - rand->mt[NN-1] = rand->mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; - - rand->index = 0; - } - - x = rand->mt[rand->index++]; - - x ^= (x >> 29) & 0x5555555555555555ULL; - x ^= (x << 17) & 0x71D67FFFEDA60000ULL; - x ^= (x << 37) & 0xFFF7EEE000000000ULL; - x ^= (x >> 43); - - return (int64_t)(x & 0x000FFFFFFFFFFFFFULL); /* return 52-bit value safe for JS */ -} - -double genRand(MTRand* rand) { - /* generates a random number on [0,1)-real-interval */ - return (genRandLong(rand) >> 11) * (1.0/9007199254740992.0); -} - -double rand_range(JSContext *js, double min, double max) -{ - MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand; - return genRand(mrand) * (max-min)+min; -} - #define JS_GETNUM(JS,VAL,I,TO,TYPE) { \ JSValue val = JS_GetPropertyUint32(JS,VAL,I); \ TO = js2##TYPE(JS, val); \ @@ -157,33 +96,11 @@ static uint32_t xorshift32(){ return rng_state = x; } -JSC_CCALL(os_rand, - MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand; - return number2js(js, genRand(mrand)); -) - -JSC_CCALL(os_randi, - MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand; - return JS_NewInt64(js, genRandLong(mrand)); -) - -JSC_CCALL(os_srand, - MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand; - m_seedRand(mrand, js2number(js,argv[0])); -) - void ffi_load(JSContext *js) { cell_rt *rt = JS_GetContextOpaque(js); JS_FreeValue(js, js_blob_use(js)); - uint64_t rr; - // randombytes is defined in qjs_crypto.c which is linked, but header was removed. - // We declare it here to avoid implicit declaration error. - extern int randombytes(void *buf, size_t n); - randombytes(&rr,4); - m_seedRand(&rt->mrand, rr); - JSValue globalThis = JS_GetGlobalObject(js); JSValue prosp = JS_NewObject(js); diff --git a/source/qjs_blob.c b/source/qjs_blob.c index b3edddb5..cc5ad3b9 100644 --- a/source/qjs_blob.c +++ b/source/qjs_blob.c @@ -538,6 +538,16 @@ void *js_get_blob_data(JSContext *js, size_t *size, JSValue v) return b->data; } +void *js_get_blob_data_bits(JSContext *js, size_t *bits, JSValue v) +{ + blob *b = js2blob(js, v); + if (!b || !b->is_stone) + return NULL; + + *bits = b->length; + return b->data; +} + int js_is_blob(JSContext *js, JSValue v) { blob *b = js2blob(js,v);