Skip to content

Commit aedba42

Browse files
authored
Merge pull request #68 from golemcloud/web-crypto
Implemented web Crypto randomUUID and getRandomValues
2 parents 34a0549 + 271c531 commit aedba42

File tree

12 files changed

+268
-4
lines changed

12 files changed

+268
-4
lines changed

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ serde_json = { workspace = true }
3737
test-r = { workspace = true }
3838
tokio = { workspace = true }
3939
tokio-util = { workspace = true }
40+
uuid = { version = "1.18.1", features = ["v4"] }
4041
wac-graph = { workspace = true }
4142
wasmtime = { workspace = true, features = ["async", "component-model"] }
4243
wasmtime-wasi = { workspace = true }

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,10 @@ Implemented by https://github.com/MattiasBuelens/web-streams-polyfill
367367
- `read`
368368
- `write`
369369

370+
### Crypto
371+
- `crypto.randomUUID`
372+
- `crypto.getRandomValues`
373+
370374
### Coming from QuickJS
371375

372376
- Global:

crates/wasm-rquickjs/skeleton/Cargo.toml_

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ futures = { version = "0.3.31", features = [] }
2727
futures-concurrency = "7.6.3"
2828
pin-project = "1.1.10"
2929
url = "2.5.7"
30+
uuid = { version = "1.18.1", features = ["v4"] }
31+
rand = "0.9.2"
3032
wasi = "0.12.1+wasi-0.2.0"
3133
wasi-async-runtime = "0.1.2"
3234
wit-bindgen-rt = { version = "0.42.1", features = ["bitflags"] }

crates/wasm-rquickjs/skeleton/src/builtin/mod.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod streams;
2222
mod timeout;
2323
mod url;
2424
mod util;
25+
mod web_crypto;
2526

2627
pub fn add_module_resolvers(
2728
resolver: rquickjs::loader::BuiltinResolver,
@@ -52,6 +53,8 @@ pub fn add_module_resolvers(
5253
.with_module("process")
5354
.with_module("__wasm_rquickjs_builtin/url_native")
5455
.with_module("__wasm_rquickjs_builtin/url")
56+
.with_module("__wasm_rquickjs_builtin/web_crypto_native")
57+
.with_module("__wasm_rquickjs_builtin/web_crypto")
5558
}
5659

5760
pub fn module_loader() -> (
@@ -81,7 +84,11 @@ pub fn module_loader() -> (
8184
"__wasm_rquickjs_builtin/process_native",
8285
process::js_native_module,
8386
)
84-
.with_module("__wasm_rquickjs_builtin/url_native", url::js_native_module),
87+
.with_module("__wasm_rquickjs_builtin/url_native", url::js_native_module)
88+
.with_module(
89+
"__wasm_rquickjs_builtin/web_crypto_native",
90+
web_crypto::js_native_module,
91+
),
8592
rquickjs::loader::BuiltinLoader::default()
8693
.with_module("__wasm_rquickjs_builtin/console", console::CONSOLE_JS)
8794
.with_module("__wasm_rquickjs_builtin/timeout", timeout::TIMEOUT_JS)
@@ -100,7 +107,11 @@ pub fn module_loader() -> (
100107
.with_module("fs", fs::FS_JS)
101108
.with_module("node:process", process::PROCESS_JS)
102109
.with_module("process", process::PROCESS_JS)
103-
.with_module("__wasm_rquickjs_builtin/url", url::URL_JS),
110+
.with_module("__wasm_rquickjs_builtin/url", url::URL_JS)
111+
.with_module(
112+
"__wasm_rquickjs_builtin/web_crypto",
113+
web_crypto::WEB_CRYPTO_JS,
114+
),
104115
)
105116
}
106117

@@ -112,6 +123,7 @@ pub fn wire_builtins() -> String {
112123
writeln!(result, "{}", streams::WIRE_JS).unwrap();
113124
writeln!(result, "{}", encoding::WIRE_JS).unwrap();
114125
writeln!(result, "{}", url::WIRE_JS).unwrap();
126+
writeln!(result, "{}", web_crypto::WIRE_JS).unwrap();
115127

116128
result
117129
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as webCryptoNative from '__wasm_rquickjs_builtin/web_crypto_native'
2+
3+
export function getRandomValues(typedArray) {
4+
if (typedArray instanceof Int8Array) {
5+
webCryptoNative.randomize_int8_array(typedArray);
6+
} else if (typedArray instanceof Uint8Array) {
7+
webCryptoNative.randomize_uint8_array(typedArray);
8+
} else if (typedArray instanceof Uint8ClampedArray) {
9+
webCryptoNative.randomize_uint8_clamped_array(typedArray);
10+
} else if (typedArray instanceof Int16Array) {
11+
webCryptoNative.randomize_int16_array(typedArray);
12+
} else if (typedArray instanceof Uint16Array) {
13+
webCryptoNative.randomize_uint16_array(typedArray);
14+
} else if (typedArray instanceof Int32Array) {
15+
webCryptoNative.randomize_int32_array(typedArray);
16+
} else if (typedArray instanceof Uint32Array) {
17+
webCryptoNative.randomize_uint32_array(typedArray);
18+
} else if (typedArray instanceof BigInt64Array) {
19+
webCryptoNative.randomize_bigint64_array(typedArray);
20+
} else if (typedArray instanceof BigUint64Array) {
21+
webCryptoNative.randomize_biguint64_array(typedArray);
22+
} else {
23+
throw new TypeError('Unsupported TypedArray type');
24+
}
25+
return typedArray;
26+
}
27+
28+
/**
29+
* Generate a random UUID
30+
* @returns A string containing a randomly generated, 36 character long v4 UUID.
31+
*/
32+
export function randomUUID() {
33+
return webCryptoNative.random_uuid_v4_string();
34+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use rand::RngCore;
2+
use rquickjs::TypedArray;
3+
use std::slice;
4+
5+
// Native functions for the crypto implementation
6+
#[rquickjs::module(rename = "camelCase")]
7+
pub mod native_module {
8+
use rquickjs::TypedArray;
9+
10+
#[rquickjs::function]
11+
pub fn random_uuid_v4_string() -> String {
12+
let uuid = uuid::Uuid::new_v4();
13+
uuid.to_string()
14+
}
15+
16+
#[rquickjs::function]
17+
pub fn randomize_int8_array<'js>(array: TypedArray<'js, i8>) {
18+
super::randomize_typed_array(array);
19+
}
20+
21+
#[rquickjs::function]
22+
pub fn randomize_uint8_array<'js>(array: TypedArray<'js, u8>) {
23+
super::randomize_typed_array(array);
24+
}
25+
26+
#[rquickjs::function]
27+
pub fn randomize_uint8_clamped_array<'js>(array: TypedArray<'js, u8>) {
28+
super::randomize_typed_array(array);
29+
}
30+
31+
#[rquickjs::function]
32+
pub fn randomize_int16_array<'js>(array: TypedArray<'js, i16>) {
33+
super::randomize_typed_array(array);
34+
}
35+
36+
#[rquickjs::function]
37+
pub fn randomize_uint16_array<'js>(array: TypedArray<'js, u16>) {
38+
super::randomize_typed_array(array);
39+
}
40+
41+
#[rquickjs::function]
42+
pub fn randomize_int32_array<'js>(array: TypedArray<'js, i32>) {
43+
super::randomize_typed_array(array);
44+
}
45+
46+
#[rquickjs::function]
47+
pub fn randomize_uint32_array<'js>(array: TypedArray<'js, u32>) {
48+
super::randomize_typed_array(array);
49+
}
50+
51+
#[rquickjs::function]
52+
pub fn randomize_bigint64_array<'js>(array: TypedArray<'js, i64>) {
53+
super::randomize_typed_array(array);
54+
}
55+
56+
#[rquickjs::function]
57+
pub fn randomize_biguint64_array<'js>(array: TypedArray<'js, u64>) {
58+
super::randomize_typed_array(array);
59+
}
60+
}
61+
62+
fn randomize_typed_array<V>(array: TypedArray<V>) {
63+
if let Some(raw) = array.as_raw() {
64+
let slice = unsafe { slice::from_raw_parts_mut(raw.ptr.as_ptr(), raw.len) };
65+
rand::rng().fill_bytes(slice);
66+
}
67+
}
68+
69+
// JS functions for the crypto implementation
70+
pub const WEB_CRYPTO_JS: &str = include_str!("web-crypto.js");
71+
72+
// JS code wiring the crypto module into the global context
73+
pub const WIRE_JS: &str = r#"
74+
import * as __wasm_rquickjs_web_crypto from '__wasm_rquickjs_builtin/web_crypto';
75+
globalThis.crypto = __wasm_rquickjs_web_crypto;
76+
"#;

crates/wasm-rquickjs/skeleton/src/wrappers.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const RESULT_ERR: &str = "err";
88

99
/// rquickjs supports passing tuples as arguments but only up to 8 elements. This wrapper
1010
/// provides support up to 26 elements.
11+
#[allow(dead_code)]
1112
pub struct JsArgs<T>(pub T);
1213

1314
macro_rules! impl_into_args {
@@ -76,6 +77,7 @@ impl_into_args!(
7677
/// The Result is encoded in an object with two fields:
7778
/// - `tag`: a string that is either "ok" or "err", indicating the result type.
7879
/// - `val`: the value of the result, which can be either the success value or the error value.
80+
#[allow(dead_code)]
7981
pub struct JsResult<Ok, Err>(pub Result<Ok, Err>);
8082

8183
impl<'js, Ok: IntoJs<'js>, Err: IntoJs<'js>> IntoJs<'js> for JsResult<Ok, Err> {
@@ -118,6 +120,7 @@ impl<'js, Ok: FromJs<'js>, Err: FromJs<'js>> FromJs<'js> for JsResult<Ok, Err> {
118120
}
119121

120122
// Wrapper type that forces the js type to be a bigint instead of the default number which can loose some bits due to
123+
#[allow(dead_code)]
121124
pub struct BigIntWrapper<T>(pub T);
122125

123126
impl<'js> IntoJs<'js> for BigIntWrapper<u64> {
@@ -150,6 +153,7 @@ impl<'js> FromJs<'js> for BigIntWrapper<i64> {
150153
}
151154
}
152155

156+
#[allow(dead_code)]
153157
pub struct UInt8Array(pub Vec<u8>);
154158

155159
impl<'js> IntoJs<'js> for UInt8Array {

examples/crypto/src/crypto.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
export function newUuids() {
3+
const uuid1 = crypto.randomUUID()
4+
const uuid2 = crypto.randomUUID()
5+
return [uuid1, uuid2]
6+
}
7+
8+
export function randomS8(count) {
9+
const buf = new Int8Array(count);
10+
crypto.getRandomValues(buf);
11+
return Array.from(buf);
12+
}
13+
14+
export function randomU32(count) {
15+
const buf = new Uint32Array(count);
16+
crypto.getRandomValues(buf);
17+
return Array.from(buf);
18+
}

examples/crypto/wit/crypto.wit

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package quickjs:crypto;
2+
3+
world crypto {
4+
export new-uuids: func() -> tuple<string, string>;
5+
export random-s8: func(count: u32) -> list<s8>;
6+
export random-u32: func(count: u32) -> list<u32>;
7+
}

0 commit comments

Comments
 (0)