diff --git a/includes/class-http.php b/includes/class-http.php index 58cace9..58551a4 100644 --- a/includes/class-http.php +++ b/includes/class-http.php @@ -34,7 +34,7 @@ class Http { 'headers' => array( 'Accept' => 'application/activity+json', 'Content-Type' => 'application/activity+json', - 'Digest' => "SHA-256=$digest", + 'Digest' => $digest, 'Signature' => $signature, 'Date' => $date, ), diff --git a/includes/class-signature.php b/includes/class-signature.php index 2c5be60..e06a226 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -131,8 +131,10 @@ class Signature { $path .= '?' . $url_parts['query']; } + $http_method = \strtolower( $http_method ); + if ( ! empty( $digest ) ) { - $signed_string = "(request-target): $http_method $path\nhost: $host\ndate: $date\ndigest: SHA-256=$digest"; + $signed_string = "(request-target): $http_method $path\nhost: $host\ndate: $date\ndigest: $digest"; } else { $signed_string = "(request-target): $http_method $path\nhost: $host\ndate: $date"; } @@ -165,7 +167,7 @@ class Signature { if ( is_object( $request ) ) { // REST Request object $headers = $request->get_headers(); $actor = isset( json_decode( $request->get_body() )->actor ) ? json_decode( $request->get_body() )->actor : ''; - $headers['(request-target)'][0] = strtolower( $request->get_method() ) . ' /' . rest_get_url_prefix() . $request->get_route(); + $headers['(request-target)'][0] = strtolower( $request->get_method() ) . ' ' . $request->get_route(); } else { $request = self::format_server_request( $request ); $headers = $request['headers']; // $_SERVER array @@ -227,7 +229,9 @@ class Signature { if ( \is_wp_error( $public_key ) ) { return $public_key; } + $verified = \openssl_verify( $signed_data, $signature_block['signature'], $public_key, $algorithm ) > 0; + if ( ! $verified ) { return new WP_Error( 'activitypub_signature', 'Invalid signature', array( 'status' => 403 ) ); } @@ -242,11 +246,6 @@ class Signature { * @return string $publicKeyPem */ public static function get_remote_key( $key_id ) { // phpcs:ignore - $pre = apply_filters( 'pre_get_remote_key', false, $key_id ); - if ( $pre ) { - return $pre; - } - $actor = \Activitypub\get_remote_metadata_by_actor( strtok( strip_fragment_from_url( $key_id ), '?' ) ); // phpcs:ignore if ( \is_wp_error( $actor ) ) { return $actor; @@ -374,7 +373,7 @@ class Signature { public static function generate_digest( $body ) { $digest = \base64_encode( \hash( 'sha256', $body, true ) ); // phpcs:ignore - return "$digest"; + return "SHA-256=$digest"; } /** diff --git a/tests/test-class-activitypub-rest-post-signature-verification.php b/tests/test-class-activitypub-rest-post-signature-verification.php index e87b1fd..f0acd34 100644 --- a/tests/test-class-activitypub-rest-post-signature-verification.php +++ b/tests/test-class-activitypub-rest-post-signature-verification.php @@ -1,41 +1,7 @@ server = $wp_rest_server; - - do_action( 'rest_api_init' ); - - } - - /** - * Tear down after test ends - */ - public function tearDown() : void { - remove_filter( 'pre_get_remote_key', array( get_called_class(), 'pre_get_remote_key' ) ); - parent::tearDown(); - - global $wp_rest_server; - $wp_rest_server = null; - - } - public function test_activity_signature() { - - $pre_http_request = new MockAction(); - add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 ); - // Activity for generate_digest $post = \wp_insert_post( array( @@ -63,11 +29,11 @@ class Test_Activitypub_Signature_Verification extends WP_UnitTestCase { $host = $url_parts['host']; $headers = array( - 'digest' => [ "SHA-256=$digest" ], - 'signature' => [ $signature ], - 'date' => [ $date ], - 'host' => [ $host ], - '(request-target)' => [ 'POST ' . $route ] + 'digest' => array( $digest ), + 'signature' => array( $signature ), + 'date' => array( $date ), + 'host' => array( $host ), + '(request-target)' => array( 'post ' . $route ), ); // Start verification @@ -75,21 +41,32 @@ class Test_Activitypub_Signature_Verification extends WP_UnitTestCase { $signature_block = Activitypub\Signature::parse_signature_header( $headers['signature'] ); $signed_headers = $signature_block['headers']; $signed_data = Activitypub\Signature::get_signed_data( $signed_headers, $signature_block, $headers ); + $public_key = Activitypub\Signature::get_public_key( 1 ); // signature_verification $verified = \openssl_verify( $signed_data, $signature_block['signature'], $public_key, 'rsa-sha256' ) > 0; $this->assertTrue( $verified ); - - remove_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10 ); } public function test_rest_activity_signature() { - - $pre_http_request = new MockAction(); - // $pre_get_remote_key = new MockAction(); - add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 ); - add_filter( 'pre_get_remote_key', array( get_called_class(), 'pre_get_remote_key' ), 10, 2 ); + add_filter( + 'pre_get_remote_metadata_by_actor', + function( $json, $actor ) { + // return ActivityPub Profile with signature + return array( + 'id' => $actor, + 'type' => 'Person', + 'publicKey' => array( + 'id' => $actor . '#main-key', + 'owner' => $actor, + 'publicKeyPem' => \Activitypub\Signature::get_public_key( 1 ), + ), + ); + }, + 10, + 2 + ); // Activity Object $post = \wp_insert_post( @@ -99,7 +76,7 @@ class Test_Activitypub_Signature_Verification extends WP_UnitTestCase { ) ); $remote_actor = \get_author_posts_url( 2 ); - $remote_actor_inbox = \get_rest_url( null, 'activitypub/1.0/inbox' ); + $remote_actor_inbox = Activitypub\get_rest_url_by_path( '/inbox' ); $activitypub_post = new \Activitypub\Model\Post( $post ); $activitypub_activity = new Activitypub\Model\Activity( 'Create' ); $activitypub_activity->from_post( $activitypub_post ); @@ -109,16 +86,16 @@ class Test_Activitypub_Signature_Verification extends WP_UnitTestCase { // generate_digest & generate_signature $digest = Activitypub\Signature::generate_digest( $activity ); $date = gmdate( 'D, d M Y H:i:s T' ); - $signature = Activitypub\Signature::generate_signature( 1, 'POST', $remote_actor, $date, $digest ); + $signature = Activitypub\Signature::generate_signature( 1, 'POST', $remote_actor_inbox, $date, $digest ); // Signed headers - $url_parts = wp_parse_url( $remote_actor ); - $route = add_query_arg( $url_parts['query'], $url_parts['path'] ); + $url_parts = wp_parse_url( $remote_actor_inbox ); + $route = $url_parts['path'] . '?' . $url_parts['query']; $host = $url_parts['host']; - $request = new WP_REST_Request( 'POST', ACTIVITYPUB_REST_NAMESPACE . '/inbox' ); + $request = new WP_REST_Request( 'POST', $route ); $request->set_header( 'content-type', 'application/activity+json' ); - $request->set_header( 'digest', "SHA-256=$digest" ); + $request->set_header( 'digest', $digest ); $request->set_header( 'signature', $signature ); $request->set_header( 'date', $date ); $request->set_header( 'host', $host ); @@ -126,19 +103,8 @@ class Test_Activitypub_Signature_Verification extends WP_UnitTestCase { // Start verification $verified = \Activitypub\Signature::verify_http_signature( $request ); - // $this->assertTRUE( $verified ); + $this->assertTrue( $verified ); - remove_filter( 'pre_get_remote_key', array( get_called_class(), 'pre_get_remote_key' ) ); - remove_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10 ); + remove_filter( 'pre_get_remote_metadata_by_actor', array( get_called_class(), 'pre_get_remote_key' ), 10, 2 ); } - - public static function pre_get_remote_key( $pre, $key_id ) { - $query = wp_parse_url( $key_id, PHP_URL_QUERY ); - parse_str( $query, $output ); - if ( is_int( $output['author'] ) ) { - return ActivityPub\Signature::get_public_key( int( $output['author'] ) ); - } - return $pre; - } - }