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);
+    }
+}