diff --git a/activitypub.php b/activitypub.php index 35e6de8..c5275ba 100644 --- a/activitypub.php +++ b/activitypub.php @@ -57,6 +57,9 @@ function init() { require_once \dirname( __FILE__ ) . '/includes/rest/class-following.php'; Rest\Following::init(); + require_once \dirname( __FILE__ ) . '/includes/rest/class-server.php'; + \Activitypub\Rest\Server::init(); + require_once \dirname( __FILE__ ) . '/includes/rest/class-webfinger.php'; Rest\Webfinger::init(); diff --git a/includes/class-signature.php b/includes/class-signature.php index 9261d99..68fb41e 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -114,7 +114,7 @@ class Signature { $headers = $request->get_headers(); if ( ! $headers ) { - return false; + return new \WP_Error( 'activitypub_signature', 'Request not signed', array( 'status' => 403 ) ); } $actor = isset( json_decode( $request->get_body() )->actor ) ? json_decode( $request->get_body() )->actor : ''; @@ -127,7 +127,7 @@ class Signature { } if ( ! isset( $signature_block ) || ! $signature_block ) { - return false; + return new \WP_Error( 'activitypub_signature', 'Incompatible request signature. keyId and signature are required', array( 'status' => 403 ) ); } $signed_headers = $signature_block['headers']; @@ -137,12 +137,12 @@ class Signature { $signed_data = self::get_signed_data( $signed_headers, $signature_block, $headers ); if ( ! $signed_data ) { - return false; + return new \WP_Error( 'activitypub_signature', 'Signed request date outside acceptable time window', array( 'status' => 403 ) ); } $algorithm = self::get_signature_algorithm( $signature_block ); if ( ! $algorithm ) { - return false; + return new \WP_Error( 'activitypub_signature', 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)', array( 'status' => 403 ) ); } if ( \in_array( 'digest', $signed_headers, true ) && isset( $body ) ) { @@ -158,13 +158,17 @@ class Signature { } if ( \base64_encode( \hash( $hashalg, $body, true ) ) !== $digest[1] ) { // phpcs:ignore - return false; + return new \WP_Error( 'activitypub_signature', 'Invalid Digest header', array( 'status' => 403 ) ); } } - $public_key = \rtrim( \Activitypub\get_publickey_by_actor( $actor, $signature_block['keyId'] ) ); // phpcs:ignore - - return \openssl_verify( $signed_data, $signature_block['signature'], $public_key, $algorithm ) > 0; + $public_key = \Activitypub\get_publickey_by_actor( $actor, $signature_block['keyId'] ); // phpcs:ignore + if ( \is_wp_error( $public_key ) ) { + return $public_key; + } else { + $public_key = \rtrim( $public_key ); + } + return \openssl_verify( $signed_data, $signature_block['signature'], $public_key, $algorithm ) > 0 ?? new \WP_Error( 'activitypub_signature', 'Invalid signature', array( 'status' => 403 ) ); } diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index 8a1f139..0257ba1 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -134,10 +134,6 @@ class Inbox { * @return WP_REST_Response */ public static function user_inbox_post( $request ) { - // SecureMode/Authorized fetch. - if ( ! \Activitypub\Signature::verify_http_signature( $request ) ) { - return new \WP_REST_Response( array(), 403 ); - } $user_id = $request->get_param( 'user_id' ); @@ -159,10 +155,6 @@ class Inbox { * @return WP_REST_Response */ public static function shared_inbox_post( $request ) { - // SecureMode/Authorized fetch. - if ( ! \Activitypub\Signature::verify_http_signature( $request ) ) { - return new \WP_REST_Response( array(), 403 ); - } $data = $request->get_params(); $type = $request->get_param( 'type' ); diff --git a/includes/rest/class-server.php b/includes/rest/class-server.php new file mode 100644 index 0000000..790728a --- /dev/null +++ b/includes/rest/class-server.php @@ -0,0 +1,55 @@ +get_route(); + if ( str_starts_with( $maybe_activitypub, '/activitypub' ) ) { + if ( 'POST' === $request->get_method() ) { + $verified_request = Signature::verify_http_signature( $request ); + + if ( \is_wp_error( $verified_request ) ) { + return $verified_request; + } + } else { + // SecureMode/Authorized fetch. + $secure_mode = \get_option( 'activitypub_use_secure_mode', '0' ); + + if ( $secure_mode ) { + if ( \is_wp_error( $verified_request ) ) { + return $verified_request; + } + } + } + } + } +}