Skip to content

Commit 8a74aa5

Browse files
authored
Store keypairs as options keyed to user IDs. (#416)
1 parent 8dcbe0c commit 8a74aa5

File tree

6 files changed

+216
-156
lines changed

6 files changed

+216
-156
lines changed

includes/class-signature.php

Lines changed: 103 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
use WP_Error;
55
use DateTime;
66
use DateTimeZone;
7-
use Activitypub\Model\User;
87
use Activitypub\Collection\Users;
98

109
/**
@@ -23,22 +22,14 @@ class Signature {
2322
*
2423
* @return mixed The public key.
2524
*/
26-
public static function get_public_key( $user_id, $force = false ) {
25+
public static function get_public_key_for( $user_id, $force = false ) {
2726
if ( $force ) {
28-
self::generate_key_pair( $user_id );
27+
self::generate_key_pair_for( $user_id );
2928
}
3029

31-
if ( User::APPLICATION_USER_ID === $user_id ) {
32-
$key = \get_option( 'activitypub_magic_sig_public_key' );
33-
} else {
34-
$key = \get_user_meta( $user_id, 'magic_sig_public_key', true );
35-
}
36-
37-
if ( ! $key ) {
38-
return self::get_public_key( $user_id, true );
39-
}
30+
$key_pair = self::get_keypair_for( $user_id );
4031

41-
return $key;
32+
return $key_pair['public_key'];
4233
}
4334

4435
/**
@@ -49,32 +40,51 @@ public static function get_public_key( $user_id, $force = false ) {
4940
*
5041
* @return mixed The private key.
5142
*/
52-
public static function get_private_key( $user_id, $force = false ) {
43+
public static function get_private_key_for( $user_id, $force = false ) {
5344
if ( $force ) {
54-
self::generate_key_pair( $user_id );
45+
self::generate_key_pair_for( $user_id );
5546
}
5647

57-
if ( User::APPLICATION_USER_ID === $user_id ) {
58-
$key = \get_option( 'activitypub_magic_sig_private_key' );
59-
} else {
60-
$key = \get_user_meta( $user_id, 'magic_sig_private_key', true );
61-
}
48+
$key_pair = self::get_keypair_for( $user_id );
6249

63-
if ( ! $key ) {
64-
return self::get_private_key( $user_id, true );
50+
return $key_pair['private_key'];
51+
}
52+
53+
/**
54+
* Return the key pair for a given user.
55+
*
56+
* @param int $user_id The WordPress User ID.
57+
*
58+
* @return array The key pair.
59+
*/
60+
public static function get_keypair_for( $user_id ) {
61+
$option_key = self::get_signature_options_key_for( $user_id );
62+
$key_pair = \get_option( $option_key );
63+
64+
if ( ! $key_pair ) {
65+
$key_pair = self::generate_key_pair_for( $user_id );
6566
}
6667

67-
return $key;
68+
return $key_pair;
6869
}
6970

7071
/**
7172
* Generates the pair keys
7273
*
7374
* @param int $user_id The WordPress User ID.
7475
*
75-
* @return void
76+
* @return array The key pair.
7677
*/
77-
public static function generate_key_pair() {
78+
protected static function generate_key_pair_for( $user_id ) {
79+
$option_key = self::get_signature_options_key_for( $user_id );
80+
$key_pair = self::check_legacy_key_pair_for( $user_id );
81+
82+
if ( $key_pair ) {
83+
\add_option( $option_key, $key_pair );
84+
85+
return $key_pair;
86+
}
87+
7888
$config = array(
7989
'digest_alg' => 'sha512',
8090
'private_key_bits' => 2048,
@@ -88,10 +98,76 @@ public static function generate_key_pair() {
8898

8999
$detail = \openssl_pkey_get_details( $key );
90100

91-
return array(
101+
// check if keys are valid
102+
if (
103+
empty( $priv_key ) || ! is_string( $priv_key ) ||
104+
! isset( $detail['key'] ) || ! is_string( $detail['key'] )
105+
) {
106+
return array(
107+
'private_key' => null,
108+
'public_key' => null,
109+
);
110+
}
111+
112+
$key_pair = array(
92113
'private_key' => $priv_key,
93114
'public_key' => $detail['key'],
94115
);
116+
117+
// persist keys
118+
\add_option( $option_key, $key_pair );
119+
120+
return $key_pair;
121+
}
122+
123+
/**
124+
* Undocumented function
125+
*
126+
* @param [type] $user_id
127+
* @return void
128+
*/
129+
protected static function get_signature_options_key_for( $user_id ) {
130+
$id = $user_id;
131+
132+
if ( $user_id > 0 ) {
133+
$user = \get_userdata( $user_id );
134+
$id = $user->user_login;
135+
}
136+
137+
return 'activitypub_keypair_for_' . $id;
138+
}
139+
140+
/**
141+
* Check if there is a legacy key pair
142+
*
143+
* @param int $user_id The WordPress User ID.
144+
*
145+
* @return array|bool The key pair or false.
146+
*/
147+
protected static function check_legacy_key_pair_for( $user_id ) {
148+
switch ( $user_id ) {
149+
case 0:
150+
$public_key = \get_option( 'activitypub_blog_user_public_key' );
151+
$private_key = \get_option( 'activitypub_blog_user_private_key' );
152+
break;
153+
case -1:
154+
$public_key = \get_option( 'activitypub_application_user_public_key' );
155+
$private_key = \get_option( 'activitypub_application_user_private_key' );
156+
break;
157+
default:
158+
$public_key = \get_user_meta( $user_id, 'magic_sig_public_key', true );
159+
$private_key = \get_user_meta( $user_id, 'magic_sig_private_key', true );
160+
break;
161+
}
162+
163+
if ( ! empty( $public_key ) && is_string( $public_key ) && ! empty( $private_key ) && is_string( $private_key ) ) {
164+
return array(
165+
'private_key' => $private_key,
166+
'public_key' => $public_key,
167+
);
168+
}
169+
170+
return false;
95171
}
96172

97173
/**
@@ -107,7 +183,7 @@ public static function generate_key_pair() {
107183
*/
108184
public static function generate_signature( $user_id, $http_method, $url, $date, $digest = null ) {
109185
$user = Users::get_by_id( $user_id );
110-
$key = $user->get__private_key();
186+
$key = self::get_private_key_for( $user->get__id() );
111187

112188
$url_parts = \wp_parse_url( $url );
113189

@@ -136,7 +212,6 @@ public static function generate_signature( $user_id, $http_method, $url, $date,
136212
\openssl_sign( $signed_string, $signature, $key, \OPENSSL_ALGO_SHA256 );
137213
$signature = \base64_encode( $signature ); // phpcs:ignore
138214

139-
$user = Users::get_by_id( $user_id );
140215
$key_id = $user->get_url() . '#main-key';
141216

142217
if ( ! empty( $digest ) ) {

includes/model/class-application-user.php

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -46,46 +46,6 @@ public function get_preferred_username() {
4646
return $this::get_name();
4747
}
4848

49-
public function get__public_key() {
50-
$key = \get_option( 'activitypub_application_user_public_key' );
51-
52-
if ( $key ) {
53-
return $key;
54-
}
55-
56-
$this->generate_key_pair();
57-
58-
$key = \get_option( 'activitypub_application_user_public_key' );
59-
60-
return $key;
61-
}
62-
63-
/**
64-
* @param int $user_id
65-
*
66-
* @return mixed
67-
*/
68-
public function get__private_key() {
69-
$key = \get_option( 'activitypub_application_user_private_key' );
70-
71-
if ( $key ) {
72-
return $key;
73-
}
74-
75-
$this->generate_key_pair();
76-
77-
return \get_option( 'activitypub_application_user_private_key' );
78-
}
79-
80-
private function generate_key_pair() {
81-
$key_pair = Signature::generate_key_pair();
82-
83-
if ( ! is_wp_error( $key_pair ) ) {
84-
\update_option( 'activitypub_application_user_public_key', $key_pair['public_key'] );
85-
\update_option( 'activitypub_application_user_private_key', $key_pair['private_key'] );
86-
}
87-
}
88-
8949
public function get_followers() {
9050
return null;
9151
}

includes/model/class-blog-user.php

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -187,48 +187,6 @@ public function get_published() {
187187
return \gmdate( 'Y-m-d\TH:i:s\Z', $time );
188188
}
189189

190-
public function get__public_key() {
191-
$key = \get_option( 'activitypub_blog_user_public_key' );
192-
193-
if ( $key ) {
194-
return $key;
195-
}
196-
197-
$this->generate_key_pair();
198-
199-
$key = \get_option( 'activitypub_blog_user_public_key' );
200-
201-
return $key;
202-
}
203-
204-
/**
205-
* Get the User-Private-Key.
206-
*
207-
* @param int $user_id
208-
*
209-
* @return mixed
210-
*/
211-
public function get__private_key() {
212-
$key = \get_option( 'activitypub_blog_user_private_key' );
213-
214-
if ( $key ) {
215-
return $key;
216-
}
217-
218-
$this->generate_key_pair();
219-
220-
return \get_option( 'activitypub_blog_user_private_key' );
221-
}
222-
223-
private function generate_key_pair() {
224-
$key_pair = Signature::generate_key_pair();
225-
226-
if ( ! is_wp_error( $key_pair ) ) {
227-
\update_option( 'activitypub_blog_user_public_key', $key_pair['public_key'] );
228-
\update_option( 'activitypub_blog_user_private_key', $key_pair['private_key'] );
229-
}
230-
}
231-
232190
public function get_attachment() {
233191
return array();
234192
}

includes/model/class-user.php

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -159,53 +159,10 @@ public function get_public_key() {
159159
return array(
160160
'id' => $this->get_id() . '#main-key',
161161
'owner' => $this->get_id(),
162-
'publicKeyPem' => $this->get__public_key(),
162+
'publicKeyPem' => Signature::get_public_key_for( $this->get__id() ),
163163
);
164164
}
165165

166-
/**
167-
* @param int $this->get__id()
168-
*
169-
* @return mixed
170-
*/
171-
public function get__public_key() {
172-
$key = \get_user_meta( $this->get__id(), 'magic_sig_public_key', true );
173-
174-
if ( $key ) {
175-
return $key;
176-
}
177-
178-
$this->generate_key_pair();
179-
180-
return \get_user_meta( $this->get__id(), 'magic_sig_public_key', true );
181-
}
182-
183-
/**
184-
* @param int $this->get__id()
185-
*
186-
* @return mixed
187-
*/
188-
public function get__private_key() {
189-
$key = \get_user_meta( $this->get__id(), 'magic_sig_private_key', true );
190-
191-
if ( $key ) {
192-
return $key;
193-
}
194-
195-
$this->generate_key_pair();
196-
197-
return \get_user_meta( $this->get__id(), 'magic_sig_private_key', true );
198-
}
199-
200-
private function generate_key_pair() {
201-
$key_pair = Signature::generate_key_pair();
202-
203-
if ( ! is_wp_error( $key_pair ) ) {
204-
\update_user_meta( $this->get__id(), 'magic_sig_public_key', $key_pair['public_key'], true );
205-
\update_user_meta( $this->get__id(), 'magic_sig_private_key', $key_pair['private_key'], true );
206-
}
207-
}
208-
209166
/**
210167
* Returns the Inbox-API-Endpoint.
211168
*

tests/test-class-activitypub-rest-post-signature-verification.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function test_activity_signature() {
4545

4646
$user = Activitypub\Collection\Users::get_by_id( 1 );
4747

48-
$public_key = $user->get__public_key();
48+
$public_key = Activitypub\Signature::get_public_key_for( $user->get__id() );
4949

5050
// signature_verification
5151
$verified = \openssl_verify( $signed_data, $signature_block['signature'], $public_key, 'rsa-sha256' ) > 0;
@@ -57,7 +57,7 @@ public function test_rest_activity_signature() {
5757
'pre_get_remote_metadata_by_actor',
5858
function( $json, $actor ) {
5959
$user = Activitypub\Collection\Users::get_by_id( 1 );
60-
$public_key = $user->get__public_key();
60+
$public_key = Activitypub\Signature::get_public_key_for( $user->get__id() );
6161
// return ActivityPub Profile with signature
6262
return array(
6363
'id' => $actor,

0 commit comments

Comments
 (0)