Name the key that actually signed in generate-signature, or name none #5

Merged
marcos-mendez merged 2 commits from fix/signature-key-identity into 19.x 2026-09-26 17:39:47 -03:00
marcos-mendez commented 2026-09-26 17:27:35 -03:00 (Migrated from github.com)

bin/generate-signature hardcoded TurnKey's release key (tkl-$CODENAME-images.asc, release-$CODENAME-images@turnkeylinux.org, the raw.githubusercontent URL) and wrote a .hash telling the reader to import that key and verify with it, while signing nothing. The layers released on 2026-09-26 carried such files and were removed from the mirror by hand.

What the script does now

BT_GPGKEY set writes <file>.hash, clearsigns it with that key, then verifies the signature it just made: the VALIDSIG primary fingerprint must be the fingerprint the file names, or the run fails. The key quoted in the text is checked against the signing key, so a published key file that is not the signer is refused. A run that dies between writing and signing removes the unsigned file.
BT_GPGKEY unset writes <file>.sha256 and <file>.sha512 in shaNsum -c format, removes a stale .hash, and names no key anywhere. Exit 0, because a caller with no key did not ask for a signature; --require-signature makes it fatal for callers that did.

Identity comes from BT_GPGKEY, BT_SIGN_KEY_EMAIL, BT_SIGN_KEY_FPR, BT_SIGN_KEY_FILE and BT_SIGN_KEY_URL. No URL is defaulted for us: https://archive.keellinux.org/keel-archive-keyring.asc does not exist yet, so the text says the key is published with the release and the reader compares the fingerprint. The operator sets BT_SIGN_KEY_URL the day the keyring is published.

Upstream stays reachable, byte for byte

When the signing key is the upstream release key of the codename, the upstream key file and URL are the defaults and the text is the one upstream's script writes, quirks included. tests/signature-reference freezes the old script; tests/signature runs both over one throwaway key and cmps the cleartext of the signed file against the reference output.

Tests

tests/signature is rewritten in the style of tests/layer: throwaway keys generated inside the test in its own GNUPGHOME, a curl stub for the one fetch, no network and no project key. It covers every refusal, including a published key file that is a different key with the same address. tests/coverage.sh runs it as a third suite.

kcov line coverage (threshold 99 percent):
 100.00    85/85    generate-signature ok
  99.49   195/196   layer-lib    ok
  99.02   101/102   bt-layer     ok
 100.00    47/47    bt-aplinfo   ok
 100.00    76/76    signature-lib ok
 100.00   186/186   aplinfo-lib  ok

expect_ok, used twice by the audit tests of #4 but never defined, is defined here: under kcov a command not found does not stop the suite, so both assertions had been skipped, while the same suite fails outright outside kcov.

Test plan

  • tests/signature and tests/coverage.sh 99 locally, gpg 2.4.7, kcov 43
  • upstream text reproduced byte for byte by cmp
  • hash file read back by bin/signature-verify -f
  • tests / coverage on this pull request
`bin/generate-signature` hardcoded TurnKey's release key (`tkl-$CODENAME-images.asc`, `release-$CODENAME-images@turnkeylinux.org`, the raw.githubusercontent URL) and wrote a `.hash` telling the reader to import that key and verify with it, while signing nothing. The layers released on 2026-09-26 carried such files and were removed from the mirror by hand. ## What the script does now | | | |---|---| | `BT_GPGKEY` set | writes `<file>.hash`, clearsigns it with that key, then verifies the signature it just made: the `VALIDSIG` primary fingerprint must be the fingerprint the file names, or the run fails. The key quoted in the text is checked against the signing key, so a published key file that is not the signer is refused. A run that dies between writing and signing removes the unsigned file. | | `BT_GPGKEY` unset | writes `<file>.sha256` and `<file>.sha512` in `shaNsum -c` format, removes a stale `.hash`, and names no key anywhere. Exit 0, because a caller with no key did not ask for a signature; `--require-signature` makes it fatal for callers that did. | Identity comes from `BT_GPGKEY`, `BT_SIGN_KEY_EMAIL`, `BT_SIGN_KEY_FPR`, `BT_SIGN_KEY_FILE` and `BT_SIGN_KEY_URL`. No URL is defaulted for us: `https://archive.keellinux.org/keel-archive-keyring.asc` does not exist yet, so the text says the key is published with the release and the reader compares the fingerprint. The operator sets `BT_SIGN_KEY_URL` the day the keyring is published. ## Upstream stays reachable, byte for byte When the signing key is the upstream release key of the codename, the upstream key file and URL are the defaults and the text is the one upstream's script writes, quirks included. `tests/signature-reference` freezes the old script; `tests/signature` runs both over one throwaway key and `cmp`s the cleartext of the signed file against the reference output. ## Tests `tests/signature` is rewritten in the style of `tests/layer`: throwaway keys generated inside the test in its own `GNUPGHOME`, a `curl` stub for the one fetch, no network and no project key. It covers every refusal, including a published key file that is a different key with the same address. `tests/coverage.sh` runs it as a third suite. ``` kcov line coverage (threshold 99 percent): 100.00 85/85 generate-signature ok 99.49 195/196 layer-lib ok 99.02 101/102 bt-layer ok 100.00 47/47 bt-aplinfo ok 100.00 76/76 signature-lib ok 100.00 186/186 aplinfo-lib ok ``` `expect_ok`, used twice by the audit tests of #4 but never defined, is defined here: under kcov a command not found does not stop the suite, so both assertions had been skipped, while the same suite fails outright outside kcov. ## Test plan - [x] `tests/signature` and `tests/coverage.sh 99` locally, gpg 2.4.7, kcov 43 - [x] upstream text reproduced byte for byte by `cmp` - [x] hash file read back by `bin/signature-verify -f` - [ ] `tests / coverage` on this pull request
Sign in to join this conversation.
No description provided.