commit 9ffc57ec1ad197b8ddb0f2a98bb5cf6ba4a52522 Author: Michael Chus Date: Thu Mar 5 10:23:47 2026 +0300 init: keys repo with keygen, sign, verify scripts and mchusavitin public key Co-Authored-By: Claude Sonnet 4.6 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4a61f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Private keys must never be committed +*.key +*.pem +/tmp/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..87687df --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# Release Signing Keys + +Centralized public key registry for all mchus.pro projects. + +Ed25519 asymmetric signatures. Public keys are safe to commit here. +Private keys stay on each developer's machine — never committed, never shared. + +## How verification works + +Release binaries are signed by one developer's private key. +The binary trusts **any** key in `developers/`. +Signature valid if at least one trusted key matches. + +## Setup (new developer, one-time) + +```sh +sh scripts/keygen.sh +``` + +This creates: +- `~/.keys/.key` — private key, keep secret +- `developers/.pub` — public key, commit to this repo + +Then commit and push the `.pub` file. Next project release will include your key. + +## Signing a release binary + +```sh +sh scripts/sign-release.sh path/to/binary +``` + +Produces `path/to/binary.sig` — upload both to the Gitea release as assets. + +## Verifying a signature locally + +```sh +sh scripts/verify-signature.sh path/to/binary +``` + +Checks `path/to/binary.sig` against all keys in `developers/`. + +## Adding / removing a developer + +**Add:** run keygen, commit the `.pub` file, rebuild affected project releases. + +**Remove:** delete their `.pub` file, commit, rebuild releases. +Previously signed binaries with their key remain valid (already distributed), +but they cannot sign new releases. + +## Requirements + +- openssl 3.x +- python3 (for verify-signature.sh only) + +## Projects using this repo + +- `git.mchus.pro/mchus/bee` — hardware audit LiveCD diff --git a/developers/.gitkeep b/developers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/developers/mchusavitin.pub b/developers/mchusavitin.pub new file mode 100644 index 0000000..cefd679 --- /dev/null +++ b/developers/mchusavitin.pub @@ -0,0 +1 @@ +ZUHNsLb0ZxguIqLjmOqjkF6dgJafNxYAlhviQu79MVY= diff --git a/scripts/keygen.sh b/scripts/keygen.sh new file mode 100755 index 0000000..9063cff --- /dev/null +++ b/scripts/keygen.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# keygen.sh — generate an Ed25519 keypair for signing release binaries +# +# Usage: +# sh scripts/keygen.sh +# +# Output: +# ~/.keys/.key — private key (KEEP SECRET, never commit) +# developers/.pub — public key (safe to commit here) +# +# Requirements: openssl 3.x + +set -e + +NAME="$1" +if [ -z "$NAME" ]; then + echo "Usage: sh scripts/keygen.sh " >&2 + echo "Example: sh scripts/keygen.sh mchusavitin" >&2 + exit 1 +fi + +PRIVATE_KEY_PATH="$HOME/.keys/${NAME}.key" +PUBLIC_KEY_PATH="$(dirname "$0")/../developers/${NAME}.pub" + +if [ -f "$PRIVATE_KEY_PATH" ]; then + echo "Private key already exists at $PRIVATE_KEY_PATH" >&2 + echo "Delete it manually if you want to regenerate." >&2 + exit 1 +fi + +mkdir -p "$HOME/.keys" +chmod 700 "$HOME/.keys" + +# Generate Ed25519 private key (PEM format) +openssl genpkey -algorithm ed25519 -out "$PRIVATE_KEY_PATH" +chmod 600 "$PRIVATE_KEY_PATH" + +# Extract raw 32-byte public key and base64-encode it +openssl pkey -in "$PRIVATE_KEY_PATH" -pubout -outform DER \ + | tail -c 32 \ + | base64 > "$PUBLIC_KEY_PATH" + +echo "Private key: $PRIVATE_KEY_PATH (DO NOT share or commit)" +echo "Public key: $PUBLIC_KEY_PATH (commit this to the keys repo)" +echo "" +echo "Next steps:" +echo " 1. git add developers/${NAME}.pub && git commit -m 'add ${NAME} public key'" +echo " 2. git push" +echo " 3. Rebuild any release binaries to include the new key" diff --git a/scripts/sign-release.sh b/scripts/sign-release.sh new file mode 100755 index 0000000..d686562 --- /dev/null +++ b/scripts/sign-release.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# sign-release.sh — sign a release binary with your Ed25519 private key +# +# Usage: +# sh scripts/sign-release.sh +# +# Output: +# .sig — raw 64-byte Ed25519 signature +# +# The .sig file must be uploaded alongside the binary to the Gitea release. +# +# Requirements: openssl 3.x + +set -e + +NAME="$1" +BINARY="$2" + +if [ -z "$NAME" ] || [ -z "$BINARY" ]; then + echo "Usage: sh scripts/sign-release.sh " >&2 + echo "Example: sh scripts/sign-release.sh mchusavitin dist/bee-audit-linux-amd64" >&2 + exit 1 +fi + +PRIVATE_KEY_PATH="$HOME/.keys/${NAME}.key" +SIG_PATH="${BINARY}.sig" + +if [ ! -f "$PRIVATE_KEY_PATH" ]; then + echo "Private key not found: $PRIVATE_KEY_PATH" >&2 + echo "Run scripts/keygen.sh $NAME first." >&2 + exit 1 +fi + +if [ ! -f "$BINARY" ]; then + echo "Binary not found: $BINARY" >&2 + exit 1 +fi + +# Sign: produce raw 64-byte Ed25519 signature +openssl pkeyutl -sign \ + -inkey "$PRIVATE_KEY_PATH" \ + -rawin \ + -in "$BINARY" \ + -out "$SIG_PATH" + +echo "Signed: $BINARY" +echo "Signature: $SIG_PATH" +echo "" +echo "Upload both files to the Gitea release as assets." diff --git a/scripts/verify-signature.sh b/scripts/verify-signature.sh new file mode 100755 index 0000000..fc1bcff --- /dev/null +++ b/scripts/verify-signature.sh @@ -0,0 +1,75 @@ +#!/bin/sh +# verify-signature.sh — verify a release binary signature against any trusted public key +# +# Usage: +# sh scripts/verify-signature.sh +# +# Verifies .sig against all keys in developers/*.pub +# Exits 0 if any key matches, 1 if none match. +# +# Requirements: openssl 3.x, python3 (for base64 decode) + +set -e + +BINARY="$1" +SIG_PATH="${BINARY}.sig" +KEYS_DIR="$(dirname "$0")/../developers" + +if [ -z "$BINARY" ]; then + echo "Usage: sh scripts/verify-signature.sh " >&2 + exit 1 +fi + +if [ ! -f "$BINARY" ]; then + echo "Binary not found: $BINARY" >&2 + exit 1 +fi + +if [ ! -f "$SIG_PATH" ]; then + echo "Signature not found: $SIG_PATH" >&2 + exit 1 +fi + +MATCHED="" + +for PUBFILE in "$KEYS_DIR"/*.pub; do + [ -f "$PUBFILE" ] || continue + NAME=$(basename "$PUBFILE" .pub) + + # Decode base64 raw public key → DER SubjectPublicKeyInfo wrapper + # Ed25519 DER prefix: 302a300506032b6570032100 + RAW_B64=$(cat "$PUBFILE") + PEM_PATH="/tmp/verify-${NAME}.pem" + + python3 - "$RAW_B64" "$PEM_PATH" <<'PYEOF' +import sys, base64, binascii +raw = base64.b64decode(sys.argv[1]) +prefix = bytes.fromhex("302a300506032b6570032100") +der = prefix + raw +b64 = base64.b64encode(der).decode() +with open(sys.argv[2], "w") as f: + f.write("-----BEGIN PUBLIC KEY-----\n") + for i in range(0, len(b64), 64): + f.write(b64[i:i+64] + "\n") + f.write("-----END PUBLIC KEY-----\n") +PYEOF + + if openssl pkeyutl -verify \ + -pubin -inkey "$PEM_PATH" \ + -rawin \ + -in "$BINARY" \ + -sigfile "$SIG_PATH" \ + 2>/dev/null; then + echo "OK: signature verified by key '$NAME'" + MATCHED="$NAME" + rm -f "$PEM_PATH" + break + fi + + rm -f "$PEM_PATH" +done + +if [ -z "$MATCHED" ]; then + echo "FAIL: signature does not match any trusted key in developers/" >&2 + exit 1 +fi