2019-05-11 12:27:21 +01:00
< ? php
2020-08-22 19:14:36 +01:00
// {{{ License
2019-05-11 12:27:21 +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/>.
2020-08-22 19:14:36 +01:00
// }}}
2019-05-11 12:27:21 +01:00
/**
2020-08-22 19:14:36 +01:00
* ActivityPub Assymetric Key Storage System
2019-05-11 12:27:21 +01:00
*
* @ package GNUsocial
2020-08-22 19:14:36 +01:00
*
2019-05-11 12:27:21 +01:00
* @ author Diogo Cordeiro < diogo @ fc . up . pt >
2021-02-19 23:29:43 +00:00
* @ author Hugo Sales < hugo @ hsal . es >
2019-05-11 12:27:21 +01:00
* @ copyright 2018 - 2019 Free Software Foundation , Inc http :// www . fsf . org
* @ license https :// www . gnu . org / licenses / agpl . html GNU AGPL v3 or later
*/
2020-08-22 19:14:36 +01:00
namespace Plugin\ActivityPub\Entity ;
2019-05-11 12:27:21 +01:00
2020-08-22 19:14:36 +01:00
class ActivityPubCryptKey
2019-05-11 12:27:21 +01:00
{
2020-08-22 19:14:36 +01:00
// {{{ Autocode
2020-09-08 00:47:09 +01:00
private int $gsactor_id ;
private ? string $private_key ;
private string $public_key ;
private ? DateTimeInterface $created ;
private DateTimeInterface $modified ;
public function setGsactorId ( int $gsactor_id ) : self
{
$this -> gsactor_id = $gsactor_id ;
return $this ;
}
public function getGsactorId () : int
{
return $this -> gsactor_id ;
}
public function setPrivateKey ( ? string $private_key ) : self
{
$this -> private_key = $private_key ;
return $this ;
}
public function getPrivateKey () : ? string
{
return $this -> private_key ;
}
public function setPublicKey ( string $public_key ) : self
{
$this -> public_key = $public_key ;
return $this ;
}
public function getPublicKey () : string
{
return $this -> public_key ;
}
public function setCreated ( ? DateTimeInterface $created ) : self
{
$this -> created = $created ;
return $this ;
}
public function getCreated () : ? DateTimeInterface
{
return $this -> created ;
}
public function setModified ( DateTimeInterface $modified ) : self
{
$this -> modified = $modified ;
return $this ;
}
public function getModified () : DateTimeInterface
{
return $this -> modified ;
}
2020-08-22 19:14:36 +01:00
// }}} Autocode
2019-05-11 12:27:21 +01:00
2019-10-11 17:08:37 +01:00
/**
* Private key getter
*
* @ param Profile $profile
2020-08-22 19:14:36 +01:00
*
2019-12-10 22:52:28 +00:00
* @ throws Exception Throws exception if tries to fetch a private key of an actor we don ' t own
2020-08-22 19:14:36 +01:00
*
* @ return string The private key
2019-10-11 17:08:37 +01:00
*/
public function get_private_key ( Profile $profile ) : string
2019-05-11 12:27:21 +01:00
{
$this -> profile_id = $profile -> getID ();
2020-08-22 19:14:36 +01:00
$apRSA = self :: getKV ( 'profile_id' , $this -> profile_id );
2019-05-11 12:27:21 +01:00
if ( ! $apRSA instanceof Activitypub_rsa ) {
2019-12-10 22:52:28 +00:00
// Nonexistent key pair for this profile
2019-05-11 12:27:21 +01:00
if ( $profile -> isLocal ()) {
self :: generate_keys ( $this -> private_key , $this -> public_key );
$this -> store_keys ();
2019-07-08 19:23:48 +01:00
$apRSA -> private_key = $this -> private_key ;
2019-05-11 12:27:21 +01:00
} else {
throw new Exception ( 'This is a remote Profile, there is no Private Key for this Profile.' );
}
}
return $apRSA -> private_key ;
}
/**
* Guarantees a Public Key for a given profile .
*
* @ param Profile $profile
2020-08-22 19:14:36 +01:00
* @ param bool $fetch = true Should attempt to fetch keys from a remote profile ?
*
2019-05-11 12:27:21 +01:00
* @ throws ServerException It should never occur , but if so , we break everything !
2019-10-11 17:08:37 +01:00
* @ throws Exception
2020-08-22 19:14:36 +01:00
*
* @ return string The public key
*
2019-05-11 12:27:21 +01:00
* @ author Diogo Cordeiro < diogo @ fc . up . pt >
*/
2019-10-11 17:08:37 +01:00
public function ensure_public_key ( Profile $profile , bool $fetch = true ) : string
2019-05-11 12:27:21 +01:00
{
$this -> profile_id = $profile -> getID ();
2020-08-22 19:14:36 +01:00
$apRSA = self :: getKV ( 'profile_id' , $this -> profile_id );
2019-05-11 12:27:21 +01:00
if ( ! $apRSA instanceof Activitypub_rsa ) {
// No existing key pair for this profile
if ( $profile -> isLocal ()) {
self :: generate_keys ( $this -> private_key , $this -> public_key );
$this -> store_keys ();
2019-07-08 19:23:48 +01:00
$apRSA -> public_key = $this -> public_key ;
2019-05-11 12:27:21 +01:00
} else {
2019-11-16 15:32:49 +00:00
// ASSERT: This should never happen, but try to recover!
2020-08-22 19:14:36 +01:00
common_log ( LOG_ERR , 'Activitypub_rsa: An impossible thing has happened... Please let the devs know that it entered in line 116 at Activitypub_rsa.php' );
2019-05-11 12:27:21 +01:00
if ( $fetch ) {
2019-12-10 22:27:32 +00:00
$res = Activitypub_explorer :: get_remote_user_activity ( $profile -> getUri ());
2019-05-11 12:27:21 +01:00
Activitypub_rsa :: update_public_key ( $profile , $res [ 'publicKey' ][ 'publicKeyPem' ]);
return self :: ensure_public_key ( $profile , false );
} else {
throw new ServerException ( 'Activitypub_rsa: Failed to find keys for given profile. That should have not happened!' );
}
}
}
return $apRSA -> public_key ;
}
/**
* Insert the current object variables into the database .
*
2019-10-11 17:08:37 +01:00
* @ throws ServerException
2020-08-22 19:14:36 +01:00
*
2019-05-11 12:27:21 +01:00
* @ author Diogo Cordeiro < diogo @ fc . up . pt >
*/
2019-10-11 17:08:37 +01:00
public function store_keys () : void
2019-05-11 12:27:21 +01:00
{
$this -> created = $this -> modified = common_sql_now ();
2020-08-22 19:14:36 +01:00
$ok = $this -> insert ();
2019-05-11 12:27:21 +01:00
if ( $ok === false ) {
throw new ServerException ( 'Cannot save ActivityPub RSA.' );
}
}
/**
* Generates a pair of RSA keys .
*
2019-11-16 15:32:49 +00:00
* @ param string $private_key out
2020-08-22 19:14:36 +01:00
* @ param string $public_key out
*
2019-10-11 17:08:37 +01:00
* @ author PHP Manual Contributed Notes < dirt @ awoms . com >
2019-05-11 12:27:21 +01:00
*/
2019-11-16 15:32:49 +00:00
public static function generate_keys ( ? string & $private_key , ? string & $public_key ) : void
2019-05-11 12:27:21 +01:00
{
$config = [
2020-08-22 19:14:36 +01:00
'digest_alg' => 'sha512' ,
2019-05-11 12:27:21 +01:00
'private_key_bits' => 2048 ,
'private_key_type' => OPENSSL_KEYTYPE_RSA ,
];
// 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
2020-08-22 19:14:36 +01:00
$pubKey = openssl_pkey_get_details ( $res );
$public_key = $pubKey [ 'key' ];
2019-05-11 12:27:21 +01:00
unset ( $pubKey );
}
/**
* Update public key .
*
2020-08-22 19:14:36 +01:00
* @ param Activitypub_profile | Profile $profile
* @ param string $public_key
*
2019-05-11 12:27:21 +01:00
* @ throws Exception
2020-08-22 19:14:36 +01:00
*
2019-05-11 12:27:21 +01:00
* @ author Diogo Cordeiro < diogo @ fc . up . pt >
*/
2019-10-11 17:08:37 +01:00
public static function update_public_key ( $profile , string $public_key ) : void
2019-05-11 12:27:21 +01:00
{
// Public Key
2020-08-22 19:14:36 +01:00
$apRSA = new Activitypub_rsa ();
2019-05-11 12:27:21 +01:00
$apRSA -> profile_id = $profile -> getID ();
$apRSA -> public_key = $public_key ;
2020-08-22 19:14:36 +01:00
$apRSA -> created = common_sql_now ();
2019-05-11 12:27:21 +01:00
if ( ! $apRSA -> update ()) {
$apRSA -> insert ();
}
}
2020-08-22 19:14:36 +01:00
public static function schemaDef ()
{
return [
'name' => 'activitypub_crypt_key' ,
'description' => 'assymetric key storage for activitypub' ,
'fields' => [
'gsactor_id' => [ 'type' => 'int' , 'not null' => true ],
'private_key' => [ 'type' => 'text' ],
'public_key' => [ 'type' => 'text' , 'not null' => true ],
'created' => [ 'type' => 'datetime' , 'description' => 'date this record was created' ],
'modified' => [ 'type' => 'timestamp' , 'not null' => true , 'description' => 'date this record was modified' ],
],
'primary key' => [ 'gsactor_id' ],
'foreign keys' => [
'activitypub_rsa_gsactor_id_fkey' => [ 'gsactor' , [ 'gsactor_id' => 'id' ]],
],
];
}
2019-05-11 12:27:21 +01:00
}