Move around some code related to Magic_envelope and signing

This commit is contained in:
Mikael Nordfeldth 2015-10-03 23:25:02 +02:00
parent aab7667cd5
commit 30a4393afa
3 changed files with 57 additions and 21 deletions

View File

@ -32,6 +32,8 @@ class MagicEnvelope
const NS = 'http://salmon-protocol.org/ns/magic-env'; 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 = null; // When stored here it is _always_ base64url encoded
protected $data_type = null; protected $data_type = null;
protected $encoding = null; protected $encoding = null;
@ -48,7 +50,7 @@ class MagicEnvelope
* @fixme may give fatal errors if some elements are missing or invalid XML * @fixme may give fatal errors if some elements are missing or invalid XML
* @fixme calling DOMDocument::loadXML statically triggers warnings in strict mode * @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)) { if (!empty($xml)) {
$dom = new DOMDocument(); $dom = new DOMDocument();
if (!$dom->loadXML($xml)) { if (!$dom->loadXML($xml)) {
@ -56,6 +58,15 @@ class MagicEnvelope
} elseif (!$this->fromDom($dom)) { } elseif (!$this->fromDom($dom)) {
throw new ServerException('Could not load MagicEnvelope from 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 * @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); assert($magicsig->privateKey instanceof Crypt_RSA);
// Prepare text and metadata for signing // Prepare text and metadata for signing
@ -290,7 +314,12 @@ class MagicEnvelope
return false; 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; 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, * Encode the given string as a signed MagicEnvelope XML document,
* using the keypair for the given local user profile. We can of * 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) public static function signAsUser($text, User $user)
{ {
// Find already stored key $magic_env = new MagicEnvelope(null, $user->getProfile());
$magicsig = Magicsig::getKV('user_id', $user->id); $magic_env->signMessage($text, 'application/atom+xml');
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);
return $magic_env; return $magic_env;
} }

View File

@ -55,12 +55,13 @@ class Salmon
try { try {
$magic_env = MagicEnvelope::signAsUser($xml, $user); $magic_env = MagicEnvelope::signAsUser($xml, $user);
$envxml = $magic_env->toXML();
} catch (Exception $e) { } catch (Exception $e) {
common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage()); common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage());
return false; return false;
} }
$envxml = $magic_env->toXML();
$headers = array('Content-Type: application/magic-envelope+xml'); $headers = array('Content-Type: application/magic-envelope+xml');
try { try {
@ -73,8 +74,10 @@ class Salmon
} }
// Diaspora wants a slightly different formatting on the POST (other Content-type, so body needs "xml=") // 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) { 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())); $user->id, $endpoint_uri, $response->getStatus(), $response->getBody()));
$headers = array('Content-Type: application/x-www-form-urlencoded'); $headers = array('Content-Type: application/x-www-form-urlencoded');
$client->setBody('xml=' . Magicsig::base64_url_encode($envxml)); $client->setBody('xml=' . Magicsig::base64_url_encode($envxml));

View File

@ -79,12 +79,8 @@ class SalmonAction extends Action
$this->clientError($e->getMessage()); $this->clientError($e->getMessage());
} }
// Cryptographic verification test // Cryptographic verification test, throws exception on failure
if (!$magic_env->verify($this->actor)) { $magic_env->verify($this->actor);
common_log(LOG_DEBUG, "Salmon signature verification failed.");
// TRANS: Client error.
$this->clientError(_m('Salmon signature verification failed.'));
}
return true; return true;
} }