diff --git a/PharUtil/RemotePharVerifier.php b/PharUtil/RemotePharVerifier.php index 24e909c..113eb58 100644 --- a/PharUtil/RemotePharVerifier.php +++ b/PharUtil/RemotePharVerifier.php @@ -160,13 +160,14 @@ protected function assertVerified($local_path) { if (!copy($this->pub_key_file, $this->getPubkeyFilename($local_path))) { throw new RuntimeException("Error copying public key file!"); } - try { - $this->verifyPharSignature($local_path); - } catch (Exception $e) { - $this->doDelete($local_path); // delete offending files - throw $e; - } } + try { + $this->verifyPharSignature($local_path); + } catch (Exception $e) { + $this->doDelete($local_path); // delete offending files + throw $e; + } + return true; } @@ -228,7 +229,7 @@ protected function verifyPharSignature($local_path) { $sig = $phar->getSignature(); unset($phar); - if ($sig['hash_type'] !== 'OpenSSL') { + if ($this->pub_key_file && $sig['hash_type'] !== 'OpenSSL') { throw new PharUtil_SignatureVerificationException("This phar is not signed with OpenSSL!"); } } catch (UnexpectedValueException $e) { diff --git a/package.xml b/package.xml index 1b61f7d..700c162 100644 --- a/package.xml +++ b/package.xml @@ -16,7 +16,7 @@ 2010-08-06 - 0.2.0 + 0.3.0 0.1 @@ -49,6 +49,7 @@ + @@ -95,6 +96,15 @@ + + + + + + + + + @@ -132,18 +142,35 @@ + + + + + + + 0.3 + 0.1 + + + beta + beta + + 2010-08-06 + MIT + Added phar-verify toolset, small changes + 0.2 0.1 diff --git a/phar-build.php b/phar-build.php index eff3054..f760c96 100644 --- a/phar-build.php +++ b/phar-build.php @@ -20,7 +20,6 @@ 'name' => 'phar-build', )); -// add an option to make the program verbose $parser->addOption('src', array( 'short_name' => '-s', 'long_name' => '--src', diff --git a/phar-extract.php b/phar-extract.php index 28e6229..8054a27 100644 --- a/phar-extract.php +++ b/phar-extract.php @@ -38,7 +38,6 @@ 'description' => "Input Phar archive filename e.g. phar.phar", )); -// add an option to make the program verbose $parser->addArgument('destination', array( 'action' => 'StoreString', 'description' => "Destination directory" @@ -104,7 +103,7 @@ throw new Exception("Phar writing support is disabled in this PHP installation, set phar.readonly=0 in php.ini!"); } echo "Extracting {$files_count} file(s) to: {$args['destination']}..." . PHP_EOL; - $phar->ExtractTo($args['destination'], null, true); + $phar->extractTo($args['destination'], null, true); } if ($options['public']) { diff --git a/phar-verify.bat b/phar-verify.bat new file mode 100644 index 0000000..e644aad --- /dev/null +++ b/phar-verify.bat @@ -0,0 +1,8 @@ +@ECHO OFF +if "%PHPBIN%" == "" set PHPBIN="@php_bin@" +if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH +GOTO RUN +:USE_PEAR_PATH +set PHPBIN="%PHP_PEAR_PHP_BIN%" +:RUN +%PHPBIN% "@bin_dir@\phar-verify" %* diff --git a/phar-verify.php b/phar-verify.php new file mode 100644 index 0000000..336483f --- /dev/null +++ b/phar-verify.php @@ -0,0 +1,94 @@ +#!/usr/bin/env php + + * @package PharUtil + */ + +// Include the Console_CommandLine package. +require_once 'Console/CommandLine.php'; +require_once 'PharUtil/RemotePharVerifier.php'; + +// create the parser +$parser = new Console_CommandLine(array( + 'description' => 'Verify Phar archive signature using a public key file', + 'version' => '@package_version@', + 'name' => 'phar-verify', +)); + +$parser->addOption('public', array( + 'short_name' => '-P', + 'long_name' => '--public', + 'action' => 'StoreString', + 'default' => './cert/pub.pem', + 'description' => "Public key file (PEM) to verify signature\n(./cert/pub.pem by default)" +)); + +$parser->addOption('nosign', array( + 'short_name' => '-n', + 'long_name' => '--ns', + 'action' => 'StoreTrue', + 'description' => 'Archive is not signed, don\'t require an OpenSSL signature' +)); + +$parser->addOption('temp', array( + 'short_name' => '-t', + 'long_name' => '--temp', + 'action' => 'StoreString', + 'description' => 'Temporary directory (' . sys_get_temp_dir() . ' by default)', +)); + +$parser->addArgument('phar', array( + 'action' => 'StoreString', + 'description' => "Input Phar archive URI e.g.\n/path/to/local/phar.phar or http://path/to/remote/phar.phar", +)); + +// run the parser +try { + $result = $parser->parse(); +} catch (Exception $exc) { + $parser->displayError($exc->getMessage()); +} + +$options = $result->options; +$args = $result->args; + +echo $parser->name . ' ' . $parser->version . PHP_EOL . PHP_EOL; + +// validate parameters +if (substr($args['phar'], -5) !== '.phar') { + $parser->displayError("Input Phar must have .phar extension, {$args['phar']} given.", 2); +} + +if ($options['nosign']) { + $options['public'] = null; // no public key +} + +if ($options['public']) { + if (!file_exists($options['public']) || !is_readable($options['public'])) { + $parser->displayError("Public key in '{$options['public']}' does not exist or is not readable.", 4); + } +} + +if (!$options['temp']) { + $options['temp'] = sys_get_temp_dir(); +} + +try { + echo "Verifying Phar archive: {$args['phar']}..." . PHP_EOL; + + $v = new PharUtil_RemotePharVerifier($options['temp'], $options['temp'], $options['public']); + + $v->verify($args['phar']); + + echo "Phar archive successfully verified." . PHP_EOL; +} catch (Exception $e) { + echo "Error: " . $e->getMessage() . PHP_EOL; + exit(1); +} + + +echo PHP_EOL . "All done, exiting." . PHP_EOL; \ No newline at end of file diff --git a/test/PharUtil/RemotePharVerifierTest.php b/test/PharUtil/RemotePharVerifierTest.php index e3bec8c..c7b7069 100644 --- a/test/PharUtil/RemotePharVerifierTest.php +++ b/test/PharUtil/RemotePharVerifierTest.php @@ -104,10 +104,12 @@ public function testFetchCleansUpOnFailure($file) { } $this->fail("PharUtil_SignatureVerificationException was not thrown"); } + public function invalidFiles() { return array( array('wrongsig.phar'), array('nosig.phar'), + array('nosigmodified.phar'), array('modified.phar'), array('test.phar.gz'), ); @@ -120,7 +122,7 @@ public function testModifiedPharsAreInvalid() { $v->fetch($this->remote_dir . 'modified.phar'); } - public function testNoPubkeyChecking() { + public function testNoPubkeyWillPassNotSignedPhars() { // no public key given $v = new PharUtil_RemotePharVerifier($this->fetch_dir, $this->verified_dir, null); @@ -128,20 +130,41 @@ public function testNoPubkeyChecking() { $this->assertFileExists($ok); $this->assertFileEquals($this->remote_dir . 'nosig.phar', $ok); - $ok = $v->fetch($this->remote_dir . 'wrongsig.phar'); - $this->assertFileExists($ok); - $this->assertFileEquals($this->remote_dir . 'wrongsig.phar', $ok); - - $ok = $v->fetch($this->remote_dir . 'modified.phar'); - $this->assertFileExists($ok); - $this->assertFileEquals($this->remote_dir . 'modified.phar', $ok); - // gzips are ok withot pubkey verification $ok = $v->fetch($this->remote_dir . 'test.phar.gz'); $this->assertFileExists($ok); $this->assertFileEquals($this->remote_dir . 'test.phar.gz', $ok); } + /** + * @dataProvider openSslFiles + */ + public function testNoPubkeyWillErrorOnAllSignedPhars($file) { + // no public key given + $v = new PharUtil_RemotePharVerifier($this->fetch_dir, $this->verified_dir, null); + + // this will pass (thankyou Phar :( ) + $this->setExpectedException('PharUtil_SignatureVerificationException'); + $ok = $v->fetch($this->remote_dir . $file); + } + + public function openSslFiles() { + return array( + array('wrongsig.phar'), + array('modified.phar'), + array('test.phar'), + ); + } + + public function testNoPubkeyWillCheckOtherChecksums() { + // no public key given + $v = new PharUtil_RemotePharVerifier($this->fetch_dir, $this->verified_dir, null); + + // simulate transfer error (SHA-1 checksum, but file is modified) + $this->setExpectedException('PharUtil_SignatureVerificationException'); + $ok = $v->fetch($this->remote_dir . 'nosigmodified.phar'); + } + /** * @dataProvider invalidFilenames */ diff --git a/test/PharUtil/data/phar/nosigmodified.phar b/test/PharUtil/data/phar/nosigmodified.phar new file mode 100644 index 0000000..74c4e73 Binary files /dev/null and b/test/PharUtil/data/phar/nosigmodified.phar differ