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;
* @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)) {
} 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);
}
}
*
* @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
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;
}
/**
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
*/
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;
}
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 {
}
// 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));