Repository navigation
feat(apt): verify OpenPGP signatures hermetically when sqv/gpgv are missing - #287
Closed
devPalacio wants to merge 2 commits into
Closed
devPalacio wants to merge 2 commits into
devPalacio wants to merge 2 commits into
Conversation
…issing Fall back to a BouncyCastle-based verifier (apt/private/Verify.java) run on a rules_java remote JDK when neither sqv nor gpgv is on PATH. The BouncyCastle jars are pinned by sha256. sqv and gpgv are still preferred when present. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contributor
|
I think I would prefer to not merge this (maintain our own verifier), for now we can get presubmit to be green by adding gnupg to the build. |
…CI job Rework the hermetic verifier after an adversarial review of the first version: - Use org.bouncycastle.openpgp.api (certificate validity, revocation, expiry, key capabilities, hash and key-strength policy) instead of the low-level signature API. Only a signature type allowlist, verification at the current time and the RFC 4880 7.1 reduction of cleartext messages are written here. - Reject a lone CR in cleartext messages, strip trailing blanks, and return the exact text that was verified. - Exit 2 for parse and I/O errors so they are not reported as bad signatures. - Clear JAVA_TOOL_OPTIONS and friends for the verifier JVM; skip the host rm for it. - apt/tests/pgp: signed fixtures and a runner (35 cases, including forgery, weak digest, expiry, revocation and lone-CR cases). - CI: run the fixtures and the e2e suites with no OpenPGP tools on PATH. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contributor
Author
yea based on how this is going, just adding gnupg seems much more pragmatic :) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
aptsignature verification needssqvorgpgvonPATH(plusgpgorsqto dearmor.asckeyrings). Without them, extension evaluation fails:Stock macOS has none of these tools. This is what fails the macOS jobs of the BCR presubmit for 0.11.0 (bazel-central-registry#10868, build 42895). It also affects any macOS user of
gpg_keys, and it is a host-tool dependency of the kind #185, #141 and #283 removed.Change
When neither
sqvnorgpgvis onPATH, fall back to a hermetic verifier instead of failing.sqvandgpgvare still preferred when present, so nothing changes for anyone who has them.apt/private/Verify.javaruns on the host'srules_javaremotejdk21_*JDK, with the three BouncyCastle 1.86 jars downloaded from Maven Central and pinned by sha256.org.bouncycastle.openpgp.api, default policy): certificate validity, revocation, expiry, key capabilities, hash and key-strength policy, and text-mode canonicalisation for detached signatures. Armored and binary keyrings, including several concatenated armored blocks, are read directly, so there is nogpg --dearmorstep.Verify.javaadds is small: an allowlist of document signature types (the library otherwise accepts any signature class, so a key certification could pass as a signature over attacker-chosen bytes), verification at the current time, and the RFC 4880 section 7.1 reduction of a cleartext-signedInReleaseto the text that was signed, since the library has no cleartext support. The text that is returned is exactly the text that was verified.sqvrule: at least one valid signature from a key in the keyrings. Exit code 1 means not verified, 2 means the verifier could not process its inputs, andpgp.bzlreports the two differently.pgp.bzlclearsJAVA_TOOL_OPTIONS,JDK_JAVA_OPTIONSand_JAVA_OPTIONSfor the verifier JVM, and does not call the hostrmfor it.MODULE.bazel:use_extension(@rules_java//java:extensions.bzl, "toolchains")anduse_repofor the nineremotejdk21_*repos (all nine exist in rules_java 8.8.0).It is stricter than
gpgvin a few places on purpose: SHA-1 and MD5 signatures, expired or revoked keys, and expired signatures are rejected, assqvdoes.Testing
apt/tests/pgp: 35 cases with checked-in signed fixtures (good, trailing whitespace, dash escaping, no final newline, CRLF, tampered text, lone CR, truncated, SHA-1, MD5, expired signature, expired key, revoked key, wrong key, certification-signature replay, standalone and certification classes, encryption-only subkey, concatenated and invalid keyrings). They run as of a fixed time, so they do not depend on the clock. I mutation-tested the suite: removing the signature-type allowlist, the whitespace stripping, the lone-CR rejection, or the check at the current time each makes exactly the expected cases fail.hermetic-pgpCI job: the fixture suite,e2e/smoke, ande2e/invalid_key, with aPATHthat has nogpg*,sqorsqv. I ran the same steps locally; the workflow itself has not run yet.PATHand a fresh Bazel cache:e2e/smokebuilds and analyzes on macOS (arm64) with Bazel 7.x and 8.5.1, and on Ubuntu 24.04 (analysis only) with both;e2e/invalid_keyfails with "GPG verification failed" on all of them.bazel test //apt/tests/...: 44 of 44 pass.mainwithout this change fails with the error above.tar.bzlbump) and BCR's incompatible flags, as in the presubmit, it also builds on macOS.Not tested: Windows and the non-host JDK platforms (declared, not exercised), and the real BCR presubmit run.
Review
The first version of this PR used BouncyCastle's low-level signature API and hand-written policy. An adversarial review by four reviewers found, among other things, that a key certification was accepted as a valid
Release.gpg, that expired signatures and keys, MD5 and SHA-1 were accepted, and that the whitespace and CR handling differed fromgpgv. This version replaces that code with the library's verifier and covers each finding with a fixture.Open questions
sqv/gpgv.Verify.javais compiled by the source launcher on every call.Prior art
rules_jvm_externaluses BouncyCastle OpenPGP classes in a Bazel-launched Java tool (MavenSigning.java), for signing.bazeldnfverifies RPMs withgolang.org/x/crypto/openpgpinside a Bazel-built binary.envoyproxy/toolshed(bazel/pgp) ships a pinned prebuiltsq, for Linux x86_64 and arm64 only.Sequoia does not publish prebuilt
sqvorsqbinaries, and BCR has no OpenPGP module, so I used the JDK this repo already depends on.Related: #280 (the BCR PRs for 0.9.5, 0.10.0 and 0.11.0 not published) and #286 (
tar.bzlbump, the other cause of the 0.11.0 presubmit failure).🤖 Generated with Claude Code