diff options
Diffstat (limited to 'crates')
4 files changed, 105 insertions, 14 deletions
diff --git a/crates/sellershut/Cargo.toml b/crates/sellershut/Cargo.toml index 43948f8..7501da5 100644 --- a/crates/sellershut/Cargo.toml +++ b/crates/sellershut/Cargo.toml @@ -12,19 +12,21 @@ activitypub_federation = { version = "0.7.0-beta.5", default-features = false, f anyhow.workspace = true async-trait.workspace = true axum = { workspace = true, features = ["macros"] } +base64 = "0.22.1" clap = { workspace = true, features = ["derive"] } config = { workspace = true, features = ["toml"] } enum_delegate = "0.2.0" futures-util.workspace = true -hmac = "0.12.1" nanoid.workspace = true +openssl = "0.10.73" serde = { workspace = true, features = ["derive"] } serde_json.workspace = true sha2 = "0.10.9" sqlx = { workspace = true, features = ["macros", "migrate", "runtime-tokio", "time", "tls-rustls", "uuid"] } +time = { version = "0.3.41", default-features = false, features = ["parsing"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal"] } tower = { workspace = true, features = ["util"] } -tower-http = { workspace = true, features = ["trace"] } +tower-http = { workspace = true, features = ["map-request-body", "trace", "util"] } tracing.workspace = true url.workspace = true uuid = { workspace = true, features = ["v7"] } diff --git a/crates/sellershut/src/server/activities/follow.rs b/crates/sellershut/src/server/activities/follow.rs index a004c6b..f0df0d9 100644 --- a/crates/sellershut/src/server/activities/follow.rs +++ b/crates/sellershut/src/server/activities/follow.rs @@ -119,8 +119,8 @@ mod tests { config.application.port ); let actor = format!( - "http://localhost:{}/users/{}", - config.application.port, hut_config.instance_name + "http://localhost/users/{}", + hut_config.instance_name ); let app = server::router(state); @@ -138,6 +138,7 @@ mod tests { .oneshot( Request::builder() .method("POST") + .header("Content-Type", "application/activity+json") .uri("/users/sellershut/inbox") .body(Body::from(body)) .unwrap(), diff --git a/crates/sellershut/src/server/middleware/sign_request.rs b/crates/sellershut/src/server/middleware/sign_request.rs index a8f2f3a..889984f 100644 --- a/crates/sellershut/src/server/middleware/sign_request.rs +++ b/crates/sellershut/src/server/middleware/sign_request.rs @@ -1,14 +1,20 @@ +mod signature; + use activitypub_federation::config::FederationConfig; -use axum::{body::Body, extract::Request, response::Response}; +use axum::{ + body::Body, + extract::Request, + http::HeaderValue, + response::Response, +}; use futures_util::future::BoxFuture; -use hmac::{Hmac, Mac}; -use sha2::{Sha256, digest::KeyInit}; -use std::task::{Context, Poll}; +use std::{ + task::{Context, Poll}, +}; use tower::{Layer, Service}; -use crate::state::AppHandle; +use crate::{server::middleware::sign_request::signature::Signature, state::AppHandle}; -type HmacSha256 = Hmac<Sha256>; #[derive(Clone)] pub struct SignRequestLayer { @@ -42,7 +48,7 @@ pub struct SignRequestMiddleware<S> { impl<S> Service<Request> for SignRequestMiddleware<S> where - S: Service<Request, Response = Response> + Send + 'static, + S: Service<Request, Response = Response> + Clone + Send + 'static, S::Future: Send + 'static, { type Response = S::Response; @@ -55,10 +61,33 @@ where } fn call(&mut self, request: Request) -> Self::Future { - let future = self.inner.call(request); + let mut inner = self.inner.clone(); + let (parts, body) = request.into_parts(); + Box::pin(async move { - let response: Response = future.await?; - Ok(response) + let bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap(); + + let signature = Signature::create( + "" + .as_bytes(), + bytes, + ) + .unwrap(); + + let mut new_request = Request::from_parts(parts, Body::from(signature.body)); + + let head = new_request.headers_mut(); + let header = format!( + "keyId=\"http://localhost/users/sellershut#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest\",signature=\"{}\"", + signature.signature, + ); + println!("{header}"); + head.insert("Host", HeaderValue::from_str(&signature.host).unwrap()); + head.insert("Date", HeaderValue::from_str(&signature.date).unwrap()); + head.insert("Digest", HeaderValue::from_str(&signature.digest).unwrap()); + head.insert("Signature", HeaderValue::from_str(&header).unwrap()); + + inner.call(new_request).await }) } } diff --git a/crates/sellershut/src/server/middleware/sign_request/signature.rs b/crates/sellershut/src/server/middleware/sign_request/signature.rs new file mode 100644 index 0000000..18e62c0 --- /dev/null +++ b/crates/sellershut/src/server/middleware/sign_request/signature.rs @@ -0,0 +1,59 @@ +use axum::body::Bytes; +use base64::{Engine, engine::general_purpose}; +use openssl::{pkey::PKey, rsa::Rsa, sign::Signer}; +use sha2::{Digest, Sha256}; +use sqlx::types::time::OffsetDateTime; +use time::format_description::well_known::Rfc2822; + +use crate::error::AppError; + +/// Signature state for verifying a request +pub struct Signature { + pub host: String, + pub date: String, + pub digest: String, + pub signature: String, + pub body: Bytes +} + +impl Signature { + pub fn create(key: &[u8], body: Bytes) -> Result<Self, AppError> { + let mut hasher = Sha256::new(); + hasher.update(&body); + + let result = hasher.finalize(); + + let digest_hash = general_purpose::STANDARD.encode(result); + let digest = format!("SHA-256={digest_hash}"); + + let inbox_path = "/users/sellershut/inbox"; + let now = OffsetDateTime::now_utc().format(&Rfc2822)?; + + let signing_string = [ + format!("(request-target): post {inbox_path}"), + "host: localhost:2210".to_owned(), + format!("date: {now}"), + format!("digest: {digest}"), + ] + .join("\n"); + + let rsa = Rsa::private_key_from_pem(key)?; + let pkey = PKey::from_rsa(rsa)?; + + // Create signer with SHA-256 + let mut signer = Signer::new(openssl::hash::MessageDigest::sha256(), &pkey)?; + signer.update(signing_string.as_bytes())?; + + // Sign and encode as base64 + let signature = signer.sign_to_vec()?; + let result = general_purpose::STANDARD.encode(signature); + + Ok(Self{ + host: "localhost:2210".to_string(), + date: now, + digest, + signature: result, + body, + }) + } +} |