dotnet/yarp · critical · Exception
Signature verification failed: {result.stderr.decode('utf-8'
Error message
Signature verification failed: {result.stderr.decode('utf-8')} What it means
This exception is raised in fetch_release_file when the gpgv subprocess returns a non-zero exit code, meaning the detached GPG signature in Release.gpg does not verify against the Release file using the provided keyring. The script uses gpgv (not gpg) for robustness on GnuPG 2.4+ hosts. The error includes stderr from gpgv, which typically explains the specific failure (expired key, missing key, bad signature). Signature verification is a security control -- it ensures the Release file (and by extension the package index checksums) were published by the repository maintainers and have not been tampered with.
Source
Thrown at eng/common/cross/install-debs.py:135
release_gpg_url = f"{mirror}/dists/{suite}/Release.gpg"
with tempfile.NamedTemporaryFile() as release_file, tempfile.NamedTemporaryFile() as release_gpg_file:
await download_file(session, release_url, release_file.name)
await download_file(session, release_gpg_url, release_gpg_file.name)
print("Verifying signature of Release with Release.gpg.")
# Use gpgv rather than gpg for verification. gpgv verifies a detached
# signature against a fixed keyring without involving gpg-agent or
# keyboxd, which makes it robust on hosts running GnuPG 2.4+ (e.g. Azure
# Linux) where "gpg --keyring" routes through keyboxd and can fail.
verify_command = ["gpgv"]
if keyring:
verify_command += ["--keyring", keyring]
verify_command += [release_gpg_file.name, release_file.name]
result = subprocess.run(verify_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode != 0:
raise Exception(f"Signature verification failed: {result.stderr.decode('utf-8')}")
print("Signature verified successfully.")
with open(release_file.name) as f:
return f.read()
def parse_release_file(content, path):
"""Parses the Release file and returns sha256 checksum of the specified path."""
# data looks like this:
# <checksum> <size> <path>
matches = re.findall(r'^ (\S*) +(\S*) +(\S*)$', content, re.MULTILINE)
for entry in matches:
# the file has both md5 and sha256 checksums, we want sha256 which has a length of 64
if entry[2] == path and len(entry[0]) == 64:
return entry[0]
View on GitHub (pinned to bd11867bee)
Solutions
- Read the gpgv stderr in the exception message -- it will say 'No public key' (missing key), 'BAD signature' (tampered), or 'signature verification failed' (corrupted).
- Install the correct keyring package for the distribution: debian-ports-archive-keyring (Debian ports), debian-archive-keyring (Debian mainstream), or ubuntu-archive-keyring (Ubuntu).
- Update the keyring to the latest version to get current signing keys.
- Verify the --keyring path is correct and the file is readable: ls -l <keyring-path>.
- Ensure gpgv is installed: which gpgv (install gnupg package if missing).
- If you trust the mirror and cannot resolve the key issue, disable verification by omitting --force-check-gpg -- but understand this removes the integrity guarantee.
- Switch to the official mirror to rule out a compromised or misconfigured third-party mirror.
Defensive patterns
Strategy: try-catch
Validate before calling
# Pre-check: verify gpgv is installed and the keyring file exists and is readable.
import shutil, os
if not shutil.which('gpgv'):
print("ERROR: gpgv not found. Install the gnupg package.")
if keyring and not os.path.isfile(keyring):
print(f"ERROR: Keyring file not found: {keyring}")
elif keyring and not os.access(keyring, os.R_OK):
print(f"ERROR: Keyring file not readable: {keyring}") Try / catch
# Catch signature verification failure and provide actionable guidance
try:
release_content = await fetch_release_file(session, mirror, suite, keyring)
except Exception as e:
stderr_msg = str(e)
if 'No public key' in stderr_msg:
print("Signing key not in keyring. Install/update the appropriate keyring package.")
elif 'BAD signature' in stderr_msg:
print("SECURITY ALERT: Release file signature is invalid. "
"Do not proceed -- the mirror may be compromised.")
raise Prevention
- Install the correct keyring package for your distribution before enabling --force-check-gpg.
- Keep the keyring package updated to get current signing keys (keys are rotated periodically).
- Verify gpgv is installed and on PATH before running the script.
- If you cannot resolve the key issue, omit --force-check-gpg but understand the security trade-off.
- Use official mirrors to reduce the risk of encountering a tampered Release file.
When it happens
Trigger: fetch_release_file (lines 113-135) downloads Release and Release.gpg, constructs a gpgv command with optional --keyring, runs it via subprocess.run, and checks result.returncode. Non-zero triggers this exception. Causes: the signing key is not present in the provided keyring; the key has expired or been revoked; the Release or Release.gpg file was corrupted in transit; gpgv is not installed or not on PATH; the keyring file path is wrong or unreadable; the mirror is serving a tampered Release file.
Common situations: Missing or wrong keyring package (e.g. debian-ports-archive-keyring for ports, ubuntu-archive-keyring for Ubuntu); the repository's signing key was rotated and the local keyring is outdated; using an Ubuntu keyring against a Debian mirror or vice versa; gpgv not installed in the build container; keyring file path passed via --keyring is incorrect or the file lacks read permissions; the mirror is compromised or serving a man-in-the-middle attack (rare but this is exactly what the check is designed to catch).
Related errors
- SHA256 mismatch for {path}: expected {packages_sha}, got {sh
- SHA256 mismatch for {url}: expected {checksum}, got {sha256}
- Failed to download {url}, Status Code: {response.status}
- Failed to download {url} after {max_retries} attempts.
- Could not find checksum for {path} in Release file.
AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13).
Data as JSON: /api/errors/67a08ae33d66f2f4.
Report an issue: GitHub.