random
This commit is contained in:
@@ -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',
|
||||
|
||||
358
scripts/crypto.c
358
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)
|
||||
|
||||
148
scripts/mersenne.c
Normal file
148
scripts/mersenne.c
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "quickjs.h"
|
||||
#include "cell.h"
|
||||
#include <stdint.h>
|
||||
#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);
|
||||
}
|
||||
10
scripts/os.c
10
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,67 +20,6 @@
|
||||
#include <dlfcn.h>
|
||||
#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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user