relay/src/middleware/verifier.rs
2022-10-29 12:22:13 -05:00

147 lines
4.1 KiB
Rust

use crate::{
apub::AcceptedActors,
data::{ActorCache, State},
error::{Error, ErrorKind},
requests::Requests,
};
use activitystreams::{base::BaseExt, iri, iri_string::types::IriString};
use actix_web::web;
use http_signature_normalization_actix::{prelude::*, verify::DeprecatedAlgorithm};
use rsa::{pkcs1v15::VerifyingKey, pkcs8::DecodePublicKey, RsaPublicKey};
use sha2::{Digest, Sha256};
use signature::{DigestVerifier, Signature};
use std::{future::Future, pin::Pin};
#[derive(Clone, Debug)]
pub(crate) struct MyVerify(pub Requests, pub ActorCache, pub State);
impl MyVerify {
#[tracing::instrument("Verify signature")]
async fn verify(
&self,
algorithm: Option<Algorithm>,
key_id: String,
signature: String,
signing_string: String,
) -> Result<bool, Error> {
let public_key_id = iri!(key_id);
let actor_id = if let Some(mut actor_id) = self
.2
.db
.actor_id_from_public_key_id(public_key_id.clone())
.await?
{
if !self.2.db.is_allowed(actor_id.clone()).await? {
return Err(ErrorKind::NotAllowed(key_id).into());
}
actor_id.set_fragment(None);
let actor = self.1.get(&actor_id, &self.0).await?;
let was_cached = actor.is_cached();
let actor = actor.into_inner();
match algorithm {
Some(Algorithm::Hs2019) => (),
Some(Algorithm::Deprecated(DeprecatedAlgorithm::RsaSha256)) => (),
Some(other) => {
return Err(ErrorKind::Algorithm(other.to_string()).into());
}
None => (),
};
let res = do_verify(&actor.public_key, signature.clone(), signing_string.clone()).await;
if let Err(e) = res {
if !was_cached {
return Err(e);
}
} else {
return Ok(true);
}
actor_id
} else {
self.0
.fetch::<PublicKeyResponse>(public_key_id.as_str())
.await?
.actor_id()
.ok_or(ErrorKind::MissingId)?
};
// Previously we verified the sig from an actor's local cache
//
// Now we make sure we fetch an updated actor
let actor = self.1.get_no_cache(&actor_id, &self.0).await?;
do_verify(&actor.public_key, signature, signing_string).await?;
Ok(true)
}
}
#[derive(serde::Deserialize)]
#[serde(untagged)]
#[serde(rename_all = "camelCase")]
enum PublicKeyResponse {
PublicKey {
#[allow(dead_code)]
id: IriString,
owner: IriString,
#[allow(dead_code)]
public_key_pem: String,
},
Actor(Box<AcceptedActors>),
}
impl PublicKeyResponse {
fn actor_id(&self) -> Option<IriString> {
match self {
PublicKeyResponse::PublicKey { owner, .. } => Some(owner.clone()),
PublicKeyResponse::Actor(actor) => actor.id_unchecked().cloned(),
}
}
}
async fn do_verify(
public_key: &str,
signature: String,
signing_string: String,
) -> Result<(), Error> {
let public_key = RsaPublicKey::from_public_key_pem(public_key)?;
web::block(move || {
let decoded = base64::decode(signature)?;
let signature = Signature::from_bytes(&decoded)?;
let hashed = Sha256::new_with_prefix(signing_string.as_bytes());
let verifying_key = VerifyingKey::new_with_prefix(public_key);
verifying_key.verify_digest(hashed, &signature)?;
Ok(()) as Result<(), Error>
})
.await??;
Ok(())
}
impl SignatureVerify for MyVerify {
type Error = Error;
type Future = Pin<Box<dyn Future<Output = Result<bool, Self::Error>>>>;
fn signature_verify(
&mut self,
algorithm: Option<Algorithm>,
key_id: String,
signature: String,
signing_string: String,
) -> Self::Future {
let this = self.clone();
Box::pin(async move {
this.verify(algorithm, key_id, signature, signing_string)
.await
})
}
}