diff options
Diffstat (limited to 'src/crypto')
-rw-r--r-- | src/crypto/mod.rs | 1 | ||||
-rw-r--r-- | src/crypto/signed.rs | 71 |
2 files changed, 72 insertions, 0 deletions
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<String, String> { + 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<String, String> { + let mut iter = signed.split(SEP); + match (iter.next(), iter.next()) { + (Some(signature), Some(nonce)) => { + let raw = iter.collect::<Vec<&str>>().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<String, String> { + let mut mac = Hmac::<Sha256>::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<String, String> { + 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()); + } +} |