{"record":{"id":"d109f8cfda562305","repo":"dotnet/efcore","slug":"sha256-mismatch-for-path-expected-packages-sha","errorCode":null,"errorMessage":"SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}","messagePattern":"SHA256 mismatch for (.+?): expected (.+?), got (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"eng/common/cross/install-debs.py","lineNumber":105,"sourceCode":"    \"\"\"Fetch and decompress the Packages.gz file.\"\"\"\n\n    path = f\"{component}/binary-{arch}/Packages.gz\"\n    url = f\"{mirror}/dists/{suite}/{path}\"\n\n    async with session.get(url) as response:\n        if response.status == 200:\n            compressed_data = await response.read()\n            decompressed_data = gzip.decompress(compressed_data).decode('utf-8')\n            print(f\"Downloaded index: {url}\")\n\n            if check_sig:\n                # Verify the package index against the sha256 recorded in the Release file\n                release_file_content = await fetch_release_file(session, mirror, suite, keyring)\n                packages_sha = parse_release_file(release_file_content, path)\n\n                sha256 = hashlib.sha256(compressed_data).hexdigest()\n                if sha256 != packages_sha:\n                    raise Exception(f\"SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}\")\n                print(f\"Checksum verified for {path}\")\n\n            return decompressed_data\n        else:\n            print(f\"Skipped index: {url} (doesn't exist)\")\n            return None\n\nasync def fetch_release_file(session, mirror, suite, keyring):\n    \"\"\"Fetch Release and Release.gpg files and verify the signature.\"\"\"\n\n    release_url = f\"{mirror}/dists/{suite}/Release\"\n    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.\")","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/eng/common/cross/install-debs.py#L87-L123","documentation":"Raised by fetch_and_decompress when --force-check-gpg is on: the SHA256 of the just-downloaded Packages.gz does not equal the SHA256 recorded for that path in the (signature-verified) Release file. A mismatch means the bytes served for the index are not the bytes the repository signed - either mirror/CDN inconsistency during a push, a truncated/corrupt download, or an active tampering attempt.","triggerScenarios":"fetch_and_decompress(..., check_sig=True) computes hashlib.sha256(compressed_data) and it differs from parse_release_file(release_file_content, path). Happens mid-mirror-update when Release and Packages.gz are briefly out of sync, when a CDN serves a stale/corrupt cached copy, or when a man-in-the-middle alters the payload.","commonSituations":"Hitting a mirror while it is mid-publish (Release updated before Packages.gz); CDN edge cache inconsistency; local transparent proxy rewriting/caching bodies; genuine MITM. Rare but security-meaningful.","solutions":["Retry once or twice - a transient mirror/CDN inconsistency usually resolves within minutes.","Switch to a different mirror to rule out a single bad edge cache.","Confirm the system clock is correct; verify the Release/Release.gpg URLs by hand.","Only as a last resort for a known-good private mirror, drop --force-check-gpg (or pass --skipsigcheck to build-rootfs.sh) - never do this to silence a mismatch on a public mirror you do not control."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"# When --force-check-gpg is on, you cannot pre-verify without re-implementing the\n# fetch; instead validate inputs: mirror is https, clock is synced, suite exists.\nfrom datetime import datetime, timezone\nimport urllib.parse, ntplib  # 'pip install ntplib'\n\ndef preflight_for_checksum(mirror):\n    if urllib.parse.urlparse(mirror).scheme != \"https\":\n        raise RuntimeError(\"Use an https mirror when verifying checksums to avoid MITM\")\n    off = abs(ntplib.NTPClient().request('pool.ntp.org', version=3).tx_time - datetime.now(timezone.utc).timestamp())\n    if off > 300:\n        raise RuntimeError(f\"System clock off by {off:.0f}s; fix NTP before checksum verification\")","typeGuard":null,"tryCatchPattern":"# A checksum mismatch is a security signal - never swallow it; surface and abort\ntry:\n    await download_package_index_parallel(mirror, arch, suites, check_sig=True, keyring=keyring)\nexcept Exception as e:\n    if \"SHA256 mismatch\" in str(e):\n        # try ONE alternate mirror; if it also mismatches, treat as a real integrity incident\n        raise SystemExit(f\"Integrity failure (do NOT ignore): {e}\")\n    raise","preventionTips":["Prefer https mirrors to reduce MITM exposure on the index payload.","Keep NTP running so signature/checksum validity windows are judged correctly.","Treat any checksum mismatch as a security incident, not a transient bug."],"tags":["security","integrity","checksum","sha256","signature"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}