Skip to content

Commit

Permalink
- added phar-verify script
Browse files Browse the repository at this point in the history
- added new tests
- RemotePharVerifier will abort on openssl files in no pubkey mode
- RemotePharVerifier will check other signature methods in no pubkey
mode
  • Loading branch information
koto committed Aug 6, 2010
1 parent 5bb567f commit c8c675f
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 20 deletions.
15 changes: 8 additions & 7 deletions PharUtil/RemotePharVerifier.php
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand Down
29 changes: 28 additions & 1 deletion package.xml
Expand Up @@ -16,7 +16,7 @@
<date>2010-08-06</date>
<time>15:20:37</time>
<version>
<release>0.2.0</release>
<release>0.3.0</release>
<api>0.1</api>
</version>
<stability>
Expand Down Expand Up @@ -49,6 +49,7 @@
<dir name="phar">
<file name="modified.phar" role="test" />
<file name="nosig.phar" role="test" />
<file name="nosigmodified.phar" role="test" />
<file name="test.phar" role="test" />
<file name="test.phar.gz" role="test" />
<file name="wrongsig.phar" role="test" />
Expand Down Expand Up @@ -95,6 +96,15 @@
<tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
<tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
</file>
<file name="phar-verify.php" role="script">
<tasks:replace from="/usr/bin/env php" to="php_bin" type="pear-config"/>
<tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="phar-verify.bat" role="script">
<tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
<tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
</file>
<file name="phar-generate-cert" role="script" />
</dir>
</contents>
Expand Down Expand Up @@ -132,18 +142,35 @@
<install as="phar-build.bat" name="phar-build.bat" />
<install as="phar-extract" name="phar-extract.php" />
<install as="phar-extract.bat" name="phar-extract.bat" />
<install as="phar-verify" name="phar-verify.php" />
<install as="phar-verify.bat" name="phar-verify.bat" />
</filelist>
</phprelease>
<phprelease>
<filelist>
<install as="phar-build" name="phar-build.php" />
<install as="phar-extract" name="phar-extract.php" />
<install as="phar-verify" name="phar-verify.php" />
<ignore name="phar-build.bat" />
<ignore name="phar-extract.bat" />
<ignore name="phar-verify.bat" />
</filelist>
</phprelease>
<changelog>
<release>
<release>
<version>
<release>0.3</release>
<api>0.1</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<date>2010-08-06</date>
<license uri="http://www.opensource.org/licenses/mit-license.html">MIT</license>
<notes>Added phar-verify toolset, small changes</notes>
</release>
<version>
<release>0.2</release>
<api>0.1</api>
Expand Down
1 change: 0 additions & 1 deletion phar-build.php
Expand Up @@ -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',
Expand Down
3 changes: 1 addition & 2 deletions phar-extract.php
Expand Up @@ -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"
Expand Down Expand Up @@ -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']) {
Expand Down
8 changes: 8 additions & 0 deletions 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" %*
94 changes: 94 additions & 0 deletions phar-verify.php
@@ -0,0 +1,94 @@
#!/usr/bin/env php
<?php
/**
* Verify Phar archive signature using a public key file
*
* This file is part of the PharUtil library.
* @author Krzysztof Kotowicz <kkotowicz at gmail dot com>
* @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;
41 changes: 32 additions & 9 deletions test/PharUtil/RemotePharVerifierTest.php
Expand Up @@ -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'),
);
Expand All @@ -120,28 +122,49 @@ 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);

$ok = $v->fetch($this->remote_dir . 'nosig.phar');
$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
*/
Expand Down
Binary file added test/PharUtil/data/phar/nosigmodified.phar
Binary file not shown.

0 comments on commit c8c675f

Please sign in to comment.