forked from mirrors/relay
149 lines
4.1 KiB
Rust
149 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::{hash::Hash, padding::PaddingScheme, pkcs8::DecodePublicKey, PublicKey, RsaPublicKey};
|
|
use sha2::{Digest, Sha256};
|
|
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 hashed = Sha256::digest(signing_string.as_bytes());
|
|
|
|
public_key.verify(
|
|
PaddingScheme::PKCS1v15Sign {
|
|
hash: Some(Hash::SHA2_256),
|
|
},
|
|
&hashed,
|
|
&decoded,
|
|
)?;
|
|
|
|
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
|
|
})
|
|
}
|
|
}
|