{"record":{"id":"412afeb8d4adc786","repo":"dotnet/efcore","slug":"signature-verification-failed-result-stderr-deco","errorCode":null,"errorMessage":"Signature verification failed: {result.stderr.decode('utf-8')}","messagePattern":"Signature verification failed: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"eng/common/cross/install-debs.py","lineNumber":135,"sourceCode":"    release_gpg_url = f\"{mirror}/dists/{suite}/Release.gpg\"\n\n    with tempfile.NamedTemporaryFile() as release_file, tempfile.NamedTemporaryFile() as release_gpg_file:\n        await download_file(session, release_url, release_file.name)\n        await download_file(session, release_gpg_url, release_gpg_file.name)\n\n        print(\"Verifying signature of Release with Release.gpg.\")\n        # Use gpgv rather than gpg for verification. gpgv verifies a detached\n        # signature against a fixed keyring without involving gpg-agent or\n        # keyboxd, which makes it robust on hosts running GnuPG 2.4+ (e.g. Azure\n        # Linux) where \"gpg --keyring\" routes through keyboxd and can fail.\n        verify_command = [\"gpgv\"]\n        if keyring:\n            verify_command += [\"--keyring\", keyring]\n        verify_command += [release_gpg_file.name, release_file.name]\n        result = subprocess.run(verify_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n\n        if result.returncode != 0:\n            raise Exception(f\"Signature verification failed: {result.stderr.decode('utf-8')}\")\n\n        print(\"Signature verified successfully.\")\n\n        with open(release_file.name) as f:\n            return f.read()\n\ndef parse_release_file(content, path):\n    \"\"\"Parses the Release file and returns sha256 checksum of the specified path.\"\"\"\n\n    # data looks like this:\n    # <checksum>  <size>  <path>\n    matches = re.findall(r'^ (\\S*) +(\\S*) +(\\S*)$', content, re.MULTILINE)\n\n    for entry in matches:\n        # the file has both md5 and sha256 checksums, we want sha256 which has a length of 64\n        if entry[2] == path and len(entry[0]) == 64:\n            return entry[0]\n","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/eng/common/cross/install-debs.py#L117-L153","documentation":"Raised by fetch_release_file when gpgv exits non-zero verifying Release.gpg against Release with the supplied keyring. gpgv is used deliberately (instead of gpg) to avoid gpg-agent/keyboxd issues on GnuPG 2.4+ hosts. A failure means the detached signature does not validate the Release file using the keys in the keyring.","triggerScenarios":"Running with --force-check-gpg and a --keyring that does not contain the signing key for the suite; the Release file has been modified after signing; the signing key is expired/revoked; the keyring path is wrong/empty; system clock is far off so signature validity windows are misjudged.","commonSituations":"Forgot to install the distro keyring package; passed an Ubuntu keyring against a Debian(-ports) mirror or vice versa; used an outdated keyring whose archive-signing key has rotated/expired; mis-typed the --keyring path; NTP off so gpgv rejects a not-yet-valid or expired signature.","solutions":["Install the matching archive keyring package for your distro/suite (e.g. debian-ports-archive-keyring, ubuntu-keyring, debian-archive-keyring) and point --keyring at it.","Read {result.stderr} from the message - gpgv states 'NO_PUBKEY <keyid>' (missing key), 'BADSIG' (tampered), or 'EXPKEYSIG' (expired) which points to the exact cause.","Sync the system clock (NTP) so signature time validity is evaluated correctly.","If you intentionally trust the source, drop --force-check-gpg / pass --skipsigcheck to build-rootfs.sh rather than ignoring the failure."],"exampleFix":"# before\npython install-debs.py --force-check-gpg --keyring /usr/share/keyrings/debian-archive-keyring.gpg --mirror http://ftp.ports.debian.org/debian-ports ...\n# after (ports mirror needs the ports keyring)\napt-get install debian-ports-archive-keyring\npython install-debs.py --force-check-gpg --keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg --mirror http://ftp.ports.debian.org/debian-ports ...","handlingStrategy":"validation","validationCode":"# Validate the keyring actually contains the suite's signing key before calling\nimport subprocess, sys\n\ndef keyring_has_key(keyring, keyid):\n    if not keyring or not __import__('os').path.exists(keyring):\n        raise RuntimeError(f\"Keyring missing or not found: {keyring!r}\")\n    out = subprocess.run(['gpgv', '--keyring', keyring, '--list-packets', '/dev/null'],\n                         capture_output=True)  # smoke: gpgv accepts the keyring\n    # Better: dump keys with gpg --no-default-keyring --keyring <keyring> --list-keys\n    res = subprocess.run(['gpg', '--no-default-keyring', '--keyring', keyring, '--list-keys', '--with-colons'],\n                         capture_output=True, text=True)\n    if keyid not in res.stdout:\n        raise RuntimeError(f\"Keyring {keyring} lacks key {keyid}; install the right archive-keyring package\")\n    return True","typeGuard":null,"tryCatchPattern":"# Signature failures are security-critical; do not retry silently\ntry:\n    content = await fetch_release_file(session, mirror, suite, keyring)\nexcept Exception as e:\n    msg = str(e)\n    if \"Signature verification failed\" in msg:\n        if \"NO_PUBKEY\" in msg:\n            hint = \"Keyring is missing the signing key - install the matching archive-keyring package.\"\n        elif \"EXPKEYSIG\" in msg or \"EXP\" in msg:\n            hint = \"Signing key expired - update the keyring package.\"\n        elif \"BADSIG\" in msg:\n            hint = \"Release file tampered with - treat as integrity incident.\"\n        else:\n            hint = \"Check system clock and keyring path.\"\n        raise SystemExit(f\"{msg}\\nHint: {hint}\")\n    raise","preventionTips":["Always pair --force-check-gpg with the correct distro-specific --keyring (debian vs debian-ports vs ubuntu).","Read the gpgv stderr token (NO_PUBKEY / EXPKEYSIG / BADSIG) before guessing.","Keep NTP enabled; signature time windows are clock-sensitive.","Install the archive-keyring package from the distro itself rather than hand-rolling keyrings."],"tags":["security","gpg","signature","keyring","verification"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}