2018-04-29 17:40:10 +02:00
|
|
|
use base64;
|
2018-05-19 09:39:59 +02:00
|
|
|
use chrono::Utc;
|
2018-04-29 17:40:10 +02:00
|
|
|
use hex;
|
2018-05-19 09:39:59 +02:00
|
|
|
use openssl::{
|
|
|
|
pkey::PKey,
|
|
|
|
rsa::Rsa,
|
|
|
|
sha::sha256
|
|
|
|
};
|
2018-10-06 10:06:06 +02:00
|
|
|
use super::request;
|
|
|
|
use rocket::http::HeaderMap;
|
2018-04-29 17:40:10 +02:00
|
|
|
use serde_json;
|
|
|
|
|
2018-05-03 21:11:04 +02:00
|
|
|
/// Returns (public key, private key)
|
|
|
|
pub fn gen_keypair() -> (Vec<u8>, Vec<u8>) {
|
|
|
|
let keypair = Rsa::generate(2048).unwrap();
|
|
|
|
let keypair = PKey::from_rsa(keypair).unwrap();
|
|
|
|
(keypair.public_key_to_pem().unwrap(), keypair.private_key_to_pem_pkcs8().unwrap())
|
|
|
|
}
|
|
|
|
|
2018-04-29 17:40:10 +02:00
|
|
|
pub trait Signer {
|
2018-06-21 17:25:32 +02:00
|
|
|
fn get_key_id(&self) -> String;
|
2018-04-29 17:40:10 +02:00
|
|
|
|
|
|
|
/// Sign some data with the signer keypair
|
2018-05-03 19:12:01 +02:00
|
|
|
fn sign(&self, to_sign: String) -> Vec<u8>;
|
2018-09-28 23:18:01 +02:00
|
|
|
/// Verify if the signature is valid
|
|
|
|
fn verify(&self, data: String, signature: Vec<u8>) -> bool;
|
2018-04-29 17:40:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub trait Signable {
|
2018-06-21 17:31:42 +02:00
|
|
|
fn sign<T>(&mut self, creator: &T) -> &mut Self where T: Signer;
|
2018-10-06 10:06:06 +02:00
|
|
|
fn verify<T>(self, creator: &T) -> bool where T: Signer;
|
2018-04-29 17:40:10 +02:00
|
|
|
|
|
|
|
fn hash(data: String) -> String {
|
|
|
|
let bytes = data.into_bytes();
|
|
|
|
hex::encode(sha256(&bytes[..]))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Signable for serde_json::Value {
|
2018-06-21 17:31:42 +02:00
|
|
|
fn sign<T: Signer>(&mut self, creator: &T) -> &mut serde_json::Value {
|
2018-05-03 19:12:01 +02:00
|
|
|
let creation_date = Utc::now().to_rfc3339();
|
2018-04-29 17:40:10 +02:00
|
|
|
let mut options = json!({
|
|
|
|
"type": "RsaSignature2017",
|
2018-06-21 17:31:42 +02:00
|
|
|
"creator": creator.get_key_id(),
|
2018-05-03 19:12:01 +02:00
|
|
|
"created": creation_date
|
2018-04-29 17:40:10 +02:00
|
|
|
});
|
|
|
|
|
2018-05-03 19:12:01 +02:00
|
|
|
let options_hash = Self::hash(json!({
|
|
|
|
"@context": "https://w3id.org/identity/v1",
|
|
|
|
"created": creation_date
|
|
|
|
}).to_string());
|
2018-04-29 17:40:10 +02:00
|
|
|
let document_hash = Self::hash(self.to_string());
|
|
|
|
let to_be_signed = options_hash + &document_hash;
|
|
|
|
|
|
|
|
let signature = base64::encode(&creator.sign(to_be_signed));
|
|
|
|
|
2018-06-27 21:45:36 +02:00
|
|
|
options["signatureValue"] = serde_json::Value::String(signature);
|
2018-04-29 17:40:10 +02:00
|
|
|
self["signature"] = options;
|
|
|
|
self
|
|
|
|
}
|
2018-10-06 10:06:06 +02:00
|
|
|
|
|
|
|
fn verify<T: Signer>(mut self, creator: &T) -> bool {
|
|
|
|
let signature_obj = if let Some(sig) = self.as_object_mut().and_then(|o| o.remove("signature")) {
|
|
|
|
sig
|
|
|
|
} else {
|
|
|
|
//signature not present
|
|
|
|
return false
|
|
|
|
};
|
|
|
|
let signature = if let Ok(sig) = base64::decode(&signature_obj["signatureValue"].as_str().unwrap_or("")) {
|
|
|
|
sig
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
};
|
|
|
|
let creation_date = &signature_obj["created"];
|
|
|
|
let options_hash = Self::hash(json!({
|
|
|
|
"@context": "https://w3id.org/identity/v1",
|
|
|
|
"created": creation_date
|
|
|
|
}).to_string());
|
|
|
|
let document_hash = Self::hash(self.to_string());
|
|
|
|
let to_be_signed = options_hash + &document_hash;
|
|
|
|
creator.verify(to_be_signed, signature)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug,Copy,Clone,PartialEq)]
|
|
|
|
pub enum SignatureValidity {
|
|
|
|
Invalid,
|
|
|
|
ValidNoDigest,
|
|
|
|
Valid,
|
|
|
|
Absent,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SignatureValidity {
|
|
|
|
pub fn is_secure(&self) -> bool {
|
|
|
|
self==&SignatureValidity::Valid
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn verify_http_headers<S: Signer+::std::fmt::Debug>(sender: &S, all_headers: HeaderMap, data: String) -> SignatureValidity{
|
|
|
|
if let Some(sig_header) = all_headers.get_one("Signature") {
|
|
|
|
let mut _key_id = None;
|
|
|
|
let mut _algorithm = None;
|
|
|
|
let mut headers = None;
|
|
|
|
let mut signature = None;
|
|
|
|
for part in sig_header.split(',') {
|
|
|
|
match part {
|
|
|
|
part if part.starts_with("keyId=") => _key_id = Some(&part[7..part.len()-1]),
|
|
|
|
part if part.starts_with("algorithm=") => _algorithm = Some(&part[11..part.len()-1]),
|
|
|
|
part if part.starts_with("headers=") => headers = Some(&part[9..part.len()-1]),
|
|
|
|
part if part.starts_with("signature=") => signature = Some(&part[11..part.len()-1]),
|
|
|
|
_ => {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if signature.is_some() && headers.is_some() {
|
|
|
|
let headers = headers.unwrap().split_whitespace().collect::<Vec<_>>();
|
|
|
|
let signature = signature.unwrap();
|
|
|
|
let h = headers.iter()
|
|
|
|
.map(|header| (header,all_headers.get_one(header)))
|
|
|
|
.map(|(header, value)| format!("{}: {}", header.to_lowercase(), value.unwrap_or("")))
|
|
|
|
.collect::<Vec<_>>().join("\n");
|
|
|
|
if sender.verify(h, base64::decode(signature).unwrap_or(Vec::new())) {
|
|
|
|
if headers.contains(&"digest") {
|
|
|
|
let digest = all_headers.get_one("digest").unwrap_or("");
|
|
|
|
let digest = request::Digest::from_header(digest);
|
|
|
|
if digest.map(|d| d.verify(data)).unwrap_or(false) {
|
|
|
|
SignatureValidity::Valid
|
|
|
|
} else {
|
|
|
|
SignatureValidity::Invalid
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SignatureValidity::ValidNoDigest
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SignatureValidity::Invalid
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SignatureValidity::Invalid
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SignatureValidity::Absent
|
|
|
|
}
|
2018-04-29 17:40:10 +02:00
|
|
|
}
|