2018-05-04 17:18:00 +02:00
|
|
|
use base64;
|
2018-10-11 13:51:45 +02:00
|
|
|
use chrono::{DateTime, offset::Utc};
|
2018-05-04 17:18:00 +02:00
|
|
|
use openssl::hash::{Hasher, MessageDigest};
|
2018-10-11 13:51:45 +02:00
|
|
|
use reqwest::header::{ACCEPT, DATE, HeaderMap, HeaderValue, USER_AGENT};
|
2018-10-03 09:31:38 +02:00
|
|
|
use std::ops::Deref;
|
2018-07-26 21:35:35 +02:00
|
|
|
use std::time::SystemTime;
|
2018-05-04 17:18:00 +02:00
|
|
|
|
2018-07-18 16:58:28 +02:00
|
|
|
use activity_pub::ap_accept_header;
|
2018-05-04 17:18:00 +02:00
|
|
|
use activity_pub::sign::Signer;
|
|
|
|
|
2018-10-11 13:51:45 +02:00
|
|
|
const PLUME_USER_AGENT: &'static str = concat!("Plume/", env!("CARGO_PKG_VERSION"));
|
2018-05-04 17:18:00 +02:00
|
|
|
|
2018-10-11 13:51:45 +02:00
|
|
|
pub struct Digest(String);
|
2018-05-04 17:18:00 +02:00
|
|
|
|
2018-10-03 09:31:38 +02:00
|
|
|
impl Digest {
|
2018-10-11 13:51:45 +02:00
|
|
|
pub fn digest(body: String) -> HeaderValue {
|
2018-10-03 09:31:38 +02:00
|
|
|
let mut hasher = Hasher::new(MessageDigest::sha256()).unwrap();
|
|
|
|
hasher.update(&body.into_bytes()[..]).unwrap();
|
|
|
|
let res = base64::encode(&hasher.finish().unwrap());
|
2018-10-11 13:51:45 +02:00
|
|
|
HeaderValue::from_str(&format!("SHA-256={}", res)).unwrap()
|
2018-10-03 09:31:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn verify(&self, body: String) -> bool {
|
|
|
|
if self.algorithm()=="SHA-256" {
|
|
|
|
let mut hasher = Hasher::new(MessageDigest::sha256()).unwrap();
|
|
|
|
hasher.update(&body.into_bytes()).unwrap();
|
|
|
|
self.value().deref()==hasher.finish().unwrap().deref()
|
|
|
|
} else {
|
|
|
|
false //algorithm not supported
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn algorithm(&self) -> &str {
|
|
|
|
let pos = self.0.find('=').unwrap();
|
|
|
|
&self.0[..pos]
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn value(&self) -> Vec<u8> {
|
|
|
|
let pos = self.0.find('=').unwrap()+1;
|
|
|
|
base64::decode(&self.0[pos..]).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_header(dig: &str) -> Result<Self, ()> {
|
|
|
|
if let Some(pos) = dig.find('=') {
|
|
|
|
let pos = pos+1;
|
|
|
|
if let Ok(_) = base64::decode(&dig[pos..]) {
|
|
|
|
Ok(Digest(dig.to_owned()))
|
|
|
|
} else {
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-11 13:51:45 +02:00
|
|
|
pub fn headers() -> HeaderMap {
|
|
|
|
let date: DateTime<Utc> = SystemTime::now().into();
|
|
|
|
let date = format!("{}", date.format("%a, %d %b %Y %T %Z"));
|
|
|
|
|
|
|
|
let mut headers = HeaderMap::new();
|
|
|
|
headers.insert(USER_AGENT, HeaderValue::from_static(PLUME_USER_AGENT));
|
|
|
|
headers.insert(DATE, HeaderValue::from_str(&date).unwrap());
|
|
|
|
headers.insert(ACCEPT, HeaderValue::from_str(&ap_accept_header().into_iter().collect::<Vec<_>>().join(", ")).unwrap());
|
2018-05-04 17:18:00 +02:00
|
|
|
headers
|
|
|
|
}
|
|
|
|
|
2018-10-11 13:51:45 +02:00
|
|
|
pub fn signature<S: Signer>(signer: &S, headers: HeaderMap) -> HeaderValue {
|
|
|
|
let signed_string = headers.iter().map(|(h,v)| format!("{}: {}", h.as_str().to_lowercase(), v.to_str().unwrap())).collect::<Vec<String>>().join("\n");
|
|
|
|
let signed_headers = headers.iter().map(|(h,_)| h.as_str()).collect::<Vec<&str>>().join(" ").to_lowercase();
|
2018-09-11 20:53:14 +02:00
|
|
|
|
2018-05-04 17:18:00 +02:00
|
|
|
let data = signer.sign(signed_string);
|
2018-05-08 21:38:37 +02:00
|
|
|
let sign = base64::encode(&data[..]);
|
2018-05-04 17:18:00 +02:00
|
|
|
|
2018-10-11 13:51:45 +02:00
|
|
|
HeaderValue::from_str(&format!(
|
2018-05-08 21:38:37 +02:00
|
|
|
"keyId=\"{key_id}\",algorithm=\"rsa-sha256\",headers=\"{signed_headers}\",signature=\"{signature}\"",
|
2018-06-21 17:31:42 +02:00
|
|
|
key_id = signer.get_key_id(),
|
2018-05-04 17:18:00 +02:00
|
|
|
signed_headers = signed_headers,
|
|
|
|
signature = sign
|
2018-10-11 13:51:45 +02:00
|
|
|
)).unwrap()
|
2018-05-04 17:18:00 +02:00
|
|
|
}
|