2021-10-19 13:48:50 +01:00
< ? php
2021-12-04 04:07:08 +00:00
2021-12-26 09:48:16 +00:00
declare ( strict_types = 1 );
2021-12-04 04:07:08 +00:00
// {{{ License
2021-10-19 13:48:50 +01:00
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
2021-12-04 04:07:08 +00:00
// }}}
2021-10-19 13:48:50 +01:00
/**
* ActivityPub implementation for GNU social
*
* @ package GNUsocial
2021-12-04 04:07:08 +00:00
* @ category ActivityPub
2021-12-26 09:48:16 +00:00
*
2021-10-19 13:48:50 +01:00
* @ author Diogo Peralta Cordeiro <@ diogo . site >
* @ copyright 2018 - 2019 , 2021 Free Software Foundation , Inc http :// www . fsf . org
* @ license https :// www . gnu . org / licenses / agpl . html GNU AGPL v3 or later
*/
2021-12-04 04:07:08 +00:00
namespace Plugin\ActivityPub\Entity ;
2022-03-27 15:19:09 +01:00
use App\Core\DB ;
2021-12-04 04:07:08 +00:00
use App\Core\Entity ;
use App\Core\Log ;
use App\Entity\Actor ;
use App\Util\Exception\ServerException ;
use DateTimeInterface ;
2022-03-01 21:20:24 +00:00
use Exception ;
use Plugin\ActivityPub\Util\Explorer ;
2021-12-04 04:07:08 +00:00
2021-10-19 13:48:50 +01:00
/**
* ActivityPub Keys System
*
2021-12-04 04:07:08 +00:00
* @ copyright 2018 - 2019 , 2021 Free Software Foundation , Inc http :// www . fsf . org
2021-10-19 13:48:50 +01:00
* @ license https :// www . gnu . org / licenses / agpl . html GNU AGPL v3 or later
*/
class ActivitypubRsa extends Entity
{
// {{{ Autocode
// @codeCoverageIgnoreStart
private int $actor_id ;
2021-12-26 21:32:09 +00:00
private ? string $private_key = null ;
2021-10-19 13:48:50 +01:00
private string $public_key ;
private DateTimeInterface $created ;
private DateTimeInterface $modified ;
public function setActorId ( int $actor_id ) : self
{
$this -> actor_id = $actor_id ;
return $this ;
}
2021-12-26 15:12:06 +00:00
public function getActorId () : int
2021-10-19 13:48:50 +01:00
{
2021-12-26 15:12:06 +00:00
return $this -> actor_id ;
2021-10-19 13:48:50 +01:00
}
2021-12-26 15:12:06 +00:00
public function setPrivateKey ( ? string $private_key ) : self
2021-10-19 13:48:50 +01:00
{
$this -> private_key = $private_key ;
return $this ;
}
2021-12-26 15:12:06 +00:00
public function getPrivateKey () : ? string
2021-10-19 13:48:50 +01:00
{
2021-12-26 15:12:06 +00:00
return $this -> private_key ;
2021-10-19 13:48:50 +01:00
}
public function setPublicKey ( string $public_key ) : self
{
$this -> public_key = $public_key ;
return $this ;
}
2021-12-26 15:12:06 +00:00
public function getPublicKey () : string
2021-10-19 13:48:50 +01:00
{
2021-12-26 15:12:06 +00:00
return $this -> public_key ;
2021-10-19 13:48:50 +01:00
}
public function setCreated ( DateTimeInterface $created ) : self
{
$this -> created = $created ;
return $this ;
}
2021-12-26 15:12:06 +00:00
public function getCreated () : DateTimeInterface
2021-10-19 13:48:50 +01:00
{
2021-12-26 15:12:06 +00:00
return $this -> created ;
2021-10-19 13:48:50 +01:00
}
public function setModified ( DateTimeInterface $modified ) : self
{
$this -> modified = $modified ;
return $this ;
}
2021-12-26 15:12:06 +00:00
public function getModified () : DateTimeInterface
{
return $this -> modified ;
}
2021-10-19 13:48:50 +01:00
// @codeCoverageIgnoreEnd
// }}} Autocode
/**
* Return table definition for Schema setup and Entity usage .
*
* @ return array array of column definitions
*/
public static function schemaDef () : array
{
return [
2021-12-26 09:48:16 +00:00
'name' => 'activitypub_rsa' ,
2021-10-19 13:48:50 +01:00
'fields' => [
2021-12-26 09:48:16 +00:00
'actor_id' => [ 'type' => 'int' , 'not null' => true ],
2021-10-19 13:48:50 +01:00
'private_key' => [ 'type' => 'text' ],
2021-12-26 09:48:16 +00:00
'public_key' => [ 'type' => 'text' , 'not null' => true ],
'created' => [ 'type' => 'datetime' , 'not null' => true , 'default' => 'CURRENT_TIMESTAMP' , 'description' => 'date this record was created' ],
'modified' => [ 'type' => 'timestamp' , 'not null' => true , 'default' => 'CURRENT_TIMESTAMP' , 'description' => 'date this record was modified' ],
2021-10-19 13:48:50 +01:00
],
2021-12-26 09:48:16 +00:00
'primary key' => [ 'actor_id' ],
2021-10-19 13:48:50 +01:00
'foreign keys' => [
'activitypub_rsa_actor_id_fkey' => [ 'actor' , [ 'actor_id' => 'id' ]],
],
];
}
/**
* Guarantees RSA keys for a given actor .
*
2021-12-04 04:07:08 +00:00
* @ param bool $fetch = true Should attempt to fetch keys from a remote profile ?
2021-12-26 09:48:16 +00:00
*
2021-10-19 13:48:50 +01:00
* @ throws ServerException It should never occur , but if so , we break everything !
2021-12-26 09:48:16 +00:00
*
* @ return ActivitypubRsa The keys ( private key is null for remote actors )
2021-10-19 13:48:50 +01:00
*/
public static function getByActor ( Actor $gsactor , bool $fetch = true ) : self
{
2022-02-18 17:48:06 +00:00
$apRSA = DB :: findOneBy ( self :: class , [ 'actor_id' => ( $actor_id = $gsactor -> getId ())], return_null : true );
2021-12-26 09:48:16 +00:00
if ( \is_null ( $apRSA )) {
2021-10-19 13:48:50 +01:00
// Nonexistent key pair for this profile
2021-11-16 23:24:06 +00:00
if ( $gsactor -> getIsLocal ()) {
2021-10-19 13:48:50 +01:00
self :: generateKeys ( $private_key , $public_key );
2021-11-30 16:47:31 +00:00
$apRSA = self :: create ([
2021-12-26 09:48:16 +00:00
'actor_id' => $actor_id ,
2021-11-30 16:47:31 +00:00
'private_key' => $private_key ,
2021-12-26 09:48:16 +00:00
'public_key' => $public_key ,
2021-11-30 16:47:31 +00:00
]);
2022-03-01 21:20:24 +00:00
DB :: wrapInTransaction ( fn () => DB :: persist ( $apRSA ));
2021-11-16 23:24:06 +00:00
} else {
2021-10-19 13:48:50 +01:00
// ASSERT: This should never happen, but try to recover!
2022-03-01 21:20:24 +00:00
Log :: error ( 'Activitypub_rsa: It seems that the RSA key for this remote actor was somehow lost. That should have never happened!' );
2021-10-19 13:48:50 +01:00
if ( $fetch ) {
2022-03-01 21:20:24 +00:00
try {
$res = Explorer :: getRemoteActorActivity ( $gsactor -> getUri ());
if ( \is_null ( $res )) {
throw new ServerException ( 'Activitypub_rsa: Failed to find keys for given profile. That should have not happened! Invalid remote actor (null response).' );
}
DB :: wrapInTransaction ( fn () => DB :: persist ( self :: create ([
'actor_id' => $gsactor -> getId (),
'public_key' => json_decode ( $res , associative : true )[ 'publicKey' ][ 'publicKeyPem' ],
])));
return self :: getByActor ( $gsactor , false );
} catch ( Exception $e ) {
throw new ServerException ( 'Activitypub_rsa: Failed to find keys for given profile. That should have not happened!' , previous : $e );
}
2021-10-19 13:48:50 +01:00
} else {
throw new ServerException ( 'Activitypub_rsa: Failed to find keys for given profile. That should have not happened!' );
}
}
}
return $apRSA ;
}
/**
* Generates a pair of RSA keys .
*
2021-12-26 09:48:16 +00:00
* @ param null | string $private_key out
* @ param null | string $public_key out
*
2021-10-19 13:48:50 +01:00
* @ author PHP Manual Contributed Notes < dirt @ awoms . com >
*/
private static function generateKeys ( ? string & $private_key , ? string & $public_key ) : void
{
$config = [
2021-12-26 09:48:16 +00:00
'digest_alg' => 'sha512' ,
2021-10-19 13:48:50 +01:00
'private_key_bits' => 2048 ,
2021-12-26 09:48:16 +00:00
'private_key_type' => \OPENSSL_KEYTYPE_RSA ,
2021-10-19 13:48:50 +01:00
];
// Create the private and public key
$res = openssl_pkey_new ( $config );
// Extract the private key from $res to $private_key
openssl_pkey_export ( $res , $private_key );
// Extract the public key from $res to $pubKey
2021-12-26 09:48:16 +00:00
$pubKey = openssl_pkey_get_details ( $res );
$public_key = $pubKey [ 'key' ];
2021-10-19 13:48:50 +01:00
unset ( $pubKey );
}
}