lemmy/server/src/apub/signatures.rs

91 lines
2.6 KiB
Rust
Raw Normal View History

2020-04-10 13:50:40 +00:00
use activitystreams::{actor::Actor, ext::Extension};
use failure::Error;
use http::request::Builder;
use http_signature_normalization::Config;
use openssl::hash::MessageDigest;
use openssl::sign::Signer;
use openssl::{pkey::PKey, rsa::Rsa};
2020-04-17 15:33:55 +00:00
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
pub struct Keypair {
pub private_key: String,
pub public_key: String,
}
/// Generate the asymmetric keypair for ActivityPub HTTP signatures.
2020-04-19 11:44:44 +00:00
pub fn generate_actor_keypair() -> Result<Keypair, Error> {
let rsa = Rsa::generate(2048)?;
let pkey = PKey::from_rsa(rsa)?;
let public_key = pkey.public_key_to_pem()?;
let private_key = pkey.private_key_to_pem_pkcs8()?;
Ok(Keypair {
private_key: String::from_utf8(private_key)?,
public_key: String::from_utf8(public_key)?,
})
}
/// Signs request headers with the given keypair.
2020-04-19 11:44:44 +00:00
/// TODO: would be nice to pass the sending actor in, instead of raw privatekey/id strings
pub fn sign(request: &Builder, private_key: &str, sender_id: &str) -> Result<String, Error> {
let signing_key_id = format!("{}#main-key", sender_id);
let config = Config::new();
let headers = request
.headers_ref()
.unwrap()
.iter()
.map(|h| -> Result<(String, String), Error> {
Ok((h.0.as_str().to_owned(), h.1.to_str()?.to_owned()))
})
.collect::<Result<BTreeMap<String, String>, Error>>()?;
let signature_header_value = config
.begin_sign(
request.method_ref().unwrap().as_str(),
request
.uri_ref()
.unwrap()
.path_and_query()
.unwrap()
.as_str(),
headers,
)
.sign(signing_key_id, |signing_string| {
2020-04-19 11:44:44 +00:00
let private_key = PKey::private_key_from_pem(private_key.as_bytes())?;
let mut signer = Signer::new(MessageDigest::sha256(), &private_key).unwrap();
signer.update(signing_string.as_bytes()).unwrap();
Ok(base64::encode(signer.sign_to_vec()?)) as Result<_, Error>
})?
.signature_header();
Ok(signature_header_value)
}
2020-04-10 13:50:40 +00:00
// The following is taken from here:
// https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html
2020-04-17 15:33:55 +00:00
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
2020-04-10 13:50:40 +00:00
#[serde(rename_all = "camelCase")]
pub struct PublicKey {
pub id: String,
pub owner: String,
pub public_key_pem: String,
}
2020-04-17 15:33:55 +00:00
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
2020-04-10 13:50:40 +00:00
#[serde(rename_all = "camelCase")]
pub struct PublicKeyExtension {
pub public_key: PublicKey,
}
impl PublicKey {
pub fn to_ext(&self) -> PublicKeyExtension {
PublicKeyExtension {
public_key: self.to_owned(),
}
}
}
impl<T> Extension<T> for PublicKeyExtension where T: Actor {}