From 30a4393afab9c426a95da69ecb7805bc00e24de2 Mon Sep 17 00:00:00 2001 From: Mikael Nordfeldth Date: Sat, 3 Oct 2015 23:25:02 +0200 Subject: [PATCH] Move around some code related to Magic_envelope and signing --- plugins/OStatus/lib/magicenvelope.php | 63 +++++++++++++++++++++------ plugins/OStatus/lib/salmon.php | 7 ++- plugins/OStatus/lib/salmonaction.php | 8 +--- 3 files changed, 57 insertions(+), 21 deletions(-) diff --git a/plugins/OStatus/lib/magicenvelope.php b/plugins/OStatus/lib/magicenvelope.php index dfd3abaeab..533cd7d201 100644 --- a/plugins/OStatus/lib/magicenvelope.php +++ b/plugins/OStatus/lib/magicenvelope.php @@ -32,6 +32,8 @@ class MagicEnvelope const NS = 'http://salmon-protocol.org/ns/magic-env'; + protected $actor = null; // Profile of user who has signed the envelope + protected $data = null; // When stored here it is _always_ base64url encoded protected $data_type = null; protected $encoding = null; @@ -48,7 +50,7 @@ class MagicEnvelope * @fixme may give fatal errors if some elements are missing or invalid XML * @fixme calling DOMDocument::loadXML statically triggers warnings in strict mode */ - public function __construct($xml=null) { + public function __construct($xml=null, Profile $actor=null) { if (!empty($xml)) { $dom = new DOMDocument(); if (!$dom->loadXML($xml)) { @@ -56,6 +58,15 @@ class MagicEnvelope } elseif (!$this->fromDom($dom)) { throw new ServerException('Could not load MagicEnvelope from DOM'); } + } elseif ($actor instanceof Profile) { + // So far we only allow setting with _either_ $xml _or_ $actor as that's + // all our circumstances require. But it may be confusing for new developers. + // The idea is that feeding XML must be followed by interpretation and then + // running $magic_env->verify($profile), just as in SalmonAction->prepare(...) + // and supplying an $actor (which right now has to be a User) will require + // defining the $data, $data_type etc. attributes manually afterwards before + // signing the envelope.. + $this->setActor($actor); } } @@ -162,8 +173,21 @@ class MagicEnvelope * * @throws Exception of various kinds on signing failure */ - public function signMessage($text, $mimetype, Magicsig $magicsig) + public function signMessage($text, $mimetype) { + if (!$this->actor instanceof Profile) { + throw new ServerException('No profile to sign message with is set.'); + } elseif (!$this->actor->isLocal()) { + throw new ServerException('Cannot sign magic envelopes with remote users since we have no private key.'); + } + + // Find already stored key + $magicsig = Magicsig::getKV('user_id', $this->actor->getID()); + if (!$magicsig instanceof Magicsig) { + // and if it doesn't exist, it is time to create one! + $magicsig = Magicsig::generate($this->actor->getUser()); + } + assert($magicsig instanceof Magicsig); assert($magicsig->privateKey instanceof Crypt_RSA); // Prepare text and metadata for signing @@ -290,7 +314,12 @@ class MagicEnvelope return false; } - return $magicsig->verify($this->signingText(), $this->getSignature()); + if (!$magicsig->verify($this->signingText(), $this->getSignature())) { + // TRANS: Client error when incoming salmon slap signature does not verify cryptographically. + throw new ClientException(_m('Salmon signature verification failed.')); + } + $this->setActor($profile); + return true; } /** @@ -323,6 +352,22 @@ class MagicEnvelope return true; } + public function setActor(Profile $actor) + { + if ($this->actor instanceof Profile) { + throw new ServerException('Cannot set a new actor profile for MagicEnvelope object.'); + } + $this->actor = $actor; + } + + public function getActor() + { + if (!$this->actor instanceof Profile) { + throw new ServerException('No actor set for this magic envelope.'); + } + return $this->actor; + } + /** * Encode the given string as a signed MagicEnvelope XML document, * using the keypair for the given local user profile. We can of @@ -342,16 +387,8 @@ class MagicEnvelope */ public static function signAsUser($text, User $user) { - // Find already stored key - $magicsig = Magicsig::getKV('user_id', $user->id); - if (!$magicsig instanceof Magicsig) { - $magicsig = Magicsig::generate($user); - } - assert($magicsig instanceof Magicsig); - assert($magicsig->privateKey instanceof Crypt_RSA); - - $magic_env = new MagicEnvelope(); - $magic_env->signMessage($text, 'application/atom+xml', $magicsig); + $magic_env = new MagicEnvelope(null, $user->getProfile()); + $magic_env->signMessage($text, 'application/atom+xml'); return $magic_env; } diff --git a/plugins/OStatus/lib/salmon.php b/plugins/OStatus/lib/salmon.php index 2097ffa77f..f81c9bdc22 100644 --- a/plugins/OStatus/lib/salmon.php +++ b/plugins/OStatus/lib/salmon.php @@ -55,12 +55,13 @@ class Salmon try { $magic_env = MagicEnvelope::signAsUser($xml, $user); - $envxml = $magic_env->toXML(); } catch (Exception $e) { common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage()); return false; } + $envxml = $magic_env->toXML(); + $headers = array('Content-Type: application/magic-envelope+xml'); try { @@ -73,8 +74,10 @@ class Salmon } // Diaspora wants a slightly different formatting on the POST (other Content-type, so body needs "xml=") + // This also gives us the opportunity to send the specially formatted Diaspora salmon slap, which + // encrypts the content of me:data if ($response->getStatus() === 422) { - common_debug(sprintf('Salmon (from profile %d) endpoint %s returned status %s. Diaspora? Will try again! Body: %s', + common_debug(sprintf('Salmon (from profile %d) endpoint %s returned status %s. We assume it is a Diaspora seed, will adapt and try again! Body: %s', $user->id, $endpoint_uri, $response->getStatus(), $response->getBody())); $headers = array('Content-Type: application/x-www-form-urlencoded'); $client->setBody('xml=' . Magicsig::base64_url_encode($envxml)); diff --git a/plugins/OStatus/lib/salmonaction.php b/plugins/OStatus/lib/salmonaction.php index 320ea6cdfa..3cb76ca336 100644 --- a/plugins/OStatus/lib/salmonaction.php +++ b/plugins/OStatus/lib/salmonaction.php @@ -79,12 +79,8 @@ class SalmonAction extends Action $this->clientError($e->getMessage()); } - // Cryptographic verification test - if (!$magic_env->verify($this->actor)) { - common_log(LOG_DEBUG, "Salmon signature verification failed."); - // TRANS: Client error. - $this->clientError(_m('Salmon signature verification failed.')); - } + // Cryptographic verification test, throws exception on failure + $magic_env->verify($this->actor); return true; }