Skip to content

[FEATURE] Quantum-safe provider doc signing: ML-DSA via crypto/mldsa, signature embedded in the doc, drop .sig sidecars #482

Closed

Description

@jeffreyaven

Replace the Ed25519 + X.509 + .sig sidecar scheme with a quantum-safe signature from the Go stdlib, embedded in the provider document itself. Coordinated with stackql/any-sdk#153, stackql-labs/omnisdk#17 and stackql/stackql#856. stackql has verification off by default (verifyConfig.nopVerify: true), so this rolls out without breaking existing users.

NOTE: Must be compliant with at least FIPS 204.

What changes

  • Algorithm: ML-DSA (FIPS 204) via crypto/mldsa, new in Go 1.27. Requires go 1.27 in go.mod (currently go 1.17).

  • Signature lives in the doc, hex encoded. No .sig files. Proposed key:

    x-stackql-signature:
      alg: ML-DSA-65
      kid: stackql-2026-01
      sig: <hex>
  • Sign: load YAML -> drop x-stackql-signature -> RFC 8785 canonical JSON (github.com/gowebpki/jcs, Apache-2.0) -> mldsa sign with context stackql-provider-doc-v1 -> write the key back. Verify: pop the key -> same canonicalisation -> mldsa.Verify.

  • Keys: the secret is the 32-byte ML-DSA seed (mldsa.NewPrivateKey(params, seed)). Public keys embedded in the verifier, selected by kid. Rotation is adding a kid. No X.509, no signed timestamps, no faketime cert renewals (the cause of the stale edcrypto test fixtures today).

  • Coverage unchanged: one signature per doc (provider.yaml and every services/*.yaml), so any-sdk's per-doc verification keeps its shape.

One shared package

Signer (CI here) and verifier (any-sdk) must canonicalise byte-for-byte identically. Canonicalise + sign + verify + embedded keyset go in one Go package in this repo (signing/pqsig), which any-sdk already imports. Tests use ephemeral keys; no production key material in fixtures.

Sequence and dependencies

  1. This repo: signing/pqsig, go 1.27, CLI sign-doc / verify-doc (in place), tagged release. No dependencies.
  2. any-sdk and stackql: consume the release, verify embedded signatures, fall back to .sig when absent. Tracked in [FEATURE] Verify ML-DSA signatures embedded in provider docs (x-stackql-signature), with .sig fallback any-sdk#153 (current verifier), [FEATURE] Verify ML-DSA signatures embedded in provider docs (x-stackql-signature) when opening a registry tree stackql-labs/omnisdk#17 (successor verifier, omnisdk is replacing any-sdk) and [FEATURE] Verify ML-DSA signatures embedded in provider docs (x-stackql-signature), replacing .sig sidecars stackql#856 (bump, config, docs). Blocked by 1.
  3. This repo: CI signs in place with a new seed secret. Keep emitting .sig + Ed25519 until a stackql release with step 2 is out, then drop .sig, edcrypto, X.509 and the Ed25519 secrets. Blocked by 2.

Decisions needed

  • Parameter set: Go's package doc says "Most applications should use MLDSA44". ML-DSA-65 is the conservative pick for long-lived archives. Signatures are 2420 / 3309 bytes, double that as hex; negligible against doc sizes.
  • Per-doc signatures (above) vs one signature in provider.yaml over a digest manifest of the service docs.
  • Canonicalisation edge cases to pin down in the package: YAML 1.1 scalars (yes/no, octal), floats, ints beyond 2^53, anchors and merge keys, non-string keys, duplicate keys. Reject docs that do not round-trip.

Related: stackql/stackql#832 (verify before extraction and trust-root policy). This issue changes how a doc is signed, not what is verified or when.

Activity

  1. added
    enhancementNew feature or request
    goPull requests that update go code
    on Oct 21, 2026
  2. jeffreyaven commented on Oct 23, 2026

    @jeffreyaven
    MemberAuthor

    Implemented by #487 with stackql/any-sdk#155, stackql-labs/omnisdk#23, stackql/stackql#869 and stackql/stackql-provider-tests#2.

    Approved changes to the proposal: use standard-library ML-DSA-65 and strictly framed exact-byte raw-v1 signatures, not RFC 8785. The corpus has 439 documents with aliases, 13 with numbers outside JSON's safe integer range, and one unquoted date; all 1,968 documents now sign and verify without rewriting their contents. The signing package is an independently publishable nested Go module because the full provider tree exceeds Go's module-zip limit. Legacy signing code is removed; runtime verification remains opt-in.

    The new production signing seed is provisioned in Actions and only its public key is committed. Registry CI, including the full signing audit, is green. Merge provider-tests first, then both SDKs, then StackQL AND publish the new binary. Merge this registry PR LAST, after its provider-test downloader receives that binary. Code-only merging does not rewrite deployed provider artifacts; legacy archives need republishing before opting in. Important closure exception: this repository defaults to dev, but the production PR targets main. GitHub will not auto-close this issue from that merge. Closing #482 immediately after merging #487 is now an explicit mandatory step in the PR cutover checklist; no default-branch setting or broad automation was changed.

  3. jeffreyaven commented on Oct 24, 2026

    @jeffreyaven
    MemberAuthor

    Implementation merged into main via #487 and synchronized into dev via #488 (c92b7b6). Verified main is an ancestor of dev, the trees are identical, and signing/v0.1.0-alpha01 is preserved in dev history. Normal feature -> dev -> main flow is restored. Verification remains opt-in; publish StackQL v0.13 before the next provider publication. Existing provider revisions acquire ML-DSA signatures incrementally as updated or republished. Post-sync CI is currently running; this closes the implemented migration issue, not a claim that v0.13 has been released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgoPull requests that update go code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions