Skip to content

Commit

Permalink
Merge pull request 'Fixes #936: Sign GET request' (#957) from sign-ge…
Browse files Browse the repository at this point in the history
  • Loading branch information
KitaitiMakoto committed Dec 5, 2021
2 parents 5815602 + 43656d8 commit 0ede2ab
Show file tree
Hide file tree
Showing 21 changed files with 400 additions and 142 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ commands:
type: boolean
default: false
steps:
- run: cargo clippy <<^parameters.no_feature>>--no-default-features --features="${FEATURES}"<</parameters.no_feature>> --release -p <<parameters.package>> -- -D warnings
- run: cargo clippy <<^parameters.no_feature>>--no-default-features --features="${FEATURES}"<</parameters.no_feature>> --release -p <<parameters.package>> -- -D warnings -A clippy::needless_borrow

run_with_coverage:
description: run command with environment for coverage
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- Upgrade Tantivy to 0.13.3 and lindera-tantivy to 0.7.1 (#878)
- Run searcher on actor system (#870)
- Use article title as its slug instead of capitalizing and inserting hyphens (#920)
- Sign GET requests to other instances (#957)

### Fixed

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions plume-cli/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,6 @@ fn new<'a>(args: &ArgMatches<'a>, conn: &Connection) {
},
)
.expect("Couldn't save instance");
Instance::cache_local(conn);
Instance::create_local_instance_user(conn).expect("Couldn't save local instance user");
}
2 changes: 1 addition & 1 deletion plume-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn main() {
e => e.map(|_| ()).unwrap(),
}
let conn = Conn::establish(CONFIG.database_url.as_str());
let _ = conn.as_ref().map(|conn| Instance::cache_local(conn));
let _ = conn.as_ref().map(Instance::cache_local);

match matches.subcommand() {
("instance", Some(args)) => {
Expand Down
3 changes: 3 additions & 0 deletions plume-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ version = "0.4"
default-features = false
git = "https://git.joinplu.me/Plume/pulldown-cmark"
branch = "bidi-plume"

[dev-dependencies]
once_cell = "1.5.2"
203 changes: 173 additions & 30 deletions plume-common/src/activity_pub/inbox.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use reqwest::header::{HeaderValue, ACCEPT};
use reqwest;
use std::fmt::Debug;

use super::{request, sign::Signer};

/// Represents an ActivityPub inbox.
///
/// It routes an incoming Activity through the registered handlers.
Expand All @@ -10,7 +12,50 @@ use std::fmt::Debug;
/// ```rust
/// # extern crate activitypub;
/// # use activitypub::{actor::Person, activity::{Announce, Create}, object::Note};
/// # use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa};
/// # use once_cell::sync::Lazy;
/// # use plume_common::activity_pub::inbox::*;
/// # use plume_common::activity_pub::sign::{gen_keypair, Error as SignError, Result as SignResult, Signer};
/// #
/// # static MY_SIGNER: Lazy<MySigner> = Lazy::new(|| MySigner::new());
/// #
/// # struct MySigner {
/// # public_key: String,
/// # private_key: String,
/// # }
/// #
/// # impl MySigner {
/// # fn new() -> Self {
/// # let (pub_key, priv_key) = gen_keypair();
/// # Self {
/// # public_key: String::from_utf8(pub_key).unwrap(),
/// # private_key: String::from_utf8(priv_key).unwrap(),
/// # }
/// # }
/// # }
/// #
/// # impl Signer for MySigner {
/// # fn get_key_id(&self) -> String {
/// # "mysigner".into()
/// # }
/// #
/// # fn sign(&self, to_sign: &str) -> SignResult<Vec<u8>> {
/// # let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap())
/// # .unwrap();
/// # let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap();
/// # signer.update(to_sign.as_bytes()).unwrap();
/// # signer.sign_to_vec().map_err(|_| SignError())
/// # }
/// #
/// # fn verify(&self, data: &str, signature: &[u8]) -> SignResult<bool> {
/// # let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap())
/// # .unwrap();
/// # let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
/// # verifier.update(data.as_bytes()).unwrap();
/// # verifier.verify(&signature).map_err(|_| SignError())
/// # }
/// # }
/// #
/// # struct User;
/// # impl FromId<()> for User {
/// # type Error = ();
Expand All @@ -23,6 +68,10 @@ use std::fmt::Debug;
/// # fn from_activity(_: &(), obj: Person) -> Result<Self, Self::Error> {
/// # Ok(User)
/// # }
/// #
/// # fn get_sender() -> &'static dyn Signer {
/// # &*MY_SIGNER
/// # }
/// # }
/// # impl AsActor<&()> for User {
/// # fn get_inbox_url(&self) -> String {
Expand All @@ -42,6 +91,10 @@ use std::fmt::Debug;
/// # fn from_activity(_: &(), obj: Note) -> Result<Self, Self::Error> {
/// # Ok(Message)
/// # }
/// #
/// # fn get_sender() -> &'static dyn Signer {
/// # &*MY_SIGNER
/// # }
/// # }
/// # impl AsObject<User, Create, &()> for Message {
/// # type Error = ();
Expand Down Expand Up @@ -311,42 +364,25 @@ pub trait FromId<C>: Sized {
id: &str,
proxy: Option<reqwest::Proxy>,
) -> Result<Self::Object, (Option<serde_json::Value>, Self::Error)> {
if let Some(proxy) = proxy {
reqwest::ClientBuilder::new().proxy(proxy)
} else {
reqwest::ClientBuilder::new()
}
.connect_timeout(Some(std::time::Duration::from_secs(5)))
.build()
.map_err(|_| (None, InboxError::DerefError.into()))?
.get(id)
.header(
ACCEPT,
HeaderValue::from_str(
&super::ap_accept_header()
.into_iter()
.collect::<Vec<_>>()
.join(", "),
)
.map_err(|_| (None, InboxError::DerefError.into()))?,
)
.send()
.map_err(|_| (None, InboxError::DerefError))
.and_then(|mut r| {
let json: serde_json::Value = r
.json()
.map_err(|_| (None, InboxError::InvalidObject(None)))?;
serde_json::from_value(json.clone())
.map_err(|_| (Some(json), InboxError::InvalidObject(None)))
})
.map_err(|(json, e)| (json, e.into()))
request::get(id, Self::get_sender(), proxy)
.map_err(|_| (None, InboxError::DerefError))
.and_then(|mut r| {
let json: serde_json::Value = r
.json()
.map_err(|_| (None, InboxError::InvalidObject(None)))?;
serde_json::from_value(json.clone())
.map_err(|_| (Some(json), InboxError::InvalidObject(None)))
})
.map_err(|(json, e)| (json, e.into()))
}

/// Builds a `Self` from its ActivityPub representation
fn from_activity(ctx: &C, activity: Self::Object) -> Result<Self, Self::Error>;

/// Tries to find a `Self` with a given ID (`id`), using `ctx` (a database)
fn from_db(ctx: &C, id: &str) -> Result<Self, Self::Error>;

fn get_sender() -> &'static dyn Signer;
}

/// Should be implemented by anything representing an ActivityPub actor.
Expand Down Expand Up @@ -385,6 +421,49 @@ pub trait AsActor<C> {
/// # extern crate activitypub;
/// # use activitypub::{activity::Create, actor::Person, object::Note};
/// # use plume_common::activity_pub::inbox::{AsActor, AsObject, FromId};
/// # use plume_common::activity_pub::sign::{gen_keypair, Error as SignError, Result as SignResult, Signer};
/// # use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa};
/// # use once_cell::sync::Lazy;
/// #
/// # static MY_SIGNER: Lazy<MySigner> = Lazy::new(|| MySigner::new());
/// #
/// # struct MySigner {
/// # public_key: String,
/// # private_key: String,
/// # }
/// #
/// # impl MySigner {
/// # fn new() -> Self {
/// # let (pub_key, priv_key) = gen_keypair();
/// # Self {
/// # public_key: String::from_utf8(pub_key).unwrap(),
/// # private_key: String::from_utf8(priv_key).unwrap(),
/// # }
/// # }
/// # }
/// #
/// # impl Signer for MySigner {
/// # fn get_key_id(&self) -> String {
/// # "mysigner".into()
/// # }
/// #
/// # fn sign(&self, to_sign: &str) -> SignResult<Vec<u8>> {
/// # let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap())
/// # .unwrap();
/// # let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap();
/// # signer.update(to_sign.as_bytes()).unwrap();
/// # signer.sign_to_vec().map_err(|_| SignError())
/// # }
/// #
/// # fn verify(&self, data: &str, signature: &[u8]) -> SignResult<bool> {
/// # let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap())
/// # .unwrap();
/// # let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
/// # verifier.update(data.as_bytes()).unwrap();
/// # verifier.verify(&signature).map_err(|_| SignError())
/// # }
/// # }
/// #
/// # struct Account;
/// # impl FromId<()> for Account {
/// # type Error = ();
Expand All @@ -397,6 +476,10 @@ pub trait AsActor<C> {
/// # fn from_activity(_: &(), obj: Person) -> Result<Self, Self::Error> {
/// # Ok(Account)
/// # }
/// #
/// # fn get_sender() -> &'static dyn Signer {
/// # &*MY_SIGNER
/// # }
/// # }
/// # impl AsActor<()> for Account {
/// # fn get_inbox_url(&self) -> String {
Expand All @@ -420,6 +503,10 @@ pub trait AsActor<C> {
/// fn from_activity(_: &(), obj: Note) -> Result<Self, Self::Error> {
/// Ok(Message { text: obj.object_props.content_string().map_err(|_| ())? })
/// }
///
/// fn get_sender() -> &'static dyn Signer {
/// &*MY_SIGNER
/// }
/// }
///
/// impl AsObject<Account, Create, ()> for Message {
Expand Down Expand Up @@ -459,7 +546,51 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::activity_pub::sign::{
gen_keypair, Error as SignError, Result as SignResult, Signer,
};
use activitypub::{activity::*, actor::Person, object::Note};
use once_cell::sync::Lazy;
use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa};

static MY_SIGNER: Lazy<MySigner> = Lazy::new(|| MySigner::new());

struct MySigner {
public_key: String,
private_key: String,
}

impl MySigner {
fn new() -> Self {
let (pub_key, priv_key) = gen_keypair();
Self {
public_key: String::from_utf8(pub_key).unwrap(),
private_key: String::from_utf8(priv_key).unwrap(),
}
}
}

impl Signer for MySigner {
fn get_key_id(&self) -> String {
"mysigner".into()
}

fn sign(&self, to_sign: &str) -> SignResult<Vec<u8>> {
let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap())
.unwrap();
let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap();
signer.update(to_sign.as_bytes()).unwrap();
signer.sign_to_vec().map_err(|_| SignError())
}

fn verify(&self, data: &str, signature: &[u8]) -> SignResult<bool> {
let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap())
.unwrap();
let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
verifier.update(data.as_bytes()).unwrap();
verifier.verify(&signature).map_err(|_| SignError())
}
}

struct MyActor;
impl FromId<()> for MyActor {
Expand All @@ -473,6 +604,10 @@ mod tests {
fn from_activity(_: &(), _obj: Person) -> Result<Self, Self::Error> {
Ok(MyActor)
}

fn get_sender() -> &'static dyn Signer {
&*MY_SIGNER
}
}

impl AsActor<&()> for MyActor {
Expand All @@ -497,6 +632,10 @@ mod tests {
fn from_activity(_: &(), _obj: Note) -> Result<Self, Self::Error> {
Ok(MyObject)
}

fn get_sender() -> &'static dyn Signer {
&*MY_SIGNER
}
}
impl AsObject<MyActor, Create, &()> for MyObject {
type Error = ();
Expand Down Expand Up @@ -601,6 +740,10 @@ mod tests {
fn from_activity(_: &(), _obj: Person) -> Result<Self, Self::Error> {
Err(())
}

fn get_sender() -> &'static dyn Signer {
&*MY_SIGNER
}
}
impl AsActor<&()> for FailingActor {
fn get_inbox_url(&self) -> String {
Expand Down
Loading

0 comments on commit 0ede2ab

Please sign in to comment.