Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions .github/workflows/signpath-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# SignPath OSS signing test — Windows binaries via SignPath.
#
# This workflow is the SignPath trial run: it signs the Windows binaries with
# the OSS organization's self-signed TEST certificate. It does NOT touch the
# Certum SimplySign release flow (release.yml) yet — once SignPath is proven
# and the production certificate is imported, the release workflow will switch.
#
# Prerequisites (one-time, see code-signing/signpath/README.md):
# * SignPath portal: project + signing policy + 'GitHub.com' trusted build
# system + artifact configurations 'mercury-windows' and 'mercury-setup'
# (XML in code-signing/signpath/artifact-configs/).
# * Repo secret SIGNPATH_API_TOKEN (SignPath API token of a submitter user).
# * Replace the d1c3b645-94b2-48ac-95fa-b72a652f3578, mercury and test-signing
# placeholders below with the real portal values.

name: SignPath test

on:
workflow_dispatch:
inputs:
build_installer:
description: 'Also build + sign the Windows installer (Wine + Inno)'
required: false
default: false
type: boolean

jobs:
# ---- Build unsigned Windows binaries (GitHub-hosted agent) ----
build:
name: Build unsigned binaries
runs-on: ubuntu-latest
container: debian:trixie
timeout-minutes: 20
outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }}
steps:
- name: Install deps
run: |
apt-get update && apt-get install -y --no-install-recommends \
ca-certificates build-essential git golang-go \
gcc-mingw-w64-x86-64 mingw-w64-x86-64-dev mingw-w64-common zip
- uses: actions/checkout@v4
with: { fetch-depth: 1 }

- name: Git config
run: git config --global --add safe.directory /__w/mercury/mercury

- name: Build binaries
run: |
make windows
make fyne-ui-windows
mkdir -p dist
cp mercury.exe dist/
cp windows-installer/mercury-ui.exe dist/
- name: Upload unsigned binaries
id: upload
uses: actions/upload-artifact@v4
with:
name: signpath-unsigned
path: |
dist/mercury.exe
dist/mercury-ui.exe
# ---- Submit to SignPath (test certificate) ----
sign:
name: Sign via SignPath
needs: build
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Submit signing request
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: 'd1c3b645-94b2-48ac-95fa-b72a652f3578'
project-slug: 'mercury'
signing-policy-slug: 'test-signing'
artifact-configuration-slug: 'mercury-windows'
github-artifact-id: '${{ needs.build.outputs.artifact-id }}'
wait-for-completion: true
output-artifact-directory: signed

- name: Verify signatures (best effort with test cert)
run: |
sudo apt-get update && sudo apt-get install -y --no-install-recommends osslsigncode
for f in signed/mercury.exe signed/mercury-ui.exe; do
[ -f "$f" ] || { echo "::error::missing signed file $f"; exit 1; }
# The OSS test certificate is self-signed, so chain validation
# cannot succeed; we confirm a signature is present and readable.
if osslsigncode verify "$f"; then
echo "verified: $f"
else
echo "::warning::chain validation failed for $f (expected with test cert); signature details:"
osslsigncode extract-signature -pem "$f" >/dev/null && echo "signature present in $f"
fi
done
- name: Upload signed binaries
uses: actions/upload-artifact@v4
with:
name: signpath-signed
path: |
signed/mercury.exe
signed/mercury-ui.exe
# ---- Installer: build with SIGNED payloads, then sign the Setup.exe ----
installer:
name: Build installer from signed payloads
if: ${{ inputs.build_installer }}
needs: sign
runs-on: ubuntu-latest
timeout-minutes: 20
outputs:
artifact-id: ${{ steps.upload-setup.outputs.artifact-id }}
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 1 }

- name: Download signed binaries
uses: actions/download-artifact@v4
with:
name: signpath-signed

- name: Install Inno Setup (Wine)
env:
INNO_URL: https://wiki.hermes.radio/reports/inno-setup-6.7.3-wine.tar.gz
INNO_SHA256: 4c9c92249663f201a33bf194c0cd37e01c60540e299c771f3626411ef463c884
run: |
set -eu
sudo apt-get update
sudo apt-get install -y --no-install-recommends wine ca-certificates curl zip
curl -fsSLo /tmp/inno.tar.gz "$INNO_URL"
echo "${INNO_SHA256} /tmp/inno.tar.gz" | sha256sum -c -
wineboot -i
PF="$HOME/.wine/drive_c/Program Files (x86)"
mkdir -p "$PF"
tar xzf /tmp/inno.tar.gz -C "$PF/"
ISCC_PATH="$PF/Inno Setup 6/ISCC.exe"
[ -f "$ISCC_PATH" ] || { echo "::error::Inno Setup tree missing ISCC.exe"; exit 1; }
echo "ISCC_PATH=$ISCC_PATH" >> "$GITHUB_ENV"
- name: Stage signed payloads and build installer
run: |
set -eu
# Payloads here are the SIGNED binaries from the previous job;
# windows-installer-stage only copies, it does not rebuild.
cp mercury.exe windows-installer/
cp mercury-ui.exe windows-installer/
cp mercury.ini.example windows-installer/mercury.ini
sed -i 's/ui_enabled = false/ui_enabled = true/g' windows-installer/mercury.ini
sed -i 's/sound_system = auto/sound_system = wasapi/g' windows-installer/mercury.ini
wine "$ISCC_PATH" windows-installer/installer.iss
SETUP=$(ls Mercury_*_Setup.exe | head -1)
[ -n "$SETUP" ] || { echo "::error::ISCC produced no installer"; exit 1; }
cp "$SETUP" mercury-setup.exe
- name: Upload unsigned installer
id: upload-setup
uses: actions/upload-artifact@v4
with:
name: signpath-setup-unsigned
path: mercury-setup.exe

# ---- Sign the installer via SignPath ----
sign-setup:
name: Sign installer via SignPath
if: ${{ inputs.build_installer }}
needs: installer
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Submit signing request (installer)
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: 'd1c3b645-94b2-48ac-95fa-b72a652f3578'
project-slug: 'mercury'
signing-policy-slug: 'test-signing'
artifact-configuration-slug: 'mercury-setup'
github-artifact-id: '${{ needs.installer.outputs.artifact-id }}'
wait-for-completion: true
output-artifact-directory: signed-setup

- name: Verify installer signature (best effort with test cert)
run: |
sudo apt-get update && sudo apt-get install -y --no-install-recommends osslsigncode
osslsigncode verify signed-setup/mercury-setup.exe || \
{ echo "::warning::chain validation failed (expected with test cert)"; \
osslsigncode extract-signature -pem signed-setup/mercury-setup.exe >/dev/null \
&& echo "signature present"; }
- name: Upload signed installer
uses: actions/upload-artifact@v4
with:
name: signpath-setup-signed
path: signed-setup/mercury-setup.exe
71 changes: 71 additions & 0 deletions code-signing/signpath/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# SignPath code signing (OSS program)

Signing the Windows binaries through the [SignPath](https://signpath.io) open
source program: no cloud-cert login gymnastics, no secrets beyond one API
token, and the certificate itself never leaves SignPath's HSM.

Status: **trial** — the workflow signs with the self-signed *test* certificate
the OSS organization provides. The Certum SimplySign release flow
(`release.yml`, `code-signing/sign.sh`) is untouched while this is validated.

```
artifact-configs/mercury-windows.xml artifact configuration: ZIP with mercury.exe + mercury-ui.exe
artifact-configs/mercury-setup.xml artifact configuration: ZIP with mercury-setup.exe (installer)
../../.github/workflows/signpath-test.yml the trial workflow
```

## One-time setup (portal, done by a maintainer with the OSS org invite)

1. **Accept the SignPath organization invitation** (from the approval email).

2. **Trusted Build System**: Organization → add *Trusted Build System* →
**GitHub.com**. Link it to the project in the next step.

3. **Project + signing policy**: create project `mercury` and a signing
policy (e.g. `test-signing`) that uses the test certificate.

4. **Artifact configurations**: in the project, add two artifact
configurations and paste the XML from `artifact-configs/`:
- `mercury-windows` ← `mercury-windows.xml`
- `mercury-setup` ← `mercury-setup.xml`
(Alternative: upload a sample ZIP of the two exes and let SignPath
generate the config, then review it.)

5. **API token**: in the SignPath portal create an API token for a user with
*submitter* permission on the project/policy.

6. **GitHub repo secret**: add the token as `SIGNPATH_API_TOKEN`
(Settings → Secrets and variables → Actions).

7. **Workflow placeholders**: replace `<ORGANIZATION_ID>`, `<PROJECT_SLUG>`
and `<POLICY_SLUG>` in `.github/workflows/signpath-test.yml` with the real
portal values (visible in the portal; project slug is what you named it).

8. **SignPath GitHub App** (recommended): install
[SignPath](https://github.com/apps/signpath) on the Rhizomatica org with
access to `mercury`. Required if we later enable source/build policies;
it also improves the origin-verification signal for the OSS review.

## Run the trial

- Actions → *SignPath test* → **Run workflow** on the `signpath-test` branch.
- Sign only the two exes: leave *build_installer* off.
- Also sign the installer: tick *build_installer* (needs Wine + Inno; the
signed payloads are staged into the installer before the Setup.exe is
submitted for signing).
- Expected: two (or three) `signpath-signed` / `signpath-setup-signed`
artifacts whose signatures verify with `osslsigncode`. Chain validation
will fail for the self-signed test cert — that is expected.

## Production certificate

After SignPath reviews the setup (origin verification), they import the real
release certificate into the organization. At that point:

1. Point the release signing policy at the production certificate.
2. Port the `sign` / `sign-setup` jobs from `signpath-test.yml` into
`.github/workflows/release.yml`, replacing the Certum jobs.
3. Keep `code-signing/sign.sh` + the Certum secrets as a manual fallback.

See [SignPath docs](https://about.signpath.io/documentation/) — the GitHub
integration page and the artifact-configuration reference.
14 changes: 14 additions & 0 deletions code-signing/signpath/artifact-configs/mercury-setup.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SignPath artifact configuration: Inno Setup installer.
The Setup.exe is renamed to a fixed mercury-setup.exe before upload so the
path never changes between releases. Portal: paste this XML when creating
the artifact configuration 'mercury-setup'.
-->
<artifact-configuration xmlns="http://signpath.io/artifact-configuration/v1">
<zip-file>
<pe-file path="mercury-setup.exe">
<authenticode-sign hash-algorithm="sha256" />
</pe-file>
</zip-file>
</artifact-configuration>
18 changes: 18 additions & 0 deletions code-signing/signpath/artifact-configs/mercury-windows.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SignPath artifact configuration: Windows release ZIP payloads.
The artifact submitted from GitHub Actions is a ZIP (upload-artifact)
containing both PE files at the root. Both are deep-signed in place.
Portal: paste this XML when creating the artifact configuration
'mercury-windows' in the SignPath project.
-->
<artifact-configuration xmlns="http://signpath.io/artifact-configuration/v1">
<zip-file>
<pe-file path="mercury.exe">
<authenticode-sign hash-algorithm="sha256" />
</pe-file>
<pe-file path="mercury-ui.exe">
<authenticode-sign hash-algorithm="sha256" />
</pe-file>
</zip-file>
</artifact-configuration>
Loading