Skip to content

Commit

Permalink
feat: expose globalThis.crypto (#370)
Browse files Browse the repository at this point in the history
  • Loading branch information
nabetti1720 authored May 8, 2024
1 parent 8b0e34d commit 982afe9
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 11 deletions.
1 change: 1 addition & 0 deletions llrt_core/src/module_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ impl ModuleBuilder {
pub fn with_default() -> Self {
Self::new()
.with_module(CryptoModule)
.with_global(crate::modules::crypto::init)
.with_module(HexModule)
.with_global(crate::modules::encoding::init)
.with_module(FsPromisesModule)
Expand Down
18 changes: 18 additions & 0 deletions llrt_core/src/modules/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,24 @@ fn random_fill_sync<'js>(
Ok(obj)
}

pub fn init(ctx: &Ctx<'_>) -> Result<()> {
let globals = ctx.globals();

let crypto = Object::new(ctx.clone())?;

crypto.set("createHash", Func::from(Hash::new))?;
crypto.set("createHmac", Func::from(Hmac::new))?;
crypto.set("randomBytes", Func::from(get_random_bytes))?;
crypto.set("randomInt", Func::from(get_random_int))?;
crypto.set("randomUUID", Func::from(uuidv4))?;
crypto.set("randomFillSync", Func::from(random_fill_sync))?;
crypto.set("randomFill", Func::from(random_fill))?;

globals.set("crypto", crypto)?;

Ok(())
}

pub struct CryptoModule;

impl ModuleDef for CryptoModule {
Expand Down
59 changes: 48 additions & 11 deletions tests/unit/crypto.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,50 @@
import crypto from "crypto";
import defaultImport from "crypto";

describe("crypto object/module", () => {
it("should have a createHash()", () => {
expect(crypto.createHash).toBeDefined();
expect(defaultImport.createHash).toBeDefined();
});
it("should have a createHmac()", () => {
expect(crypto.createHmac).toBeDefined();
expect(defaultImport.createHmac).toBeDefined();
});
it("should have a randomBytes()", () => {
expect(crypto.randomBytes).toBeDefined();
expect(defaultImport.randomBytes).toBeDefined();
});
it("should have a randomInt()", () => {
expect(crypto.randomInt).toBeDefined();
expect(defaultImport.randomInt).toBeDefined();
});
it("should have a randomUUID()", () => {
expect(crypto.randomUUID).toBeDefined();
expect(defaultImport.randomUUID).toBeDefined();
});
it("should have a randomFillSync()", () => {
expect(crypto.randomFillSync).toBeDefined();
expect(defaultImport.randomFillSync).toBeDefined();
});
it("should have a randomFill()", () => {
expect(crypto.randomFill).toBeDefined();
expect(defaultImport.randomFill).toBeDefined();
});
});

describe("Hashing", () => {
it("should hash to sha256 with b64 encoding", () => {
let hash = crypto.createHash("sha256").update("message").digest("base64");
let hash = defaultImport
.createHash("sha256")
.update("message")
.digest("base64");
expect(hash).toEqual("q1MKE+RZFJgrefm34/uplM/R8/si9xzqGvvwK0YMbR0=");
});

it("should hash to sha256 with hex encoding", () => {
let hash = crypto.createHash("sha256").update("message").digest("hex");
let hash = defaultImport
.createHash("sha256")
.update("message")
.digest("hex");
expect(hash).toEqual(
"ab530a13e45914982b79f9b7e3fba994cfd1f3fb22f71cea1afbf02b460c6d1d"
);
Expand All @@ -34,12 +71,12 @@ describe("Hashing", () => {

describe("random", () => {
it("should generate a random buffer synchronously using randomFillSync", () => {
const buffer = crypto.randomFillSync(Buffer.alloc(16));
const buffer = defaultImport.randomFillSync(Buffer.alloc(16));
expect(buffer.length).toEqual(16);
});

it("should generate a random buffer asynchronously using randomFill", (done) => {
crypto.randomFill(Buffer.alloc(16), (err, buffer) => {
defaultImport.randomFill(Buffer.alloc(16), (err, buffer) => {
expect(err).toBeNull();
expect(buffer.length).toEqual(16);
done();
Expand All @@ -48,7 +85,7 @@ describe("random", () => {

it("should generate random bytes synchronously into a Uint8Array using randomFillSync", () => {
const uint8Array = new Uint8Array(16);
crypto.randomFillSync(uint8Array);
defaultImport.randomFillSync(uint8Array);
expect(uint8Array.length).toEqual(16);
for (const byte of uint8Array) {
expect(byte >= 0 && byte <= 255).toBeTruthy();
Expand All @@ -57,7 +94,7 @@ describe("random", () => {

it("should generate random bytes asynchronously into a DataView using randomFill", (done) => {
const dataView = new DataView(new ArrayBuffer(32));
crypto.randomFill(dataView, (err, buffer) => {
defaultImport.randomFill(dataView, (err, buffer) => {
expect(err).toBeNull();
expect(buffer.buffer).toEqual(dataView.buffer);
expect(dataView.byteLength).toEqual(32);
Expand All @@ -71,23 +108,23 @@ describe("random", () => {
});

it("should generate a random UUID using randomUUID", () => {
const uuid = crypto.randomUUID();
const uuid = defaultImport.randomUUID();
expect(uuid.length).toEqual(36);
const uuidRegex =
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
expect(uuid).toMatch(uuidRegex);
});

it("should generate a random bytes buffer using randomBytes", () => {
const buffer = crypto.randomBytes(16);
const buffer = defaultImport.randomBytes(16);
expect(buffer).toBeInstanceOf(Buffer);
expect(buffer.length).toEqual(16);
});

it("should generate a random int using randomInt", () => {
// Do it 10 times, to make sure we respect min and max
for (const number of [...Array(10).keys()]) {
const randomInt = crypto.randomInt(
const randomInt = defaultImport.randomInt(
Number.MAX_SAFE_INTEGER - 1,
Number.MAX_SAFE_INTEGER
);
Expand All @@ -98,7 +135,7 @@ describe("random", () => {

// Do it 20 times to make sure we never get values outside the range
for (const number of [...Array(20).keys()]) {
const randomInt = crypto.randomInt(0, 5);
const randomInt = defaultImport.randomInt(0, 5);
expect(randomInt).toBeLessThan(5);
expect(randomInt).toBeGreaterThanOrEqual(0);
}
Expand Down

0 comments on commit 982afe9

Please sign in to comment.