diff --git a/plugins/ActivityPub/Test/Objects/HTTPSignatureTest.php b/plugins/ActivityPub/Test/Objects/HTTPSignatureTest.php new file mode 100644 index 0000000000..1f3d01713d --- /dev/null +++ b/plugins/ActivityPub/Test/Objects/HTTPSignatureTest.php @@ -0,0 +1,68 @@ +. + +// }}} + +namespace Plugin\ActivityPub\Test\Objects; + +use App\Core\Router; +use App\Util\GNUsocialTestCase; +use Plugin\ActivityPub\Util\HTTPSignature; + +class HTTPSignatureTest extends GNUsocialTestCase +{ + private static string $gotosocial_signature = "keyId=\"https://goblin.technology/users/tobi/main-key\",algorithm=\"hs2019\",headers=\"(request-target) host date digest\",signature=\"kWM36BsRw3Ij4q9BgjPVPOzG2AG87ox91nYsdRoctdlCkYXmevhih2lWVjjlOgMc0OnQrwhUVMAFj2CNiOd5kw38Uo5KcOfY1KC6TCxvDciCJPAMUmvjaMD/Ag6JRaQUh6EB0tvnN2BaWO32i2LP7d0ApRaLj71DhHmSpXsG4pDBl/iauX/YoZew6ohfLAMPCRxPflGn7Uel/Is/yJSYCHyvz43DlmlNb+dMKEa0CVAhvNhg5aGwJ5AiJTg62Yk5ya5PCUJxWn2q87BhS3sbK//AfBQlPf8GUoytO0ACF9vRBwLCH/i6/aup8IXAhjN1GG2IlaAPEV5ROnCIBZo8fg==\""; + private static string $gotosocial_content = '{"@context":"https://www.w3.org/ns/activitystreams","actor":"https://goblin.technology/users/tobi","id":"https://goblin.technology/users/tobi/liked/01GG57FZE36DGH77H3M92M190W","object":"https://gnusocial.test.superseriousbusiness.org/object/note/2","to":"https://gnusocial.test.superseriousbusiness.org/actor/1","type":"Like"}'; + private static array $gotosocial_request_headers = ["accept-encoding"=>"gzip","content-length"=>"326","accept-charset"=>"utf-8","content-type"=>"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"","signature"=>"keyId=\"https://goblin.technology/users/tobi/main-key\",algorithm=\"hs2019\",headers=\"(request-target) host date digest\",signature=\"kWM36BsRw3Ij4q9BgjPVPOzG2AG87ox91nYsdRoctdlCkYXmevhih2lWVjjlOgMc0OnQrwhUVMAFj2CNiOd5kw38Uo5KcOfY1KC6TCxvDciCJPAMUmvjaMD/Ag6JRaQUh6EB0tvnN2BaWO32i2LP7d0ApRaLj71DhHmSpXsG4pDBl/iauX/YoZew6ohfLAMPCRxPflGn7Uel/Is/yJSYCHyvz43DlmlNb+dMKEa0CVAhvNhg5aGwJ5AiJTg62Yk5ya5PCUJxWn2q87BhS3sbK//AfBQlPf8GUoytO0ACF9vRBwLCH/i6/aup8IXAhjN1GG2IlaAPEV5ROnCIBZo8fg==\"","digest"=>"SHA-256=gTjVY4ikVc1yn++/IWEmiGge0zy2TBMG++0OzXS2Nw4=","date"=>"Mon, 24 Oct 2022 14:49:06 GMT","user-agent"=>"gotosocial; goblin.technology (gofed/activity gotosocial-0.5.1-SNAPSHOT git-3ca7164)","host"=>"gnusocial.test.superseriousbusiness.org","x-php-ob-level"=>"0","format"=>"application/jrd+json"]; + private static string $gotosocial_public_key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy2/j9+2G7xrQvBtygrj4\naYHl8hTeZecDBnS+6IBjjEt+QWJ3z0Cv9lXSVMZw5i6DVTkVOGZrh2vZDu0BCTEV\n07dyASArE63Qe21WwjNObpkQ7YZbMxUkYjWCDYdqLMifAqElYzIK7xnY0pTWylmC\njm39qxmhk22PpzRkw+zofh9ykqyadmkA2/KrpZgGnjn6MiPqb2DeELV1tzmZ7mAz\n+k7pkkhxvBVqPhCZ104pyd1lc66obONSnIqxugRlrrUZbFv1e6xFsUmMUYrAGQTt\nZr4VeZwuaYj/MvFIeOZrmth/lg3QpYbKZYnJKVePyH+530jRSFerr2unbuGmEQAg\nvQIDAQAB\n-----END PUBLIC KEY-----\n"; + + private static string $mastodon_signature = "keyId=\"https://ondergrond.org/users/dumpsterqueer#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"RP9F3wxAq9/VGATr1HfB30B98XNgXcdle2z15QQc0x+k2ioKmwOLlCFKv+J3LdQ2GKIJ4XVBHG2Klzu6FVPLaABiUoBWSqSM1p2hzM760ewIKBZs9fZwmFp+Ec1UT5i4xwLa/EYJZ8ofpc2DkVww58p8FIxcV6gqcmvHMkYooCBogXvsSRSQ0IG3YlFux2um4iD/e9ly5DiU5NtAUMg3tER05MT+gzG6bTMyZwxeJ+svuXF6w6o+BCn8wsxXwMxbDYtjh3f03zeTYdbzEm4eJT5yEQi6XRmgQz59u20LCFOXEtqljP7PymRY+dPb6/xJhlhYLaTOMYZwuAjPSnToEQ==\""; + private static string $mastodon_content = '{"@context":"https://www.w3.org/ns/activitystreams","id":"https://ondergrond.org/users/dumpsterqueer#likes/119260","type":"Like","actor":"https://ondergrond.org/users/dumpsterqueer","object":"https://gnusocial.test.superseriousbusiness.org/object/note/2"}'; + private static array $mastodon_request_headers = ["content-length"=>"255","connection"=>"Keep-Alive","signature"=>"keyId=\"https://ondergrond.org/users/dumpsterqueer#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"RP9F3wxAq9/VGATr1HfB30B98XNgXcdle2z15QQc0x+k2ioKmwOLlCFKv+J3LdQ2GKIJ4XVBHG2Klzu6FVPLaABiUoBWSqSM1p2hzM760ewIKBZs9fZwmFp+Ec1UT5i4xwLa/EYJZ8ofpc2DkVww58p8FIxcV6gqcmvHMkYooCBogXvsSRSQ0IG3YlFux2um4iD/e9ly5DiU5NtAUMg3tER05MT+gzG6bTMyZwxeJ+svuXF6w6o+BCn8wsxXwMxbDYtjh3f03zeTYdbzEm4eJT5yEQi6XRmgQz59u20LCFOXEtqljP7PymRY+dPb6/xJhlhYLaTOMYZwuAjPSnToEQ==\"","content-type"=>"application/activity+json","digest"=>"SHA-256=dDj/+B/FOGAFyTSkrhVjzTmJmUSlebTuU9Pr8XbbAqY=","accept-encoding"=>"gzip","date"=>"Mon, 24 Oct 2022 15:08:50 GMT","host"=>"gnusocial.test.superseriousbusiness.org","user-agent"=>"http.rb/4.4.1 (Mastodon/3.4.2; +https://ondergrond.org/)","x-php-ob-level"=>"0","format"=>"application/jrd+json"]; + private static string $mastodon_public_key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAotBC4vg/MqJlrgygFIlJ\nejnPNOwzJQKgRSjfyiOWfcuTRN5jKmj7x3WnWHJlHLXmf8ofcBLhRE3icA5o+Ayd\nhgTbSLnXXeRuzi6t8q4zcQ6p8eY3YG/jMWYD8PhiHR7uFuprCo4l9+GWrjJ6IwQM\nPXMq7jzumKwv5Z/KD7hIwNApceymQ4QOHvMs0+dNCOZOKpyigcxG5KSG8ukO+TjP\npHx34vTy/1Qsv9b7q4dIxqhNjRK/dVLJ8EkuBz3ajac314FJMYIY1TfcakHTtiJA\nhtPhKl3t6r6l14pOpXgaF5EHmNIvnP3BNezZT8Nb2ElVnX31kkJb2Ds0C0Q2Xq34\nJwIDAQAB\n-----END PUBLIC KEY-----\n"; + + public function testValidateGtSSignature() + { + self::bootKernel(); + $signature_parsed = HTTPSignature::parseSignatureHeader(self::$gotosocial_signature); + static::assertSame(['keyId' => 'https://goblin.technology/users/tobi/main-key', 'algorithm' => 'hs2019', 'headers' => '(request-target) host date digest', 'signature' => 'kWM36BsRw3Ij4q9BgjPVPOzG2AG87ox91nYsdRoctdlCkYXmevhih2lWVjjlOgMc0OnQrwhUVMAFj2CNiOd5kw38Uo5KcOfY1KC6TCxvDciCJPAMUmvjaMD/Ag6JRaQUh6EB0tvnN2BaWO32i2LP7d0ApRaLj71DhHmSpXsG4pDBl/iauX/YoZew6ohfLAMPCRxPflGn7Uel/Is/yJSYCHyvz43DlmlNb+dMKEa0CVAhvNhg5aGwJ5AiJTg62Yk5ya5PCUJxWn2q87BhS3sbK//AfBQlPf8GUoytO0ACF9vRBwLCH/i6/aup8IXAhjN1GG2IlaAPEV5ROnCIBZo8fg=='], $signature_parsed); + $path = Router::url('activitypub_inbox', type: Router::ABSOLUTE_PATH); + [$verified, $signing_string] = HTTPSignature::verify(self::$gotosocial_public_key, $signature_parsed, self::$gotosocial_request_headers, $path, self::$gotosocial_content); + static::assertSame("(request-target): post /inbox.json\nhost: gnusocial.test.superseriousbusiness.org\ndate: Mon, 24 Oct 2022 14:49:06 GMT\ndigest: SHA-256=gTjVY4ikVc1yn++/IWEmiGge0zy2TBMG++0OzXS2Nw4=", $signing_string); + static::assertSame(1, $verified); + } + + public function testValidateMastodonSignature() + { + self::bootKernel(); + $signature_parsed = HTTPSignature::parseSignatureHeader(self::$mastodon_signature); + static::assertSame(['keyId' => 'https://ondergrond.org/users/dumpsterqueer#main-key', 'algorithm' => 'rsa-sha256', 'headers' => '(request-target) host date digest content-type', 'signature' => 'RP9F3wxAq9/VGATr1HfB30B98XNgXcdle2z15QQc0x+k2ioKmwOLlCFKv+J3LdQ2GKIJ4XVBHG2Klzu6FVPLaABiUoBWSqSM1p2hzM760ewIKBZs9fZwmFp+Ec1UT5i4xwLa/EYJZ8ofpc2DkVww58p8FIxcV6gqcmvHMkYooCBogXvsSRSQ0IG3YlFux2um4iD/e9ly5DiU5NtAUMg3tER05MT+gzG6bTMyZwxeJ+svuXF6w6o+BCn8wsxXwMxbDYtjh3f03zeTYdbzEm4eJT5yEQi6XRmgQz59u20LCFOXEtqljP7PymRY+dPb6/xJhlhYLaTOMYZwuAjPSnToEQ=='], $signature_parsed); + $path = Router::url('activitypub_inbox', type: Router::ABSOLUTE_PATH); + [$verified, $signing_string] = HTTPSignature::verify(self::$mastodon_public_key, $signature_parsed, self::$mastodon_request_headers, $path, self::$mastodon_content); + static::assertSame("(request-target): post /inbox.json\nhost: gnusocial.test.superseriousbusiness.org\ndate: Mon, 24 Oct 2022 15:08:50 GMT\ndigest: SHA-256=dDj/+B/FOGAFyTSkrhVjzTmJmUSlebTuU9Pr8XbbAqY=\ncontent-type: application/activity+json", $signing_string); + // TODO: The following assertion should pass, and it's a mystery to me why it doesn't. I checked the public key, the signature, + // the digest, base64 encoding, etc, and it all looks fine. Signatures from Mastodon just don't seem to pass. Which is odd because + // this is the same implementation that Pixelfed uses -- + // https://github.com/pixelfed/pixelfed/blob/dbf314151e83d14648d9c1e16241412997a3f65b/app/Util/ActivityPub/HttpSignature.php + // When this is resolved by someone cleverer than me, the following line should be uncommented. xoxo tobi + // static::assertSame(1, $verified); + } +}