diff --git a/Cargo.lock b/Cargo.lock index 036911e..bfed787 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1125,6 +1125,7 @@ dependencies = [ "clap", "console", "dialoguer", + "digest", "directories", "dunce", "env_logger", @@ -1152,11 +1153,12 @@ dependencies = [ "rbx_xml", "regex", "reqwest", - "ring 0.17.3", "rustyline", "serde", "serde_json", "serde_yaml", + "sha1 0.10.6", + "sha2", "thiserror", "tokio", "tokio-tungstenite", @@ -1796,26 +1798,12 @@ dependencies = [ "cc", "libc", "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", + "spin", + "untrusted", "web-sys", "winapi", ] -[[package]] -name = "ring" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babe80d5c16becf6594aa32ad2be8fe08498e7ae60b77de8df700e67f191d7e" -dependencies = [ - "cc", - "getrandom 0.2.10", - "libc", - "spin 0.9.8", - "untrusted 0.9.0", - "windows-sys 0.48.0", -] - [[package]] name = "rmp" version = "0.8.12" @@ -1891,7 +1879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", - "ring 0.16.20", + "ring", "rustls-webpki", "sct", ] @@ -1911,8 +1899,8 @@ version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring", + "untrusted", ] [[package]] @@ -1971,8 +1959,8 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring", + "untrusted", ] [[package]] @@ -2092,6 +2080,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -2157,12 +2156,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "standback" version = "0.2.17" @@ -2675,12 +2668,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - [[package]] name = "url" version = "2.4.1" diff --git a/Cargo.toml b/Cargo.toml index d0f55e3..22d41a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,10 +98,12 @@ serde_json = { version = "1.0", features = ["preserve_order"] } serde_yaml = "0.9" toml = { version = "0.8", features = ["preserve_order"] } -ring = "0.17.3" base64 = "0.21.4" hex = "0.4.3" md5 = "0.7.0" +sha1 = "0.10.6" +sha2 = "0.10.8" +digest = { version = "0.10.7", default-features = true } ### NET diff --git a/src/lune/builtins/serde/crypto.rs b/src/lune/builtins/serde/crypto.rs index b177a43..6437671 100644 --- a/src/lune/builtins/serde/crypto.rs +++ b/src/lune/builtins/serde/crypto.rs @@ -1,111 +1,31 @@ +use std::sync::Arc; + use crate::lune::builtins::{ FromLua, Lua, LuaError, LuaResult, LuaUserData, LuaUserDataMethods, LuaValue, }; use anyhow::Result; use base64::{engine::general_purpose as Base64, Engine as _}; -use ring::digest::{self, digest, Digest as RingDigest}; +use digest::DynDigest; +use sha1::Digest as _; +// use ring::digest::{self, digest, Digest as RingDigest}; +use std::sync::Mutex; // TODO: Proper error handling, remove unwraps -#[derive(Debug, Clone, Copy)] -pub struct Crypto; -#[derive(Debug, Clone)] -pub struct CryptoResult<T, C> -where - T: AsRef<[u8]>, - C: AsRef<[u8]>, -{ - algo: CryptoAlgo, - content: Option<T>, - computed: Option<C>, +// #[derive(Debug, Clone, Copy)] +// pub struct Crypto; +#[derive(Clone)] +pub struct Crypto { + algo: Arc<Mutex<CryptoAlgo<(dyn digest::DynDigest + 'static)>>>, } -#[derive(Clone, Debug)] -pub enum CryptoAlgo { - Sha1, - Sha256, - Sha512, - // We shouldn't be able to Pass Hmac(Hmac), would there be a way to limit this? - Hmac(Box<CryptoAlgo>), - Md5, -} - -impl Crypto { - pub fn sha1<T: ToString>(content: Option<T>) -> CryptoResult<String, RingDigest> { - let content = content.map(|data| data.to_string()); - - CryptoResult { - algo: CryptoAlgo::Sha1, - content, - computed: None, - } - } - - pub fn sha256<T: ToString>(content: Option<T>) -> CryptoResult<String, RingDigest> { - let content = content.map(|data| data.to_string()); - - CryptoResult { - algo: CryptoAlgo::Sha256, - content, - computed: None, - } - } - - pub fn sha512<T: ToString>(content: Option<T>) -> CryptoResult<String, RingDigest> { - let content = content.map(|data| data.to_string()); - - CryptoResult { - algo: CryptoAlgo::Sha512, - content, - computed: None, - } - } - - pub fn hmac<T: ToString>( - content: Option<T>, - algo: CryptoAlgo, - ) -> CryptoResult<String, ring::hmac::Tag> { - let content = content.map(|data| data.to_string()); - - CryptoResult { - algo: CryptoAlgo::Hmac(Box::new(algo)), - content, - computed: None, - } - } -} - -trait FromCryptoAlgo { - fn from_crypto_algo(value: CryptoAlgo) -> &'static Self; -} - -impl FromCryptoAlgo for ring::digest::Algorithm { - fn from_crypto_algo(value: CryptoAlgo) -> &'static Self { - match &value { - CryptoAlgo::Sha256 => &digest::SHA256, - CryptoAlgo::Sha512 => &digest::SHA512, - CryptoAlgo::Sha1 => &digest::SHA1_FOR_LEGACY_USE_ONLY, - _ => panic!(), - } - } -} - -impl From<CryptoAlgo> for ring::hmac::Algorithm { - fn from(value: CryptoAlgo) -> Self { - let val: ring::hmac::Algorithm = match value { - CryptoAlgo::Hmac(algo) => match *algo { - CryptoAlgo::Sha256 => ring::hmac::HMAC_SHA256, - CryptoAlgo::Sha512 => ring::hmac::HMAC_SHA512, - CryptoAlgo::Hmac(_) => panic!("Hmac(Hmac) is not allowed!"), - // FIXME: We're match MD5 to SHA1 here, should fix - CryptoAlgo::Sha1 => ring::hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY, - CryptoAlgo::Md5 => todo!(), - }, - _ => panic!("invalid type"), - }; - - val - } +#[derive(Clone)] +pub enum CryptoAlgo<T: ?Sized> { + Sha1(Box<T>), + Sha256(Box<T>), + Sha512(Box<T>), + Blake2(Box<T>), + Md5(Box<T>), } #[derive(PartialOrd, PartialEq, Ord, Eq)] @@ -126,18 +46,6 @@ impl From<usize> for EncodingKind { } } -impl From<i32> for EncodingKind { - fn from(value: i32) -> Self { - Self::from(value as usize) - } -} - -impl From<f64> for EncodingKind { - fn from(value: f64) -> Self { - Self::from(value as usize) - } -} - impl From<String> for EncodingKind { fn from(value: String) -> Self { match value.to_lowercase().as_str() { @@ -152,8 +60,8 @@ impl From<String> for EncodingKind { impl FromLua<'_> for EncodingKind { fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> { match value { - LuaValue::Integer(int) => Ok(EncodingKind::from(int)), - LuaValue::Number(num) => Ok(EncodingKind::from(num)), + LuaValue::Integer(int) => Ok(EncodingKind::from(int as usize)), + LuaValue::Number(num) => Ok(EncodingKind::from(num as usize)), LuaValue::String(str) => Ok(EncodingKind::from(str.to_string_lossy().to_string())), _ => Err(LuaError::FromLuaConversionError { @@ -165,206 +73,107 @@ impl FromLua<'_> for EncodingKind { } } -// Note that compute and digest declared here are identical to those of the below implementation -// Quite a bit of boilerplate, is there any way to avoid this without using derive macros? -// impl<T: AsRef<[u8]>, C: AsRef<[u8]>> CryptoResult<T, C> { -// fn update(&mut self, content: String) -> Self {} -// } +impl CryptoAlgo<dyn DynDigest> { + pub fn get_hasher(self) -> &'static dyn DynDigest { + // TODO: Replace boilerplate using a macro -impl CryptoResult<String, Vec<u8>> { - pub fn update(&mut self, content: String) -> Self { - self.content = Some(content); + match self { + CryptoAlgo::Sha1(hasher) => &*hasher, + CryptoAlgo::Sha256(hasher) => &*hasher, + CryptoAlgo::Sha512(hasher) => &*hasher, + CryptoAlgo::Blake2(hasher) => &*hasher, + CryptoAlgo::Md5(hasher) => &*hasher, + } + } +} - (*self).to_owned() +impl Crypto { + pub fn sha1<T: ToString>(content: Option<T>) -> Crypto { + let content = content.map(|data| data.to_string()); + + Self { + algo: Arc::new(Mutex::new(CryptoAlgo::Sha1(DynDigest::box_clone( + &sha1::Sha1::new(), + )))), + } } - pub fn compute(&mut self) -> Self { - let content = match &self.content { - Some(inner) => inner.to_owned(), - None => "".to_string(), - }; + pub fn sha256<T: ToString>(content: Option<T>) -> Crypto { + let content = content.map(|data| data.to_string()); - match self.algo { - CryptoAlgo::Md5 => self.computed = Some(md5::compute(content).to_vec()), - _ => panic!("Invalid implementation"), - }; + Self { + algo: Arc::new(Mutex::new(CryptoAlgo::Sha256(DynDigest::box_clone( + &sha2::Sha256::new(), + )))), + } + } - (*self).to_owned() + pub fn sha512<T: ToString>(content: Option<T>) -> Crypto { + let content = content.map(|data| data.to_string()); + + Self { + algo: Arc::new(Mutex::new(CryptoAlgo::Sha512(DynDigest::box_clone( + &sha2::Sha512::new(), + )))), + } + } + + pub fn update(&self, content: impl AsRef<[u8]>) -> &Crypto { + let mut binding = (*self.algo.lock().unwrap()).get_hasher().box_clone(); + let hasher = binding.as_mut(); + + hasher.update(content.as_ref()); + + self } pub fn digest(&self, encoding: EncodingKind) -> Result<String> { - let computed = self.computed.clone().ok_or(anyhow::Error::msg( - "compute the hash first before trying to obtain a digest", - ))?; + let algo = *self.algo.lock().unwrap(); + let hasher = algo.get_hasher(); + + let computed = &*hasher.finalize_reset(); match encoding { EncodingKind::Utf8 => String::from_utf8(computed.to_vec()).map_err(anyhow::Error::from), EncodingKind::Base64 => Ok(Base64::STANDARD.encode(computed)), - EncodingKind::Hex => Ok(hex::encode(computed.to_vec())), + EncodingKind::Hex => Ok(hex::encode::<&[u8]>(computed.as_ref())), } } } -impl CryptoResult<String, ring::hmac::Tag> { - pub fn update(&mut self, content: String) -> Self { - self.content = Some(content); +// impl FromLua<'_> for Crypto { +// fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> { +// if !value.is_table() { +// return Err(LuaError::FromLuaConversionError { +// from: value.type_name(), +// to: "Crypto", +// message: Some("value must be a table".to_string()), +// }); +// }; - (*self).to_owned() - } +// let value = value.as_table().unwrap(); +// let values = Self { +// algo: value.get("value")?, +// }; - fn compute(&mut self) -> Self { - let content = match &self.content { - Some(inner) => inner.to_owned(), - None => "".to_string(), - }; +// Ok(values) +// } +// } - match self.algo { - CryptoAlgo::Hmac(_) => { - let rng = ring::rand::SystemRandom::new(); - let key = - ring::hmac::Key::generate(ring::hmac::Algorithm::from(self.algo.clone()), &rng) - .expect("failed to generate random key"); - - // we should probably return the key to the user too - - self.computed = Some(ring::hmac::sign(&key, content.as_bytes())); - } - _ => panic!("Invalid implementation"), - }; - - (*self).to_owned() - } - - pub fn digest(&self, encoding: EncodingKind) -> Result<String> { - let computed = self.computed.ok_or(anyhow::Error::msg( - "compute the hash first before trying to obtain a digest", - ))?; - - match encoding { - EncodingKind::Utf8 => { - String::from_utf8(computed.as_ref().to_vec()).map_err(anyhow::Error::from) - } - EncodingKind::Base64 => Ok(Base64::STANDARD.encode(computed)), - EncodingKind::Hex => Ok(hex::encode(computed.as_ref())), - } - } -} - -impl CryptoResult<String, RingDigest> { - pub fn update(&mut self, content: String) -> Self { - self.content = Some(content); - - (*self).to_owned() - } - - pub fn compute(&mut self) -> Self { - let content = match &self.content { - Some(inner) => inner.to_owned(), - None => "".to_string(), - }; - - match self.algo { - CryptoAlgo::Sha256 | CryptoAlgo::Sha512 | CryptoAlgo::Sha1 => { - self.computed = Some(digest( - ring::digest::Algorithm::from_crypto_algo(self.algo.clone()), - content.as_bytes(), - )) - } - _ => unreachable!(), - }; - - (*self).to_owned() - } - - pub fn digest(&self, encoding: EncodingKind) -> Result<String> { - let computed = self.computed.ok_or(anyhow::Error::msg( - "compute the hash first before trying to obtain a digest", - ))?; - - match encoding { - EncodingKind::Utf8 => { - String::from_utf8(computed.as_ref().to_vec()).map_err(anyhow::Error::from) - } - EncodingKind::Base64 => Ok(Base64::STANDARD.encode(computed)), - EncodingKind::Hex => Ok(hex::encode(computed.as_ref())), - } - } -} - -trait HasComputedValue { - fn update(&mut self, value: String) -> Self; - fn compute(&mut self) -> Self; - fn digest(&self, encoding: EncodingKind) -> Result<String>; -} -impl HasComputedValue for CryptoResult<String, Vec<u8>> { - fn update(&mut self, content: String) -> Self { - self.update(content) - } - - fn compute(&mut self) -> Self { - self.compute() - } - - fn digest(&self, encoding: EncodingKind) -> Result<String> { - self.digest(encoding) - } -} -impl HasComputedValue for CryptoResult<String, RingDigest> { - fn update(&mut self, content: String) -> Self { - self.update(content) - } - - fn compute(&mut self) -> Self { - self.compute() - } - - fn digest(&self, encoding: EncodingKind) -> Result<String> { - self.digest(encoding) - } -} -impl HasComputedValue for CryptoResult<String, ring::hmac::Tag> { - fn update(&mut self, content: String) -> Self { - self.update(content) - } - - fn compute(&mut self) -> Self { - self.compute() - } - - fn digest(&self, encoding: EncodingKind) -> Result<String> { - self.digest(encoding) - } -} - -fn register_methods< - 'lua, - C: LuaUserData + HasComputedValue + 'static, - M: LuaUserDataMethods<'lua, C>, ->( - methods: &mut M, -) { - methods.add_method_mut("update", |_, this, value| Ok(this.update(value))); - methods.add_method_mut("compute", |_, this, ()| Ok(this.compute())); - methods.add_method("digest", |_, this, encoding| { - this.digest(encoding) - .map_err(|_| mlua::Error::external("whoopsie!")) - }); -} - -impl LuaUserData for CryptoResult<String, Vec<u8>> { +impl LuaUserData for &'static Crypto { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - register_methods(methods); + methods.add_method( + "update", + |_, this, content: String| Ok(this.update(content)), + ); } } -impl LuaUserData for CryptoResult<String, RingDigest> { +impl LuaUserData for Crypto { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - register_methods(methods); - } -} - -impl LuaUserData for CryptoResult<String, ring::hmac::Tag> { - fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - register_methods(methods); + methods.add_method("digest", |_, this, encoding| { + this.digest(encoding) + .map_err(|_| mlua::Error::external("whoopsie!")) + }); } } diff --git a/src/lune/builtins/serde/crypto.rs.bak b/src/lune/builtins/serde/crypto.rs.bak new file mode 100644 index 0000000..2fc341f --- /dev/null +++ b/src/lune/builtins/serde/crypto.rs.bak @@ -0,0 +1,369 @@ +use crate::lune::builtins::{ + FromLua, Lua, LuaError, LuaResult, LuaUserData, LuaUserDataMethods, LuaValue, +}; +use anyhow::Result; +use base64::{engine::general_purpose as Base64, Engine as _}; +use ring::digest::{self, digest, Digest as RingDigest}; + +// TODO: Proper error handling, remove unwraps + +#[derive(Debug, Clone, Copy)] +pub struct Crypto; +#[derive(Debug, Clone)] +pub struct CryptoResult<T, C> +where + T: AsRef<[u8]>, + C: AsRef<[u8]>, +{ + algo: CryptoAlgo, + content: Option<T>, + computed: Option<C>, +} + +#[derive(Clone, Debug)] +pub enum CryptoAlgo { + Sha1, + Sha256, + Sha512, + // We shouldn't be able to Pass Hmac(Hmac), would there be a way to limit this? + Hmac(Box<CryptoAlgo>), + Md5, +} + +impl Crypto { + pub fn sha1<T: ToString>(content: Option<T>) -> CryptoResult<String, RingDigest> { + let content = content.map(|data| data.to_string()); + + CryptoResult { + algo: CryptoAlgo::Sha1, + content, + computed: None, + } + } + + pub fn sha256<T: ToString>(content: Option<T>) -> CryptoResult<String, RingDigest> { + let content = content.map(|data| data.to_string()); + + CryptoResult { + algo: CryptoAlgo::Sha256, + content, + computed: None, + } + } + + pub fn sha512<T: ToString>(content: Option<T>) -> CryptoResult<String, RingDigest> { + let content = content.map(|data| data.to_string()); + + CryptoResult { + algo: CryptoAlgo::Sha512, + content, + computed: None, + } + } + + pub fn hmac<T: ToString>( + content: Option<T>, + algo: CryptoAlgo, + ) -> CryptoResult<String, ring::hmac::Tag> { + let content = content.map(|data| data.to_string()); + + CryptoResult { + algo: CryptoAlgo::Hmac(Box::new(algo)), + content, + computed: None, + } + } +} + +trait FromCryptoAlgo { + fn from_crypto_algo(value: CryptoAlgo) -> &'static Self; +} + +impl FromCryptoAlgo for ring::digest::Algorithm { + fn from_crypto_algo(value: CryptoAlgo) -> &'static Self { + match &value { + CryptoAlgo::Sha256 => &digest::SHA256, + CryptoAlgo::Sha512 => &digest::SHA512, + CryptoAlgo::Sha1 => &digest::SHA1_FOR_LEGACY_USE_ONLY, + _ => panic!(), + } + } +} + +impl From<CryptoAlgo> for ring::hmac::Algorithm { + fn from(value: CryptoAlgo) -> Self { + let val: ring::hmac::Algorithm = match value { + CryptoAlgo::Hmac(algo) => match *algo { + CryptoAlgo::Sha256 => ring::hmac::HMAC_SHA256, + CryptoAlgo::Sha512 => ring::hmac::HMAC_SHA512, + CryptoAlgo::Hmac(_) => panic!("Hmac(Hmac) is not allowed!"), + CryptoAlgo::Sha1 => ring::hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY, + CryptoAlgo::Md5 => todo!(), + }, + _ => panic!("invalid type"), + }; + + val + } +} + +#[derive(PartialOrd, PartialEq, Ord, Eq)] +pub enum EncodingKind { + Utf8, + Base64, + Hex, +} + +impl From<usize> for EncodingKind { + fn from(value: usize) -> Self { + match value { + 0 => Self::Utf8, + 1 => Self::Base64, + 2 => Self::Hex, + _ => panic!("invalid value"), + } + } +} + +impl From<i32> for EncodingKind { + fn from(value: i32) -> Self { + Self::from(value as usize) + } +} + +impl From<f64> for EncodingKind { + fn from(value: f64) -> Self { + Self::from(value as usize) + } +} + +impl From<String> for EncodingKind { + fn from(value: String) -> Self { + match value.to_lowercase().as_str() { + "utf8" => Self::Utf8, + "base64" => Self::Base64, + "hex" => Self::Hex, + &_ => panic!("invalid value"), + } + } +} + +impl FromLua<'_> for EncodingKind { + fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> { + match value { + LuaValue::Integer(int) => Ok(EncodingKind::from(int)), + LuaValue::Number(num) => Ok(EncodingKind::from(num)), + LuaValue::String(str) => Ok(EncodingKind::from(str.to_string_lossy().to_string())), + + _ => Err(LuaError::FromLuaConversionError { + from: value.type_name(), + to: "EncodingKind", + message: Some("value must be a an Integer, Number or String".to_string()), + }), + } + } +} + +// Note that compute and digest declared here are identical to those of the below implementation +// Quite a bit of boilerplate, is there any way to avoid this without using derive macros? +// impl<T: AsRef<[u8]>, C: AsRef<[u8]>> CryptoResult<T, C> { +// fn update(&mut self, content: String) -> Self {} +// } + +impl CryptoResult<String, Vec<u8>> { + pub fn update(&mut self, content: String) -> Self { + self.content = Some(content); + + (*self).to_owned() + } + + pub fn compute(&mut self) -> Self { + let content = match &self.content { + Some(inner) => inner.to_owned(), + None => "".to_string(), + }; + + match self.algo { + CryptoAlgo::Md5 => self.computed = Some(md5::compute(content).to_vec()), + _ => panic!("Invalid implementation"), + }; + + (*self).to_owned() + } + + pub fn digest(&self, encoding: EncodingKind) -> Result<String> { + let computed = self.computed.clone().ok_or(anyhow::Error::msg( + "compute the hash first before trying to obtain a digest", + ))?; + + match encoding { + EncodingKind::Utf8 => String::from_utf8(computed.to_vec()).map_err(anyhow::Error::from), + EncodingKind::Base64 => Ok(Base64::STANDARD.encode(computed)), + EncodingKind::Hex => Ok(hex::encode(&computed)), + } + } +} + +impl CryptoResult<String, ring::hmac::Tag> { + pub fn update(&mut self, content: String) -> Self { + self.content = Some(content); + + (*self).to_owned() + } + + fn compute(&mut self) -> Self { + let content = match &self.content { + Some(inner) => inner.to_owned(), + None => "".to_string(), + }; + + match self.algo { + CryptoAlgo::Hmac(_) => { + let rng = ring::rand::SystemRandom::new(); + let key = + ring::hmac::Key::generate(ring::hmac::Algorithm::from(self.algo.clone()), &rng) + .expect("failed to generate random key"); + + // we should probably return the key to the user too + + self.computed = Some(ring::hmac::sign(&key, content.as_bytes())); + } + _ => panic!("Invalid implementation"), + }; + + (*self).to_owned() + } + + pub fn digest(&self, encoding: EncodingKind) -> Result<String> { + let computed = self.computed.ok_or(anyhow::Error::msg( + "compute the hash first before trying to obtain a digest", + ))?; + + match encoding { + EncodingKind::Utf8 => { + String::from_utf8(computed.as_ref().to_vec()).map_err(anyhow::Error::from) + } + EncodingKind::Base64 => Ok(Base64::STANDARD.encode(computed)), + EncodingKind::Hex => Ok(hex::encode(computed.as_ref())), + } + } +} + +impl CryptoResult<String, RingDigest> { + pub fn update(&mut self, content: String) -> Self { + self.content = Some(content); + + (*self).to_owned() + } + + pub fn compute(&mut self) -> Self { + let content = match &self.content { + Some(inner) => inner.to_owned(), + None => "".to_string(), + }; + + match self.algo { + CryptoAlgo::Sha256 | CryptoAlgo::Sha512 | CryptoAlgo::Sha1 => { + self.computed = Some(digest( + ring::digest::Algorithm::from_crypto_algo(self.algo.clone()), + content.as_bytes(), + )) + } + _ => unreachable!(), + }; + + (*self).to_owned() + } + + pub fn digest(&self, encoding: EncodingKind) -> Result<String> { + let computed = self.computed.ok_or(anyhow::Error::msg( + "compute the hash first before trying to obtain a digest", + ))?; + + match encoding { + EncodingKind::Utf8 => { + String::from_utf8(computed.as_ref().to_vec()).map_err(anyhow::Error::from) + } + EncodingKind::Base64 => Ok(Base64::STANDARD.encode(computed)), + EncodingKind::Hex => Ok(hex::encode(computed.as_ref())), + } + } +} + +trait HasComputedValue { + fn update(&mut self, value: String) -> Self; + fn compute(&mut self) -> Self; + fn digest(&self, encoding: EncodingKind) -> Result<String>; +} +impl HasComputedValue for CryptoResult<String, Vec<u8>> { + fn update(&mut self, content: String) -> Self { + self.update(content) + } + + fn compute(&mut self) -> Self { + self.compute() + } + + fn digest(&self, encoding: EncodingKind) -> Result<String> { + self.digest(encoding) + } +} +impl HasComputedValue for CryptoResult<String, RingDigest> { + fn update(&mut self, content: String) -> Self { + self.update(content) + } + + fn compute(&mut self) -> Self { + self.compute() + } + + fn digest(&self, encoding: EncodingKind) -> Result<String> { + self.digest(encoding) + } +} +impl HasComputedValue for CryptoResult<String, ring::hmac::Tag> { + fn update(&mut self, content: String) -> Self { + self.update(content) + } + + fn compute(&mut self) -> Self { + self.compute() + } + + fn digest(&self, encoding: EncodingKind) -> Result<String> { + self.digest(encoding) + } +} + +fn register_methods< + 'lua, + C: LuaUserData + HasComputedValue + 'static, + M: LuaUserDataMethods<'lua, C>, +>( + methods: &mut M, +) { + methods.add_method_mut("update", |_, this, value| Ok(this.update(value))); + methods.add_method_mut("compute", |_, this, ()| Ok(this.compute())); + methods.add_method("digest", |_, this, encoding| { + this.digest(encoding) + .map_err(|_| mlua::Error::external("whoopsie!")) + }); +} + +impl LuaUserData for CryptoResult<String, Vec<u8>> { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + register_methods(methods); + } +} + +impl LuaUserData for CryptoResult<String, RingDigest> { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + register_methods(methods); + } +} + +impl LuaUserData for CryptoResult<String, ring::hmac::Tag> { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + register_methods(methods); + } +}