Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
41c8be1
Merge pull request #397 from stackql/dev
jeffreyaven May 27, 2026
300457e
Merge pull request #399 from stackql/dev
jeffreyaven May 29, 2026
b40ace3
Merge pull request #401 from stackql/dev
jeffreyaven May 31, 2026
4c2c43c
Merge pull request #403 from stackql/dev
jeffreyaven May 31, 2026
76980e9
Merge pull request #405 from stackql/dev
general-kroll-4-life Jun 2, 2026
c0ffec6
Merge pull request #408 from stackql/dev
jeffreyaven Jun 26, 2026
74d333b
Merge pull request #410 from stackql/dev
jeffreyaven Jun 27, 2026
e5536ba
Merge pull request #412 from stackql/dev
jeffreyaven Jul 6, 2026
d98e315
Merge pull request #414 from stackql/dev
jeffreyaven Jul 7, 2026
8012259
Merge pull request #416 from stackql/dev
jeffreyaven Jul 9, 2026
305d797
Merge pull request #418 from stackql/dev
jeffreyaven Jul 9, 2026
9518da0
Merge pull request #420 from stackql/dev
jeffreyaven Jul 14, 2026
cf61765
Merge pull request #422 from stackql/dev
jeffreyaven Jul 15, 2026
290130c
Merge pull request #424 from stackql/dev
jeffreyaven Jul 21, 2026
67441e8
Merge pull request #426 from stackql/dev
jeffreyaven Jul 28, 2026
dd629b6
Merge pull request #428 from stackql/dev
jeffreyaven Jul 28, 2026
2afa00f
Merge pull request #430 from stackql/dev
jeffreyaven Jul 30, 2026
2d11aaf
Merge pull request #432 from stackql/dev
jeffreyaven Jul 31, 2026
e09feff
Merge pull request #434 from stackql/dev
jeffreyaven Aug 3, 2026
2e4d2ee
Merge pull request #436 from stackql/dev
jeffreyaven Aug 4, 2026
8ac3a85
Merge pull request #438 from stackql/dev
jeffreyaven Aug 4, 2026
f473a7a
Merge pull request #440 from stackql/dev
jeffreyaven Aug 4, 2026
7345d87
Merge pull request #442 from stackql/dev
jeffreyaven Aug 17, 2026
5f1e27a
Merge pull request #444 from stackql/dev
jeffreyaven Aug 20, 2026
25c1370
Merge pull request #446 from stackql/dev
jeffreyaven Aug 20, 2026
bf3e324
Merge pull request #448 from stackql/dev
jeffreyaven Aug 20, 2026
b98d6b0
Merge pull request #453 from stackql/dev
jeffreyaven Aug 24, 2026
673347f
Merge pull request #455 from stackql/dev
jeffreyaven Aug 26, 2026
9ba2062
Merge pull request #457 from stackql/dev
jeffreyaven Aug 28, 2026
ac2f76c
Merge pull request #459 from stackql/dev
jeffreyaven Sep 15, 2026
a998a65
Merge pull request #461 from stackql/dev
jeffreyaven Sep 15, 2026
3e289bb
Merge pull request #463 from stackql/dev
jeffreyaven Sep 16, 2026
193d54b
Merge pull request #465 from stackql/dev
jeffreyaven Sep 26, 2026
a93725b
Merge pull request #467 from stackql/dev
jeffreyaven Sep 28, 2026
2bc1d3f
Merge pull request #469 from stackql/dev
jeffreyaven Sep 28, 2026
dd439e7
Merge pull request #471 from stackql/dev
jeffreyaven Sep 30, 2026
46f5dfb
Merge pull request #473 from stackql/dev
jeffreyaven Oct 4, 2026
57fe472
Merge pull request #475 from stackql/dev
jeffreyaven Oct 5, 2026
63d5b59
Merge pull request #477 from stackql/dev
jeffreyaven Oct 12, 2026
4a2a87f
Merge pull request #479 from stackql/dev
jeffreyaven Oct 19, 2026
9e727c4
Merge pull request #481 from stackql/dev
jeffreyaven Oct 21, 2026
13fc024
Merge pull request #484 from stackql/dev
jeffreyaven Oct 21, 2026
8e74eb1
Merge pull request #486 from stackql/dev
jeffreyaven Oct 23, 2026
7736697
ml-dsa-provider-signatures
jeffreyaven Oct 23, 2026
eb23fc4
Document v0.13 minimum for quantum-safe signature verification
jeffreyaven Oct 24, 2026
5466ef2
Merge pull request #487 from stackql/feature/ml-dsa-provider-signatures
jeffreyaven Oct 24, 2026
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
23 changes: 17 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,17 @@ jobs:
if: env.NUM_PROVIDERS > 0
uses: actions/setup-go@v6
with:
go-version: ^1.19
go-version-file: go.mod
cache-dependency-path: signing/go.sum
check-latest: true
cache: true
id: go

- name: "[PACKAGE] build sign tool"
if: env.NUM_PROVIDERS > 0
run: |
go build -o ed25519tool ./signing/Ed25519/app/cmd/main
cd signing
go build -o ../pqsig ./cmd/pqsig

- name: "[PACKAGE] prepare provider dirs"
if: env.NUM_PROVIDERS > 0
Expand All @@ -94,12 +96,17 @@ jobs:
- name: "[PACKAGE] sign provider docs"
if: env.NUM_PROVIDERS > 0
env:
SIGNING_VERSION: v1
SIGNING_CERT: ${{ secrets.V1_SIGNING_CERT }}
SIGNING_PRIV_KEY: '${{ secrets.V1_SIGNING_PRIV_KEY }}'
SIGNING_PUB_KEY: ${{ secrets.V1_SIGNING_PUB_KEY }}
STACKQL_SIGNING_KID: stackql-2026-01
STACKQL_SIGNING_SEED: ${{ github.event_name != 'pull_request' && secrets.MLDSA_SIGNING_SEED || '' }}
run: |
# PRs exercise the complete packaging path without a production signing key.
if [ "${{ github.event_name }}" = "pull_request" ]; then
export STACKQL_SIGNING_SEED="$(python -c 'import secrets; print(secrets.token_hex(32))')"
fi
python scripts/package/sign-provider-docs.py
if [ "${{ github.event_name }}" != "pull_request" ]; then
find signed/providers/src -type f -name '*.yaml' -exec ./pqsig verify-doc {} +
fi

- name: "[PACKAGE] package provider docs"
if: env.NUM_PROVIDERS > 0
Expand All @@ -122,6 +129,10 @@ jobs:
do
echo "testing ${provider}..."
sh test-provider.sh "$provider" false "$providersdir"
if [ "${{ github.event_name }}" != "pull_request" ]; then
python verify-provider.py "$provider" \
--registry-root "$providersdir" --stackql-bin "$(pwd)/stackql"
fi
done < ../providers.txt

#
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/signing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Provider document signatures

on:
pull_request:
branches: [main, dev]
push:
branches: [main, dev]
workflow_dispatch:

permissions:
contents: read

jobs:
signing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache-dependency-path: |
go.sum
signing/go.sum
- run: go test ./... && go vet ./...
- run: go test ./... && go vet ./...
working-directory: signing
env:
PQSIG_PROVIDER_ROOT: ${{ github.workspace }}/providers/src
- run: python -m unittest discover -s scripts/package -p 'test_*.py'
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
ed25519tool
/pqsig
/pqsig.exe
# Preserve ignored local key material left by the retired tool.
/signing/Ed25519/
.DS_Store
scripts/*/node_modules/
scripts/*/package-lock.json
Expand Down
46 changes: 0 additions & 46 deletions .vscode/launch.json

This file was deleted.

8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

A repository of `provider` interface documents supporting [stackql](https://stackql.io/) ([stackql GitHub repo](https://github.com/stackql/stackql)).

> [!IMPORTANT]
> New and updated provider revisions published after the signing cutover use
> quantum-safe ML-DSA-65 signatures. Signature verification requires
> **StackQL v0.13 or later** and remains opt-in, disabled by default. Existing
> provider revisions gain the new signatures as they are updated or republished;
> legacy revisions must be republished before they can pass the new verifier.
> See [signing and verification](docs/signing-and-verification.md).

## Table of Contents

<!-- BEGIN TABLE OF CONTENTS -->
Expand Down
102 changes: 34 additions & 68 deletions docs/command-line-tool.md
Original file line number Diff line number Diff line change
@@ -1,75 +1,41 @@
# Provider signing command line

# Working with the command line tool
Requires Go 1.27.3 or later:

To build the signing and verification tool:

```
go build -o ed25519tool ./signing/Ed25519/app/cmd/main
```sh
cd signing
go build -o ../pqsig ./cmd/pqsig
cd ..
```

Then, from the repository root directory:

```bash
. ./scripts/setup-env.sh

CREDENTIALS_DIR="${REPOSITORY_ROOT_DIR}/signing/Ed25519/setup/scratchpad"
TESTING_INPUT_DIR="${REPOSITORY_ROOT_DIR}/signing/Ed25519/test"
TESTING_OUTPUT_DIR="${CREDENTIALS_DIR}"

PRIVATE_KEY_FILE="smoke-testing-private-key.pem"

PUBLIC_KEY_FILE="smoke-testing-public-key.pem"

CERT_FILE="smoke-testing-cert.pem"

CSR_FILE="smoke-testing.csr"

## Create key pair and cert
./ed25519tool createkeys ${CREDENTIALS_DIR}/${PRIVATE_KEY_FILE} ${CREDENTIALS_DIR}/${PUBLIC_KEY_FILE} ${CREDENTIALS_DIR}/${CERT_FILE} ${CREDENTIALS_DIR}/${CSR_FILE}

## Store the private key in an env var
SIGNING_KEY_SECRET=$(cat ${CREDENTIALS_DIR}/${PRIVATE_KEY_FILE})

## Sign some files
./ed25519tool sign --privatekeyenvvar="SIGNING_KEY_SECRET" --signaturetime="Jan 2 15:04:05 2006" ${TESTING_INPUT_DIR}/sample-infile.txt -o ${TESTING_OUTPUT_DIR}/old-timestamp-sample-infile.txt.sig

./ed25519tool sign --privatekeyenvvar="SIGNING_KEY_SECRET" --signaturetime="Jan 2 15:04:05 2023" ${TESTING_INPUT_DIR}/sample-infile.txt -o ${TESTING_OUTPUT_DIR}/acceptable-timestamp-sample-infile.txt.sig

./ed25519tool sign --privatekeyenvvar="SIGNING_KEY_SECRET" --signaturetime="now" ${TESTING_INPUT_DIR}/sample-infile.txt -o ${TESTING_OUTPUT_DIR}/now-timestamp-sample-infile.txt.sig
Provision a new production key without writing or displaying the private seed:

./ed25519tool sign --privatekeyenvvar="SIGNING_KEY_SECRET" --signaturetime="Jan 2 15:04:05 2033" ${TESTING_INPUT_DIR}/sample-infile.txt -o ${TESTING_OUTPUT_DIR}/future-timestamp-sample-infile.txt.sig

## Now, verify

## will succeed
./ed25519tool verify --publickeypath=${CREDENTIALS_DIR}/${PUBLIC_KEY_FILE} ${TESTING_INPUT_DIR}/sample-infile.txt ${TESTING_OUTPUT_DIR}/old-timestamp-sample-infile.txt.sig

## should and will fail with timestamp message
./ed25519tool certverify --localcerts.signingbundle=${CREDENTIALS_DIR}/${CERT_FILE} --localcerts.cabundle=${CREDENTIALS_DIR}/${CERT_FILE} ${TESTING_INPUT_DIR}/sample-infile.txt ${TESTING_OUTPUT_DIR}/old-timestamp-sample-infile.txt.sig

## will succeed
./ed25519tool verify --publickeypath=${CREDENTIALS_DIR}/${PUBLIC_KEY_FILE} ${TESTING_INPUT_DIR}/sample-infile.txt ${TESTING_OUTPUT_DIR}/acceptable-timestamp-sample-infile.txt.sig

## will succeed
./ed25519tool certverify --localcerts.signingbundle=${CREDENTIALS_DIR}/${CERT_FILE} --localcerts.cabundle=${CREDENTIALS_DIR}/${CERT_FILE} ${TESTING_INPUT_DIR}/sample-infile.txt ${TESTING_OUTPUT_DIR}/acceptable-timestamp-sample-infile.txt.sig

## will succeed
./ed25519tool verify --publickeypath=${CREDENTIALS_DIR}/${PUBLIC_KEY_FILE} ${TESTING_INPUT_DIR}/sample-infile.txt ${TESTING_OUTPUT_DIR}/now-timestamp-sample-infile.txt.sig

## will succeed
./ed25519tool certverify --localcerts.signingbundle=${CREDENTIALS_DIR}/${CERT_FILE} --localcerts.cabundle=${CREDENTIALS_DIR}/${CERT_FILE} ${TESTING_INPUT_DIR}/sample-infile.txt ${TESTING_OUTPUT_DIR}/now-timestamp-sample-infile.txt.sig


## all good with self signed CA if we supply it as a command line arg
./ed25519tool certverify --localcerts.signingbundle=${CREDENTIALS_DIR}/${CERT_FILE} --localcerts.cabundle=${CREDENTIALS_DIR}/${CERT_FILE} ${TESTING_INPUT_DIR}/sample-infile.txt --strict=true ${TESTING_OUTPUT_DIR}/acceptable-timestamp-sample-infile.txt.sig

## will fail due to self-signed certificate in strict mode, if we do not supply same as CA
./ed25519tool certverify --localcerts.signingbundle=${CREDENTIALS_DIR}/${CERT_FILE} ${TESTING_INPUT_DIR}/sample-infile.txt --strict=true ${TESTING_OUTPUT_DIR}/acceptable-timestamp-sample-infile.txt.sig

## will succeed
./ed25519tool verify --publickeypath=${CREDENTIALS_DIR}/${PUBLIC_KEY_FILE} ${TESTING_INPUT_DIR}/sample-infile.txt ${TESTING_OUTPUT_DIR}/future-timestamp-sample-infile.txt.sig
```sh
./pqsig keygen -kid stackql-2026-01 \
-public-keys new-public-keys.json \
-github-secret MLDSA_SIGNING_SEED \
-repo stackql/stackql-provider-registry
```

## should and will fail with timestamp message
./ed25519tool certverify --localcerts.signingbundle=${CREDENTIALS_DIR}/${CERT_FILE} --localcerts.cabundle=${CREDENTIALS_DIR}/${CERT_FILE} ${TESTING_INPUT_DIR}/sample-infile.txt ${TESTING_OUTPUT_DIR}/future-timestamp-sample-infile.txt.sig
The command generates an ML-DSA-65 key, pipes its hex-encoded 32-byte seed
directly to `gh secret set`, and writes only the public key. It refuses to
overwrite the public key file. Review and add the public entry to
[`signing/pqsig/keys.json`](../signing/pqsig/keys.json); never commit a seed.
For rotation, add rather than replace public entries until old archives expire.
Ship the updated verifier before using the new key to publish documents.

Sign in place, or write to a separate output:

```sh
# STACKQL_SIGNING_SEED is supplied by the secret manager, not a command argument.
./pqsig sign-doc -kid stackql-2026-01 provider.yaml
./pqsig sign-doc -kid stackql-2026-01 -out signed.yaml provider.yaml
./pqsig verify-doc signed.yaml
./pqsig verify-doc -public-keys custom-public-keys.json signed.yaml
./pqsig validate-doc provider.yaml
```

```
`sign-doc` checks the generated signature before replacing the output. It does
not assert that the key is an embedded production key; `verify-doc` does that.
CI runs both before publishing. Missing seeds, unknown keys, malformed documents
and invalid signatures exit nonzero. See [the format and policy](signing-and-verification.md).
Loading
Loading