diff --git a/Cargo.toml b/Cargo.toml index 94f589d04..9313e5c48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,13 +12,17 @@ categories = ["command-line-utilities"] build = "build/main.rs" rust-version = "1.63" +[lib] +crate-type = ["cdylib", "rlib"] + [features] -default = ["ffi", "repl", "hostname", "tls", "http"] +default = ["ffi", "repl", "hostname", "tls", "http", "crypto-full"] ffi = ["dep:libffi"] repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"] hostname = ["dep:hostname"] tls = ["dep:native-tls"] http = ["dep:hyper", "dep:reqwest"] +crypto-full = [] [build-dependencies] indexmap = "1.0.2" @@ -79,7 +83,19 @@ tokio = { version = "1.28.2", features = ["full"] } [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { version = "0.2.10", features = ["js"] } -tokio = { version = "1.28.2", features = ["sync", "macros", "io-util", "rt", "time"] } +tokio = { version = "1.28.2", features = ["sync", "macros", "io-util", "rt"] } + +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] +console_error_panic_hook = "0.1" +console_log = "1.0" +wasm-bindgen = "0.2.87" +wasm-bindgen-futures = "0.4" +serde-wasm-bindgen = "0.5" +web-sys = { version = "0.3", features = [ + "Document", + "Window", + "Element", +]} [target.'cfg(target_os = "wasi")'.dependencies] ring-wasi = { version = "0.16.25" } diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 52fcd8b7a..7d3f26096 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -496,22 +496,28 @@ enum SystemClauseType { CryptoDataHKDF, #[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_password_hash")))] CryptoPasswordHash, + #[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_curve_scalar_mult")))] + CryptoCurveScalarMult, + #[strum_discriminants(strum(props(Arity = "3", Name = "$curve25519_scalar_mult")))] + Curve25519ScalarMult, + #[cfg(feature = "crypto-full")] #[strum_discriminants(strum(props(Arity = "7", Name = "$crypto_data_encrypt")))] CryptoDataEncrypt, + #[cfg(feature = "crypto-full")] #[strum_discriminants(strum(props(Arity = "6", Name = "$crypto_data_decrypt")))] CryptoDataDecrypt, - #[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_curve_scalar_mult")))] - CryptoCurveScalarMult, + #[cfg(feature = "crypto-full")] #[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_sign")))] Ed25519Sign, + #[cfg(feature = "crypto-full")] #[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_verify")))] Ed25519Verify, + #[cfg(feature = "crypto-full")] #[strum_discriminants(strum(props(Arity = "1", Name = "$ed25519_new_keypair")))] Ed25519NewKeyPair, + #[cfg(feature = "crypto-full")] #[strum_discriminants(strum(props(Arity = "2", Name = "$ed25519_keypair_public_key")))] Ed25519KeyPairPublicKey, - #[strum_discriminants(strum(props(Arity = "3", Name = "$curve25519_scalar_mult")))] - Curve25519ScalarMult, #[strum_discriminants(strum(props(Arity = "2", Name = "$first_non_octet")))] FirstNonOctet, #[strum_discriminants(strum(props(Arity = "3", Name = "$load_html")))] @@ -1828,13 +1834,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallCryptoDataHash | &Instruction::CallCryptoDataHKDF | &Instruction::CallCryptoPasswordHash | - &Instruction::CallCryptoDataEncrypt | - &Instruction::CallCryptoDataDecrypt | &Instruction::CallCryptoCurveScalarMult | - &Instruction::CallEd25519Sign | - &Instruction::CallEd25519Verify | - &Instruction::CallEd25519NewKeyPair | - &Instruction::CallEd25519KeyPairPublicKey | &Instruction::CallCurve25519ScalarMult | &Instruction::CallFirstNonOctet | &Instruction::CallLoadHTML | @@ -1891,6 +1891,17 @@ fn generate_instruction_preface() -> TokenStream { functor!(atom!("call"), [atom(name), fixnum(arity)]) } // + #[cfg(feature = "crypto-full")] + &Instruction::CallCryptoDataEncrypt | + &Instruction::CallCryptoDataDecrypt | + &Instruction::CallEd25519Sign | + &Instruction::CallEd25519Verify | + &Instruction::CallEd25519NewKeyPair | + &Instruction::CallEd25519KeyPairPublicKey => { + let (name, arity) = self.to_name_and_arity(); + functor!(atom!("call"), [atom(name), fixnum(arity)]) + } + // &Instruction::ExecuteAtomChars | &Instruction::ExecuteAtomCodes | &Instruction::ExecuteAtomLength | @@ -2056,13 +2067,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteCryptoDataHash | &Instruction::ExecuteCryptoDataHKDF | &Instruction::ExecuteCryptoPasswordHash | - &Instruction::ExecuteCryptoDataEncrypt | - &Instruction::ExecuteCryptoDataDecrypt | &Instruction::ExecuteCryptoCurveScalarMult | - &Instruction::ExecuteEd25519Sign | - &Instruction::ExecuteEd25519Verify | - &Instruction::ExecuteEd25519NewKeyPair | - &Instruction::ExecuteEd25519KeyPairPublicKey | &Instruction::ExecuteCurve25519ScalarMult | &Instruction::ExecuteFirstNonOctet | &Instruction::ExecuteLoadHTML | @@ -2119,6 +2124,17 @@ fn generate_instruction_preface() -> TokenStream { functor!(atom!("execute"), [atom(name), fixnum(arity)]) } // + #[cfg(feature = "crypto-full")] + &Instruction::ExecuteCryptoDataEncrypt | + &Instruction::ExecuteCryptoDataDecrypt | + &Instruction::ExecuteEd25519Sign | + &Instruction::ExecuteEd25519Verify | + &Instruction::ExecuteEd25519NewKeyPair | + &Instruction::ExecuteEd25519KeyPairPublicKey => { + let (name, arity) = self.to_name_and_arity(); + functor!(atom!("execute"), [atom(name), fixnum(arity)]) + } + // &Instruction::Deallocate => { functor!(atom!("deallocate")) } diff --git a/src/lib.rs b/src/lib.rs index acae77ebc..aec9e0d2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,3 +38,17 @@ mod targets; pub mod types; use instructions::instr; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +pub fn eval_code(s: &str) -> String { + use web_sys::console; + use machine::mock_wam::*; + + let mut wam = Machine::with_test_streams(); + let bytes = wam.test_load_string(s); + String::from_utf8_lossy(&bytes).to_string() +} diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index dc55ed861..abd483228 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4663,70 +4663,82 @@ impl Machine { self.crypto_password_hash(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallCryptoCurveScalarMult => { + self.crypto_curve_scalar_mult(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteCryptoCurveScalarMult => { + self.crypto_curve_scalar_mult(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallCurve25519ScalarMult => { + self.curve25519_scalar_mult(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteCurve25519ScalarMult => { + self.curve25519_scalar_mult(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + #[cfg(feature = "crypto-full")] &Instruction::CallCryptoDataEncrypt => { self.crypto_data_encrypt(); step_or_fail!(self, self.machine_st.p += 1); } + #[cfg(feature = "crypto-full")] &Instruction::ExecuteCryptoDataEncrypt => { self.crypto_data_encrypt(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + #[cfg(feature = "crypto-full")] &Instruction::CallCryptoDataDecrypt => { self.crypto_data_decrypt(); step_or_fail!(self, self.machine_st.p += 1); } + #[cfg(feature = "crypto-full")] &Instruction::ExecuteCryptoDataDecrypt => { self.crypto_data_decrypt(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoCurveScalarMult => { - self.crypto_curve_scalar_mult(); - step_or_fail!(self, self.machine_st.p += 1); - } - &Instruction::ExecuteCryptoCurveScalarMult => { - self.crypto_curve_scalar_mult(); - step_or_fail!(self, self.machine_st.p = self.machine_st.cp); - } + #[cfg(feature = "crypto-full")] &Instruction::CallEd25519Sign => { self.ed25519_sign(); step_or_fail!(self, self.machine_st.p += 1); } + #[cfg(feature = "crypto-full")] &Instruction::ExecuteEd25519Sign => { self.ed25519_sign(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + #[cfg(feature = "crypto-full")] &Instruction::CallEd25519Verify => { self.ed25519_verify(); step_or_fail!(self, self.machine_st.p += 1); } + #[cfg(feature = "crypto-full")] &Instruction::ExecuteEd25519Verify => { self.ed25519_verify(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + #[cfg(feature = "crypto-full")] &Instruction::CallEd25519NewKeyPair => { self.ed25519_new_key_pair(); step_or_fail!(self, self.machine_st.p += 1); } + #[cfg(feature = "crypto-full")] &Instruction::ExecuteEd25519NewKeyPair => { self.ed25519_new_key_pair(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + #[cfg(feature = "crypto-full")] &Instruction::CallEd25519KeyPairPublicKey => { self.ed25519_key_pair_public_key(); step_or_fail!(self, self.machine_st.p += 1); } + #[cfg(feature = "crypto-full")] &Instruction::ExecuteEd25519KeyPairPublicKey => { self.ed25519_key_pair_public_key(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurve25519ScalarMult => { - self.curve25519_scalar_mult(); - step_or_fail!(self, self.machine_st.p += 1); - } - &Instruction::ExecuteCurve25519ScalarMult => { - self.curve25519_scalar_mult(); - step_or_fail!(self, self.machine_st.p = self.machine_st.cp); - } &Instruction::CallFirstNonOctet => { self.first_non_octet(); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 9fda84ecd..655bd0fce 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -335,6 +335,19 @@ impl Machine { self.load_file(file.into(), stream); self.user_output.bytes().map(|b| b.unwrap()).collect() } + + pub fn test_load_string(&mut self, code: &str) -> Vec { + use std::io::Read; + + let stream = Stream::from_owned_string( + code.to_owned(), + &mut self.machine_st.arena, + ); + + self.load_file("".into(), stream); + self.user_output.bytes().map(|b| b.unwrap()).collect() + } + } #[cfg(test)] diff --git a/src/machine/mod.rs b/src/machine/mod.rs index ea96f1b43..3d2f79dab 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -427,10 +427,10 @@ impl Machine { let user_output = Stream::stdout(&mut machine_st.arena); let user_error = Stream::stderr(&mut machine_st.arena); - #[cfg(not(target_os = "wasi"))] + #[cfg(not(target_arch = "wasm32"))] let runtime = tokio::runtime::Runtime::new() .unwrap(); - #[cfg(target_os = "wasi")] + #[cfg(target_arch = "wasm32")] let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() .build() diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index b158d428e..a7c680938 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -60,7 +60,7 @@ use std::process; use std::str::FromStr; use chrono::{offset::Local, DateTime}; -#[cfg(not(target_os = "wasi"))] +#[cfg(not(target_arch = "wasm32"))] use cpu_time::ProcessTime; use std::time::{Duration, SystemTime}; @@ -71,8 +71,11 @@ use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use blake2::{Blake2b, Blake2s}; use ring::rand::{SecureRandom, SystemRandom}; +use ring::{digest, hkdf, pbkdf2}; + +#[cfg(feature = "crypto-full")] use ring::{ - aead, digest, hkdf, pbkdf2, + aead, signature::{self, KeyPair}, }; use ripemd160::{Digest, Ripemd160}; @@ -4193,7 +4196,7 @@ impl Machine { self.machine_st.fail = result; } - #[cfg(not(target_os = "wasi"))] + #[cfg(not(target_arch = "wasm32"))] #[inline(always)] pub(crate) fn cpu_now(&mut self) { let secs = ProcessTime::now().as_duration().as_secs_f64(); @@ -4202,7 +4205,7 @@ impl Machine { self.machine_st.unify_f64(secs, self.machine_st.registers[1]); } - #[cfg(target_os = "wasi")] + #[cfg(target_arch = "wasm32")] #[inline(always)] pub(crate) fn cpu_now(&mut self) { // TODO @@ -7159,6 +7162,38 @@ impl Machine { unify!(self.machine_st, self.machine_st.registers[4], ints_list); } + #[inline(always)] + pub(crate) fn crypto_curve_scalar_mult(&mut self) { + + let stub_gen = || functor_stub(atom!("crypto_curve_scalar_mult"), 4); + let scalar_bytes = self.machine_st.integers_to_bytevec(self.machine_st.registers[2], stub_gen); + let point_bytes = self.machine_st.integers_to_bytevec(self.machine_st.registers[3], stub_gen); + + let mut point = secp256k1::Point::decode(&point_bytes).unwrap(); + let scalar = secp256k1::Scalar::decode_reduce(&scalar_bytes); + point *= scalar; + + let uncompressed = self.u8s_to_string(&point.encode_uncompressed()); + + unify!(self.machine_st, self.machine_st.registers[4], uncompressed); + } + + #[inline(always)] + pub(crate) fn curve25519_scalar_mult(&mut self) { + let stub1_gen = || functor_stub(atom!("curve25519_scalar_mult"), 3); + let scalar_bytes = self.machine_st.integers_to_bytevec(self.machine_st.registers[1], stub1_gen); + let stub2_gen = || functor_stub(atom!("curve25519_scalar_mult"), 3); + let point_bytes = self.machine_st.integers_to_bytevec(self.machine_st.registers[2], stub2_gen); + + let result = x25519::x25519(&<[u8; 32]>::try_from(&point_bytes[..]).unwrap(), + &<[u8; 32]>::try_from(&scalar_bytes[..]).unwrap()); + + let string = self.u8s_to_string(&result[..]); + + unify!(self.machine_st, self.machine_st.registers[3], string); + } + + #[cfg(feature = "crypto-full")] #[inline(always)] pub(crate) fn crypto_data_encrypt(&mut self) { let encoding = cell_as_atom!(self.deref_register(3)); @@ -7205,6 +7240,7 @@ impl Machine { unify!(self.machine_st, self.machine_st.registers[7], complete_string); } + #[cfg(feature = "crypto-full")] #[inline(always)] pub(crate) fn crypto_data_decrypt(&mut self) { let data = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); @@ -7257,22 +7293,7 @@ impl Machine { unify!(self.machine_st, self.machine_st.registers[6], complete_string); } - #[inline(always)] - pub(crate) fn crypto_curve_scalar_mult(&mut self) { - - let stub_gen = || functor_stub(atom!("crypto_curve_scalar_mult"), 4); - let scalar_bytes = self.machine_st.integers_to_bytevec(self.machine_st.registers[2], stub_gen); - let point_bytes = self.machine_st.integers_to_bytevec(self.machine_st.registers[3], stub_gen); - - let mut point = secp256k1::Point::decode(&point_bytes).unwrap(); - let scalar = secp256k1::Scalar::decode_reduce(&scalar_bytes); - point *= scalar; - - let uncompressed = self.u8s_to_string(&point.encode_uncompressed()); - - unify!(self.machine_st, self.machine_st.registers[4], uncompressed); - } - + #[cfg(feature = "crypto-full")] #[inline(always)] pub(crate) fn ed25519_new_key_pair(&mut self) { let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(rng()).unwrap(); @@ -7281,6 +7302,7 @@ impl Machine { unify!(self.machine_st, self.machine_st.registers[1], complete_string) } + #[cfg(feature = "crypto-full")] #[inline(always)] pub(crate) fn ed25519_key_pair_public_key(&mut self) { let bytes = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); @@ -7298,6 +7320,7 @@ impl Machine { unify!(self.machine_st, self.machine_st.registers[2], complete_string); } + #[cfg(feature = "crypto-full")] #[inline(always)] pub(crate) fn ed25519_sign(&mut self) { let key = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); @@ -7326,6 +7349,7 @@ impl Machine { unify!(self.machine_st, self.machine_st.registers[4], sig_list); } + #[cfg(feature = "crypto-full")] #[inline(always)] pub(crate) fn ed25519_verify(&mut self) { let key = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); @@ -7344,21 +7368,6 @@ impl Machine { } } - #[inline(always)] - pub(crate) fn curve25519_scalar_mult(&mut self) { - let stub1_gen = || functor_stub(atom!("curve25519_scalar_mult"), 3); - let scalar_bytes = self.machine_st.integers_to_bytevec(self.machine_st.registers[1], stub1_gen); - let stub2_gen = || functor_stub(atom!("curve25519_scalar_mult"), 3); - let point_bytes = self.machine_st.integers_to_bytevec(self.machine_st.registers[2], stub2_gen); - - let result = x25519::x25519(&<[u8; 32]>::try_from(&point_bytes[..]).unwrap(), - &<[u8; 32]>::try_from(&scalar_bytes[..]).unwrap()); - - let string = self.u8s_to_string(&result[..]); - - unify!(self.machine_st, self.machine_st.registers[3], string); - } - #[inline(always)] pub(crate) fn first_non_octet(&mut self) { let addr = self.deref_register(1);