From 6b372861d65b15bd199cf7b91048abb0f6a90685 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 3 May 2018 18:12:01 +0100 Subject: [PATCH] WIP: make it possible for users to sign activities --- .../2018-05-03-163427_user_add_keys/down.sql | 3 ++ .../2018-05-03-163427_user_add_keys/up.sql | 3 ++ src/activity_pub/sign.rs | 22 +++++---- src/models/users.rs | 46 +++++++++++++++++-- src/schema.rs | 2 + 5 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 migrations/2018-05-03-163427_user_add_keys/down.sql create mode 100644 migrations/2018-05-03-163427_user_add_keys/up.sql diff --git a/migrations/2018-05-03-163427_user_add_keys/down.sql b/migrations/2018-05-03-163427_user_add_keys/down.sql new file mode 100644 index 0000000..056b832 --- /dev/null +++ b/migrations/2018-05-03-163427_user_add_keys/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE users DROP COLUMN private_key; +ALTER TABLE users DROP COLUMN public_key; diff --git a/migrations/2018-05-03-163427_user_add_keys/up.sql b/migrations/2018-05-03-163427_user_add_keys/up.sql new file mode 100644 index 0000000..869f823 --- /dev/null +++ b/migrations/2018-05-03-163427_user_add_keys/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE users ADD COLUMN private_key TEXT; +ALTER TABLE users ADD COLUMN public_key TEXT NOT NULL DEFAULT ''; diff --git a/src/activity_pub/sign.rs b/src/activity_pub/sign.rs index 54bb5d3..48d470b 100644 --- a/src/activity_pub/sign.rs +++ b/src/activity_pub/sign.rs @@ -1,20 +1,19 @@ use base64; +use diesel::PgConnection; use hex; use chrono::Utc; use openssl::sha::sha256; use serde_json; -// (Comments are from the Mastodon source code, to remember what to do.) - pub trait Signer { - fn get_key_id(&self) -> String; + fn get_key_id(&self, conn: &PgConnection) -> String; /// Sign some data with the signer keypair - fn sign(&self, to_sign: String) -> String; // Base64.strict_encode64(creator.keypair.sign(OpenSSL::Digest::SHA256.new, to_be_signed)) + fn sign(&self, to_sign: String) -> Vec; } pub trait Signable { - fn sign(&mut self, creator: T) -> &mut Self where T: Signer; + fn sign(&mut self, creator: T, conn: &PgConnection) -> &mut Self where T: Signer; fn hash(data: String) -> String { let bytes = data.into_bytes(); @@ -23,15 +22,18 @@ pub trait Signable { } impl Signable for serde_json::Value { - fn sign(&mut self, creator: T) -> &mut serde_json::Value where T: Signer { + fn sign(&mut self, creator: T, conn: &PgConnection) -> &mut serde_json::Value { + let creation_date = Utc::now().to_rfc3339(); let mut options = json!({ "type": "RsaSignature2017", - "creator": creator.get_key_id(), // [ActivityPub::TagManager.instance.uri_for(creator), '#main-key'].join, - "created": Utc::now().to_rfc3339() + "creator": creator.get_key_id(conn), + "created": creation_date }); - //options_hash = hash(options.without('type', 'id', 'signatureValue').merge('@context' => CONTEXT)) - let options_hash = Self::hash(String::from("")); + let options_hash = Self::hash(json!({ + "@context": "https://w3id.org/identity/v1", + "created": creation_date + }).to_string()); let document_hash = Self::hash(self.to_string()); let to_be_signed = options_hash + &document_hash; diff --git a/src/models/users.rs b/src/models/users.rs index 0cf2241..7f3a6dc 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -2,6 +2,10 @@ use bcrypt; use chrono::NaiveDateTime; use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, BelongingToDsl, PgConnection}; use diesel::dsl::any; +use openssl::hash::MessageDigest; +use openssl::pkey::{PKey, Private}; +use openssl::rsa::Rsa; +use openssl::sign::Signer; use reqwest::Client; use reqwest::header::{Accept, qitem}; use reqwest::mime::Mime; @@ -16,6 +20,7 @@ use activity_pub::activity::{Create, Activity}; use activity_pub::actor::{ActorType, Actor}; use activity_pub::inbox::Inbox; use activity_pub::outbox::Outbox; +use activity_pub::sign; use activity_pub::webfinger::{Webfinger, resolve}; use db_conn::DbConn; use models::follows::Follow; @@ -39,7 +44,9 @@ pub struct User { pub hashed_password: Option, pub instance_id: i32, pub creation_date: NaiveDateTime, - pub ap_url: String + pub ap_url: String, + pub private_key: Option, + pub public_key: String } #[derive(Insertable)] @@ -54,7 +61,9 @@ pub struct NewUser { pub email: Option, pub hashed_password: Option, pub instance_id: i32, - pub ap_url: String + pub ap_url: String, + pub private_key: Option, + pub public_key: String } impl User { @@ -153,7 +162,9 @@ impl User { email: None, hashed_password: None, instance_id: instance.id, - ap_url: acct["id"].as_str().unwrap().to_string() + ap_url: acct["id"].as_str().unwrap().to_string(), + public_key: acct["publicKey"]["publicKeyPem"].as_str().unwrap().to_string(), + private_key: None }) } @@ -208,6 +219,10 @@ impl User { let follows = follows::table.filter(follows::follower_id.eq(self.id)).select(follows::following_id); users::table.filter(users::id.eq(any(follows))).load::(conn).unwrap() } + + pub fn get_keypair(&self) -> PKey { + PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap() + } } impl<'a, 'r> FromRequest<'a, 'r> for User { @@ -303,6 +318,19 @@ impl Webfinger for User { } } +impl sign::Signer for User { + fn get_key_id(&self, conn: &PgConnection) -> String { + format!("{}#main-key", self.compute_id(conn)) + } + + fn sign(&self, to_sign: String) -> Vec { + let key = self.get_keypair(); + let mut signer = Signer::new(MessageDigest::sha256(), &key).unwrap(); + signer.update(to_sign.as_bytes()).unwrap(); + signer.sign_to_vec().unwrap() + } +} + impl NewUser { /// Creates a new local user pub fn new_local( @@ -314,6 +342,7 @@ impl NewUser { password: String, instance_id: i32 ) -> NewUser { + let (pub_key, priv_key) = NewUser::gen_keypair(); NewUser { username: username, display_name: display_name, @@ -324,7 +353,16 @@ impl NewUser { email: Some(email), hashed_password: Some(password), instance_id: instance_id, - ap_url: String::from("") + ap_url: String::from(""), + public_key: String::from_utf8(pub_key).unwrap(), + private_key: Some(String::from_utf8(priv_key).unwrap()) } } + + // Returns (public key, private key) + fn gen_keypair() -> (Vec, Vec) { + let keypair = Rsa::generate(2048).unwrap(); + let keypair = PKey::from_rsa(keypair).unwrap(); + (keypair.public_key_to_pem().unwrap(), keypair.private_key_to_pem_pkcs8().unwrap()) + } } diff --git a/src/schema.rs b/src/schema.rs index 2e696d2..30a65d8 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -75,6 +75,8 @@ table! { instance_id -> Int4, creation_date -> Timestamp, ap_url -> Text, + private_key -> Nullable, + public_key -> Text, } }