new key pair; prints the public key for PackageSignature * php bin/release-sign.php sign prints the signature (the .sig asset) * php bin/release-sign.php verify checks against the key built into the plugin */ declare(strict_types=1); // The class guards against direct web access; this is the command line. define('ABSPATH', __DIR__ . '/'); require_once __DIR__ . '/../src/Updater/PackageSignature.php'; use LIT\MCP\Updater\PackageSignature; function fail(string $message): never { fwrite(STDERR, $message . "\n"); exit(1); } [, $command, $first, $second] = $argv + [null, '', '', '']; switch ($command) { case 'keygen': if ($first === '' || file_exists($first)) { fail('usage: keygen (an existing file is never overwritten)'); } $pair = sodium_crypto_sign_keypair(); if (! is_dir(dirname($first)) && ! mkdir(dirname($first), 0700, true)) { fail('cannot create ' . dirname($first)); } // 0600 before the secret is written, so it is never readable by others. touch($first); chmod($first, 0600); file_put_contents($first, base64_encode(sodium_crypto_sign_secretkey($pair))); echo base64_encode(sodium_crypto_sign_publickey($pair)), "\n"; break; case 'sign': $secret = is_readable($second) ? base64_decode(trim((string) file_get_contents($second)), true) : false; if (! is_readable($first) || $secret === false || strlen($secret) !== SODIUM_CRYPTO_SIGN_SECRETKEYBYTES) { fail('usage: sign (readable ZIP, key file from keygen)'); } $message = PackageSignature::message(basename($first), $first); echo base64_encode(sodium_crypto_sign_detached($message, $secret)), "\n"; break; case 'verify': if (! is_readable($first) || ! is_readable($second)) { fail('usage: verify '); } if (! (new PackageSignature())->verifies(basename($first), $first, (string) file_get_contents($second))) { fail('signature does not verify against the key built into the plugin'); } echo "ok\n"; break; default: fail('usage: keygen | sign | verify '); }