From f4cdfc037b4748bfb0e47595622da32e8da8fb04 Mon Sep 17 00:00:00 2001 From: Eric Walker Date: Mon, 1 Jul 2024 18:45:29 -0400 Subject: [PATCH 1/5] Add crypto hmac-sha256 --- Cargo.lock | 18 ++++++ crates/core/src/runtime.rs | 3 +- crates/javy/Cargo.toml | 2 + crates/javy/src/apis/cryptox/mod.rs | 92 +++++++++++++++++++++++++++++ crates/javy/src/apis/mod.rs | 2 + crates/javy/src/config.rs | 9 +++ crates/javy/src/runtime.rs | 9 ++- 7 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 crates/javy/src/apis/cryptox/mod.rs diff --git a/Cargo.lock b/Cargo.lock index baa355dc..ae2ac0ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -873,6 +873,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -1277,6 +1278,15 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.9" @@ -1689,6 +1699,7 @@ dependencies = [ "anyhow", "bitflags", "fastrand", + "hmac", "javy-test-macros", "quickcheck", "rmp-serde", @@ -1698,6 +1709,7 @@ dependencies = [ "serde", "serde-transcode", "serde_json", + "sha2", "simd-json", ] @@ -2967,6 +2979,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "swc_atoms" version = "0.6.7" diff --git a/crates/core/src/runtime.rs b/crates/core/src/runtime.rs index b973bd7b..ee366cd8 100644 --- a/crates/core/src/runtime.rs +++ b/crates/core/src/runtime.rs @@ -12,7 +12,8 @@ pub(crate) fn new(shared_config: SharedConfig) -> Result { // we're disabling this temporarily. It will be enabled once we have a // fix forward. .override_json_parse_and_stringify(false) - .javy_json(false); + .javy_json(false) + .javy_cryptox(true); Runtime::new(std::mem::take(config)) } diff --git a/crates/javy/Cargo.toml b/crates/javy/Cargo.toml index a8257dcb..253648a0 100644 --- a/crates/javy/Cargo.toml +++ b/crates/javy/Cargo.toml @@ -24,6 +24,8 @@ quickcheck = "1" bitflags = { workspace = true } fastrand = "2.1.0" simd-json = { version = "0.13.10", optional = true, default-features = false, features = ["big-int-as-float", "serde_impl"] } +sha2 = "0.10.8" +hmac = "0.12.1" [dev-dependencies] javy-test-macros = { path = "../test-macros/" } diff --git a/crates/javy/src/apis/cryptox/mod.rs b/crates/javy/src/apis/cryptox/mod.rs new file mode 100644 index 00000000..b7143376 --- /dev/null +++ b/crates/javy/src/apis/cryptox/mod.rs @@ -0,0 +1,92 @@ +use crate::quickjs::{context::Intrinsic, qjs, Ctx, Object, String as JSString, Value, Function}; +use crate::{ + hold, hold_and_release, val_to_string, + to_js_error, Args +}; +use anyhow::{bail, Error, Result}; + +use sha2::Sha256; +use hmac::{Hmac, Mac}; + +/// An implemetation of crypto APIs to optimize fuel. +/// Currently, hmacSHA256 is the only function implemented. +pub struct Cryptox; + +impl Intrinsic for Cryptox { + unsafe fn add_intrinsic(ctx: std::ptr::NonNull) { + register(Ctx::from_raw(ctx)).expect("`Cryptox` APIs to succeed") + } +} + +fn register(this: Ctx<'_>) -> Result<()> { + let globals = this.globals(); + + // let crypto_obj = Object::new(cx)?; + let crypto_obj = Object::new(this.clone())?; + + crypto_obj.set( + "hmacSHA256", + Function::new(this.clone(), |this, args| { + let (this, args) = hold_and_release!(this, args); + hmac_sha256(hold!(this.clone(), args)).map_err(|e| to_js_error(this, e)) + }), + )?; + + globals.set("cryptox", crypto_obj)?; + + Ok::<_, Error>(()) +} +/// hmac_sha256 applies the HMAC algorithm using sha256 for hashing. +/// Arg[0] - secret +/// Arg[1] - message +/// returns - hex encoded string of hmac. +fn hmac_sha256(args: Args<'_>) -> Result> { + let (cx, args) = args.release(); + + if args.len() != 2 { + bail!("Wrong number of arguments. Expected 2. Got {}", args.len()); + } + + let js_string_secret = val_to_string(&cx, args[0].clone())?; + let js_string_message = val_to_string(&cx, args[1].clone())?; + + /// Create alias for HMAC-SHA256 + type HmacSha256 = Hmac; + + let mut mac = HmacSha256::new_from_slice(&js_string_secret.as_bytes()) + .expect("HMAC can take key of any size"); + mac.update(&js_string_message.as_bytes()); + + let result = mac.finalize(); + let code_bytes = result.into_bytes(); + let code : String = format!("{code_bytes:x}"); + let js_string = JSString::from_str(cx, &code); + Ok(Value::from_string(js_string?)) +} + +#[cfg(test)] +mod tests { + use crate::{quickjs::Value, Config, Runtime}; + use anyhow::{Error, Result}; + + #[test] + fn test_text_encoder_decoder() -> Result<()> { + let mut config = Config::default(); + config.javy_cryptox(true); + let runtime = Runtime::new(config)?; + + runtime.context().with(|this| { + let result: Value<'_> = this.eval( + r#" + let expectedHex = "97d2a569059bbcd8ead4444ff99071f4c01d005bcefe0d3567e1be628e5fdcd9"; + let result = cryptox.hmacSHA256("my secret and secure key", "input message"); + expectedHex === result; + "#, + )?; + + assert!(result.as_bool().unwrap()); + Ok::<_, Error>(()) + })?; + Ok(()) + } +} \ No newline at end of file diff --git a/crates/javy/src/apis/mod.rs b/crates/javy/src/apis/mod.rs index 67979d7e..9b9d8202 100644 --- a/crates/javy/src/apis/mod.rs +++ b/crates/javy/src/apis/mod.rs @@ -62,6 +62,7 @@ pub(crate) mod json; pub(crate) mod random; pub(crate) mod stream_io; pub(crate) mod text_encoding; +pub(crate) mod cryptox; pub(crate) use console::*; #[cfg(feature = "json")] @@ -69,3 +70,4 @@ pub(crate) use json::*; pub(crate) use random::*; pub(crate) use stream_io::*; pub(crate) use text_encoding::*; +pub(crate) use cryptox::*; diff --git a/crates/javy/src/config.rs b/crates/javy/src/config.rs index 6f542642..f482ecbe 100644 --- a/crates/javy/src/config.rs +++ b/crates/javy/src/config.rs @@ -38,6 +38,7 @@ bitflags! { pub(crate) struct JavyIntrinsics: u32 { const STREAM_IO = 1; const JSON = 1 << 1; + const CRYPTOX = 1 << 2; } } @@ -179,6 +180,14 @@ impl Config { self } + /// Whether the `Javy.CRYPTOX` intrinsic will be available. + /// Enabled by default. + // #[cfg(feature = "cryptox")] + pub fn javy_cryptox(&mut self, enable: bool) -> &mut Self { + self.javy_intrinsics.set(JavyIntrinsics::CRYPTOX, enable); + self + } + /// Enables whether the output of console.log will be redirected to /// `stderr`. pub fn redirect_stdout_to_stderr(&mut self, enable: bool) -> &mut Self { diff --git a/crates/javy/src/runtime.rs b/crates/javy/src/runtime.rs index 22d2f9ab..c94e113f 100644 --- a/crates/javy/src/runtime.rs +++ b/crates/javy/src/runtime.rs @@ -1,7 +1,7 @@ // use crate::quickjs::JSContextRef; use super::from_js_error; use crate::{ - apis::{Console, NonStandardConsole, Random, StreamIO, TextEncoding}, + apis::{Cryptox, Console, NonStandardConsole, Random, StreamIO, TextEncoding}, config::{JSIntrinsics, JavyIntrinsics}, Config, }; @@ -144,6 +144,13 @@ impl Runtime { JavyJson::add_intrinsic(ctx.as_raw()) } } + + if javy_intrinsics.contains(JavyIntrinsics::CRYPTOX) { + // #[cfg(feature = "cryptox")] + unsafe { + Cryptox::add_intrinsic(ctx.as_raw()) + } + } }); Ok(ManuallyDrop::new(context)) From 65fe6727daa588c659620282b42483818918c6b4 Mon Sep 17 00:00:00 2001 From: Eric Walker Date: Tue, 2 Jul 2024 08:43:35 -0400 Subject: [PATCH 2/5] Use configuration for loading cryptox --- crates/config/src/lib.rs | 2 ++ crates/core/src/runtime.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 8836b9e2..f1dcbbfd 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -31,6 +31,7 @@ bitflags! { const JAVY_STREAM_IO = 1 << 2; const REDIRECT_STDOUT_TO_STDERR = 1 << 3; const TEXT_ENCODING = 1 << 4; + const JAVY_CRYPTOX = 1 << 5; } } @@ -44,5 +45,6 @@ mod tests { assert!(Config::JAVY_STREAM_IO == Config::from_bits(1 << 2).unwrap()); assert!(Config::REDIRECT_STDOUT_TO_STDERR == Config::from_bits(1 << 3).unwrap()); assert!(Config::TEXT_ENCODING == Config::from_bits(1 << 4).unwrap()); + assert!(Config::JAVY_CRYPTOX == Config::from_bits(1 << 5).unwrap()); } } diff --git a/crates/core/src/runtime.rs b/crates/core/src/runtime.rs index ee366cd8..1e52c74b 100644 --- a/crates/core/src/runtime.rs +++ b/crates/core/src/runtime.rs @@ -13,7 +13,7 @@ pub(crate) fn new(shared_config: SharedConfig) -> Result { // fix forward. .override_json_parse_and_stringify(false) .javy_json(false) - .javy_cryptox(true); + .javy_cryptox(shared_config.contains(SharedConfig::JAVY_CRYPTOX)); Runtime::new(std::mem::take(config)) } From f379f9a53d10f4498de68be085fdc22ffbc14d69 Mon Sep 17 00:00:00 2001 From: Eric Walker Date: Tue, 2 Jul 2024 10:34:14 -0400 Subject: [PATCH 3/5] Address linting feedback HmacSha256::new_from_slice(&js_string_secret.as_bytes()) => HmacSha256::new_from_slice(js_string_secret.as_bytes()) No need to pass in address here. Same for js_string_message. --- crates/javy/src/apis/cryptox/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/javy/src/apis/cryptox/mod.rs b/crates/javy/src/apis/cryptox/mod.rs index b7143376..862db964 100644 --- a/crates/javy/src/apis/cryptox/mod.rs +++ b/crates/javy/src/apis/cryptox/mod.rs @@ -53,9 +53,9 @@ fn hmac_sha256(args: Args<'_>) -> Result> { /// Create alias for HMAC-SHA256 type HmacSha256 = Hmac; - let mut mac = HmacSha256::new_from_slice(&js_string_secret.as_bytes()) + let mut mac = HmacSha256::new_from_slice(js_string_secret.as_bytes()) .expect("HMAC can take key of any size"); - mac.update(&js_string_message.as_bytes()); + mac.update(js_string_message.as_bytes()); let result = mac.finalize(); let code_bytes = result.into_bytes(); From 4e6f967f06e520013b9a6e8dcac954c76301a483 Mon Sep 17 00:00:00 2001 From: Eric Walker Date: Tue, 2 Jul 2024 11:09:04 -0400 Subject: [PATCH 4/5] Switch from cryptox to crypto namespace Also drop javy_ prefix in anticipation of switching to winter cg. --- crates/config/src/lib.rs | 4 ++-- crates/core/src/runtime.rs | 2 +- crates/javy/src/apis/{cryptox => crypto}/mod.rs | 15 +++++++-------- crates/javy/src/apis/mod.rs | 4 ++-- crates/javy/src/config.rs | 10 +++++----- crates/javy/src/runtime.rs | 8 ++++---- 6 files changed, 21 insertions(+), 22 deletions(-) rename crates/javy/src/apis/{cryptox => crypto}/mod.rs (89%) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index f1dcbbfd..2fdc6133 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -31,7 +31,7 @@ bitflags! { const JAVY_STREAM_IO = 1 << 2; const REDIRECT_STDOUT_TO_STDERR = 1 << 3; const TEXT_ENCODING = 1 << 4; - const JAVY_CRYPTOX = 1 << 5; + const CRYPTO = 1 << 5; } } @@ -45,6 +45,6 @@ mod tests { assert!(Config::JAVY_STREAM_IO == Config::from_bits(1 << 2).unwrap()); assert!(Config::REDIRECT_STDOUT_TO_STDERR == Config::from_bits(1 << 3).unwrap()); assert!(Config::TEXT_ENCODING == Config::from_bits(1 << 4).unwrap()); - assert!(Config::JAVY_CRYPTOX == Config::from_bits(1 << 5).unwrap()); + assert!(Config::CRYPTO == Config::from_bits(1 << 5).unwrap()); } } diff --git a/crates/core/src/runtime.rs b/crates/core/src/runtime.rs index 1e52c74b..416e39d0 100644 --- a/crates/core/src/runtime.rs +++ b/crates/core/src/runtime.rs @@ -13,7 +13,7 @@ pub(crate) fn new(shared_config: SharedConfig) -> Result { // fix forward. .override_json_parse_and_stringify(false) .javy_json(false) - .javy_cryptox(shared_config.contains(SharedConfig::JAVY_CRYPTOX)); + .crypto(shared_config.contains(SharedConfig::CRYPTO)); Runtime::new(std::mem::take(config)) } diff --git a/crates/javy/src/apis/cryptox/mod.rs b/crates/javy/src/apis/crypto/mod.rs similarity index 89% rename from crates/javy/src/apis/cryptox/mod.rs rename to crates/javy/src/apis/crypto/mod.rs index 862db964..d42e3764 100644 --- a/crates/javy/src/apis/cryptox/mod.rs +++ b/crates/javy/src/apis/crypto/mod.rs @@ -10,14 +10,13 @@ use hmac::{Hmac, Mac}; /// An implemetation of crypto APIs to optimize fuel. /// Currently, hmacSHA256 is the only function implemented. -pub struct Cryptox; +pub struct Crypto; -impl Intrinsic for Cryptox { +impl Intrinsic for Crypto { unsafe fn add_intrinsic(ctx: std::ptr::NonNull) { - register(Ctx::from_raw(ctx)).expect("`Cryptox` APIs to succeed") + register(Ctx::from_raw(ctx)).expect("`Crypto` APIs to succeed") } } - fn register(this: Ctx<'_>) -> Result<()> { let globals = this.globals(); @@ -32,7 +31,7 @@ fn register(this: Ctx<'_>) -> Result<()> { }), )?; - globals.set("cryptox", crypto_obj)?; + globals.set("crypto", crypto_obj)?; Ok::<_, Error>(()) } @@ -72,14 +71,14 @@ mod tests { #[test] fn test_text_encoder_decoder() -> Result<()> { let mut config = Config::default(); - config.javy_cryptox(true); + config.crypto(true); let runtime = Runtime::new(config)?; runtime.context().with(|this| { let result: Value<'_> = this.eval( r#" let expectedHex = "97d2a569059bbcd8ead4444ff99071f4c01d005bcefe0d3567e1be628e5fdcd9"; - let result = cryptox.hmacSHA256("my secret and secure key", "input message"); + let result = crypto.hmacSHA256("my secret and secure key", "input message"); expectedHex === result; "#, )?; @@ -89,4 +88,4 @@ mod tests { })?; Ok(()) } -} \ No newline at end of file +} diff --git a/crates/javy/src/apis/mod.rs b/crates/javy/src/apis/mod.rs index 9b9d8202..f1b023d9 100644 --- a/crates/javy/src/apis/mod.rs +++ b/crates/javy/src/apis/mod.rs @@ -62,7 +62,7 @@ pub(crate) mod json; pub(crate) mod random; pub(crate) mod stream_io; pub(crate) mod text_encoding; -pub(crate) mod cryptox; +pub(crate) mod crypto; pub(crate) use console::*; #[cfg(feature = "json")] @@ -70,4 +70,4 @@ pub(crate) use json::*; pub(crate) use random::*; pub(crate) use stream_io::*; pub(crate) use text_encoding::*; -pub(crate) use cryptox::*; +pub(crate) use crypto::*; diff --git a/crates/javy/src/config.rs b/crates/javy/src/config.rs index f482ecbe..c678f53c 100644 --- a/crates/javy/src/config.rs +++ b/crates/javy/src/config.rs @@ -19,6 +19,7 @@ bitflags! { const OPERATORS = 1 << 12; const BIGNUM_EXTENSION = 1 << 13; const TEXT_ENCODING = 1 << 14; + const CRYPTO = 1 << 15; } } @@ -38,7 +39,6 @@ bitflags! { pub(crate) struct JavyIntrinsics: u32 { const STREAM_IO = 1; const JSON = 1 << 1; - const CRYPTOX = 1 << 2; } } @@ -180,11 +180,11 @@ impl Config { self } - /// Whether the `Javy.CRYPTOX` intrinsic will be available. + /// Whether the `crypto` intrinsic will be available. /// Enabled by default. - // #[cfg(feature = "cryptox")] - pub fn javy_cryptox(&mut self, enable: bool) -> &mut Self { - self.javy_intrinsics.set(JavyIntrinsics::CRYPTOX, enable); + // #[cfg(feature = "crypto")] + pub fn crypto(&mut self, enable: bool) -> &mut Self { + self.intrinsics.set(JSIntrinsics::CRYPTO, enable); self } diff --git a/crates/javy/src/runtime.rs b/crates/javy/src/runtime.rs index c94e113f..cdcb8d4a 100644 --- a/crates/javy/src/runtime.rs +++ b/crates/javy/src/runtime.rs @@ -1,7 +1,7 @@ // use crate::quickjs::JSContextRef; use super::from_js_error; use crate::{ - apis::{Cryptox, Console, NonStandardConsole, Random, StreamIO, TextEncoding}, + apis::{Crypto, Console, NonStandardConsole, Random, StreamIO, TextEncoding}, config::{JSIntrinsics, JavyIntrinsics}, Config, }; @@ -145,10 +145,10 @@ impl Runtime { } } - if javy_intrinsics.contains(JavyIntrinsics::CRYPTOX) { - // #[cfg(feature = "cryptox")] + if intrinsics.contains(JSIntrinsics::CRYPTO) { + // #[cfg(feature = "crypto")] unsafe { - Cryptox::add_intrinsic(ctx.as_raw()) + Crypto::add_intrinsic(ctx.as_raw()) } } }); From 1d0bda1262c12bc57b34e54c1b34a37d1c3d4a05 Mon Sep 17 00:00:00 2001 From: Eric Walker Date: Tue, 2 Jul 2024 11:52:58 -0400 Subject: [PATCH 5/5] Apply linting changes Ran `cargo fmt -- --check`, and applied suggestions, per ci feedback. --- crates/javy/src/apis/crypto/mod.rs | 11 ++++------- crates/javy/src/apis/mod.rs | 4 ++-- crates/javy/src/runtime.rs | 6 ++---- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/crates/javy/src/apis/crypto/mod.rs b/crates/javy/src/apis/crypto/mod.rs index d42e3764..2fac230c 100644 --- a/crates/javy/src/apis/crypto/mod.rs +++ b/crates/javy/src/apis/crypto/mod.rs @@ -1,12 +1,9 @@ -use crate::quickjs::{context::Intrinsic, qjs, Ctx, Object, String as JSString, Value, Function}; -use crate::{ - hold, hold_and_release, val_to_string, - to_js_error, Args -}; +use crate::quickjs::{context::Intrinsic, qjs, Ctx, Function, Object, String as JSString, Value}; +use crate::{hold, hold_and_release, to_js_error, val_to_string, Args}; use anyhow::{bail, Error, Result}; -use sha2::Sha256; use hmac::{Hmac, Mac}; +use sha2::Sha256; /// An implemetation of crypto APIs to optimize fuel. /// Currently, hmacSHA256 is the only function implemented. @@ -58,7 +55,7 @@ fn hmac_sha256(args: Args<'_>) -> Result> { let result = mac.finalize(); let code_bytes = result.into_bytes(); - let code : String = format!("{code_bytes:x}"); + let code: String = format!("{code_bytes:x}"); let js_string = JSString::from_str(cx, &code); Ok(Value::from_string(js_string?)) } diff --git a/crates/javy/src/apis/mod.rs b/crates/javy/src/apis/mod.rs index f1b023d9..d440d6c5 100644 --- a/crates/javy/src/apis/mod.rs +++ b/crates/javy/src/apis/mod.rs @@ -57,17 +57,17 @@ //! //! Disabled by default. pub(crate) mod console; +pub(crate) mod crypto; #[cfg(feature = "json")] pub(crate) mod json; pub(crate) mod random; pub(crate) mod stream_io; pub(crate) mod text_encoding; -pub(crate) mod crypto; pub(crate) use console::*; +pub(crate) use crypto::*; #[cfg(feature = "json")] pub(crate) use json::*; pub(crate) use random::*; pub(crate) use stream_io::*; pub(crate) use text_encoding::*; -pub(crate) use crypto::*; diff --git a/crates/javy/src/runtime.rs b/crates/javy/src/runtime.rs index cdcb8d4a..306a715c 100644 --- a/crates/javy/src/runtime.rs +++ b/crates/javy/src/runtime.rs @@ -1,7 +1,7 @@ // use crate::quickjs::JSContextRef; use super::from_js_error; use crate::{ - apis::{Crypto, Console, NonStandardConsole, Random, StreamIO, TextEncoding}, + apis::{Console, Crypto, NonStandardConsole, Random, StreamIO, TextEncoding}, config::{JSIntrinsics, JavyIntrinsics}, Config, }; @@ -147,9 +147,7 @@ impl Runtime { if intrinsics.contains(JSIntrinsics::CRYPTO) { // #[cfg(feature = "crypto")] - unsafe { - Crypto::add_intrinsic(ctx.as_raw()) - } + unsafe { Crypto::add_intrinsic(ctx.as_raw()) } } });