From 8c689db1c8fa06ddb9119e626e7b1149f3493905 Mon Sep 17 00:00:00 2001 From: Joris Date: Sat, 12 Aug 2023 20:05:09 +0200 Subject: Sign cookie with secret key --- src/crypto/mod.rs | 1 + src/crypto/signed.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/crypto/mod.rs create mode 100644 src/crypto/signed.rs (limited to 'src/crypto') diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs new file mode 100644 index 0000000..41e9259 --- /dev/null +++ b/src/crypto/mod.rs @@ -0,0 +1 @@ +pub mod signed; diff --git a/src/crypto/signed.rs b/src/crypto/signed.rs new file mode 100644 index 0000000..436f3d1 --- /dev/null +++ b/src/crypto/signed.rs @@ -0,0 +1,71 @@ +use hex; +use hmac::{Hmac, Mac}; +use sha2::Sha256; +use std::str; +use std::time::{SystemTime, UNIX_EPOCH}; + +const SEP: &str = "-"; + +pub fn sign(key: &str, raw: &str) -> Result { + let nonce = get_nonce()?; + let joined = format!("{nonce}{SEP}{raw}"); + let signature = get_signature(key, &joined)?; + Ok(format!("{signature}{SEP}{joined}")) +} + +pub fn verify(key: &str, signed: &str) -> Result { + let mut iter = signed.split(SEP); + match (iter.next(), iter.next()) { + (Some(signature), Some(nonce)) => { + let raw = iter.collect::>().join(SEP); + if signature == get_signature(key, &format!("{nonce}{SEP}{raw}"))? { + Ok(raw) + } else { + Err("Signature does not match".to_string()) + } + } + _ => Err("Malformed signed".to_string()), + } +} + +fn get_signature(key: &str, message: &str) -> Result { + let mut mac = Hmac::::new_from_slice(key.as_bytes()) + .map_err(|e| format!("Error initializing MAC: {e}"))?; + mac.update(message.as_bytes()); + let result = mac.finalize(); + Ok(hex::encode(result.into_bytes())) +} + +fn get_nonce() -> Result { + Ok(SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|e| format!("Failure getting unix expoch: {e}"))? + .as_millis() + .to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sign_and_validate() { + let key = "xagrlBUobnTj32Rm8tvmsZ6mh8qLfip5".to_string(); + assert_eq!(verify(&key, &sign(&key, "").unwrap()), Ok("".to_string())); + assert_eq!( + verify(&key, &sign(&key, "hello").unwrap()), + Ok("hello".to_string()) + ); + assert_eq!( + verify(&key, &sign(&key, "with-sep").unwrap()), + Ok("with-sep".to_string()) + ); + } + + #[test] + fn fail_when_key_mismatch() { + let key1 = "xagrlBUobnTj32Rm8tvmsZ6mh8qLfip5".to_string(); + let key2 = "8KJBK6axEr9wQ390GgdWA8Pjn8FwILDa".to_string(); + assert!(verify(&key1, &sign(&key2, "hello").unwrap()).is_err()); + } +} -- cgit v1.2.3