From 9c371b472044ea32f5ad4f085e8daa31b46b2135 Mon Sep 17 00:00:00 2001 From: Jeremy Dormitzer Date: Sat, 6 Oct 2018 08:01:50 -0400 Subject: [PATCH] Sign outbound activities --- includes/pgp.php | 21 +++++++++++++++++++++ includes/server/deliver.php | 31 +++++++++++++++++++------------ includes/server/inbox.php | 6 +++--- includes/server/outbox.php | 7 +++---- includes/util.php | 6 ++++++ 5 files changed, 52 insertions(+), 19 deletions(-) diff --git a/includes/pgp.php b/includes/pgp.php index 8d581e9..59dfb6a 100644 --- a/includes/pgp.php +++ b/includes/pgp.php @@ -19,10 +19,31 @@ function persist_key( $actor_id, $public_key, $private_key ) { ); } +function sign_data( $data, $actor_id ) { + xdebug_break(); + $secret_key = get_private_key( $actor_id ); + $sig = null; + openssl_sign( $data, $sig, $secret_key ); + if ( ! $sig ) { + return new \WP_Error( + 'pgp_error', + __( 'Unable to sign data', 'pterotype' ) + ); + } + return $sig; +} + function get_public_key( $actor_id ) { global $wpdb; return $wpdb->get_var( $wpdb->prepare( 'SELECT public_key FROM pterotype_keys WHERE actor_id = %d', $actor_id ) ); } + +function get_private_key( $actor_id ) { + global $wpdb; + return $wpdb->get_var( $wpdb->prepare( + 'SELECT private_key FROM pterotype_keys WHERE actor_id = %d', $actor_id + ) ); +} ?> diff --git a/includes/server/deliver.php b/includes/server/deliver.php index 3b422ab..b731eca 100644 --- a/includes/server/deliver.php +++ b/includes/server/deliver.php @@ -2,13 +2,16 @@ namespace deliver; require_once plugin_dir_path( __FILE__ ) . 'activities.php'; +require_once plugin_dir_path( __FILE__ ) . 'actors.php'; +require_once plugin_dir_path( __FILE__ ) . '../pgp.php'; require_once plugin_dir_path( __FILE__ ) . '../util.php'; // TODO look at inReplyTo, object, target, and tag objects // and deliver to their audience as well. Recurse through these // objects up to some limit -function deliver_activity( $activity ) { +function deliver_activity( $actor_slug, $activity ) { + $actor_id = \actors\get_actor_id( $actor_slug ); $activity = \util\dereference_object( $activity ); $recipients = array(); foreach ( array( 'to', 'bto', 'cc', 'bcc', 'audience' ) as $field ) { @@ -22,7 +25,7 @@ function deliver_activity( $activity ) { $recipients = remove_actor_inbox_from_recipients( $actor, $recipients ); } $activity = \activities\strip_private_fields( $activity ); - post_activity_to_inboxes( $activity, $recipients ); + post_activity_to_inboxes( $actor_id, $activity, $recipients ); } function remove_actor_inbox_from_recipients( $actor, $recipients ) { @@ -52,11 +55,11 @@ function get_recipient_urls( $object, $depth, $acc ) { if ( $depth === 30 ) { return $acc; } - if ( array_key_exists( 'type', $object ) ) { + if ( is_array( $object ) && array_key_exists( 'type', $object ) ) { // It's an Actor, Link, or Collection switch ( $object['type'] ) { - case Collection: - case OrderedCollection: + case 'Collection': + case 'OrderedCollection': $items = array(); if ( array_key_exists( 'items', $object ) ) { $items = $object['items']; @@ -71,13 +74,13 @@ function get_recipient_urls( $object, $depth, $acc ) { ); } return $recipients; - case Link: + case 'Link': if ( array_key_exists( 'href', $object ) ) { $response = \util\get_object_from_url( $object['href'] ); if ( is_wp_error( $response ) ) { return array(); } - return get_recipient_urls( $link_target, $depth + 1, $acc ); + return get_recipient_urls( $response, $depth + 1, $acc ); } else { return array(); } @@ -106,7 +109,7 @@ function get_recipient_urls( $object, $depth, $acc ) { if ( is_wp_error( $response ) ) { return array(); } - return get_recipient_urls( $response_body, $depth + 1, $acc ); + return get_recipient_urls( $response, $depth + 1, $acc ); } else { return array(); } @@ -114,19 +117,23 @@ function get_recipient_urls( $object, $depth, $acc ) { } } -function post_activity_to_inboxes( $activity, $recipients ) { +function post_activity_to_inboxes( $actor_id, $activity, $recipients ) { foreach ( $recipients as $inbox ) { if ( \util\is_local_url( $inbox ) ) { $request = \WP_REST_Request::from_url( $inbox ); $request->set_method('POST'); $request->set_body( $activity ); $request->add_header( 'Content-Type', 'application/ld+json' ); + $request->add_header( 'Signature', signature_header( $inbox, $actor_id ) ); $server = rest_get_server(); $server->dispatch( $request ); } else { $args = array( 'body' => $activity, - 'headers' => array( 'Content-Type' => 'application/ld+json' ) + 'headers' => array( + 'Content-Type' => 'application/ld+json', + 'Signature' => get_signing_string( $inbox, $actor_id ), + ) ); wp_remote_post( $inbox, $args ); } @@ -139,10 +146,10 @@ function get_signing_string( $inbox_url ) { $parsed = parse_url( $inbox_url ); return "(request-target): post $parsed[path] host: $parsed[host] -date: $now_str" +date: $now_str"; } function signature_header( $inbox_url, $actor_id ) { - // TODO + return \pgp\sign_data( get_signing_string( $inbox_url ), $actor_id ); } ?> diff --git a/includes/server/inbox.php b/includes/server/inbox.php index 1c6b211..e52008d 100644 --- a/includes/server/inbox.php +++ b/includes/server/inbox.php @@ -32,7 +32,7 @@ function handle_activity( $actor_slug, $activity ) { array( 'status' => 400 ) ); } - forward_activity( $activity ); + forward_activity( $actor_slug, $activity ); $activity = persist_activity( $actor_slug, $activity ); if ( is_wp_error( $activity ) ) { return $activity; @@ -70,7 +70,7 @@ function handle_activity( $actor_slug, $activity ) { return $res; } -function forward_activity( $activity ) { +function forward_activity( $actor_slug, $activity ) { if ( !array_key_exists( 'id', $activity ) ) { return; } @@ -88,7 +88,7 @@ function forward_activity( $activity ) { if ( count( $collections ) === 0 ) { return; } - \deliver\deliver_activity( $activity ); + \deliver\deliver_activity( $actor_slug, $activity ); } function references_local_object( $object, $depth ) { diff --git a/includes/server/outbox.php b/includes/server/outbox.php index 1f6c812..e3cfdee 100644 --- a/includes/server/outbox.php +++ b/includes/server/outbox.php @@ -25,7 +25,6 @@ require_once plugin_dir_path( __FILE__ ) . 'activities/undo.php'; require_once plugin_dir_path( __FILE__ ) . '../util.php'; function handle_activity( $actor_slug, $activity ) { - xdebug_break(); // TODO handle authentication/authorization $activity = \util\dereference_object( $activity ); if ( is_wp_error( $activity ) ) { @@ -118,7 +117,7 @@ function handle_activity( $actor_slug, $activity ) { if ( is_wp_error( $activity ) ) { return $activity; } - deliver_activity( $activity ); + deliver_activity( $actor_slug, $activity ); $res = new \WP_REST_Response(); $res->set_status(201); $res->header( 'Location', $activity['id'] ); @@ -157,8 +156,8 @@ function get_outbox( $actor_slug ) { ) ); } -function deliver_activity( $activity ) { - \deliver\deliver_activity( $activity ); +function deliver_activity( $actor_slug, $activity ) { + \deliver\deliver_activity( $actor_slug, $activity ); $activity = \activities\strip_private_fields( $activity ); return $activity; } diff --git a/includes/util.php b/includes/util.php index 03e1cca..2330281 100644 --- a/includes/util.php +++ b/includes/util.php @@ -58,6 +58,12 @@ function get_object_from_url_helper( $url, $depth ) { function retrieve_local_object( $url ) { $server = rest_get_server(); $request = \WP_REST_Request::from_url( $url ); + if ( ! $request ) { + return new \WP_Error( + 'not_local_url', + __( 'Expected a local URL', 'pterotype' ) + ); + } $response = $server->dispatch( $request ); if ( $response->is_error() ) { return $response->as_error();