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

  1. Read the gpgv stderr in the exception message -- it will say 'No public key' (missing key), 'BAD signature' (tampered), or 'signature verification failed' (corrupted).
  2. Install the correct keyring package for the distribution: debian-ports-archive-keyring (Debian ports), debian-archive-keyring (Debian mainstream), or ubuntu-archive-keyring (Ubuntu).
  3. Update the keyring to the latest version to get current signing keys.
  4. Verify the --keyring path is correct and the file is readable: ls -l <keyring-path>.
  5. Ensure gpgv is installed: which gpgv (install gnupg package if missing).
  6. 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.
  7. 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

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


AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13). Data as JSON: /api/errors/67a08ae33d66f2f4. Report an issue: GitHub.