throw new Exception(_m('Unable to locate signer public key.'));
}
+ /**
+ * The current MagicEnvelope spec as used in StatusNet 0.9.7 and later
+ * includes both the original data and some signing metadata fields as
+ * the input plaintext for the signature hash.
+ *
+ * @param array $env
+ * @return string
+ */
+ public function signingText($env) {
+ return implode('.', array($env['data'], // this field is pre-base64'd
+ Magicsig::base64_url_encode($env['data_type']),
+ Magicsig::base64_url_encode($env['encoding']),
+ Magicsig::base64_url_encode($env['alg'])));
+ }
/**
*
{
$signature_alg = Magicsig::fromString($keypair);
$armored_text = Magicsig::base64_url_encode($text);
-
- return array(
+ $env = array(
'data' => $armored_text,
'encoding' => MagicEnvelope::ENCODING,
'data_type' => $mimetype,
- 'sig' => $signature_alg->sign($armored_text),
+ 'sig' => '',
'alg' => $signature_alg->getName()
);
+
+ $env['sig'] = $signature_alg->sign($this->signingText($env));
+
+ return $env;
}
/**
return false;
}
- return $verifier->verify($env['data'], $env['sig']);
+ return $verifier->verify($this->signingText($env), $env['sig']);
}
/**
);
}
}
+
+/**
+ * Variant of MagicEnvelope using the earlier signature form listed in the MagicEnvelope
+ * spec in early 2010; this was used in StatusNet up through 0.9.6, so for backwards compatiblity
+ * we still need to accept and sometimes send this format.
+ */
+class MagicEnvelopeCompat extends MagicEnvelope {
+
+ /**
+ * StatusNet through 0.9.6 used an earlier version of the MagicEnvelope spec
+ * which used only the input data, without the additional fields, as the plaintext
+ * for signing.
+ *
+ * @param array $env
+ * @return string
+ */
+ public function signingText($env) {
+ return $env['data'];
+ }
+}
+
--- /dev/null
+<?php
+
+if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
+ print "This script must be run from the command line\n";
+ exit();
+}
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
+define('STATUSNET', true);
+
+require_once INSTALLDIR . '/lib/common.php';
+
+class MagicEnvelopeTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Test that MagicEnvelope builds the correct plaintext for signing.
+ * @dataProvider provider
+ */
+ public function testSignatureText($env, $expected)
+ {
+ $magic = new MagicEnvelope;
+ $text = $magic->signingText($env);
+
+ $this->assertEquals($expected, $text, "'$text' should be '$expected'");
+ }
+
+ static public function provider()
+ {
+ return array(
+ array(
+ // Sample case given in spec:
+ // http://salmon-protocol.googlecode.com/svn/trunk/draft-panzer-magicsig-00.html#signing
+ array(
+ 'data' => 'Tm90IHJlYWxseSBBdG9t',
+ 'data_type' => 'application/atom+xml',
+ 'encoding' => 'base64url',
+ 'alg' => 'RSA-SHA256'
+ ),
+ 'Tm90IHJlYWxseSBBdG9t.YXBwbGljYXRpb24vYXRvbSt4bWw=.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='
+ )
+ );
+ }
+
+
+ /**
+ * Test that MagicEnvelope builds the correct plaintext for signing.
+ * @dataProvider provider
+ */
+ public function testSignatureTextCompat($env, $expected)
+ {
+ // Our old code didn't add the extra fields, just used the armored text.
+ $alt = $env['data'];
+
+ $magic = new MagicEnvelopeCompat;
+ $text = $magic->signingText($env);
+
+ $this->assertEquals($alt, $text, "'$text' should be '$alt'");
+ }
+
+}