signing infrastructure
authorMichael Wallner <mike@php.net>
Wed, 6 Apr 2016 17:11:42 +0000 (19:11 +0200)
committerMichael Wallner <mike@php.net>
Wed, 6 Apr 2016 17:11:42 +0000 (19:11 +0200)
.gitignore
bin/gpg-sign [new file with mode: 0755]
bin/missing-sigs [new file with mode: 0755]
bin/pecl+sig [new file with mode: 0755]
bin/rsa-sign [new file with mode: 0755]
public/4093AEF6.pub [new file with mode: 0644]
public/replicator.pub [new file with mode: 0644]
public/sigs/.gitkeep [new file with mode: 0644]

index 27c837942f9cab92190d4c0c70a516d5aead52f4..0aa71467669a95e8450ab2e424ac91ff7076aedc 100644 (file)
@@ -5,8 +5,12 @@ tmp*
 *.tmp
 public/phars/*
 !public/phars/.gitkeep
+public/sigs/*
+!public/sigs/.gitkeep
 build/*
 !build/.gitkeep
 skip/*
 !skip/.gitkeep
 vendor
+composer.phar
+.gnupg/
diff --git a/bin/gpg-sign b/bin/gpg-sign
new file mode 100755 (executable)
index 0000000..df0ddc9
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/env php
+<?php
+$log = __DIR__."/../build/gpg-sign-error.log";
+
+ini_set("error_reporting", E_ALL);
+ini_set("display_errors", true);
+ini_set("log_errors", true);
+ini_set("error_log", $log);
+
+if ($argc != 3) {
+       fprintf(STDERR, "Usage: %s <pkg name> <phar path>\n\n", basename($argv[0]));
+       exit(1);
+}
+
+list(, $pkg, $src) = $argv;
+$basename = basename($src);
+$dir = __DIR__."/../public/sigs/$pkg";
+
+if (!is_dir($dir) && !mkdir($dir, 0755, true)) {
+       exit(2);
+}
+
+if (($pwd = getenv("REPLICATOR_PWD"))) {
+       if (!($stream = fopen("php://temp", "r+"))) {
+               exit(4);
+       }
+       fwrite($stream, $pwd);
+       rewind($stream);
+} else {
+       $stream = STDIN;
+}
+
+$proc = proc_open("gpg --batch --no-tty -a --detach-sign --passphrase-fd 3 --output $dir/$basename.asc $src", [
+       ["pipe", "r"],
+       ["pipe", "w"],
+       ["file", $log, "a"],
+       $stream
+], $fds);
+
+if (!is_resource($proc)) {
+       exit(3);
+}
+
+fclose($fds[0]);
+
+fpassthru($fds[1]);
+fclose($fds[1]);
+
+exit(proc_close($proc));
diff --git a/bin/missing-sigs b/bin/missing-sigs
new file mode 100755 (executable)
index 0000000..d14c1af
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh
+CWD=$(dirname "$0")
+SIGS="asc sig"
+PHARS="$CWD/../public/phars"
+
+test -d "$PWD" || PwD=$(pwd)
+
+checksig() {
+       local phar
+       local sigs
+
+       phar="$1"
+       sigs=$(printf "%s" "$phar" | sed 's/phars\//sigs\//')
+
+       for sig in $SIGS; do
+               local name
+               local file
+               local sign
+
+               if ! test -f "$sigs.$sig"; then
+                       name=$(basename "$(dirname \"$phar\")")
+                       file=$(realpath --relative-to="$PWD" "$phar")
+
+                       case "$sig" in
+                       asc)
+                               sign=$(realpath --relative-to="$PWD" "$CWD/gpg-sign")
+                               ;;
+                       sig)
+                               sign=$(realpath --relative-to="$PWD" "$CWD/rsa-sign")
+                               ;;
+                       esac
+                       echo "'$sign'" "'$name'" "'$file'"
+               fi
+       done
+}
+checksigs() {
+       local phar
+       while read phar; do
+               checksig $phar
+       done
+}
+
+find $PHARS -type f -name '*.ext.phar*' | checksigs
diff --git a/bin/pecl+sig b/bin/pecl+sig
new file mode 100755 (executable)
index 0000000..33e44d2
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/php -dphar.readonly=0
+<?php
+
+ini_set("display_errors", 0);
+ini_set("log_errors", 1);
+
+/* needed for the packager, so the pharstub task can find includes */
+set_include_path(__DIR__."/../vendor/m6w6/pharext/src:".get_include_path());
+
+$what = !empty($argv[1]) ? $argv[1] : "latest";
+$pids = [];
+
+function skip($skp) {
+       $dir = dirname($skp);
+       if (is_dir($dir) || mkdir(dirname($skp), 0755, true)) {
+               touch($skp);
+       }
+}
+
+function fail($pkg, $ver, $skp, $fmt) {
+       $msg = call_user_func_array("sprintf", array_slice(func_get_args(), 3));
+       fprintf(STDERR, "FAILURE: %s-%s, %s; skipping next time\n", $pkg, $ver, $msg);
+       skip($skp);
+}
+
+function sign($pkg, $ext) {
+       $fmt = "%s/bin/%s-sign %s %s";
+       foreach (["rsa", "gpg"] as $sig) {
+               passthru(sprintf($fmt, __DIR__, $sig, $pkg, $ext));
+       }
+}
+
+function wait(&$pids) {
+       $status = null;
+       switch ($pid = pcntl_wait($status)) {
+               case -1:
+                       // meh
+                       break;
+               default:
+                       extract($pids[$pid]);
+                       unset($pids[$pid]);
+                       if (pcntl_wifexited($status) && ($rc = pcntl_wexitstatus($status))) {
+                               fail($pkg, $ver, $skp, "exit code: %d", $rc);
+                       } elseif (pcntl_wifsignaled($status) && ($rc = pcntl_wtermsig($status))) {
+                               fail($pkg, $ver, $skp, "signal: %d", $rc);
+                       } else {
+                               printf("SUCCESS: %s-%s\n", $pkg, $ver);
+                               /* create skipfile, ensuring we do not build a package with
+                                * different name in registry/package.xml again
+                                */
+                               skip($skp);
+                               /* create signatures */
+                               sign($pkg, $ext);
+                       }
+                       break;
+       }
+       return $pid > 0;
+};
+
+function work($url, $dir) {
+       is_dir($dir) || mkdir($dir, 0777, true);
+       require_once __DIR__."/../vendor/autoload.php";
+       $packager = new pharext\Packager;
+       $packager->run(5, [
+               $_SERVER["argv"][0], 
+               "-qps", 
+               $url, 
+               "-Zzd", 
+               $dir
+       ]);
+};
+
+if (($sxe = simplexml_load_file("http://pecl.php.net/feeds/$what.rss"))) {
+       foreach ($sxe->item as $item) {
+               $url = (string) $item->link;
+               $pkg = basename(dirname($url));
+               $ver = basename($url);
+               $skp = sprintf("%s/../skip/%s/%s", __DIR__, $pkg, $ver);
+               $ext = sprintf("%s/../public/phars/%s/%s-%s.ext.phar", __DIR__, $pkg, $pkg, $ver);
+               $dir = dirname($ext);
+
+               if (!is_file($skp) && !is_file($ext)) {
+                       switch ($pid = pcntl_fork()) {
+                               case -1:
+                                       exit;
+                               case 0:
+                                       work($url, $dir);
+                                       exit;
+                               default:
+                                       $pids[$pid] = compact("url", "pkg", "ver", "skp", "ext", "dir");
+                                       break;
+                       }
+               }
+               
+               if (count($pids) > 5) {
+                       wait($pids);
+               }
+       }
+       while (wait($pids))
+               ;
+       
+}
+
+
diff --git a/bin/rsa-sign b/bin/rsa-sign
new file mode 100755 (executable)
index 0000000..d086242
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/env php
+<?php
+$log = __DIR__."/../build/rsa-sign-error.log";
+
+ini_set("error_reporting", E_ALL);
+ini_set("display_errors", true);
+ini_set("log_errors", true);
+ini_set("error_log", $log);
+
+if ($argc != 3) {
+       fprintf(STDERR, "Usage: %s <pkg name> <phar path>\n\n", basename($argv[0]));
+       exit(1);
+}
+
+list(, $pkg, $src) = $argv;
+$basename = basename($src);
+$dir = __DIR__."/../public/sigs/$pkg";
+$key = __DIR__."/../build/replicator.key";
+
+if (!is_dir($dir) && !mkdir($dir, 0755, true)) {
+       exit(2);
+}
+
+if (($pwd = getenv("REPLICATOR_PWD"))) {
+       if (!($stream = fopen("php://temp", "r+"))) {
+               exit(4);
+       }
+       fwrite($stream, $pwd);
+       rewind($stream);
+} else {
+       $stream = STDIN;
+}
+
+
+$proc = proc_open("openssl dgst -sha256 -sign $key -passin fd:3 -out $dir/$basename.sig $src", [
+       ["pipe", "r"],
+       ["pipe", "w"],
+       ["file", $log, "a"],
+       $stream
+], $fds);
+
+if (!is_resource($proc)) {
+       exit(3);
+}
+
+fclose($fds[0]);
+
+fpassthru($fds[1]);
+fclose($fds[1]);
+
+exit(proc_close($proc));
diff --git a/public/4093AEF6.pub b/public/4093AEF6.pub
new file mode 100644 (file)
index 0000000..abc732f
--- /dev/null
@@ -0,0 +1,18 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFcBXgsBCACfOwNc2jHlXRX0YtydcS6TGeEWz378Ayb//WokCg3A/qyCZhKG
+7Ijc0NnMKZPu04hympbQjg/wPU+yt+3Kpu2yl50C/I4sItldo64TXfRoyW9+oSI7
+6zxlVkvxocsT+istuk57ESzfdrWPqyGXkLoyiiPMre5hxwvhcl+PS3aesL5cx9YT
+2hAa/Itm/e00LMfg7LuSdZVXLXQQpsqA9+2LMIVH7SY9jDflE3eAAau6D6SGdVEx
+E2lLsHS5noTb0GCyu532X3WZVusv4NqhpJogUZ5i4UZ7ZmkdR/F/XpLTDvJs+usk
+mwPpxg63BeNFmj7i7Ho90pn6beGcpaiP0gU7ABEBAAG0K1BIQVJleHQgUmVwbGlj
+YXRvciA8cmVwbGljYXRvckBwaGFyZXh0Lm9yZz6JATcEEwEKACEFAlcBXgsCGwMF
+CwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQZJhrlUCTrvbYGAgAi120YHruidld
+uPTUS05/ZLoSn3orKkmkskOsjBrUqJvQHx1s8mqJpNJdbIrgPIxQPHauiE6Fj72q
+uv6TsVRxM+7VjiCHTbHmDheP5Zcyac7Nd/e62DsCYP7LAAx7MHbQvki6XQg4EsQZ
+cXMKRYuuizJxNGVUeZpusY5WXmc5PRIigsI4eh/2l96IK/eqTDSZiDUwv9ze+HMf
+JxOunBZVebYUQ3RYEWx1NseInxbiAnEdGM7phZH43jkohxPLROr3nWBmrJbBqULn
+m6M5fRucJoldU8VIzMdy0xxu+3PuX8aug96njK448r53wjb7yRf6WLonwjlFqTWq
+0tZzZR3Ndw==
+=avra
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/public/replicator.pub b/public/replicator.pub
new file mode 100644 (file)
index 0000000..2d8f880
--- /dev/null
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzsDXNox5V0V9GLcnXEu
+kxnhFs9+/AMm//1qJAoNwP6sgmYShuyI3NDZzCmT7tOIcpqW0I4P8D1Psrftyqbt
+spedAvyOLCLZXaOuE130aMlvfqEiO+s8ZVZL8aHLE/orLbpOexEs33a1j6shl5C6
+MoojzK3uYccL4XJfj0t2nrC+XMfWE9oQGvyLZv3tNCzH4Oy7knWVVy10EKbKgPft
+izCFR+0mPYw35RN3gAGrug+khnVRMRNpS7B0uZ6E29Bgsrud9l91mVbrL+DaoaSa
+IFGeYuFGe2ZpHUfxf16S0w7ybPrrJJsD6cYOtwXjRZo+4ux6PdKZ+m3hnKWoj9IF
+OwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/public/sigs/.gitkeep b/public/sigs/.gitkeep
new file mode 100644 (file)
index 0000000..e69de29