diff --git a/plugins/OStatus/lib/magicenvelope.php b/plugins/OStatus/lib/magicenvelope.php index 6d3956cb9b..384506280d 100644 --- a/plugins/OStatus/lib/magicenvelope.php +++ b/plugins/OStatus/lib/magicenvelope.php @@ -80,6 +80,20 @@ class MagicEnvelope 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']))); + } /** * @@ -93,14 +107,17 @@ class MagicEnvelope { $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; } /** @@ -239,7 +256,7 @@ class MagicEnvelope return false; } - return $verifier->verify($env['data'], $env['sig']); + return $verifier->verify($this->signingText($env), $env['sig']); } /** @@ -290,3 +307,24 @@ class MagicEnvelope ); } } + +/** + * 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']; + } +} + diff --git a/plugins/OStatus/tests/MagicEnvelopeTest.php b/plugins/OStatus/tests/MagicEnvelopeTest.php new file mode 100644 index 0000000000..5ee0362d28 --- /dev/null +++ b/plugins/OStatus/tests/MagicEnvelopeTest.php @@ -0,0 +1,60 @@ +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'"); + } + +}