92 lines
3.0 KiB
PHP
92 lines
3.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace LIT\MCP\Tests\Unit\Updater;
|
|
|
|
use LIT\MCP\Updater\PackageSignature;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class PackageSignatureTest extends TestCase
|
|
{
|
|
private string $zip = '';
|
|
private string $secretKey = '';
|
|
private PackageSignature $signature;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$pair = sodium_crypto_sign_keypair();
|
|
$this->secretKey = sodium_crypto_sign_secretkey($pair);
|
|
$this->signature = new PackageSignature(base64_encode(sodium_crypto_sign_publickey($pair)));
|
|
$this->zip = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-sig');
|
|
file_put_contents($this->zip, 'zip bytes');
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
unlink($this->zip);
|
|
}
|
|
|
|
public function testASignatureOverNameAndContentVerifies(): void
|
|
{
|
|
$signature = $this->sign('wp-lit-mcp-1.0.0.zip');
|
|
|
|
self::assertTrue($this->signature->verifies('wp-lit-mcp-1.0.0.zip', $this->zip, $signature));
|
|
}
|
|
|
|
public function testAnotherAssetNameDoesNotVerify(): void
|
|
{
|
|
$signature = $this->sign('wp-lit-mcp-1.0.0.zip');
|
|
|
|
self::assertFalse($this->signature->verifies('wp-lit-mcp-1.0.1.zip', $this->zip, $signature));
|
|
}
|
|
|
|
public function testChangedContentDoesNotVerify(): void
|
|
{
|
|
$signature = $this->sign('x.zip');
|
|
file_put_contents($this->zip, 'changed bytes');
|
|
|
|
self::assertFalse($this->signature->verifies('x.zip', $this->zip, $signature));
|
|
}
|
|
|
|
public function testAnotherKeyDoesNotVerify(): void
|
|
{
|
|
$foreign = new PackageSignature(base64_encode(sodium_crypto_sign_publickey(sodium_crypto_sign_keypair())));
|
|
|
|
self::assertFalse($foreign->verifies('x.zip', $this->zip, $this->sign('x.zip')));
|
|
}
|
|
|
|
/** @dataProvider malformedInput */
|
|
public function testMalformedInputFailsClosed(string $signature, string $publicKey, string $zipPath): void
|
|
{
|
|
$subject = new PackageSignature($publicKey === 'real' ? $this->realKey() : $publicKey);
|
|
|
|
self::assertFalse($subject->verifies('x.zip', $zipPath === 'zip' ? $this->zip : $zipPath, $signature));
|
|
}
|
|
|
|
/** @return array<string, array{string, string, string}> */
|
|
public static function malformedInput(): array
|
|
{
|
|
return [
|
|
'signature is no base64' => ['not base64 !!', 'real', 'zip'],
|
|
'signature is empty' => ['', 'real', 'zip'],
|
|
'signature has the wrong length' => [base64_encode('short'), 'real', 'zip'],
|
|
'no key configured' => ['AAAA', '', 'zip'],
|
|
'key has the wrong length' => ['AAAA', base64_encode('short'), 'zip'],
|
|
'file is missing' => ['AAAA', 'real', '/nonexistent/x.zip'],
|
|
];
|
|
}
|
|
|
|
private function sign(string $assetName): string
|
|
{
|
|
$message = PackageSignature::message($assetName, $this->zip);
|
|
|
|
return base64_encode(sodium_crypto_sign_detached($message, $this->secretKey));
|
|
}
|
|
|
|
private function realKey(): string
|
|
{
|
|
return base64_encode(sodium_crypto_sign_publickey(sodium_crypto_sign_keypair()));
|
|
}
|
|
}
|