Skip to content

Commit c28dadc

Browse files
authored
Signature: Add compat for more key encodings (#1557)
1 parent 166e54b commit c28dadc

File tree

3 files changed

+192
-5
lines changed

3 files changed

+192
-5
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: changed
3+
4+
Increased compatibility with Mobilizon and other platforms by improving signature verification for different key formats.

includes/class-signature.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ public static function verify_http_signature( $request ) {
326326
*
327327
* @param string $key_id The URL to the public key.
328328
*
329-
* @return WP_Error|string The public key or WP_Error.
329+
* @return resource|WP_Error The public key resource or WP_Error.
330330
*/
331331
public static function get_remote_key( $key_id ) {
332332
$actor = get_remote_metadata_by_actor( strip_fragment_from_url( $key_id ) );
@@ -337,9 +337,14 @@ public static function get_remote_key( $key_id ) {
337337
array( 'status' => 401 )
338338
);
339339
}
340+
340341
if ( isset( $actor['publicKey']['publicKeyPem'] ) ) {
341-
return \rtrim( $actor['publicKey']['publicKeyPem'] );
342+
$key_resource = \openssl_pkey_get_public( \rtrim( $actor['publicKey']['publicKeyPem'] ) );
343+
if ( $key_resource ) {
344+
return $key_resource;
345+
}
342346
}
347+
343348
return new WP_Error(
344349
'activitypub_no_remote_key_found',
345350
__( 'No Public-Key found', 'activitypub' ),

tests/includes/class-test-signature.php

Lines changed: 181 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,65 @@
1616
* @coversDefaultClass \Activitypub\Signature
1717
*/
1818
class Test_Signature extends \WP_UnitTestCase {
19+
20+
/**
21+
* The public key in PKCS#1 format.
22+
*
23+
* @var string
24+
*/
25+
private $pkcs1_key = '-----BEGIN RSA PUBLIC KEY-----
26+
MIIBCgKCAQEAtAVnFFbWG+6NBFKhMZdt59Gx2/vKxWxbxOAYyi/ypZ/9aDY6C/UB
27+
Rei8SqnhKcKXQaiSwme/wpqgCdkrf53H85OioBitCEvKNA6uDxkCtcdgtQ3X55QD
28+
XmatWd32ln6elRmKG45U9R386j82OHzff8Ju65QxGL1LlyCKQ/XFx/pgvblF3cGj
29+
shk0dhNcyGAztODN5HFp9Qzf9d7+gi+xdKeGNhXBAulXoaDzx8FvLEXNfPJb3jUM
30+
1Ug0STFsiICcf7VxmQow6N6d0+HtWxrdtjUBdXrPxz998Ns/cu9jjg06d+XV3TcS
31+
U+AOldmGLJuB/AWV/+F9c9DlczqmnXqd1QIDAQAB
32+
-----END RSA PUBLIC KEY-----
33+
';
34+
35+
/**
36+
* The public key in X.509 format.
37+
*
38+
* @var string
39+
*/
40+
private $x509_key = '-----BEGIN PUBLIC KEY-----
41+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA19218d19uYisOYUZ3oqN
42+
wSRyixAX8V1JHJSngbjAjZr1vYcwMte8CPqqELbNwtQWAMy42UnQpyIqgvLpOaVr
43+
vQWjUuR+7i8wETrVNJq8JQNNCiQ+8+I4TPcGyZDBclHkLtKiCoBtjUH0itVh4Sg0
44+
KQLSb8ZHu9lGh8TJMcLXVUdVkvkUjqHl6I5BoftMVDSKQF+V4X8Qyk7qP7wU8mpE
45+
+O6RuhUpZ3QXM+dBIalyey8NKLf2yN6CmKyW1220wdNupOYHbc8DSYEq6NDQZfZb
46+
yP2KLHN3rdNwsnlAP02Ws1qroBivHSV71KLebQUDU2KpDLKQF2Ix6X47IBFOXnb9
47+
FwIDAQAB
48+
-----END PUBLIC KEY-----
49+
';
50+
51+
/**
52+
* The public key in EC format.
53+
*
54+
* @var string
55+
*/
56+
private $ec_key = '-----BEGIN PUBLIC KEY-----
57+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/jw3kftaHGIB2OTKTYFUTTqyzDs0
58+
eWKe+6k1Kh6HSrinXriBLbIhMPY9pQsvqkeT6wW975NDn7+8awb8kHRmIg==
59+
-----END PUBLIC KEY-----
60+
';
61+
62+
/**
63+
* The public key in PKCS#8 format.
64+
*
65+
* @var string
66+
*/
67+
private $pkcs8_key = '-----BEGIN PUBLIC KEY-----
68+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy8dfWmTltr09m49uyESj
69+
x6UnQ9G/iVq+3dJbUdCdVEPR256UD6DLHE8uM4DgXhtoLVrBcvTAl9h0nRGX4uVN
70+
5jE+pTh47B9IUim0bVw2sOBNwPCTUuKbMVx3Cso/6UxJsot41q7+FHIxcAurDxfR
71+
xfJkf+1ecYSb5czoeOG+NUcTEQv1LQntAOJ1ngrmjKyL4UlKZgcs2TfueqlK1v2t
72+
Gw4ylFOQYRx1Nj5YttQAuXc+VpGfztyRK90R74WkE/N6miOoDHcvc+7AeW4zyWsh
73+
ZfLXCbngI45TVhUr3ljxWs1Ykc8d4Xt3JrtcUzltbc6nWS0vstcUmxTLTRURn3SX
74+
4wIDAQAB
75+
-----END PUBLIC KEY-----
76+
';
77+
1978
/**
2079
* Tear down.
2180
*/
@@ -113,11 +172,11 @@ public function test_signature_legacy() {
113172
}
114173

115174
/**
116-
* Test signature consistancy.
175+
* Test signature consistency.
117176
*
118177
* @covers ::get_keypair_for
119178
*/
120-
public function test_signature_consistancy() {
179+
public function test_signature_consistency() {
121180
// Check user.
122181
$user = Actors::get_by_id( 1 );
123182

@@ -148,7 +207,7 @@ public function test_signature_consistancy() {
148207
*
149208
* @covers ::get_keypair_for
150209
*/
151-
public function test_signature_consistancy2() {
210+
public function test_signature_consistency2() {
152211
$user = Actors::get_by_id( 1 );
153212

154213
$key_pair = Signature::get_keypair_for( $user->get__id() );
@@ -168,4 +227,123 @@ public function test_signature_consistancy2() {
168227
$this->assertEquals( $key_pair['public_key'], $public_key );
169228
$this->assertEquals( $key_pair['private_key'], $private_key );
170229
}
230+
231+
/**
232+
* Test handling of different public key formats.
233+
*
234+
* @covers ::get_remote_key
235+
*/
236+
public function test_key_format_handling() {
237+
$expected = '-----BEGIN PUBLIC KEY-----
238+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtAVnFFbWG+6NBFKhMZdt
239+
59Gx2/vKxWxbxOAYyi/ypZ/9aDY6C/UBRei8SqnhKcKXQaiSwme/wpqgCdkrf53H
240+
85OioBitCEvKNA6uDxkCtcdgtQ3X55QDXmatWd32ln6elRmKG45U9R386j82OHzf
241+
f8Ju65QxGL1LlyCKQ/XFx/pgvblF3cGjshk0dhNcyGAztODN5HFp9Qzf9d7+gi+x
242+
dKeGNhXBAulXoaDzx8FvLEXNfPJb3jUM1Ug0STFsiICcf7VxmQow6N6d0+HtWxrd
243+
tjUBdXrPxz998Ns/cu9jjg06d+XV3TcSU+AOldmGLJuB/AWV/+F9c9DlczqmnXqd
244+
1QIDAQAB
245+
-----END PUBLIC KEY-----
246+
';
247+
248+
\add_filter( 'pre_get_remote_metadata_by_actor', array( $this, 'pre_get_remote_metadata_by_actor' ), 10, 2 );
249+
250+
// X.509 key should remain unchanged.
251+
$result = Signature::get_remote_key( 'https://example.com/author/x509' );
252+
$key_resource = \openssl_pkey_get_details( $result );
253+
$this->assertNotFalse( $key_resource );
254+
$this->assertSame( $this->x509_key, $key_resource['key'] );
255+
256+
// PKCS#1 key should be converted to X.509 format.
257+
$result = Signature::get_remote_key( 'https://example.com/author/pkcs1' );
258+
$key_resource = \openssl_pkey_get_details( $result );
259+
$this->assertNotFalse( $key_resource );
260+
$this->assertSame( $expected, $key_resource['key'] );
261+
262+
// EC key should be handled correctly.
263+
$result = Signature::get_remote_key( 'https://example.com/author/ec' );
264+
$key_resource = \openssl_pkey_get_details( $result );
265+
$this->assertNotFalse( $key_resource );
266+
267+
// PKCS#8 key should be handled correctly.
268+
$result = Signature::get_remote_key( 'https://example.com/author/pkcs8' );
269+
$key_resource = \openssl_pkey_get_details( $result );
270+
$this->assertNotFalse( $key_resource );
271+
272+
// Test with invalid key.
273+
$result = Signature::get_remote_key( 'https://example.com/author/invalid' );
274+
$this->assertWPError( $result );
275+
276+
\remove_filter( 'pre_get_remote_metadata_by_actor', array( $this, 'pre_get_remote_metadata_by_actor' ) );
277+
}
278+
279+
/**
280+
* Pre get remote metadata by actor.
281+
*
282+
* @param mixed $value The value.
283+
* @param string $url The URL.
284+
* @return array|\WP_Error
285+
*/
286+
public function pre_get_remote_metadata_by_actor( $value, $url ) {
287+
if ( 'https://example.com/author/x509' === $url ) {
288+
return array(
289+
'name' => 'Test Actor',
290+
'url' => 'https://example.com/author/x509',
291+
'publicKey' => array(
292+
'id' => 'https://example.com/author#main-key',
293+
'owner' => 'https://example.com/author',
294+
'publicKeyPem' => $this->x509_key,
295+
),
296+
);
297+
}
298+
299+
if ( 'https://example.com/author/pkcs1' === $url ) {
300+
return array(
301+
'name' => 'Test Actor',
302+
'url' => 'https://example.com/author/pkcs1',
303+
'publicKey' => array(
304+
'id' => 'https://example.com/author#main-key',
305+
'owner' => 'https://example.com/author',
306+
'publicKeyPem' => $this->pkcs1_key,
307+
),
308+
);
309+
}
310+
311+
if ( 'https://example.com/author/ec' === $url ) {
312+
return array(
313+
'name' => 'Test Actor',
314+
'url' => 'https://example.com/author/ec',
315+
'publicKey' => array(
316+
'id' => 'https://example.com/author#main-key',
317+
'owner' => 'https://example.com/author',
318+
'publicKeyPem' => $this->ec_key,
319+
),
320+
);
321+
}
322+
323+
if ( 'https://example.com/author/pkcs8' === $url ) {
324+
return array(
325+
'name' => 'Test Actor',
326+
'url' => 'https://example.com/author/pkcs8',
327+
'publicKey' => array(
328+
'id' => 'https://example.com/author#main-key',
329+
'owner' => 'https://example.com/author',
330+
'publicKeyPem' => $this->pkcs8_key,
331+
),
332+
);
333+
}
334+
335+
if ( 'https://example.com/author/invalid' === $url ) {
336+
return array(
337+
'name' => 'Test Actor',
338+
'url' => 'https://example.com/author/invalid',
339+
'publicKey' => array(
340+
'id' => 'https://example.com/author#main-key',
341+
'owner' => 'https://example.com/author',
342+
'publicKeyPem' => 'INVALID KEY DATA',
343+
),
344+
);
345+
}
346+
347+
return new \WP_Error( 'invalid_url', $url );
348+
}
171349
}

0 commit comments

Comments
 (0)