Skip to content

store: prepare for signing-key rotation (canonical signer + staged second key)#12

Open
cyberb wants to merge 2 commits into
masterfrom
add-second-signing-key
Open

store: prepare for signing-key rotation (canonical signer + staged second key)#12
cyberb wants to merge 2 commits into
masterfrom
add-second-signing-key

Conversation

@cyberb

@cyberb cyberb commented Jul 6, 2026

Copy link
Copy Markdown
Member

Phase 1 of rotating the syncloud signing key. Two coupled changes to the store (both touch crypto/signer.go):

1. Canonical signer — signatures now actually verify

The store hand-assembled assertion text with the wrong header set and framing for every type (primary-key, publisher-id, developer-id, revision, sha3-384 … and missing required headers like name). snapd verifies over its own canonical serialization, so these never matched — which is the whole reason the snapd fork disabled signature verification (SignatureCheck returns nil).

This rewrites signing to snapd's canonical form: type, authority-id, primary keys, remaining headers lexicographically, body-length, sign-key-sha3-384, then body — plus the per-type required headers (account-key: name/since; snap-declaration: publisher-id/timestamp; snap-revision: developer-id/timestamp).

Proven: a test emits each assertion (account-key, snap-declaration, snap-revision) and verifies it with raw openpgp (independent of the fork's disabled check), for both the old and the new key. Cross-checked out-of-band that all three also pass snapd's real asserts.Decode + signature verification.

This is the prerequisite for removing the signature-check hack from the snapd fork (a later, separately-verified step).

Also fixes a latent typo surfaced by real verification: model.SnapRevision.Sha384 json tag was snap-sha3-385, so it parsed empty; the snap-revision snap-sha3-384 now comes from the request key (authoritative).

2. Staged second signing key (not enabled)

  • crypto.NewSigner(logger, activeKey, newKeyArmored) — selectable key, defaults to old, so behaviour is unchanged.
  • New private key injected from the signing_key_new_base64 Drone secret (added) via config/*/secret.yaml + ci/deploy-prepare.sh, base64 single-line; never in source. Wired into uat + prod deploy steps.
  • Only decoded when signing_key_active == new.

New key: public-key-sha3-384: NX7IJUak… (self-signature independently verified). Pairs with syncloud/snapd#5 which trusts its public half.

To enable later

Set signing_key_active: new and redeploy — after the fleet is on snapd#5, and after the canonical signer has shipped so verification stays honest.

cyberb added 2 commits July 6, 2026 09:38
Add a selectable signing key so the store can be switched to the new
rotation key without a code change:

- config: signing_key_active (default: old) + signing_key_new_base64
- crypto.NewSigner takes the active selection and the new key material;
  with 'old' (the default) it keeps signing with the existing key, so
  behaviour is unchanged
- cmd/store: only decode the new key when active is 'new'
- the new private key is injected from the 'signing_key_new_base64'
  Drone secret via config/*/secret.yaml + deploy-prepare.sh; it never
  lives in source
- new key is base64-encoded so it is a safe single-line secret value

To flip later: set signing_key_active: new and redeploy. Pairs with the
snapd change trusting the new key's public half.
Replace the hand-assembled assertion text (wrong header set and framing,
which is why the snapd fork had to disable signature verification) with
snapd's canonical serialization: type, authority-id, primary keys,
remaining headers lexicographically, body-length, sign-key-sha3-384,
then body. Add the per-type required headers snapd validates
(account-key: name/since; snap-declaration: publisher-id/timestamp;
snap-revision: developer-id/timestamp).

Use the request key as the snap-revision snap-sha3-384 primary key; the
model's Sha384 json tag is 'snap-sha3-385' (a typo) so it parsed empty
and was only ever masked by the disabled verification.

Verified: every produced assertion (account-key, snap-declaration,
snap-revision) passes snapd's real asserts.Decode plus independent raw
openpgp signature verification, for BOTH the old and the new key. This
is the prerequisite for removing the Syncloud signature-check hack.
@cyberb cyberb changed the title signer: stage a second signing key (rotation Phase 1, not enabled) store: prepare for signing-key rotation (canonical signer + staged second key) Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant