aboutsummaryrefslogtreecommitdiff
path: root/src/crypto/signed.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto/signed.rs')
-rw-r--r--src/crypto/signed.rs71
1 files changed, 71 insertions, 0 deletions
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());
+ }
+}