mirror of
https://git.asonix.dog/asonix/relay.git
synced 2024-12-29 22:00:31 +00:00
Add http signature verification
This commit is contained in:
parent
eea0577686
commit
9fdd3bec18
5 changed files with 92 additions and 13 deletions
|
@ -81,6 +81,9 @@ pub struct AcceptedActors {
|
|||
pub inbox: XsdAnyUri,
|
||||
|
||||
pub endpoints: Endpoints,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub public_key: Option<PublicKey>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
|
|
|
@ -27,6 +27,12 @@ pub enum MyError {
|
|||
#[error("Couldn't parse the signature header")]
|
||||
HeaderValidation(#[from] actix_web::http::header::InvalidHeaderValue),
|
||||
|
||||
#[error("Couldn't decode base64")]
|
||||
Base64(#[from] base64::DecodeError),
|
||||
|
||||
#[error("Invalid algorithm provided to verifier")]
|
||||
Algorithm,
|
||||
|
||||
#[error("Wrong ActivityPub kind")]
|
||||
Kind,
|
||||
|
||||
|
@ -50,6 +56,9 @@ pub enum MyError {
|
|||
|
||||
#[error("URI is missing domain field")]
|
||||
Domain,
|
||||
|
||||
#[error("Public key is missing")]
|
||||
MissingKey,
|
||||
}
|
||||
|
||||
impl ResponseError for MyError {
|
||||
|
|
28
src/inbox.rs
28
src/inbox.rs
|
@ -1,3 +1,9 @@
|
|||
use crate::{
|
||||
apub::{AcceptedActors, AcceptedObjects, ValidTypes},
|
||||
db_actor::{DbActor, DbQuery, Pool},
|
||||
error::MyError,
|
||||
state::{State, UrlKind},
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::apub::{Accept, Announce, Follow, Undo},
|
||||
context,
|
||||
|
@ -8,13 +14,6 @@ use actix_web::{client::Client, web, HttpResponse};
|
|||
use futures::join;
|
||||
use log::error;
|
||||
|
||||
use crate::{
|
||||
apub::{AcceptedActors, AcceptedObjects, ValidTypes},
|
||||
db_actor::{DbActor, DbQuery, Pool},
|
||||
error::MyError,
|
||||
state::{State, UrlKind},
|
||||
};
|
||||
|
||||
pub async fn inbox(
|
||||
db_actor: web::Data<Addr<DbActor>>,
|
||||
state: web::Data<State>,
|
||||
|
@ -23,7 +22,12 @@ pub async fn inbox(
|
|||
) -> Result<HttpResponse, MyError> {
|
||||
let input = input.into_inner();
|
||||
|
||||
let actor = fetch_actor(state.clone(), &client, &input.actor).await?;
|
||||
let actor = fetch_actor(
|
||||
state.clone().into_inner(),
|
||||
client.clone().into_inner(),
|
||||
&input.actor,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match input.kind {
|
||||
ValidTypes::Announce | ValidTypes::Create => {
|
||||
|
@ -217,15 +221,15 @@ async fn handle_follow(
|
|||
let client = client.into_inner();
|
||||
let accept2 = accept.clone();
|
||||
actix::Arbiter::spawn(async move {
|
||||
let _ = deliver(&state.into_inner(), &client, actor_inbox, &accept2).await;
|
||||
let _ = deliver(&state.into_inner(), &client.clone(), actor_inbox, &accept2).await;
|
||||
});
|
||||
|
||||
Ok(response(accept))
|
||||
}
|
||||
|
||||
async fn fetch_actor(
|
||||
state: web::Data<State>,
|
||||
client: &web::Data<Client>,
|
||||
pub async fn fetch_actor(
|
||||
state: std::sync::Arc<State>,
|
||||
client: std::sync::Arc<Client>,
|
||||
actor_id: &XsdAnyUri,
|
||||
) -> Result<AcceptedActors, MyError> {
|
||||
if let Some(actor) = state.get_actor(actor_id).await {
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -2,7 +2,9 @@
|
|||
use activitystreams::{actor::apub::Application, context, endpoint::EndpointProperties};
|
||||
use actix_web::{client::Client, middleware::Logger, web, App, HttpServer, Responder};
|
||||
use bb8_postgres::tokio_postgres;
|
||||
use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature};
|
||||
use rsa_pem::KeyExt;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
mod apub;
|
||||
mod db_actor;
|
||||
|
@ -10,6 +12,7 @@ mod error;
|
|||
mod inbox;
|
||||
mod label;
|
||||
mod state;
|
||||
mod verifier;
|
||||
mod webfinger;
|
||||
|
||||
use self::{
|
||||
|
@ -18,6 +21,8 @@ use self::{
|
|||
error::MyError,
|
||||
label::ArbiterLabelFactory,
|
||||
state::{State, UrlKind},
|
||||
verifier::MyVerify,
|
||||
webfinger::RelayResolver,
|
||||
};
|
||||
|
||||
async fn index() -> impl Responder {
|
||||
|
@ -84,6 +89,11 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||
let client = Client::default();
|
||||
|
||||
App::new()
|
||||
.wrap(VerifyDigest::new(Sha256::new()))
|
||||
.wrap(VerifySignature::new(
|
||||
MyVerify(state.clone(), client.clone()),
|
||||
Default::default(),
|
||||
))
|
||||
.wrap(Logger::default())
|
||||
.data(actor)
|
||||
.data(state.clone())
|
||||
|
@ -91,7 +101,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||
.service(web::resource("/").route(web::get().to(index)))
|
||||
.service(web::resource("/inbox").route(web::post().to(inbox::inbox)))
|
||||
.service(web::resource("/actor").route(web::get().to(actor_route)))
|
||||
.service(actix_webfinger::resource::<_, webfinger::RelayResolver>())
|
||||
.service(actix_webfinger::resource::<_, RelayResolver>())
|
||||
})
|
||||
.bind("127.0.0.1:8080")?
|
||||
.run()
|
||||
|
|
53
src/verifier.rs
Normal file
53
src/verifier.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use crate::{error::MyError, state::State};
|
||||
use actix_web::client::Client;
|
||||
use http_signature_normalization_actix::prelude::*;
|
||||
use rsa::{hash::Hashes, padding::PaddingScheme, PublicKey, RSAPublicKey};
|
||||
use rsa_pem::KeyExt;
|
||||
use std::{future::Future, pin::Pin, sync::Arc};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MyVerify(pub State, pub Client);
|
||||
|
||||
impl SignatureVerify for MyVerify {
|
||||
type Error = MyError;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<bool, Self::Error>>>>;
|
||||
|
||||
fn signature_verify(
|
||||
&mut self,
|
||||
algorithm: Option<Algorithm>,
|
||||
key_id: &str,
|
||||
signature: &str,
|
||||
signing_string: &str,
|
||||
) -> Self::Future {
|
||||
let key_id = key_id.to_owned();
|
||||
let signature = signature.to_owned();
|
||||
let signing_string = signing_string.to_owned();
|
||||
|
||||
let state = Arc::new(self.0.clone());
|
||||
let client = Arc::new(self.1.clone());
|
||||
|
||||
Box::pin(async move {
|
||||
let actor = crate::inbox::fetch_actor(state, client, &key_id.parse()?).await?;
|
||||
|
||||
let public_key = actor.public_key.ok_or(MyError::MissingKey)?;
|
||||
|
||||
let public_key = RSAPublicKey::from_pem_pkcs8(&public_key.public_key_pem)?;
|
||||
|
||||
match algorithm {
|
||||
Some(Algorithm::Hs2019) => (),
|
||||
_ => return Err(MyError::Algorithm),
|
||||
};
|
||||
|
||||
let decoded = base64::decode(signature)?;
|
||||
|
||||
public_key.verify(
|
||||
PaddingScheme::PKCS1v15,
|
||||
Some(&Hashes::SHA2_256),
|
||||
&decoded,
|
||||
signing_string.as_bytes(),
|
||||
)?;
|
||||
|
||||
Ok(true)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue