diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index 54680deb3ca..74afe74b649 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -26,3 +26,15 @@ paths: ".github/workflows/test-wheel-windows.yml": ignore: - 'specifying action "\$/[^"]+" in invalid format because ref is missing' + ".github/workflows/build-wheel.yml": + ignore: + - 'specifying action "\$/[^"]+" in invalid format because ref is missing' + ".github/workflows/ci.yml": + ignore: + - 'specifying action "\$/[^"]+" in invalid format because ref is missing' + ".github/workflows/test-sdist-windows.yml": + ignore: + - 'specifying action "\$/[^"]+" in invalid format because ref is missing' + ".github/workflows/coverage.yml": + ignore: + - 'specifying action "\$/[^"]+" in invalid format because ref is missing' diff --git a/.github/actions/fetch_ctk/action.yml b/.github/actions/fetch_ctk/action.yml index 994e35924fc..a946947cdff 100644 --- a/.github/actions/fetch_ctk/action.yml +++ b/.github/actions/fetch_ctk/action.yml @@ -75,6 +75,11 @@ runs: CTK_JSON_URL="$CTK_BASE_URL/redistrib_${{ inputs.cuda-version }}.json" CTK_JSON_FILE="$CACHE_TMP_DIR/redistrib.json" curl -LSs "$CTK_JSON_URL" -o "$CTK_JSON_FILE" + # Pin the metadata JSON: the per-archive sha256 checks below are only + # trustworthy if the JSON that carries those hashes is itself trusted. + python "$CTK_REDIST_TOOL" verify-metadata \ + --cuda-version "${{ inputs.cuda-version }}" \ + --metadata-path "$CTK_JSON_FILE" if [[ "${{ inputs.host-platform }}" == linux* ]]; then function extract() { tar -xvf $1 -C $CACHE_TMP_DIR --strip-components=1 @@ -102,6 +107,13 @@ runs: CTK_COMPONENT_URL="${CTK_BASE_URL}/${CTK_COMPONENT_REL_PATH}" CTK_COMPONENT_COMPONENT_FILENAME="$(basename $CTK_COMPONENT_REL_PATH)" download $CTK_COMPONENT_URL $CTK_COMPONENT_COMPONENT_FILENAME + # Verify the downloaded archive against the sha256 in the (pinned) + # metadata before extracting; fail closed on any mismatch. + CTK_COMPONENT_SHA256="$(python "$CTK_REDIST_TOOL" component-sha256 \ + --host-platform "${{ inputs.host-platform }}" \ + --component "$CTK_COMPONENT" \ + --metadata-path "$CTK_JSON_FILE")" + echo "${CTK_COMPONENT_SHA256} ${CTK_COMPONENT_COMPONENT_FILENAME}" | sha256sum --check --strict - extract $CTK_COMPONENT_COMPONENT_FILENAME rm $CTK_COMPONENT_COMPONENT_FILENAME } diff --git a/.github/actions/harden-pip/action.yml b/.github/actions/harden-pip/action.yml deleted file mode 100644 index 868c30295f5..00000000000 --- a/.github/actions/harden-pip/action.yml +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# SPDX-License-Identifier: Apache-2.0 - -name: Harden pip -description: >- - Install the security-fixed pip (26.2.2) from a hash-pinned wheel so that later - pip invocations do not fall back on the vendored certifi bundle that trusts the - compromised eMudhra/emSign intermediate CAs. The sha256 fragment guarantees the - bootstrap download's integrity even if the bootstrapping pip's trust store is - compromised. See https://github.com/NVIDIA/cuda-python/issues/3195 and - https://discuss.python.org/t/109576. - -inputs: - python: - description: Python interpreter whose pip should be hardened (e.g. a venv's python). - default: python - -runs: - using: composite - steps: - - name: Install hash-pinned pip 26.2.2 - shell: bash --noprofile --norc -xeuo pipefail {0} - env: - HARDEN_PIP_PYTHON: ${{ inputs.python }} - run: | - "${HARDEN_PIP_PYTHON}" -m pip install \ - "pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70" diff --git a/.github/actions/install-sccache/action.yml b/.github/actions/install-sccache/action.yml new file mode 100644 index 00000000000..890b47fc2ea --- /dev/null +++ b/.github/actions/install-sccache/action.yml @@ -0,0 +1,75 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Install pinned rapidsai/sccache +description: Download, verify, and install a pinned rapidsai/sccache binary on Linux + +# Regenerate hashes when bumping SCCACHE_VERSION: +# for arch in x86_64 aarch64; do +# curl -fsSL "https://github.com/rapidsai/sccache/releases/download//sccache-${arch}-unknown-linux-musl.tar.gz" | sha256sum +# done +# Cross-check against the `.sha256` files published on the GitHub release. +# +# Security note (eMudhra/emSign CA incident): +# - Every version bump MUST repeat the embedded-roots check on the extracted +# `sccache` binary: +# grep -ac 'ISRG Root X1' sccache # >0 means a root list is embedded +# grep -ac 'emSign ECC Root CA - G3' sccache # must be 0 +# Pass criteria: either no embedded root list (both return 0; binary defers +# to the OS trust store via `rustls-platform-verifier`, as verified for +# `rapidsai/sccache` v0.18.0-rapids.7), or an embedded `webpki-roots` list +# at 1.0.10+ where `ISRG Root X1` matches and `emSign ECC Root CA - G3` +# returns 0. Match the full root name: 1.0.10 removes only that emSign root +# and still ships other emSign roots. +# - Do NOT switch to or bump `mozilla/sccache` to v0.19.0 (or later without +# repeating the roots check): `mozilla/sccache` v0.19.0 re-embeds +# `webpki-roots` 1.0.5, which still trusts `emSign ECC Root CA - G3`. + +runs: + using: composite + steps: + - name: Install pinned rapidsai/sccache + shell: bash --noprofile --norc -xeuo pipefail {0} + env: + SCCACHE_VERSION: v0.18.0-rapids.7 + run: | + if [[ "$(uname -s)" != "Linux" ]]; then + echo "::error::install-sccache only supports Linux (got $(uname -s))" >&2 + exit 1 + fi + + arch="$(uname -m)" + case "${arch}" in + x86_64) + expected_sha256="44106a2e01aeff99767e66b1a5ae6be074306c518458b367f75287de4532974c" + ;; + aarch64) + expected_sha256="1bad10208481786f2c2e943d7cca8006eaa7e620d122859ef2d61861a733d3d1" + ;; + *) + echo "::error::Unsupported architecture for install-sccache: ${arch}" >&2 + exit 1 + ;; + esac + + tarball="sccache-${arch}-unknown-linux-musl.tar.gz" + url="https://github.com/rapidsai/sccache/releases/download/${SCCACHE_VERSION}/${tarball}" + + tmp_dir="$(mktemp -d)" + trap 'rm -rf "${tmp_dir}"' EXIT + + curl -fsSL -o "${tmp_dir}/${tarball}" "${url}" + ( + cd "${tmp_dir}" + echo "${expected_sha256} ${tarball}" | sha256sum --check --strict - + ) + + if [[ "$(id -u)" -eq 0 ]]; then + tar -C /usr/local/bin -xvzf "${tmp_dir}/${tarball}" --wildcards --strip-components=1 -x '*/sccache' + else + sudo tar -C /usr/local/bin -xvzf "${tmp_dir}/${tarball}" --wildcards --strip-components=1 -x '*/sccache' + fi + + echo "SCCACHE_PATH=/usr/local/bin/sccache" >> "${GITHUB_ENV}" + /usr/local/bin/sccache --version diff --git a/.github/actions/setup-hardened-python/action.yml b/.github/actions/setup-hardened-python/action.yml new file mode 100644 index 00000000000..a3ff530641e --- /dev/null +++ b/.github/actions/setup-hardened-python/action.yml @@ -0,0 +1,150 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Set up hardened Python +description: >- + Install CPython with a pinned uv, create a venv from it, and put the venv + first on PATH. setup-uv verifies the uv binary and uv verifies the + python-build-standalone archive against embedded checksums, so the + interpreter's integrity does not depend on TLS. The job then fails unless + the interpreter's ssl module rejects the compromised eMudhra/emSign CA keys + (CVE-2026-115557; first fixed releases: 3.11.18, 3.12.16, 3.13.18, 3.14.10, + 3.15.2), and pip is updated through that ssl module. Unlike setup-python, + this provides patched Windows builds of maintenance series, for which + python.org publishes no installers. Series that have no fixed release + (3.10 and older, which this branch still builds and tests) are installed + with a warning instead, and their pip is replaced by a sha256-pinned + wheel, so that the pip update does not depend on TLS. See + https://github.com/NVIDIA/cuda-python/issues/3195 and + https://discuss.python.org/t/109678. + +inputs: + python-version: + description: >- + uv Python request, e.g. "3.12" or "3.14t" (free-threaded). The newest + patch known to the pinned uv is installed. Series with a fixed release + listed in the interpreter check (3.11-3.15) must carry it. Series + without a fixed release (3.10 and older) are accepted with a warning, + and pip is installed from a sha256-pinned wheel. + required: true + venv: + description: Directory for the venv. Defaults to a new directory under RUNNER_TEMP. + default: "" + +outputs: + python-path: + description: Absolute path of the venv's interpreter. + value: ${{ steps.venv.outputs.python-path }} + python-version: + description: Full version of the interpreter, e.g. "3.14.10". + value: ${{ steps.venv.outputs.python-version }} + +runs: + using: composite + steps: + - name: Install uv + uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 + with: + # The newest uv whose checksum ships inside the pinned setup-uv, so + # the binary is verified against the SHA-pinned action itself. The uv + # release determines the newest patch of each CPython series, so + # bumping the setup-uv SHA also moves the CPython patch versions. + version: "latest-known" + enable-cache: false + # Some callers (e.g. release verification) have no checkout. + ignore-empty-workdir: true + + - name: Install Python and create venv + id: venv + shell: bash --noprofile --norc -euo pipefail {0} + env: + PY_REQUEST: ${{ inputs.python-version }} + VENV_DIR: ${{ inputs.venv }} + # A per-job install directory ensures the newest patch known to the + # pinned uv is used, never an older interpreter left on a persistent + # self-hosted runner. + UV_PYTHON_INSTALL_DIR: ${{ runner.temp }}/setup-hardened-python/interpreters + UV_NO_CONFIG: "1" + run: | + uv python install --no-bin --no-registry "${PY_REQUEST}" + base="$(uv python find --managed-python --no-project "${PY_REQUEST}")" + venv="${VENV_DIR}" + if [[ -z "${venv}" ]]; then + # A fresh directory per call, so a second call in the same job + # (e.g. build-wheel.yml) leaves the first venv intact. + venv="$("${base}" -c 'import sys, tempfile; print(tempfile.mkdtemp(prefix="venv-", dir=sys.argv[1]))' "${RUNNER_TEMP}")" + fi + # The stdlib venv seeds pip from the interpreter's bundled wheel, so + # nothing is downloaded before the ssl check below. + "${base}" -m venv --clear "${venv}" + if [[ "${RUNNER_OS}" == Windows ]]; then + py="${venv}/Scripts/python.exe" + else + py="${venv}/bin/python" + fi + # Let the interpreter report native absolute paths (Windows included). + python_path="$("${py}" -c 'import sys; print(sys.executable)')" + "${py}" -c 'import os, sys; print(os.path.dirname(sys.executable))' >> "${GITHUB_PATH}" + { + echo "python-path=${python_path}" + echo "python-version=$("${py}" -c 'import platform; print(platform.python_version())')" + } >> "${GITHUB_OUTPUT}" + + - name: Check Python interpreter is patched (CVE-2026-115557) + id: check + # Runs the venv's python through the PATH entry added above. + shell: python {0} + env: + PYTHON: ${{ steps.venv.outputs.python-path }} + run: | + import os + import platform + import sys + + if os.path.normcase(sys.executable) != os.path.normcase(os.environ["PYTHON"]): + print(f"::error::PATH resolves python to {sys.executable}, not the venv interpreter {os.environ['PYTHON']}.") + sys.exit(1) + + FIXED = {(3, 11): 18, (3, 12): 16, (3, 13): 18, (3, 14): 10, (3, 15): 2} + # Series that will never get a fixed release (end of life before the + # fix). Their pip is installed from a pinned wheel instead. + UNPATCHABLE_BELOW = (3, 11) + ssl_patched = "true" + v = sys.version_info + series = (v.major, v.minor) + name = f"{v.major}.{v.minor}" + full = platform.python_version() + print("sys.version:", sys.version) + if series in FIXED: + floor = f"{name}.{FIXED[series]}" + if v.micro < FIXED[series]: + print(f"::error::Python {full} is older than {floor}, the first {name} release whose ssl module rejects the compromised eMudhra CA keys (CVE-2026-115557).") + sys.exit(1) + print(f"Python {full}: ssl module carries the fix (first fixed: {floor}).") + elif series < UNPATCHABLE_BELOW: + ssl_patched = "false" + print(f"::warning::Python {full}: no {name} release rejects the compromised eMudhra CA keys (CVE-2026-115557); pip will be installed from a sha256-pinned wheel instead.") + else: + # Fail closed: a newer series is only accepted once its fixed release is listed. + print(f"::error::Python {full}: no {name} release is known to reject the compromised eMudhra CA keys (CVE-2026-115557).") + sys.exit(1) + + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f: + f.write(f"ssl-patched={ssl_patched}\n") + + - name: Update pip + shell: bash --noprofile --norc -xeuo pipefail {0} + env: + PYTHON: ${{ steps.venv.outputs.python-path }} + SSL_PATCHED: ${{ steps.check.outputs.ssl-patched }} + run: | + if [[ "${SSL_PATCHED}" == "true" ]]; then + "${PYTHON}" -m pip install -U pip + else + # The interpreter's ssl module still trusts the compromised CA keys, + # so do not fetch pip through it. The sha256 fragment guarantees the + # wheel's integrity regardless of the transport. + "${PYTHON}" -m pip install \ + "pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70" + fi diff --git a/.github/actions/upgrade-ca-certificates/action.yml b/.github/actions/upgrade-ca-certificates/action.yml new file mode 100644 index 00000000000..0a8747bbce4 --- /dev/null +++ b/.github/actions/upgrade-ca-certificates/action.yml @@ -0,0 +1,144 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Temporary stopgap for the eMudhra/emSign CA key compromise (CVE-2026-111943) +# on GitHub-hosted runners until GitHub ships fixed images. +# +# On Linux: upgrades ca-certificates via apt so the compromised emSign ECC Root +# CA - G3 anchor is removed from /etc/ssl/certs/ca-certificates.crt. Ubuntu's +# bundle does not include AAA Certificate Services, so the Sectigo cross-signs +# do not chain to a trusted root there. +# On Windows: adds the eight compromised intermediates and their eight Sectigo +# cross-signs (impacted-intermediates.json) to Cert:\LocalMachine\Disallowed so +# schannel-based clients (git, gh, PowerShell/.NET) reject chains through them, +# including chains to AAA Certificate Services, which Windows still trusts. +# +# This action is a no-op on self-hosted runners (runner.environment != 'github-hosted') +# because self-hosted runner operators own and update their own host images. +# +# Removal criteria and implementation notes: security/emudhra-plan2.md +# Tracked by: actions/runner-images#15018 + +name: Harden OS certificate trust (eMudhra/emSign CA stopgap) +description: > + Temporary per-job stopgap for CVE-2026-111943. Upgrades ca-certificates on + GitHub-hosted Linux runners and adds the compromised eMudhra intermediates + and cross-signs to the Disallowed store on GitHub-hosted Windows runners. + No-op on self-hosted. + +runs: + using: composite + steps: + + # ------------------------------------------------------------------------- + # Windows path: add the compromised intermediates to LocalMachine\Disallowed. + # Takes effect immediately for all schannel-based clients in subsequent steps + # (git, gh, PowerShell/.NET, WinHTTP). Must precede actions/checkout. + # ------------------------------------------------------------------------- + - name: Distrust compromised eMudhra intermediates (Windows, GitHub-hosted) + if: ${{ runner.os == 'Windows' && runner.environment == 'github-hosted' }} + shell: pwsh + env: + ACTION_PATH: ${{ github.action_path }} + run: | + $ErrorActionPreference = 'Stop' + . (Join-Path $env:ACTION_PATH 'emudhra_intermediates.ps1') + + $certs = @(Get-EmudhraIntermediates) + try { + # Adding is idempotent, so no early exit when the image is already fixed. + $store = [System.Security.Cryptography.X509Certificates.X509Store]::new('Disallowed', 'LocalMachine') + $store.Open('ReadWrite') + try { + foreach ($cert in $certs) { $store.Add($cert) } + } finally { + $store.Close() + } + + $notDistrusted = @($certs | Where-Object { -not (Test-ExplicitlyDistrusted $_) } | ForEach-Object { $_.EmudhraLabel }) + if ($notDistrusted.Count -gt 0) { + Write-Error "Not explicitly distrusted after adding to LocalMachine\Disallowed: $($notDistrusted -join '; ')" + exit 1 + } + Write-Host "Distrusted $($certs.Count) compromised eMudhra intermediates and cross-signs." + } finally { + $certs | ForEach-Object { $_.Dispose() } + } + + # ------------------------------------------------------------------------- + # Linux path: upgrade ca-certificates via apt. + # The Ubuntu archive GPG check does not depend on the trust store being + # fixed, so this bootstrap is safe. Retries guard against transient mirror + # failures. Must precede actions/checkout. + # ------------------------------------------------------------------------- + - name: Upgrade ca-certificates (Linux, GitHub-hosted) + if: ${{ runner.os == 'Linux' && runner.environment == 'github-hosted' }} + shell: bash + run: | + set -euo pipefail + EMSIGN_G3_FP="86:A1:EC:BA:08:9C:4A:8D:3B:BE:27:34:C6:12:BA:34:1D:81:3E:04:3C:F9:E8:A8:62:CD:5C:57:A3:6B:BE:6B" + CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt" + VERSION_ID="$(. /etc/os-release && echo "${VERSION_ID}")" + + # Fail closed if openssl is missing or the bundle cannot be parsed + # (mirrors ci/tools/runner_trust_probe.sh). + if ! command -v openssl >/dev/null 2>&1; then + echo "::error::openssl not found; cannot verify CA bundle" + exit 1 + fi + if [[ ! -f "${CA_BUNDLE}" ]]; then + echo "::error::CA bundle not found: ${CA_BUNDLE}" + exit 1 + fi + + fingerprints_in_bundle() { + awk '/-----BEGIN CERTIFICATE-----/{cert=""} {cert=cert $0 "\n"} + /-----END CERTIFICATE-----/{print cert | "openssl x509 -noout -fingerprint -sha256 2>/dev/null"; close("openssl x509 -noout -fingerprint -sha256 2>/dev/null")}' \ + "${CA_BUNDLE}" + } + + fps="$(fingerprints_in_bundle)" + cert_count="$(grep -c . <<< "${fps}" || true)" + if (( cert_count == 0 )); then + echo "::error::No certificates parsed from ${CA_BUNDLE}; openssl may have failed" + exit 1 + fi + + if grep -qiF "${EMSIGN_G3_FP}" <<< "${fps}"; then + before_state="not fixed" + else + before_state="already fixed" + fi + before_ver="$(dpkg-query -W -f='${Version}' ca-certificates 2>/dev/null || echo unknown)" + echo "::notice::emSign ECC Root CA - G3 on ${VERSION_ID}: ${before_state} (ca-certificates ${before_ver})" + + if [ "${before_state}" = "already fixed" ]; then + exit 0 + fi + + for attempt in 1 2 3; do + if sudo apt-get update -q && sudo apt-get install --only-upgrade -y ca-certificates; then + break + fi + if [ "${attempt}" -lt 3 ]; then + echo "apt-get attempt ${attempt} failed; retrying in 10s..." + sleep 10 + else + echo "::error::apt-get upgrade failed after ${attempt} attempts" + exit 1 + fi + done + + after_ver="$(dpkg-query -W -f='${Version}' ca-certificates 2>/dev/null || echo unknown)" + after_fps="$(fingerprints_in_bundle)" + after_count="$(grep -c . <<< "${after_fps}" || true)" + if (( after_count == 0 )); then + echo "::error::No certificates parsed after upgrade; cannot verify fix" + exit 1 + fi + if grep -qiF "${EMSIGN_G3_FP}" <<< "${after_fps}"; then + echo "::error::emSign ECC Root CA - G3 still present after upgrade (ca-certificates ${after_ver} on ${VERSION_ID})" + exit 1 + fi + echo "ca-certificates upgraded to ${after_ver}; emSign ECC Root CA - G3 absent." diff --git a/.github/actions/upgrade-ca-certificates/emudhra_intermediates.ps1 b/.github/actions/upgrade-ca-certificates/emudhra_intermediates.ps1 new file mode 100644 index 00000000000..0324c5df448 --- /dev/null +++ b/.github/actions/upgrade-ca-certificates/emudhra_intermediates.ps1 @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Helpers for the compromised eMudhra intermediates and their Sectigo +# cross-signs listed in impacted-intermediates.json. Dot-sourced by this +# action's Windows step and by ci/tools/runner_trust_probe.ps1. +# +# impacted-intermediates.json is the disclosure's file, unmodified (SHA-256 +# f67b3abe9b4879f4e91874ed041beccd71257f1db41c588c724ccf12aa90c59e). The set +# is closed: new certificates for these keys would have to be issued by +# another CA, and all existing ones are revoked. + +$EmudhraIntermediatesPath = Join-Path $PSScriptRoot 'impacted-intermediates.json' +$EmudhraIntermediatesCount = 16 + +# Returns the certificates as X509Certificate2 objects; callers dispose them. +function Get-EmudhraIntermediates { + $entries = @(Get-Content -LiteralPath $EmudhraIntermediatesPath -Raw | ConvertFrom-Json) + if ($entries.Count -ne $EmudhraIntermediatesCount) { + throw "Expected $EmudhraIntermediatesCount certificates in $EmudhraIntermediatesPath, found $($entries.Count)." + } + foreach ($entry in $entries) { + $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::CreateFromPem($entry.pem) + if ($cert.GetCertHashString('SHA256') -ne $entry.sha256.ToUpperInvariant()) { + $cert.Dispose() + throw "SHA-256 mismatch for $($entry.name) (issued by $($entry.issued_by))." + } + Add-Member -InputObject $cert -NotePropertyName EmudhraLabel -NotePropertyValue "$($entry.name) (issued by $($entry.issued_by))" + $cert + } +} + +# True if Windows chain building reports the certificate as explicitly +# distrusted. This covers both a local LocalMachine\Disallowed entry and +# Microsoft's automatically updated disallowed list. Revocation checking and +# AIA downloads are off so the result reflects the local trust state only. +function Test-ExplicitlyDistrusted([System.Security.Cryptography.X509Certificates.X509Certificate2]$Cert) { + $explicitDistrust = [System.Security.Cryptography.X509Certificates.X509ChainStatusFlags]::ExplicitDistrust + $chain = [System.Security.Cryptography.X509Certificates.X509Chain]::new() + try { + $chain.ChainPolicy.RevocationMode = [System.Security.Cryptography.X509Certificates.X509RevocationMode]::NoCheck + $chain.ChainPolicy.DisableCertificateDownloads = $true + [void]$chain.Build($Cert) + $statuses = @($chain.ChainStatus) + @($chain.ChainElements | ForEach-Object { $_.ChainElementStatus }) + return [bool]($statuses | Where-Object { $_.Status.HasFlag($explicitDistrust) }) + } finally { + $chain.Dispose() + } +} diff --git a/.github/actions/upgrade-ca-certificates/impacted-intermediates.json b/.github/actions/upgrade-ca-certificates/impacted-intermediates.json new file mode 100644 index 00000000000..034fe6e7b4c --- /dev/null +++ b/.github/actions/upgrade-ca-certificates/impacted-intermediates.json @@ -0,0 +1,466 @@ +[ + { + "name": "EM DV TLS CA - G3B", + "usage": "tls", + "issued_by": "emSign Root TLS CA - G3", + "issuer_operator": "eMudhra", + "issuer_sha256": "7DD78D5F4F13459A83DFF9ABBBA62EDBAF6F2D102BF257FD712F4D9F2746ED8D", + "subject": "CN=EM DV TLS CA - G3B,O=eMudhra BV,C=NL", + "issuer": "CN=emSign Root TLS CA - G3,O=eMudhra Technologies Limited,C=IN", + "serial_hex": "A4AEE678F23362B4C0E460B772F214", + "sha256": "42C4ACCD12C4F59597925E1A08A7A90102608543809308D50EC427E118BA1493", + "spki_sha256": "EBED6475C2C2D80FCB199F3FE8DD6E6B3CE60B77EB15D4E42C3DEC0C6F5B5105", + "subject_key_identifier": "E21E2FE71C19390E554B62C120245735248A2514", + "authority_key_identifier": "E13AAB217CF1E34D54AED472B7FD8B5942209054", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "ecdsa-with-SHA384", + "not_before": "2025-04-30T15:54:08Z", + "not_after": "2040-04-28T18:30:00Z", + "revocation": { + "status": "revoked", + "date": "2026-10-18T09:24:32Z", + "reason": "ca_compromise", + "crl_url": "http://crl.emsign.com/?roottlscag3.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDaDCCAu6gAwIBAgIQAKSu5njyM2K0wORgt3LyFDAKBggqhkjOPQQDAzBWMQsw\nCQYDVQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl\nZDEgMB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwHhcNMjUwNDMwMTU1\nNDA4WhcNNDAwNDI4MTgzMDAwWjA/MQswCQYDVQQGEwJOTDETMBEGA1UECgwKZU11\nZGhyYSBCVjEbMBkGA1UEAwwSRU0gRFYgVExTIENBIC0gRzNCMHYwEAYHKoZIzj0C\nAQYFK4EEACIDYgAE557ZwEqXc8RzXlERHahawr1eZKs3S4DhLLwgOOweTw47gS/J\nhZ4eZ6+wTi+LyX90hbplqpEe6KTkW2LVzAPVa+Z4s8OMtNyTfKJGDiOEU8iSk10p\n1E+R4VYGtjzO2rRoo4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L\nWUIgkFQwHQYDVR0OBBYEFOIeL+ccGTkOVUtiwSAkVzUkiiUUMB0GA1UdJQQWMBQG\nCCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow\nKAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB\nBAGDjiEBAmQwCAYGZ4EMAQIBMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY\naHR0cDovL29jc3AtYi5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw\nb3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNRFZUTFNDQUczQi5wN2MwNwYDVR0f\nBDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j\ncmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E\nAwMDaAAwZQIwLmsGXi6oFMJ2klkcEMXMug6gvxQWZWkewiDWVzQyl2L6z6DuQfPO\nIbh1grvdULDCAjEAxZvLlxUavG69J6wnSjC8XBbqNIQsJ6CE2QdNOe84Ptxb6VG8\ngt2TlfJbHii+KNvK\n-----END CERTIFICATE-----" + }, + { + "name": "EM DV TLS CA - G3B", + "usage": "tls", + "issued_by": "AAA Certificate Services", + "issuer_operator": "Sectigo", + "issuer_sha256": "D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", + "subject": "CN=EM DV TLS CA - G3B,O=eMudhra BV,C=NL", + "issuer": "CN=AAA Certificate Services,O=Comodo CA Limited,L=Salford,ST=Greater Manchester,C=GB", + "serial_hex": "A024FDF4BA665C785CE4A020483858B7", + "sha256": "45A883F349AEDFD5F3A649B5498FFB121712BA8B448F3F2A91DBC5A766FCDDBE", + "spki_sha256": "EBED6475C2C2D80FCB199F3FE8DD6E6B3CE60B77EB15D4E42C3DEC0C6F5B5105", + "subject_key_identifier": "E21E2FE71C19390E554B62C120245735248A2514", + "authority_key_identifier": "A0110A233E96F107ECE2AF29EF82A57FD030A4B4", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "sha256WithRSAEncryption", + "not_before": "2025-05-14T00:00:00Z", + "not_after": "2028-12-31T23:59:59Z", + "revocation": { + "status": "revoked", + "date": "2026-10-19T10:35:36Z", + "reason": "ca_compromise", + "crl_url": "http://crl.comodoca.com/AAACertificateServices.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDzjCCAragAwIBAgIRAKAk/fS6Zlx4XOSgIEg4WLcwDQYJKoZIhvcNAQELBQAw\nezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\nA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV\nBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y\nODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVkaHJhIEJW\nMRswGQYDVQQDDBJFTSBEViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIBBgUrgQQA\nIgNiAATnntnASpdzxHNeUREdqFrCvV5kqzdLgOEsvCA47B5PDjuBL8mFnh5nr7BO\nL4vJf3SFumWqkR7opORbYtXMA9Vr5nizw4y03JN8okYOI4RTyJKTXSnUT5HhVga2\nPM7atGijggE1MIIBMTAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd\nBgNVHQ4EFgQU4h4v5xwZOQ5VS2LBICRXNSSKJRQwDgYDVR0PAQH/BAQDAgEGMBIG\nA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC\nMDEGA1UdIAQqMCgwCAYGZ4EMAQIBMA0GCysGAQQBg44hAQABMA0GCysGAQQBg44h\nAQJkMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FB\nQUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEF\nBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBCwUAA4IB\nAQBzQ1IoqpdotM6gFrR4Q3YrKifOLeaj5XXunldpVE/vZn2TxC2y0s8oYiLR5ICE\nwqVXSB7N6BM1Mp+Nif7Jbhb2IswAfSR4Iik8+B0iRmi7lYoNlycCgHjmsjOmZaxM\nQkPmjXAJbG5f4V41OsvJZmS7lI3r0IuvEDVX3GAZ13on6IImSs9ybNpHQ7NCqkMQ\n7wH4uAO1dtpFycgJSdHaCOhHjrox2BTYGLdpUrYwY3EOAjlZ8UGgsxs7mG+xquMl\nbN1LFURwfnkVMP3T/DBIZ/ys2+lNFyxK3FlArrV4/000uTnVB1lO8oGORYdXoT5V\nGUyRCEIDhF5TBUuKF4E3kf1h\n-----END CERTIFICATE-----" + }, + { + "name": "EM OV TLS CA - G3B", + "usage": "tls", + "issued_by": "emSign Root TLS CA - G3", + "issuer_operator": "eMudhra", + "issuer_sha256": "7DD78D5F4F13459A83DFF9ABBBA62EDBAF6F2D102BF257FD712F4D9F2746ED8D", + "subject": "CN=EM OV TLS CA - G3B,O=eMudhra BV,C=NL", + "issuer": "CN=emSign Root TLS CA - G3,O=eMudhra Technologies Limited,C=IN", + "serial_hex": "6CADA367FB1C100088120EA202D568", + "sha256": "98B896625C0C230ADEEC3EB2CF6AAD5001ECF9156A68644E82374CE5833BC25B", + "spki_sha256": "43BFFA19D6EF651451F6841E019931E96F6F2EAEFF28AB4B6F1CE4576DE2717B", + "subject_key_identifier": "9BD86551AFE7E72C37AD08EE6919D9BF3784AE0A", + "authority_key_identifier": "E13AAB217CF1E34D54AED472B7FD8B5942209054", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "ecdsa-with-SHA384", + "not_before": "2025-04-30T16:00:12Z", + "not_after": "2040-04-28T18:30:00Z", + "revocation": { + "status": "revoked", + "date": "2026-10-18T09:37:19Z", + "reason": "ca_compromise", + "crl_url": "http://crl.emsign.com/?roottlscag3.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDZjCCAu2gAwIBAgIPbK2jZ/scEACIEg6iAtVoMAoGCCqGSM49BAMDMFYxCzAJ\nBgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk\nMSAwHgYDVQQDDBdlbVNpZ24gUm9vdCBUTFMgQ0EgLSBHMzAeFw0yNTA0MzAxNjAw\nMTJaFw00MDA0MjgxODMwMDBaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVk\naHJhIEJWMRswGQYDVQQDDBJFTSBPViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIB\nBgUrgQQAIgNiAARziXqg6zz9yosSQC/68KvE4SErr43ud4rJ7i1tuD+sKAxeBWOK\n56Ks1xwyKo42HN659pxDArndjXPRzGyqsswK7k4hbOl53VGKSvS2JuVoTN1lgDft\nDIVOTMEvTPhkbQ2jggGWMIIBkjAfBgNVHSMEGDAWgBThOqshfPHjTVSu1HK3/YtZ\nQiCQVDAdBgNVHQ4EFgQUm9hlUa/n5yw3rQjuaRnZvzeErgowHQYDVR0lBBYwFAYI\nKwYBBQUHAwEGCCsGAQUFBwMCMF0GA1UdIARWMFQwOQYLKwYBBAGDjiEBAAEwKjAo\nBggrBgEFBQcCARYcaHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbTANBgsrBgEE\nAYOOIQECbjAIBgZngQwBAgIwdQYIKwYBBQUHAQEEaTBnMCQGCCsGAQUFBzABhhho\ndHRwOi8vb2NzcC1iLmVtc2lnbi5jb20wPwYIKwYBBQUHMAKGM2h0dHA6Ly9yZXBv\nc2l0b3J5LmVtc2lnbi5jb20vY2VydHMvRU1PVlRMU0NBRzNCLnA3YzA3BgNVHR8E\nMDAuMCygKqAohiZodHRwOi8vY3JsLmVtc2lnbi5jb20vP3Jvb3R0bHNjYWczLmNy\nbDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAKBggqhkjOPQQD\nAwNnADBkAjBAk+5BizdJhrED3wb7F2Gm7bWI60dlNd075nghT2TrR4NWKZtJZcbc\n28xxIY/NONICMF6mPdsjosaqmvEMWqKcBh4vsIvXSKnQj5o8zrFB4oKS+accjYTe\n8Dz3bQjauvy9uw==\n-----END CERTIFICATE-----" + }, + { + "name": "EM OV TLS CA - G3B", + "usage": "tls", + "issued_by": "AAA Certificate Services", + "issuer_operator": "Sectigo", + "issuer_sha256": "D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", + "subject": "CN=EM OV TLS CA - G3B,O=eMudhra BV,C=NL", + "issuer": "CN=AAA Certificate Services,O=Comodo CA Limited,L=Salford,ST=Greater Manchester,C=GB", + "serial_hex": "E94223BDE92F914E5AB9ACABFDAC3710", + "sha256": "5F1DED4DD590AF74631290CF073B122A2AAC6A1FA479778A0BD43579BD832699", + "spki_sha256": "43BFFA19D6EF651451F6841E019931E96F6F2EAEFF28AB4B6F1CE4576DE2717B", + "subject_key_identifier": "9BD86551AFE7E72C37AD08EE6919D9BF3784AE0A", + "authority_key_identifier": "A0110A233E96F107ECE2AF29EF82A57FD030A4B4", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "sha256WithRSAEncryption", + "not_before": "2025-05-14T00:00:00Z", + "not_after": "2028-12-31T23:59:59Z", + "revocation": { + "status": "revoked", + "date": "2026-10-19T10:35:36Z", + "reason": "ca_compromise", + "crl_url": "http://crl.comodoca.com/AAACertificateServices.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDzjCCAragAwIBAgIRAOlCI73pL5FOWrmsq/2sNxAwDQYJKoZIhvcNAQELBQAw\nezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\nA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV\nBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y\nODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVkaHJhIEJW\nMRswGQYDVQQDDBJFTSBPViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIBBgUrgQQA\nIgNiAARziXqg6zz9yosSQC/68KvE4SErr43ud4rJ7i1tuD+sKAxeBWOK56Ks1xwy\nKo42HN659pxDArndjXPRzGyqsswK7k4hbOl53VGKSvS2JuVoTN1lgDftDIVOTMEv\nTPhkbQ2jggE1MIIBMTAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd\nBgNVHQ4EFgQUm9hlUa/n5yw3rQjuaRnZvzeErgowDgYDVR0PAQH/BAQDAgEGMBIG\nA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC\nMDEGA1UdIAQqMCgwCAYGZ4EMAQICMA0GCysGAQQBg44hAQABMA0GCysGAQQBg44h\nAQJuMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FB\nQUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEF\nBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBCwUAA4IB\nAQALRoOSvkzRa+tN5Rje9W5GiXd/qsicd9N6Gnh0SWC7VyWH+HycIqAnFCMYZznj\nCeGusomFYHE42SHWr85JDIbgzy6y3dO4i9n9w0Bx7yrXWBbDzFS8VgA3+EY4Cg7y\nmtoC3RPOJVtaXXPZ+68X804g1Mj+3vCSHj2lNPVgco0OzSIOJ/KMg1R9MLBkiKzb\nbERAGZHejY0HBhYd76IINZN8z6bBNah2UdFYxsOisLynPQUkol6gRLum024/FzVl\nCCDfnUusrkr+mAfPAvD/Vb8bhpA7jYtepuU+2mIdShj5lLlvAwZphn+ypWycuo2x\n0k9mldz2dqI9nhCiY+TTIOOR\n-----END CERTIFICATE-----" + }, + { + "name": "EM EV TLS CA - G3B", + "usage": "tls", + "issued_by": "emSign Root TLS CA - G3", + "issuer_operator": "eMudhra", + "issuer_sha256": "7DD78D5F4F13459A83DFF9ABBBA62EDBAF6F2D102BF257FD712F4D9F2746ED8D", + "subject": "CN=EM EV TLS CA - G3B,O=eMudhra BV,C=NL", + "issuer": "CN=emSign Root TLS CA - G3,O=eMudhra Technologies Limited,C=IN", + "serial_hex": "C7C41EFE92D825F05EAE841A4A82C1", + "sha256": "E7568FE6E12331F4FD7DE61DA4C7559D0F8EB192A218D4741A3BA75184333838", + "spki_sha256": "E0254C7FCDC265E2F0F8A428DABB75AB4AE039B4872D62F5EE57F0877D5A8A41", + "subject_key_identifier": "3EDC20D9894698877B873FD88147B25C15D84706", + "authority_key_identifier": "E13AAB217CF1E34D54AED472B7FD8B5942209054", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "ecdsa-with-SHA384", + "not_before": "2025-04-30T16:03:31Z", + "not_after": "2040-04-28T18:30:00Z", + "revocation": { + "status": "revoked", + "date": "2026-10-18T09:28:53Z", + "reason": "ca_compromise", + "crl_url": "http://crl.emsign.com/?roottlscag3.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDaDCCAu2gAwIBAgIQAMfEHv6S2CXwXq6EGkqCwTAKBggqhkjOPQQDAzBWMQsw\nCQYDVQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl\nZDEgMB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwHhcNMjUwNDMwMTYw\nMzMxWhcNNDAwNDI4MTgzMDAwWjA/MQswCQYDVQQGEwJOTDETMBEGA1UECgwKZU11\nZGhyYSBCVjEbMBkGA1UEAwwSRU0gRVYgVExTIENBIC0gRzNCMHYwEAYHKoZIzj0C\nAQYFK4EEACIDYgAEJduoWmTTl9oLEFLp6lRuSHvv6pydnWigxerc/u91ppu11vwB\n9Cv7ym60C81kwHZ+Nv/QXZeoE8WqDGyVMOec9fCGZGjxOAZCCqXIUYQLHiUlQOeQ\n5LZUw4Uu1krx6GV9o4IBlTCCAZEwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L\nWUIgkFQwHQYDVR0OBBYEFD7cINmJRpiHe4c/2IFHslwV2EcGMB0GA1UdJQQWMBQG\nCCsGAQUFBwMBBggrBgEFBQcDAjBcBgNVHSAEVTBTMDkGCysGAQQBg44hAQABMCow\nKAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB\nBAGDjiEBAngwBwYFZ4EMAQEwdQYIKwYBBQUHAQEEaTBnMCQGCCsGAQUFBzABhhho\ndHRwOi8vb2NzcC1iLmVtc2lnbi5jb20wPwYIKwYBBQUHMAKGM2h0dHA6Ly9yZXBv\nc2l0b3J5LmVtc2lnbi5jb20vY2VydHMvRU1FVlRMU0NBRzNCLnA3YzA3BgNVHR8E\nMDAuMCygKqAohiZodHRwOi8vY3JsLmVtc2lnbi5jb20vP3Jvb3R0bHNjYWczLmNy\nbDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAKBggqhkjOPQQD\nAwNpADBmAjEA9Jq999oaOUKhFwopw33iCmt0OHIn3gjT43GfuOHR/YckXdwoNdDA\nlIEhz0NgRO3/AjEAvY/a3OMvktIZLYjn+WLKmp0QhrIMV+e6EpS7j4GVeOKouNp6\nMN/P+ByGeiRMghzB\n-----END CERTIFICATE-----" + }, + { + "name": "EM EV TLS CA - G3B", + "usage": "tls", + "issued_by": "AAA Certificate Services", + "issuer_operator": "Sectigo", + "issuer_sha256": "D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", + "subject": "CN=EM EV TLS CA - G3B,O=eMudhra BV,C=NL", + "issuer": "CN=AAA Certificate Services,O=Comodo CA Limited,L=Salford,ST=Greater Manchester,C=GB", + "serial_hex": "D8F8685E4F2BD9D82402A85A1C1EB731", + "sha256": "579480AEA166290BD3C643589E795FC0D0E54E86505D180F1DFF99EFD3199512", + "spki_sha256": "E0254C7FCDC265E2F0F8A428DABB75AB4AE039B4872D62F5EE57F0877D5A8A41", + "subject_key_identifier": "3EDC20D9894698877B873FD88147B25C15D84706", + "authority_key_identifier": "A0110A233E96F107ECE2AF29EF82A57FD030A4B4", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "sha256WithRSAEncryption", + "not_before": "2025-05-14T00:00:00Z", + "not_after": "2028-12-31T23:59:59Z", + "revocation": { + "status": "revoked", + "date": "2026-10-19T10:35:36Z", + "reason": "ca_compromise", + "crl_url": "http://crl.comodoca.com/AAACertificateServices.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDzTCCArWgAwIBAgIRANj4aF5PK9nYJAKoWhwetzEwDQYJKoZIhvcNAQELBQAw\nezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\nA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV\nBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y\nODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVkaHJhIEJW\nMRswGQYDVQQDDBJFTSBFViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIBBgUrgQQA\nIgNiAAQl26haZNOX2gsQUunqVG5Ie+/qnJ2daKDF6tz+73Wmm7XW/AH0K/vKbrQL\nzWTAdn42/9Bdl6gTxaoMbJUw55z18IZkaPE4BkIKpchRhAseJSVA55DktlTDhS7W\nSvHoZX2jggE0MIIBMDAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd\nBgNVHQ4EFgQUPtwg2YlGmId7hz/YgUeyXBXYRwYwDgYDVR0PAQH/BAQDAgEGMBIG\nA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC\nMDAGA1UdIAQpMCcwBwYFZ4EMAQEwDQYLKwYBBAGDjiEBAAEwDQYLKwYBBAGDjiEB\nAngwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFB\nQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUF\nBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQELBQADggEB\nAJNFuAFW9Gnw/8+kCO4KIlZdnM1Sh2u+8cK0EYAHhXpb5FlGvYkcARigo5oS7VoM\n7FOYlTueV6yICZKNAYawZPJPhNCGK+rp3cQBEz6d0n8OZEYZov9lb3ll8ORgSDuf\nFVIW6Gagg/UnOamLBTOR8eD5V73SCme/B2cHUDw+m5E8cc+0Ujzz3Cn3ex5JrhXk\n2T3s8PoVzEmhTgGboFfcWO1EYhPPrYwdxPYRQIXAXUXhLEOVNL9ASbZJLFI8E1IM\nm7NwxVN2s38jWpaCUaaF0tg7hfiy7oe13rzJfHsdqldJVTTUTPWVx8TC4ju0VLet\nbAlwhwFT6AP8uIVMGUHNw24=\n-----END CERTIFICATE-----" + }, + { + "name": "EM DV TLS CA - G3C", + "usage": "tls", + "issued_by": "emSign Root TLS CA - G3", + "issuer_operator": "eMudhra", + "issuer_sha256": "7DD78D5F4F13459A83DFF9ABBBA62EDBAF6F2D102BF257FD712F4D9F2746ED8D", + "subject": "CN=EM DV TLS CA - G3C,O=eMudhra Inc,C=US", + "issuer": "CN=emSign Root TLS CA - G3,O=eMudhra Technologies Limited,C=IN", + "serial_hex": "588B2E3C2BB8CDA13E1A1209254321", + "sha256": "0164D7CEAE5BB909C272AF151FF44B1177AE9B47D9424B94F1D9558C30934D19", + "spki_sha256": "49DD353DC2B919A3770645117D0E60C16115E7D1BC259EEFE734B00EA434FE5F", + "subject_key_identifier": "AE7E105C3C2996C1A847270559F1E03DD4E50AF3", + "authority_key_identifier": "E13AAB217CF1E34D54AED472B7FD8B5942209054", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "ecdsa-with-SHA384", + "not_before": "2025-04-30T15:35:09Z", + "not_after": "2040-04-28T18:30:00Z", + "revocation": { + "status": "revoked", + "date": "2026-10-18T09:27:07Z", + "reason": "ca_compromise", + "crl_url": "http://crl.emsign.com/?roottlscag3.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDaDCCAu6gAwIBAgIPWIsuPCu4zaE+GhIJJUMhMAoGCCqGSM49BAMDMFYxCzAJ\nBgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk\nMSAwHgYDVQQDDBdlbVNpZ24gUm9vdCBUTFMgQ0EgLSBHMzAeFw0yNTA0MzAxNTM1\nMDlaFw00MDA0MjgxODMwMDBaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVk\naHJhIEluYzEbMBkGA1UEAwwSRU0gRFYgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0C\nAQYFK4EEACIDYgAEOnp4YsjETTJN3WkDOmeCwO/u/kBV5ooSP6A3G6O9kZRRTuFy\npDPjYw8CvRiAN9nlFJ+bd9uUooHfmHsv6oiLdL3+j9pMEJnubKGVZv4+8OX2sJpV\nbMyLpQVh3xsqRB0Bo4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L\nWUIgkFQwHQYDVR0OBBYEFK5+EFw8KZbBqEcnBVnx4D3U5QrzMB0GA1UdJQQWMBQG\nCCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow\nKAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB\nBAGDjiEBAmQwCAYGZ4EMAQIBMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY\naHR0cDovL29jc3AtYy5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw\nb3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNRFZUTFNDQUczQy5wN2MwNwYDVR0f\nBDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j\ncmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E\nAwMDaAAwZQIxAMEN2HWMocbuWdy1d33pH6JpMvP/V/DIS5PtZbv1Q7TXEbxrcyxg\nyuddOJscYDJJlAIwB4sH1BpbScDzkdD4tJBSwrG/1Mx82vb8YaySehNuu1+1oXNd\nAEKwaJ0mVzScy2+6\n-----END CERTIFICATE-----" + }, + { + "name": "EM DV TLS CA - G3C", + "usage": "tls", + "issued_by": "AAA Certificate Services", + "issuer_operator": "Sectigo", + "issuer_sha256": "D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", + "subject": "CN=EM DV TLS CA - G3C,O=eMudhra Inc,C=US", + "issuer": "CN=AAA Certificate Services,O=Comodo CA Limited,L=Salford,ST=Greater Manchester,C=GB", + "serial_hex": "91F347F1A5B7D39962B95E0EE4E299D1", + "sha256": "62B7F9868263F72F6911FC78CEAF72C1979158B7368BF648683BB2AA39B02E72", + "spki_sha256": "49DD353DC2B919A3770645117D0E60C16115E7D1BC259EEFE734B00EA434FE5F", + "subject_key_identifier": "AE7E105C3C2996C1A847270559F1E03DD4E50AF3", + "authority_key_identifier": "A0110A233E96F107ECE2AF29EF82A57FD030A4B4", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "sha256WithRSAEncryption", + "not_before": "2025-05-14T00:00:00Z", + "not_after": "2028-12-31T23:59:59Z", + "revocation": { + "status": "revoked", + "date": "2026-10-19T10:35:36Z", + "reason": "ca_compromise", + "crl_url": "http://crl.comodoca.com/AAACertificateServices.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDzzCCAregAwIBAgIRAJHzR/Glt9OZYrleDuTimdEwDQYJKoZIhvcNAQELBQAw\nezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\nA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV\nBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y\nODEyMzEyMzU5NTlaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVkaHJhIElu\nYzEbMBkGA1UEAwwSRU0gRFYgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0CAQYFK4EE\nACIDYgAEOnp4YsjETTJN3WkDOmeCwO/u/kBV5ooSP6A3G6O9kZRRTuFypDPjYw8C\nvRiAN9nlFJ+bd9uUooHfmHsv6oiLdL3+j9pMEJnubKGVZv4+8OX2sJpVbMyLpQVh\n3xsqRB0Bo4IBNTCCATEwHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQw\nHQYDVR0OBBYEFK5+EFw8KZbBqEcnBVnx4D3U5QrzMA4GA1UdDwEB/wQEAwIBBjAS\nBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD\nAjAxBgNVHSAEKjAoMAgGBmeBDAECATANBgsrBgEEAYOOIQEAATANBgsrBgEEAYOO\nIQECZDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9B\nQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYB\nBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQsFAAOC\nAQEAr7b6Mx11TIJgVzHt6EH5qGNWeiiqoMrp8E5/k8s+jvupx1n083pZ5XOzUzND\nwEUe0W4C4FIGUHNQ4XDwqOw0yK42xqm2dvCKgsko2UvDlT0haZVtTCeWMQOe5J/J\nziaMfZwsZNfZ3aaI6XPtWDZtwY8AaNfyaeCpbtm5GcGnyNWk4jSNpbtpo6NjfXuP\nCFOm3rTjJ4zytP7Tso6iMm0L01cYFOlTmoPv4515R2r3SPnKDtijoB/D/vgxzojv\nDafgyG9afX8+laO9NbMMz4sqC1vZx3984i/sBNFiugm3Vvdq5OdJRjuGC1QILIs6\ndg/K3i8VQqRpbfaM9kIlMzZ3dQ==\n-----END CERTIFICATE-----" + }, + { + "name": "EM OV TLS CA - G3C", + "usage": "tls", + "issued_by": "emSign Root TLS CA - G3", + "issuer_operator": "eMudhra", + "issuer_sha256": "7DD78D5F4F13459A83DFF9ABBBA62EDBAF6F2D102BF257FD712F4D9F2746ED8D", + "subject": "CN=EM OV TLS CA - G3C,O=eMudhra Inc,C=US", + "issuer": "CN=emSign Root TLS CA - G3,O=eMudhra Technologies Limited,C=IN", + "serial_hex": "296926C227A3A1526392B19482A504", + "sha256": "CB6E15165AC802E9B3F993F16C25D41F4E81C21EBDE71AE1B012AC00BF9C7C3F", + "spki_sha256": "63ECEFEE431D49523BC1136C4E72D0206A477086CAF606CD5CF025686C109CF8", + "subject_key_identifier": "34EAEA0CEBC90ADAF9D0C5E2ED9B78C9879E1B6C", + "authority_key_identifier": "E13AAB217CF1E34D54AED472B7FD8B5942209054", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "ecdsa-with-SHA384", + "not_before": "2025-04-30T15:37:41Z", + "not_after": "2040-04-28T18:30:00Z", + "revocation": { + "status": "revoked", + "date": "2026-10-18T09:38:46Z", + "reason": "ca_compromise", + "crl_url": "http://crl.emsign.com/?roottlscag3.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDaDCCAu6gAwIBAgIPKWkmwiejoVJjkrGUgqUEMAoGCCqGSM49BAMDMFYxCzAJ\nBgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk\nMSAwHgYDVQQDDBdlbVNpZ24gUm9vdCBUTFMgQ0EgLSBHMzAeFw0yNTA0MzAxNTM3\nNDFaFw00MDA0MjgxODMwMDBaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVk\naHJhIEluYzEbMBkGA1UEAwwSRU0gT1YgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0C\nAQYFK4EEACIDYgAEE4JRzVKskpjByKrZdzId65fnCb0LTKCspV3IrVHc/J0ViaFZ\nfjpRIOHv1jHGPhg1ysrimGmmLhYx6KKBgatWYW3EXZGKvAnzqw5jz3kqpNztc4e+\nN7ulaVSfHAKycO1no4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L\nWUIgkFQwHQYDVR0OBBYEFDTq6gzryQra+dDF4u2beMmHnhtsMB0GA1UdJQQWMBQG\nCCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow\nKAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB\nBAGDjiEBAm4wCAYGZ4EMAQICMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY\naHR0cDovL29jc3AtYy5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw\nb3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNT1ZUTFNDQUczQy5wN2MwNwYDVR0f\nBDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j\ncmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E\nAwMDaAAwZQIxAMSZt2xsPeumRdeSMSQ3CbzIZeQ/q7GDCWfeE1Pq+mhgA6gRM44c\nNVF3xJ+wsxJk+wIwFJbaDMokD8Ei6eNOiA8yUMYvfzQyngmrhZQMdPCK1SewrR79\n4rSb9vGqxZzelQth\n-----END CERTIFICATE-----" + }, + { + "name": "EM OV TLS CA - G3C", + "usage": "tls", + "issued_by": "AAA Certificate Services", + "issuer_operator": "Sectigo", + "issuer_sha256": "D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", + "subject": "CN=EM OV TLS CA - G3C,O=eMudhra Inc,C=US", + "issuer": "CN=AAA Certificate Services,O=Comodo CA Limited,L=Salford,ST=Greater Manchester,C=GB", + "serial_hex": "8D5A4811939EF447D77111CDD9849AE9", + "sha256": "7485B51C0ED192878FA1D2F948FB529AE1842CC5086C4B67F7BE5B2039DE9489", + "spki_sha256": "63ECEFEE431D49523BC1136C4E72D0206A477086CAF606CD5CF025686C109CF8", + "subject_key_identifier": "34EAEA0CEBC90ADAF9D0C5E2ED9B78C9879E1B6C", + "authority_key_identifier": "A0110A233E96F107ECE2AF29EF82A57FD030A4B4", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "sha256WithRSAEncryption", + "not_before": "2025-05-14T00:00:00Z", + "not_after": "2028-12-31T23:59:59Z", + "revocation": { + "status": "revoked", + "date": "2026-10-19T10:35:36Z", + "reason": "ca_compromise", + "crl_url": "http://crl.comodoca.com/AAACertificateServices.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDzzCCAregAwIBAgIRAI1aSBGTnvRH13ERzdmEmukwDQYJKoZIhvcNAQELBQAw\nezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\nA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV\nBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y\nODEyMzEyMzU5NTlaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVkaHJhIElu\nYzEbMBkGA1UEAwwSRU0gT1YgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0CAQYFK4EE\nACIDYgAEE4JRzVKskpjByKrZdzId65fnCb0LTKCspV3IrVHc/J0ViaFZfjpRIOHv\n1jHGPhg1ysrimGmmLhYx6KKBgatWYW3EXZGKvAnzqw5jz3kqpNztc4e+N7ulaVSf\nHAKycO1no4IBNTCCATEwHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQw\nHQYDVR0OBBYEFDTq6gzryQra+dDF4u2beMmHnhtsMA4GA1UdDwEB/wQEAwIBBjAS\nBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD\nAjAxBgNVHSAEKjAoMAgGBmeBDAECAjANBgsrBgEEAYOOIQEAATANBgsrBgEEAYOO\nIQECbjBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9B\nQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYB\nBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQsFAAOC\nAQEAHlbhZpjhkPEAQVhPHRtBDaHqiisVD20pQ1TUeSx3l62Qe7NQ0b7vWQy7DN0i\nKOu7BGpP5EnIjvXbz9FhpfPJ4FhKEt3tQ51rqXz2oD8vRaryvExWPobDUz3j7HN6\nOzBsVMIe3PD7AAkpky0x8zMSi/ep5R9OMzybmNptbtppyuB5ytgq+h7lQdRIP+CQ\nTaJX3qTgfMqzntYre5GljmEB5hU2/rSn8zoSU2APkJidlZFpghtgbD4I2VRHZvCw\nFFr/SrFM9pwrgbJNZ1MtyTSVgI5NlNb0aMfLR+/r6jJt3ROeY2yh2QQ2dx+Yqgdb\nuL5ChFWOQU+ErSDnUq7yr+pjBQ==\n-----END CERTIFICATE-----" + }, + { + "name": "EM EV TLS CA - G3C", + "usage": "tls", + "issued_by": "emSign Root TLS CA - G3", + "issuer_operator": "eMudhra", + "issuer_sha256": "7DD78D5F4F13459A83DFF9ABBBA62EDBAF6F2D102BF257FD712F4D9F2746ED8D", + "subject": "CN=EM EV TLS CA - G3C,O=eMudhra Inc,C=US", + "issuer": "CN=emSign Root TLS CA - G3,O=eMudhra Technologies Limited,C=IN", + "serial_hex": "8009928A64107601AF05FA2F19F109", + "sha256": "432EC25F0D28DE9006D97BF752F17BBB2CD41F669933FCB072D9A1148D0261F3", + "spki_sha256": "5C0660A2B164910D8FF589C18E14552E9E0EC237DCEFE2CB736D41A4811F81A0", + "subject_key_identifier": "A6255E80A94DB7F3967C1BC791DF92BCEE4BEB5F", + "authority_key_identifier": "E13AAB217CF1E34D54AED472B7FD8B5942209054", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "ecdsa-with-SHA384", + "not_before": "2025-04-30T15:39:31Z", + "not_after": "2040-04-28T18:30:00Z", + "revocation": { + "status": "revoked", + "date": "2026-10-18T09:30:49Z", + "reason": "ca_compromise", + "crl_url": "http://crl.emsign.com/?roottlscag3.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDaDCCAu6gAwIBAgIQAIAJkopkEHYBrwX6LxnxCTAKBggqhkjOPQQDAzBWMQsw\nCQYDVQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl\nZDEgMB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwHhcNMjUwNDMwMTUz\nOTMxWhcNNDAwNDI4MTgzMDAwWjBAMQswCQYDVQQGEwJVUzEUMBIGA1UECgwLZU11\nZGhyYSBJbmMxGzAZBgNVBAMMEkVNIEVWIFRMUyBDQSAtIEczQzB2MBAGByqGSM49\nAgEGBSuBBAAiA2IABC1W6Cmm+4LGSkaX2WCvlR6QZ66R+SbTQwbaSKiq63hptqZ+\nbSdCbgRCqgOzbhmI3/Y2UU7hzwf1uks/8qBmRa59MdWGatMao9JG6wsxd+6Zy3Vm\nxRbOdICKZo+R8MR/yqOCAZUwggGRMB8GA1UdIwQYMBaAFOE6qyF88eNNVK7Ucrf9\ni1lCIJBUMB0GA1UdDgQWBBSmJV6AqU2385Z8G8eR35K87kvrXzAdBgNVHSUEFjAU\nBggrBgEFBQcDAQYIKwYBBQUHAwIwXAYDVR0gBFUwUzA5BgsrBgEEAYOOIQEAATAq\nMCgGCCsGAQUFBwIBFhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMA0GCysG\nAQQBg44hAQJ4MAcGBWeBDAEBMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY\naHR0cDovL29jc3AtYy5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw\nb3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNRVZUTFNDQUczQy5wN2MwNwYDVR0f\nBDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j\ncmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E\nAwMDaAAwZQIwBy1wTnzH4lXDTy2PD91ysLjvnlVN3JbJmc1BIMEU8TOLkYVHibMc\n9HeyonikFSfmAjEA6uE3QRxlrZTz/1/lXPTEMUhoauAtycagtlNFp79kjhvNa2/f\nAal1GNxsOutLnr0Y\n-----END CERTIFICATE-----" + }, + { + "name": "EM EV TLS CA - G3C", + "usage": "tls", + "issued_by": "AAA Certificate Services", + "issuer_operator": "Sectigo", + "issuer_sha256": "D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", + "subject": "CN=EM EV TLS CA - G3C,O=eMudhra Inc,C=US", + "issuer": "CN=AAA Certificate Services,O=Comodo CA Limited,L=Salford,ST=Greater Manchester,C=GB", + "serial_hex": "05F078752DECE7A1BB8595D303167171", + "sha256": "FD9D5C2DBBD4995AB69B8D50C439D9527B00529199C2FD01B2174EB58F07021F", + "spki_sha256": "5C0660A2B164910D8FF589C18E14552E9E0EC237DCEFE2CB736D41A4811F81A0", + "subject_key_identifier": "A6255E80A94DB7F3967C1BC791DF92BCEE4BEB5F", + "authority_key_identifier": "A0110A233E96F107ECE2AF29EF82A57FD030A4B4", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "sha256WithRSAEncryption", + "not_before": "2025-05-14T00:00:00Z", + "not_after": "2028-12-31T23:59:59Z", + "revocation": { + "status": "revoked", + "date": "2026-10-19T10:35:36Z", + "reason": "ca_compromise", + "crl_url": "http://crl.comodoca.com/AAACertificateServices.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDzTCCArWgAwIBAgIQBfB4dS3s56G7hZXTAxZxcTANBgkqhkiG9w0BAQsFADB7\nMQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD\nVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE\nAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTI1MDUxNDAwMDAwMFoXDTI4\nMTIzMTIzNTk1OVowQDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2VNdWRocmEgSW5j\nMRswGQYDVQQDDBJFTSBFViBUTFMgQ0EgLSBHM0MwdjAQBgcqhkjOPQIBBgUrgQQA\nIgNiAAQtVugppvuCxkpGl9lgr5UekGeukfkm00MG2kioqut4abamfm0nQm4EQqoD\ns24ZiN/2NlFO4c8H9bpLP/KgZkWufTHVhmrTGqPSRusLMXfumct1ZsUWznSAimaP\nkfDEf8qjggE0MIIBMDAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd\nBgNVHQ4EFgQUpiVegKlNt/OWfBvHkd+SvO5L618wDgYDVR0PAQH/BAQDAgEGMBIG\nA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC\nMDAGA1UdIAQpMCcwBwYFZ4EMAQEwDQYLKwYBBAGDjiEBAAEwDQYLKwYBBAGDjiEB\nAngwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFB\nQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUF\nBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQELBQADggEB\nAIFfHiaMSjnrdZygRYV4ekRBmG7ybcFk4hGQWMMyMFi0cAEk2+dGfz5bl8IaIbLq\nz+uVJzhOgVEX7WqIfgbWfFsfPYHYjPHBmy7SlZ3BwA86IQ6qF/etpX2/qL9A5yRN\nN3nVowUpaqV6H3aCpkAh3wR2ZNkvAbLDvPz3J0l+sEWjWv+ZISOV4R+hXDTdPXV0\n1lKxnbwqEsdo/sE2QkCDKwFrZh/0uKjWspydJj+jSaDLTrGNx50Dgmn7VEcD1ZI5\nFXjknVUiO25YlmS1dtVZSBbyUKSjlrP/yDwPcZunoAsmCAAtCKsFLviLaymzvsKI\neSSDGxpSySp2VCxompGDk8g=\n-----END CERTIFICATE-----" + }, + { + "name": "EM OV CS CA - G3B", + "usage": "code-signing", + "issued_by": "emSign Root CS CA - G3", + "issuer_operator": "eMudhra", + "issuer_sha256": "F2EDAAFF3D9B701FB4D14C9F285F8C0BAD90FC3BCC1FA07D47F4718EDABA34A5", + "subject": "CN=EM OV CS CA - G3B,O=eMudhra BV,C=NL", + "issuer": "CN=emSign Root CS CA - G3,O=eMudhra Technologies Limited,C=IN", + "serial_hex": "7818933F8F85B4619A2B35FBA83D0A", + "sha256": "9E39DE2881273ACC225A149C72BA3E0F51424963669AB633EE1DC7DF5DB067B4", + "spki_sha256": "EC4FFA1AD0AC88081314B7AAFFEF6A91F97CD57291BB5BA686C5415F5EB4F6B6", + "subject_key_identifier": "79FAE8ECCFDB0D758DA458D69FB6EDDC7C49DEE6", + "authority_key_identifier": "687403F885CA8D5450571D16E0C8DE450DABCD2D", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "ecdsa-with-SHA384", + "not_before": "2025-04-30T16:08:27Z", + "not_after": "2040-04-28T18:30:00Z", + "revocation": { + "status": "revoked", + "date": "2026-10-18T09:33:37Z", + "reason": "ca_compromise", + "crl_url": "http://crl.emsign.com/?rootcscag3.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDWzCCAuCgAwIBAgIPeBiTP4+FtGGaKzX7qD0KMAoGCCqGSM49BAMDMFUxCzAJ\nBgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk\nMR8wHQYDVQQDDBZlbVNpZ24gUm9vdCBDUyBDQSAtIEczMB4XDTI1MDQzMDE2MDgy\nN1oXDTQwMDQyODE4MzAwMFowPjELMAkGA1UEBhMCTkwxEzARBgNVBAoMCmVNdWRo\ncmEgQlYxGjAYBgNVBAMMEUVNIE9WIENTIENBIC0gRzNCMHYwEAYHKoZIzj0CAQYF\nK4EEACIDYgAEqrsPsGZqz88B+tUr/P1MK/LLPDSX3nOVDlgZth/mlQmHQcjEkUOb\nNKBDHK/+W/m0wcjorgAB+fTcitgsWfNjnpbNbD1NsM7+wmw9YlF7N3YTcnQ/jord\nd7EAZPKr5eBQo4IBizCCAYcwHwYDVR0jBBgwFoAUaHQD+IXKjVRQVx0W4MjeRQ2r\nzS0wHQYDVR0OBBYEFHn66OzP2w11jaRY1p+27dx8Sd7mMBMGA1UdJQQMMAoGCCsG\nAQUFBwMDMF4GA1UdIARXMFUwOQYLKwYBBAGDjiEBAAEwKjAoBggrBgEFBQcCARYc\naHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbTAOBgwrBgEEAYOOIQECgUgwCAYG\nZ4EMAQQBMHQGCCsGAQUFBwEBBGgwZjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3At\nYi5lbXNpZ24uY29tMD4GCCsGAQUFBzAChjJodHRwOi8vcmVwb3NpdG9yeS5lbXNp\nZ24uY29tL2NlcnRzL0VNT1ZDU0NBRzNCLnA3YzA2BgNVHR8ELzAtMCugKaAnhiVo\ndHRwOi8vY3JsLmVtc2lnbi5jb20vP3Jvb3Rjc2NhZzMuY3JsMA4GA1UdDwEB/wQE\nAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMAoGCCqGSM49BAMDA2kAMGYCMQC/zr03\nYEELsNKqzUtH2dVFi4BaKFdyrr/6G1dIWp8KAVgsw2/u4lyyboUDaC9jDvICMQC9\nHTxku84tNX9dmHOtH7aC2Nu/JM0jTf6KjZAvovCJzzv1IFWSZpVLZibynMLXOrM=\n-----END CERTIFICATE-----" + }, + { + "name": "EM OV CS CA - G3B", + "usage": "code-signing", + "issued_by": "AAA Certificate Services", + "issuer_operator": "Sectigo", + "issuer_sha256": "D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", + "subject": "CN=EM OV CS CA - G3B,O=eMudhra BV,C=NL", + "issuer": "CN=AAA Certificate Services,O=Comodo CA Limited,L=Salford,ST=Greater Manchester,C=GB", + "serial_hex": "41E32B73AC185D6CE0D7480BACF84E69", + "sha256": "A496E23AC0CAB8155FC9B117B97EAE368EFCBB9041AEC8804D5A308D2D01C2D8", + "spki_sha256": "EC4FFA1AD0AC88081314B7AAFFEF6A91F97CD57291BB5BA686C5415F5EB4F6B6", + "subject_key_identifier": "79FAE8ECCFDB0D758DA458D69FB6EDDC7C49DEE6", + "authority_key_identifier": "A0110A233E96F107ECE2AF29EF82A57FD030A4B4", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "sha256WithRSAEncryption", + "not_before": "2025-05-14T00:00:00Z", + "not_after": "2028-12-31T23:59:59Z", + "revocation": { + "status": "revoked", + "date": "2026-10-19T10:35:36Z", + "reason": "ca_compromise", + "crl_url": "http://crl.comodoca.com/AAACertificateServices.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDwzCCAqugAwIBAgIQQeMrc6wYXWzg10gLrPhOaTANBgkqhkiG9w0BAQsFADB7\nMQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD\nVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE\nAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTI1MDUxNDAwMDAwMFoXDTI4\nMTIzMTIzNTk1OVowPjELMAkGA1UEBhMCTkwxEzARBgNVBAoMCmVNdWRocmEgQlYx\nGjAYBgNVBAMMEUVNIE9WIENTIENBIC0gRzNCMHYwEAYHKoZIzj0CAQYFK4EEACID\nYgAEqrsPsGZqz88B+tUr/P1MK/LLPDSX3nOVDlgZth/mlQmHQcjEkUObNKBDHK/+\nW/m0wcjorgAB+fTcitgsWfNjnpbNbD1NsM7+wmw9YlF7N3YTcnQ/jordd7EAZPKr\n5eBQo4IBLDCCASgwHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD\nVR0OBBYEFHn66OzP2w11jaRY1p+27dx8Sd7mMA4GA1UdDwEB/wQEAwIBBjASBgNV\nHRMBAf8ECDAGAQH/AgEAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDIGA1UdIAQrMCkw\nCAYGZ4EMAQQBMA0GCysGAQQBg44hAQABMA4GDCsGAQQBg44hAQKBSDBDBgNVHR8E\nPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9BQUFDZXJ0aWZpY2F0\nZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6\nLy9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAIh0OzWsdBYhY\n7+yxPGyosRL+uUSN6/Mrd30fBuougs5FVWQ3cRgLhImnUUgXADY9buugWhMmfT3c\nyU5T1MFXZXClCILm5mfg+oqm/ikUWJl3Ayf2GvYf2O++6vzqvkn418nI5Lpdt0O7\nLi524YS/3CZHDA+zZQ7rrHpZXgQ9Hmp8AUQ4FneaGjNanFoYxiZxwCyW5z4u7AoK\n0bmOVqKtcDFoVNz5++G9TYDJdhWOXmKKgzqRo1MK0PjazNT7MGHqknP93SqMVJ1g\nmX/qNWxYteAjlvFanQxBNX/+TRxDjuFsEHO/7BvqSpIIck0UEEfizhN8zNpWLbcl\nEqVgl0g39A==\n-----END CERTIFICATE-----" + }, + { + "name": "EM OV CS CA - G3C", + "usage": "code-signing", + "issued_by": "emSign Root CS CA - G3", + "issuer_operator": "eMudhra", + "issuer_sha256": "F2EDAAFF3D9B701FB4D14C9F285F8C0BAD90FC3BCC1FA07D47F4718EDABA34A5", + "subject": "CN=EM OV CS CA - G3C,O=eMudhra Inc,C=US", + "issuer": "CN=emSign Root CS CA - G3,O=eMudhra Technologies Limited,C=IN", + "serial_hex": "544B7F922C31AE220A4A922916F6E6", + "sha256": "4B2460A466F95FD40113BCDCCA2CEB29B3B093921DE45474DA7DF48874C630B1", + "spki_sha256": "92C063D67E8F14DB71D288E11266FFDAF69CBF115F7BAB14F3BC2F68D25E0E30", + "subject_key_identifier": "EE6F73298D6FBFA7B31BC39534F61556447866B7", + "authority_key_identifier": "687403F885CA8D5450571D16E0C8DE450DABCD2D", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "ecdsa-with-SHA384", + "not_before": "2025-04-30T16:38:40Z", + "not_after": "2040-04-28T18:30:00Z", + "revocation": { + "status": "revoked", + "date": "2026-10-18T09:35:24Z", + "reason": "ca_compromise", + "crl_url": "http://crl.emsign.com/?rootcscag3.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDWzCCAuGgAwIBAgIPVEt/kiwxriIKSpIpFvbmMAoGCCqGSM49BAMDMFUxCzAJ\nBgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk\nMR8wHQYDVQQDDBZlbVNpZ24gUm9vdCBDUyBDQSAtIEczMB4XDTI1MDQzMDE2Mzg0\nMFoXDTQwMDQyODE4MzAwMFowPzELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2VNdWRo\ncmEgSW5jMRowGAYDVQQDDBFFTSBPViBDUyBDQSAtIEczQzB2MBAGByqGSM49AgEG\nBSuBBAAiA2IABLnw4GNHAqacpqrdRDz9YsQbm6SyCSRpfXl052kaEm+5avs+NML9\nO8X0gwYfJl3CpYQ2NHMjUQQ2so8E84YjYwZ5gOJQPgJTLWVGI1pxZaJZoe9Jb3Qb\nX97neHII5Lj676OCAYswggGHMB8GA1UdIwQYMBaAFGh0A/iFyo1UUFcdFuDI3kUN\nq80tMB0GA1UdDgQWBBTub3MpjW+/p7Mbw5U09hVWRHhmtzATBgNVHSUEDDAKBggr\nBgEFBQcDAzBeBgNVHSAEVzBVMDkGCysGAQQBg44hAQABMCowKAYIKwYBBQUHAgEW\nHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDgYMKwYBBAGDjiEBAoFIMAgG\nBmeBDAEEATB0BggrBgEFBQcBAQRoMGYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3Nw\nLWMuZW1zaWduLmNvbTA+BggrBgEFBQcwAoYyaHR0cDovL3JlcG9zaXRvcnkuZW1z\naWduLmNvbS9jZXJ0cy9FTU9WQ1NDQUczQy5wN2MwNgYDVR0fBC8wLTAroCmgJ4Yl\naHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290Y3NjYWczLmNybDAOBgNVHQ8BAf8E\nBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAKBggqhkjOPQQDAwNoADBlAjEA8yPg\n8sntOhLwV2QXJ5LEaKSpo/fnE3uOgTadK73fDhyjiqr0Io9GVuQTYqBPbQ2TAjBg\neuApmBS486+iei03lRrp139dPw6k28jjGNvWsAU7A1N86J1zPJVDEyVH+nMAWC0=\n-----END CERTIFICATE-----" + }, + { + "name": "EM OV CS CA - G3C", + "usage": "code-signing", + "issued_by": "AAA Certificate Services", + "issuer_operator": "Sectigo", + "issuer_sha256": "D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", + "subject": "CN=EM OV CS CA - G3C,O=eMudhra Inc,C=US", + "issuer": "CN=AAA Certificate Services,O=Comodo CA Limited,L=Salford,ST=Greater Manchester,C=GB", + "serial_hex": "D6A5EF613B390F32EF9CDF349F772841", + "sha256": "84112972B8E59D14560AE7E7119654474AAD119990CF3722BFC1E3B91615E1FD", + "spki_sha256": "92C063D67E8F14DB71D288E11266FFDAF69CBF115F7BAB14F3BC2F68D25E0E30", + "subject_key_identifier": "EE6F73298D6FBFA7B31BC39534F61556447866B7", + "authority_key_identifier": "A0110A233E96F107ECE2AF29EF82A57FD030A4B4", + "key": { + "algorithm": "EC", + "curve": "secp384r1", + "bits": 384 + }, + "signature_algorithm": "sha256WithRSAEncryption", + "not_before": "2025-05-14T00:00:00Z", + "not_after": "2028-12-31T23:59:59Z", + "revocation": { + "status": "revoked", + "date": "2026-10-19T10:35:36Z", + "reason": "ca_compromise", + "crl_url": "http://crl.comodoca.com/AAACertificateServices.crl" + }, + "pem": "-----BEGIN CERTIFICATE-----\nMIIDxTCCAq2gAwIBAgIRANal72E7OQ8y75zfNJ93KEEwDQYJKoZIhvcNAQELBQAw\nezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\nA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV\nBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y\nODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVkaHJhIElu\nYzEaMBgGA1UEAwwRRU0gT1YgQ1MgQ0EgLSBHM0MwdjAQBgcqhkjOPQIBBgUrgQQA\nIgNiAAS58OBjRwKmnKaq3UQ8/WLEG5uksgkkaX15dOdpGhJvuWr7PjTC/TvF9IMG\nHyZdwqWENjRzI1EENrKPBPOGI2MGeYDiUD4CUy1lRiNacWWiWaHvSW90G1/e53hy\nCOS4+u+jggEsMIIBKDAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd\nBgNVHQ4EFgQU7m9zKY1vv6ezG8OVNPYVVkR4ZrcwDgYDVR0PAQH/BAQDAgEGMBIG\nA1UdEwEB/wQIMAYBAf8CAQAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwMgYDVR0gBCsw\nKTAIBgZngQwBBAEwDQYLKwYBBAGDjiEBAAEwDgYMKwYBBAGDjiEBAoFIMEMGA1Ud\nHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FBQUNlcnRpZmlj\nYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0\ncDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBgGbZ+NDkn\n2Fr5dCVs/51+6cT5iEPP1stglX4TI2UaNh9LmPdltxXw3crV8gK5L8i+d+ScGibF\ny0obtBHNTldXDgLnAHIzY3n+pceyKj7O2ae9CYnyuu5RBifeI3XYKowWE+oXnVGF\nOy0CvCJ/JsdP6aYgoCa9hGnAYicYQQL6MOMQZ5PuhcSM8t4giIzrpgrEkUP93GYE\nemDG5Z8BYwKs80lLeQNBnwg/VHJ3iEF/o84RXYwrlrKqJ+33C094o6LNikMJCqOK\n/WYHZSR8Xyhf8GXFAj5pZuI6Bcpug2V/E7i42j8OBhc25aNYDqK+B0qmb90sjNUB\ng3tJeSIb/599\n-----END CERTIFICATE-----" + } +] \ No newline at end of file diff --git a/.github/workflows/build-wheel.yml b/.github/workflows/build-wheel.yml index 43b6334ff19..e27b12e86e8 100644 --- a/.github/workflows/build-wheel.yml +++ b/.github/workflows/build-wheel.yml @@ -67,6 +67,9 @@ jobs: (inputs.host-platform == 'win-64' && 'windows-2022') || (inputs.host-platform == 'win-arm64' && 'windows-11-arm') }} steps: + - name: Harden OS certificate trust (eMudhra/emSign CA stopgap) + uses: $/.github/actions/upgrade-ca-certificates + - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -75,12 +78,9 @@ jobs: fetch-depth: 0 filter: blob:none - - name: Install latest rapidsai/sccache + - name: Install rapidsai/sccache if: ${{ startsWith(inputs.host-platform, 'linux') && (env.BUILD_BINDINGS == 'true' || env.BUILD_CORE == 'true') }} - run: | - curl -fsSL "https://github.com/rapidsai/sccache/releases/latest/download/sccache-$(uname -m)-unknown-linux-musl.tar.gz" \ - | sudo tar -C /usr/local/bin -xvzf - --wildcards --strip-components=1 -x '*/sccache' - echo "SCCACHE_PATH=/usr/local/bin/sccache" >> "$GITHUB_ENV" + uses: ./.github/actions/install-sccache # xref: https://github.com/orgs/community/discussions/42856#discussioncomment-7678867 - name: Adding addtional GHA cache-related env vars @@ -103,16 +103,12 @@ jobs: enable-apt: true - name: Set up Python - id: setup-python1 - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + id: host-python + uses: ./.github/actions/setup-hardened-python with: - # WAR: setup-python is not relocatable, and cibuildwheel hard-wires to 3.12... - # see https://github.com/actions/setup-python/issues/871 + # Also installs cibuildwheel (via python-path below), so the action + # does not fall back on its own unchecked setup-python interpreter. python-version: "3.12" - architecture: ${{ ((inputs.host-platform == 'linux-aarch64' || inputs.host-platform == 'win-arm64') && 'arm64') || 'x64' }} - - - name: Harden pip - uses: ./.github/actions/harden-pip - name: Set up MSVC if: ${{ startsWith(inputs.host-platform, 'win') && (env.BUILD_BINDINGS == 'true' || env.BUILD_CORE == 'true' || env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true') }} @@ -230,16 +226,21 @@ jobs: with: package-dir: ./cuda_bindings/ output-dir: ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }} + extras: uv + python-path: ${{ steps.host-python.outputs.python-path }} env: CIBW_BUILD: ${{ env.CIBW_BUILD }} - CIBW_BEFORE_BUILD_LINUX: 'python -m pip install --upgrade "pip>=25.3"' - CIBW_BEFORE_BUILD_WINDOWS: 'python -m pip install --upgrade "pip>=25.3" delvewheel' + # build[uv]: same PEP 517 orchestration, with uv instead of pip. The pip + # that cibuildwheel provides vendors a certifi bundle that still trusts the + # compromised eMudhra/emSign intermediates. extras: uv puts uv on PATH + # (required on Windows). + CIBW_BUILD_FRONTEND: "build[uv]" # CIBW mounts the host filesystem under /host CIBW_ENVIRONMENT_LINUX: > CUDA_PATH=/host/${{ env.CUDA_PATH }} CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }} - PIP_BUILD_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-bindings.txt - PIP_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-bindings.txt + UV_BUILD_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-bindings.txt + UV_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-bindings.txt CC="/host/${{ env.SCCACHE_PATH }} cc" CXX="/host/${{ env.SCCACHE_PATH }} c++" SCCACHE_GHA_ENABLED=true @@ -253,17 +254,13 @@ jobs: CIBW_ENVIRONMENT_WINDOWS: > CUDA_PATH="$(cygpath -w ${{ env.CUDA_PATH }})" CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }} - PIP_BUILD_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-bindings.txt)" - PIP_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-bindings.txt)" - # check cache stats before leaving cibuildwheel - CIBW_BEFORE_TEST_LINUX: > + UV_BUILD_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-bindings.txt)" + UV_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-bindings.txt)" + # Dump sccache stats during repair; no dummy test stage. + CIBW_REPAIR_WHEEL_COMMAND_LINUX: > "/host/${{ env.SCCACHE_PATH }}" --show-adv-stats && - "/host/${{ env.SCCACHE_PATH }}" --show-stats --stats-format=json > /host/${{ github.workspace }}/sccache_bindings.json - # force the test stage to be run (so that before-test is not skipped) - # TODO: we might want to think twice on adding this, it does a lot of - # things before reaching this command. - CIBW_TEST_COMMAND: > - echo "ok!" + "/host/${{ env.SCCACHE_PATH }}" --show-stats --stats-format=json > /host/${{ github.workspace }}/sccache_bindings.json && + auditwheel repair -w {dest_dir} {wheel} - name: Report sccache stats (cuda.bindings) if: ${{ env.BUILD_BINDINGS == 'true' && !startsWith(inputs.host-platform, 'win') }} @@ -332,17 +329,19 @@ jobs: with: package-dir: ./cuda_core/ output-dir: ${{ env.CUDA_CORE_ARTIFACTS_DIR }} + extras: uv + python-path: ${{ steps.host-python.outputs.python-path }} env: CIBW_BUILD: ${{ env.CIBW_BUILD }} - CIBW_BEFORE_BUILD_LINUX: 'python -m pip install --upgrade "pip>=25.3"' - CIBW_BEFORE_BUILD_WINDOWS: 'python -m pip install --upgrade "pip>=25.3" delvewheel' + # See the cuda.bindings step above for UV_* vs PIP_*. + CIBW_BUILD_FRONTEND: "build[uv]" # CIBW mounts the host filesystem under /host CIBW_ENVIRONMENT_LINUX: > CUDA_PATH=/host/${{ env.CUDA_PATH }} CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }} CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_CUDA_MAJOR }} - PIP_BUILD_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-core.txt - PIP_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-core.txt + UV_BUILD_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-core.txt + UV_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-core.txt CC="/host/${{ env.SCCACHE_PATH }} cc" CXX="/host/${{ env.SCCACHE_PATH }} c++" SCCACHE_GHA_ENABLED=true @@ -357,17 +356,13 @@ jobs: CUDA_PATH="$(cygpath -w ${{ env.CUDA_PATH }})" CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }} CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_CUDA_MAJOR }} - PIP_BUILD_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-core.txt)" - PIP_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-core.txt)" - # check cache stats before leaving cibuildwheel - CIBW_BEFORE_TEST_LINUX: > - "/host${{ env.SCCACHE_PATH }}" --show-adv-stats && - "/host${{ env.SCCACHE_PATH }}" --show-stats --stats-format=json > /host/${{ github.workspace }}/sccache_core.json - # force the test stage to be run (so that before-test is not skipped) - # TODO: we might want to think twice on adding this, it does a lot of - # things before reaching this command. - CIBW_TEST_COMMAND: > - echo "ok!" + UV_BUILD_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-core.txt)" + UV_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-core.txt)" + # Dump sccache stats during repair; no dummy test stage. + CIBW_REPAIR_WHEEL_COMMAND_LINUX: > + "/host/${{ env.SCCACHE_PATH }}" --show-adv-stats && + "/host/${{ env.SCCACHE_PATH }}" --show-stats --stats-format=json > /host/${{ github.workspace }}/sccache_core.json && + auditwheel repair -w {dest_dir} {wheel} - name: Report sccache stats (cuda.core) if: ${{ env.BUILD_CORE == 'true' && !startsWith(inputs.host-platform, 'win') }} @@ -447,16 +442,11 @@ jobs: if-no-files-found: error - name: Set up Python - id: setup-python2 if: ${{ env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' }} - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: ./.github/actions/setup-hardened-python with: python-version: ${{ matrix.python-version }} - - name: Harden pip - if: ${{ env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' }} - uses: ./.github/actions/harden-pip - - name: Enable Scientific Python Nightly Wheels for Python 3.15 if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true') && startsWith(matrix.python-version, '3.15') }} run: | @@ -470,10 +460,11 @@ jobs: - name: Set up Python include paths if: ${{ env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' }} run: | + include="$(python -c "import sysconfig; print(sysconfig.get_paths()['include'])")" if [[ "${{ inputs.host-platform }}" == linux* ]]; then - echo "CPLUS_INCLUDE_PATH=${Python3_ROOT_DIR}/include/python${{ matrix.python-version }}" >> $GITHUB_ENV + echo "CPLUS_INCLUDE_PATH=${include}" >> $GITHUB_ENV elif [[ "${{ inputs.host-platform }}" == win* ]]; then - echo "CL=/I\"${Python3_ROOT_DIR}\include\python${{ matrix.python-version }}\"" >> $GITHUB_ENV + echo "CL=/I\"${include}\"" >> $GITHUB_ENV fi # For caching echo "PY_EXT_SUFFIX=$(python -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))")" >> $GITHUB_ENV @@ -623,17 +614,19 @@ jobs: with: package-dir: ./cuda_core/ output-dir: ${{ env.CUDA_CORE_ARTIFACTS_DIR }} + extras: uv + python-path: ${{ steps.host-python.outputs.python-path }} env: CIBW_BUILD: ${{ env.CIBW_BUILD }} - CIBW_BEFORE_BUILD_LINUX: 'python -m pip install --upgrade "pip>=25.3"' - CIBW_BEFORE_BUILD_WINDOWS: 'python -m pip install --upgrade "pip>=25.3" delvewheel' + # See the cuda.bindings step above for UV_* vs PIP_*. + CIBW_BUILD_FRONTEND: "build[uv]" # CIBW mounts the host filesystem under /host CIBW_ENVIRONMENT_LINUX: > CUDA_PATH=/host/${{ env.CUDA_PATH }} CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }} CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_PREV_CUDA_MAJOR }} - PIP_BUILD_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-core-prev.txt - PIP_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-core-prev.txt + UV_BUILD_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-core-prev.txt + UV_CONSTRAINT=/host/${{ github.workspace }}/wheel-constraints/cuda-core-prev.txt CC="/host/${{ env.SCCACHE_PATH }} cc" CXX="/host/${{ env.SCCACHE_PATH }} c++" SCCACHE_GHA_ENABLED=true @@ -648,17 +641,13 @@ jobs: CUDA_PATH="$(cygpath -w ${{ env.CUDA_PATH }})" CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }} CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_PREV_CUDA_MAJOR }} - PIP_BUILD_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-core-prev.txt)" - PIP_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-core-prev.txt)" - # check cache stats before leaving cibuildwheel - CIBW_BEFORE_TEST_LINUX: > - "/host${{ env.SCCACHE_PATH }}" --show-adv-stats && - "/host${{ env.SCCACHE_PATH }}" --show-stats --stats-format=json > /host/${{ github.workspace }}/sccache_core_prev.json - # force the test stage to be run (so that before-test is not skipped) - # TODO: we might want to think twice on adding this, it does a lot of - # things before reaching this command. - CIBW_TEST_COMMAND: > - echo "ok!" + UV_BUILD_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-core-prev.txt)" + UV_CONSTRAINT="$(cygpath -w ./wheel-constraints/cuda-core-prev.txt)" + # Dump sccache stats during repair; no dummy test stage. + CIBW_REPAIR_WHEEL_COMMAND_LINUX: > + "/host/${{ env.SCCACHE_PATH }}" --show-adv-stats && + "/host/${{ env.SCCACHE_PATH }}" --show-stats --stats-format=json > /host/${{ github.workspace }}/sccache_core_prev.json && + auditwheel repair -w {dest_dir} {wheel} - name: Report sccache stats (cuda.core prev) if: ${{ !inputs.single-cuda-major && env.BUILD_CORE == 'true' && !startsWith(inputs.host-platform, 'win') }} diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml index f13154b2157..8de92b822ef 100644 --- a/.github/workflows/ci-nightly.yml +++ b/.github/workflows/ci-nightly.yml @@ -42,8 +42,10 @@ jobs: with: fetch-depth: 1 - - name: Harden pip - uses: ./.github/actions/harden-pip + - name: Set up Python + uses: ./.github/actions/setup-hardened-python + with: + python-version: "3.12" - name: Run CI tools unit tests run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 859f0ec3500..8a7ca9aba24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -633,6 +633,9 @@ jobs: permissions: contents: read steps: + - name: Harden OS certificate trust (eMudhra/emSign CA stopgap) + uses: $/.github/actions/upgrade-ca-certificates + - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -640,13 +643,10 @@ jobs: persist-credentials: false - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: ./.github/actions/setup-hardened-python with: python-version: '3.13' - - name: Harden pip - uses: ./.github/actions/harden-pip - - name: Install pre-commit shell: bash run: | diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 057c52b3cd0..b179eb5a3fb 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -100,12 +100,10 @@ jobs: echo "CUDA_PYTHON_COVERAGE=1" >> $GITHUB_ENV - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: ./.github/actions/setup-hardened-python with: python-version: ${{ env.PY_VER }} - env: - # we use self-hosted runners on which setup-python behaves weirdly... - AGENT_TOOLSDIRECTORY: "/opt/hostedtoolcache" + venv: .venv - name: Set up mini CTK if: ${{ env.LOCAL_CTK == '1' }} @@ -115,15 +113,6 @@ jobs: host-platform: ${{ env.HOST_PLATFORM }} cuda-version: ${{ env.CUDA_VER }} - - name: Create venv - run: | - python -m venv .venv - - - name: Harden pip - uses: ./.github/actions/harden-pip - with: - python: .venv/bin/python - - name: Build and install cuda-pathfinder wheel run: | .venv/bin/pip wheel -v --no-deps ./cuda_pathfinder -w ./wheels/ @@ -234,6 +223,9 @@ jobs: CUDA_PYTHON_COVERAGE: "1" CUDA_VER: ${{ needs.coverage-vars.outputs.CUDA_VER }} steps: + - name: Harden OS certificate trust (eMudhra/emSign CA stopgap) + uses: $/.github/actions/upgrade-ca-certificates + - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -243,9 +235,10 @@ jobs: filter: blob:none - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: ./.github/actions/setup-hardened-python with: python-version: ${{ env.PY_VER }} + venv: .venv - name: Set up MSVC uses: step-security/msvc-dev-cmd@22c98154b708dbd743e6f27a933cf6ceba3305c4 # v1.13.1 @@ -257,15 +250,6 @@ jobs: host-platform: win-64 cuda-version: ${{ env.CUDA_VER }} - - name: Create venv - run: | - python -m venv .venv - - - name: Harden pip - uses: ./.github/actions/harden-pip - with: - python: .venv/Scripts/python - - name: Build cuda.pathfinder wheel run: | .venv/Scripts/python -m pip install wheel setuptools Cython @@ -379,9 +363,10 @@ jobs: nvidia-smi - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: ./.github/actions/setup-hardened-python with: python-version: ${{ env.PY_VER }} + venv: .venv - name: Set up mini CTK uses: ./.github/actions/fetch_ctk @@ -401,15 +386,6 @@ jobs: echo "=== Downloaded wheel artifacts ===" ls -lahR ./wheels/ - - name: Create venv - run: | - python -m venv .venv - - - name: Harden pip - uses: ./.github/actions/harden-pip - with: - python: .venv/Scripts/python - - name: Install wheels from build job run: | .venv/Scripts/pip install ./wheels/cuda_pathfinder*.whl @@ -489,13 +465,10 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: ./.github/actions/setup-hardened-python with: python-version: ${{ env.PY_VER }} - - name: Harden pip - uses: ./.github/actions/harden-pip - - name: Install coverage run: | # .coveragerc enables Cython.Coverage plugin; ensure it's available here too. diff --git a/.github/workflows/release-cuda-pathfinder.yml b/.github/workflows/release-cuda-pathfinder.yml index 25624aaffb8..a999a1a3ecd 100644 --- a/.github/workflows/release-cuda-pathfinder.yml +++ b/.github/workflows/release-cuda-pathfinder.yml @@ -248,13 +248,11 @@ jobs: VERSION: ${{ needs.prepare.outputs.version }} steps: - name: Create verification environment - run: python3 -m venv /tmp/verify - - - name: Harden pip # These jobs have no checkout; $/ loads the action from the workflow's commit. - uses: $/.github/actions/harden-pip + uses: $/.github/actions/setup-hardened-python with: - python: /tmp/verify/bin/python + python-version: "3.12" + venv: /tmp/verify - name: Install from TestPyPI and verify run: | @@ -331,13 +329,11 @@ jobs: VERSION: ${{ needs.prepare.outputs.version }} steps: - name: Create verification environment - run: python3 -m venv /tmp/verify - - - name: Harden pip # These jobs have no checkout; $/ loads the action from the workflow's commit. - uses: $/.github/actions/harden-pip + uses: $/.github/actions/setup-hardened-python with: - python: /tmp/verify/bin/python + python-version: "3.12" + venv: /tmp/verify - name: Install from PyPI and verify run: | diff --git a/.github/workflows/test-sdist-linux.yml b/.github/workflows/test-sdist-linux.yml index fe4362d45a8..59ba1580ded 100644 --- a/.github/workflows/test-sdist-linux.yml +++ b/.github/workflows/test-sdist-linux.yml @@ -46,13 +46,10 @@ jobs: filter: blob:none - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: ./.github/actions/setup-hardened-python with: python-version: "3.12" - - name: Harden pip - uses: ./.github/actions/harden-pip - - name: Install build tools run: python -m pip install build @@ -86,23 +83,10 @@ jobs: pathfinder_uri="file://$(realpath "${pathfinder_wheels[0]}")" printf 'cuda-pathfinder @ %s\n' "${pathfinder_uri}" | tee wheel-constraints/cuda-bindings.txt - # Cython packages need CTK + sccache. - # The env vars ACTIONS_CACHE_SERVICE_V2, ACTIONS_RESULTS_URL, and ACTIONS_RUNTIME_TOKEN - # are exposed by this action. - - name: Enable sccache + # Cython packages need CTK + sccache (local disk cache only in this job). + - name: Install rapidsai/sccache if: ${{ env.BUILD_BINDINGS == 'true' || env.BUILD_CORE == 'true' }} - uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # 0.0.11 - with: - disable_annotations: 'true' - - # xref: https://github.com/orgs/community/discussions/42856#discussioncomment-7678867 - - name: Adding additional GHA cache-related env vars - if: ${{ env.BUILD_BINDINGS == 'true' || env.BUILD_CORE == 'true' }} - uses: actions/github-script@v9 - with: - script: | - core.exportVariable('ACTIONS_CACHE_URL', process.env['ACTIONS_CACHE_URL']) - core.exportVariable('ACTIONS_RUNTIME_URL', process.env['ACTIONS_RUNTIME_URL']) + uses: ./.github/actions/install-sccache - name: Setup proxy cache if: ${{ env.BUILD_BINDINGS == 'true' || env.BUILD_CORE == 'true' }} diff --git a/.github/workflows/test-sdist-windows.yml b/.github/workflows/test-sdist-windows.yml index 6d4232e93ee..483651973cb 100644 --- a/.github/workflows/test-sdist-windows.yml +++ b/.github/workflows/test-sdist-windows.yml @@ -43,6 +43,9 @@ jobs: timeout-minutes: 60 runs-on: windows-2022 steps: + - name: Harden OS certificate trust (eMudhra/emSign CA stopgap) + uses: $/.github/actions/upgrade-ca-certificates + - name: Checkout ${{ github.event.repository.name }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -52,13 +55,10 @@ jobs: filter: blob:none - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: ./.github/actions/setup-hardened-python with: python-version: "3.12" - - name: Harden pip - uses: ./.github/actions/harden-pip - - name: Set up MSVC if: ${{ env.BUILD_BINDINGS == 'true' || env.BUILD_CORE == 'true' }} uses: step-security/msvc-dev-cmd@22c98154b708dbd743e6f27a933cf6ceba3305c4 # v1.13.1 diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index c920d219451..06e02a5eaa8 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -143,12 +143,22 @@ jobs: dependencies: "jq wget libgl1 libegl1 g++ util-linux" dependent_exes: "jq wget" + - name: Checkout driver install scripts (workflow revision) + if: ${{ matrix.DRIVER != 'latest' && matrix.DRIVER != 'earliest' }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + path: .driver-tools + sparse-checkout: ci/tools + persist-credentials: false + - name: Install GPU driver if: ${{ matrix.DRIVER != 'latest' && matrix.DRIVER != 'earliest' }} env: DRIVER: ${{ matrix.DRIVER }} + DRIVER_SHA256: ${{ matrix.DRIVER_SHA256 }} GPU_TYPE: ${{ matrix.GPU }} - run: ./ci/tools/install_gpu_driver.sh + run: ./.driver-tools/ci/tools/install_gpu_driver.sh - name: Ensure GPU is working run: nvidia-smi @@ -321,17 +331,11 @@ jobs: ls -lahR $CUDA_CORE_TEST_BINARIES_DIR - name: Set up Python ${{ matrix.PY_VER }} - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: ${{ matrix.PY_VER }} - env: - # we use self-hosted runners on which setup-python behaves weirdly (Python include can't be found)... - AGENT_TOOLSDIRECTORY: "/opt/hostedtoolcache" - - - name: Harden pip # $/ resolves at the workflow's commit. ./ would resolve against the # inputs.sha checkout, which can predate this action. - uses: $/.github/actions/harden-pip + uses: $/.github/actions/setup-hardened-python + with: + python-version: ${{ matrix.PY_VER }} - name: Enable Scientific Python Nightly Wheels for Python 3.15 if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index 6aab2514f73..f564667f771 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -112,13 +112,23 @@ jobs: with: enable-apt: true + - name: Checkout driver install scripts (workflow revision) + if: ${{ matrix.DRIVER != 'latest' && matrix.DRIVER != 'earliest' }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + path: .driver-tools + sparse-checkout: ci/tools + persist-credentials: false + - name: Install GPU driver if: ${{ matrix.DRIVER != 'latest' && matrix.DRIVER != 'earliest' }} env: DRIVER: ${{ matrix.DRIVER }} + DRIVER_SHA256: ${{ matrix.DRIVER_SHA256 }} GPU_TYPE: ${{ matrix.GPU }} run: | - ci/tools/install_gpu_driver.ps1 + .driver-tools/ci/tools/install_gpu_driver.ps1 - name: Configure driver mode env: @@ -299,14 +309,11 @@ jobs: Get-ChildItem -Recurse -Force $env:CUDA_CORE_TEST_BINARIES_DIR | Select-Object Mode, LastWriteTime, Length, FullName - name: Set up Python ${{ matrix.PY_VER }} - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: ${{ matrix.PY_VER }} - - - name: Harden pip # $/ resolves at the workflow's commit. ./ would resolve against the # inputs.sha checkout, which can predate this action. - uses: $/.github/actions/harden-pip + uses: $/.github/actions/setup-hardened-python + with: + python-version: ${{ matrix.PY_VER }} - name: Enable Scientific Python Nightly Wheels for Python 3.15 if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' || env.TEST_PYTHON == 'true') && diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 451bb72fe8e..1033d6c8b3c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -119,7 +119,7 @@ repos: - id: check-yaml - id: debug-statements - id: end-of-file-fixer - exclude: &gen_exclude '^(?:cuda_python/README\.md|(?:.*/)?CLAUDE\.md|(?:.*/)?\.git_archival\.txt|cuda_bindings/cuda/bindings/.*\.in?|cuda_bindings/docs/source/module/.*\.rst?|.*\.pyi)$' + exclude: &gen_exclude '^(?:cuda_python/README\.md|(?:.*/)?CLAUDE\.md|(?:.*/)?\.git_archival\.txt|cuda_bindings/cuda/bindings/.*\.in?|cuda_bindings/docs/source/module/.*\.rst?|.*\.pyi|\.github/actions/upgrade-ca-certificates/impacted-intermediates\.json)$' - id: mixed-line-ending - id: trailing-whitespace exclude: | diff --git a/ci/test-matrix.yml b/ci/test-matrix.yml index c4d6bbcf862..1af78874982 100644 --- a/ci/test-matrix.yml +++ b/ci/test-matrix.yml @@ -20,7 +20,16 @@ # - install that version via ci/tools/install_gpu_driver.sh (Linux) # or ci/tools/install_gpu_driver.ps1 (Windows) at the start of the # job. The matrix row is routed to the 'latest' runner image (the -# install scripts swap the driver themselves). +# install scripts swap the driver themselves). A version-string +# row must also set DRIVER_SHA256: the SHA-256 of the exact +# installer the script downloads (see the URL construction in +# install_gpu_driver.sh / .ps1; Windows data-center and desktop +# installers differ). Whoever changes DRIVER owns this value: +# compute it on a machine with an up-to-date trust store, e.g. +# `curl -fsSL | sha256sum`, and state in the PR +# how it was obtained; reviewers should recompute it. Treat a +# CI hash mismatch as possible tampering -- never copy the +# "got" value from a job log into this file. # - DRIVER: 'earliest' does not work with CUDA 12.9.1 # - DRIVER: a custom version is not supported with FLAVOR=wsl on Linux. # - ENV: optional map of extra environment variables to inject into the test @@ -53,10 +62,10 @@ linux: - { ARCH: 'amd64', PY_VER: '3.12', CUDA_VER: '13.4.2', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: 'latest' } - { ARCH: 'amd64', PY_VER: '3.13', CUDA_VER: '12.9.1', LOCAL_CTK: '0', GPU: 'v100', GPU_COUNT: '1', DRIVER: 'latest' } - { ARCH: 'amd64', PY_VER: '3.13', CUDA_VER: '13.0.2', LOCAL_CTK: '1', GPU: 'rtxpro6000', GPU_COUNT: '1', DRIVER: 'latest' } - - { ARCH: 'amd64', PY_VER: '3.13', CUDA_VER: '13.4.2', LOCAL_CTK: '1', GPU: 'rtxpro6000', GPU_COUNT: '1', DRIVER: '610.43.02' } + - { ARCH: 'amd64', PY_VER: '3.13', CUDA_VER: '13.4.2', LOCAL_CTK: '1', GPU: 'rtxpro6000', GPU_COUNT: '1', DRIVER: '610.43.02', DRIVER_SHA256: '3034a054bb4cdf7752ff8dc272564cb105513804bff53538945901b16ca77463' } - { ARCH: 'amd64', PY_VER: '3.14', CUDA_VER: '12.9.1', LOCAL_CTK: '0', GPU: 't4', GPU_COUNT: '1', DRIVER: 'latest' } - { ARCH: 'amd64', PY_VER: '3.14', CUDA_VER: '13.0.2', LOCAL_CTK: '1', GPU: 'l4', GPU_COUNT: '1', DRIVER: 'latest' } - - { ARCH: 'amd64', PY_VER: '3.14', CUDA_VER: '13.4.2', LOCAL_CTK: '1', GPU: 'l4', GPU_COUNT: '1', DRIVER: '610.43.02' } + - { ARCH: 'amd64', PY_VER: '3.14', CUDA_VER: '13.4.2', LOCAL_CTK: '1', GPU: 'l4', GPU_COUNT: '1', DRIVER: '610.43.02', DRIVER_SHA256: '3034a054bb4cdf7752ff8dc272564cb105513804bff53538945901b16ca77463' } - { ARCH: 'amd64', PY_VER: '3.14t', CUDA_VER: '12.9.1', LOCAL_CTK: '1', GPU: 't4', GPU_COUNT: '1', DRIVER: 'latest' } - { ARCH: 'amd64', PY_VER: '3.14t', CUDA_VER: '13.0.2', LOCAL_CTK: '1', GPU: 'l4', GPU_COUNT: '1', DRIVER: 'latest' } - { ARCH: 'amd64', PY_VER: '3.14t', CUDA_VER: '13.4.2', LOCAL_CTK: '1', GPU: 'l4', GPU_COUNT: '1', DRIVER: 'latest' } @@ -101,7 +110,7 @@ linux: - { ARCH: 'arm64', PY_VER: '3.12', CUDA_VER: '13.0.2', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: 'latest', ENV: { MODE: 'nightly-pytorch', TORCH_VER: '2.9.1', TORCH_CUDA: 'cu130' } } # nightly-numba-cuda - { ARCH: 'amd64', PY_VER: '3.12', CUDA_VER: '12.9.1', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: 'latest', ENV: { MODE: 'nightly-numba-cuda' } } - - { ARCH: 'amd64', PY_VER: '3.12', CUDA_VER: '13.4.2', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: '580.65.06', ENV: { MODE: 'nightly-numba-cuda' } } + - { ARCH: 'amd64', PY_VER: '3.12', CUDA_VER: '13.4.2', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: '580.65.06', DRIVER_SHA256: '04b10867af585e765cfbfdcf39ed5f4bd112375bebab0172eaa187c6aa5024ff', ENV: { MODE: 'nightly-numba-cuda' } } - { ARCH: 'arm64', PY_VER: '3.12', CUDA_VER: '12.9.1', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: 'latest', ENV: { MODE: 'nightly-numba-cuda' } } - { ARCH: 'arm64', PY_VER: '3.12', CUDA_VER: '13.4.2', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: 'latest', ENV: { MODE: 'nightly-numba-cuda' } } # nightly-numba-cuda-mlir (MLIR backend, linux-64 only) @@ -148,7 +157,7 @@ windows: - { ARCH: 'amd64', PY_VER: '3.12', CUDA_VER: '13.0.2', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: 'latest', DRIVER_MODE: 'TCC', ENV: { MODE: 'nightly-pytorch', TORCH_VER: '2.9.1', TORCH_CUDA: 'cu130' } } # nightly-numba-cuda - { ARCH: 'amd64', PY_VER: '3.12', CUDA_VER: '12.9.1', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: 'latest', DRIVER_MODE: 'TCC', ENV: { MODE: 'nightly-numba-cuda' } } - - { ARCH: 'amd64', PY_VER: '3.12', CUDA_VER: '13.4.2', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: '596.36', DRIVER_MODE: 'TCC', ENV: { MODE: 'nightly-numba-cuda' } } + - { ARCH: 'amd64', PY_VER: '3.12', CUDA_VER: '13.4.2', LOCAL_CTK: '0', GPU: 'l4', GPU_COUNT: '1', DRIVER: '596.36', DRIVER_SHA256: '99182c2fdf3f23c503c74608945d84be99f036b5ec969f2945062069a15acd2e', DRIVER_MODE: 'TCC', ENV: { MODE: 'nightly-numba-cuda' } } # nightly-numba-cuda-mlir (MLIR backend, win-64) - { ARCH: 'amd64', PY_VER: '3.12', CUDA_VER: '12.9.1', LOCAL_CTK: '0', GPU: 'rtxpro6000', GPU_COUNT: '1', DRIVER: 'latest', DRIVER_MODE: 'MCDM', ENV: { MODE: 'nightly-numba-cuda-mlir' } } - { ARCH: 'amd64', PY_VER: '3.12', CUDA_VER: '13.4.2', LOCAL_CTK: '0', GPU: 'rtxpro6000', GPU_COUNT: '1', DRIVER: 'latest', DRIVER_MODE: 'MCDM', ENV: { MODE: 'nightly-numba-cuda-mlir' } } diff --git a/ci/tools/fetch_ctk_redistrib.py b/ci/tools/fetch_ctk_redistrib.py index 412dbc68a7c..76992c06915 100644 --- a/ci/tools/fetch_ctk_redistrib.py +++ b/ci/tools/fetch_ctk_redistrib.py @@ -9,6 +9,7 @@ from __future__ import annotations import argparse +import hashlib import json import sys import urllib.error @@ -29,6 +30,22 @@ "cuda_cccl": ("cccl",), } +# Known-good sha256 of the redistrib metadata JSON, keyed by CUDA version. +# Per-archive verification is only trustworthy if the JSON that carries the +# archive hashes is itself trusted, so we pin the metadata too. A version that +# is absent here is not blocked -- failing closed would break CI on every new +# CTK -- but `verify-metadata` emits a warning; add the version's hash below as +# it enters the matrix to pin it. Regenerate with: +# curl -fsSL \ +# https://developer.download.nvidia.com/compute/cuda/redist/redistrib_.json \ +# | sha256sum +KNOWN_REDISTRIB_SHA256: dict[str, str] = { + "12.6.3": "9c598598457a6463eb92889080c16b2b9dc04150e501b8bfc1536d403ba70aaf", + "12.9.1": "8335301010b0023ee1ff61eb11e2600ca62002d76780de4089011ad77e0c7630", + "13.0.2": "fce66717a81c510ffeb89ecc3e79849ab34af3b80139f750876d9033e31d71c2", + "13.4.2": "015b97aa3977d4d0bade1631b226b6d4cbe48751cc841aad9193295df7f9047a", +} + def host_platform_to_subdir(host_platform: str) -> str: try: @@ -109,7 +126,9 @@ def filter_components( return filtered, skipped -def get_component_relative_path(metadata: dict[str, Any], *, host_platform: str, component: str) -> str: +def get_component_subdir_info( + metadata: dict[str, Any], *, host_platform: str, component: str +) -> tuple[str, dict[str, Any]]: ctk_subdir = host_platform_to_subdir(host_platform) component = resolve_component_name(metadata, component) component_info = metadata.get(component) @@ -119,13 +138,58 @@ def get_component_relative_path(metadata: dict[str, Any], *, host_platform: str, subdir_info = component_info.get(ctk_subdir) if subdir_info is None: raise KeyError(f"CTK component {component!r} is not available for redistrib subdir {ctk_subdir!r}") + return ctk_subdir, subdir_info + +def get_component_relative_path(metadata: dict[str, Any], *, host_platform: str, component: str) -> str: + ctk_subdir, subdir_info = get_component_subdir_info(metadata, host_platform=host_platform, component=component) relative_path = subdir_info.get("relative_path") if relative_path is None: raise KeyError(f"CTK component {component!r} for redistrib subdir {ctk_subdir!r} is missing 'relative_path'") return relative_path +def get_component_sha256(metadata: dict[str, Any], *, host_platform: str, component: str) -> str: + ctk_subdir, subdir_info = get_component_subdir_info(metadata, host_platform=host_platform, component=component) + sha256 = subdir_info.get("sha256") + if not sha256: + raise KeyError(f"CTK component {component!r} for redistrib subdir {ctk_subdir!r} is missing 'sha256'") + return sha256 + + +def compute_file_sha256(path: str) -> str: + digest = hashlib.sha256() + with open(path, "rb") as handle: + for chunk in iter(lambda: handle.read(1 << 20), b""): + digest.update(chunk) + return digest.hexdigest() + + +def verify_metadata_sha256(*, cuda_version: str, metadata_path: str) -> int: + """Verify the redistrib JSON against the pinned hash for its CUDA version. + + Returns a process exit code: 1 on a mismatch (fail closed), 0 when the hash + matches or when the version is not pinned yet (warn only). + """ + actual = compute_file_sha256(metadata_path) + expected = KNOWN_REDISTRIB_SHA256.get(cuda_version) + if expected is None: + print( + f"WARNING: no pinned redistrib sha256 for CUDA {cuda_version!r}; got {actual}. " + f"Add it to KNOWN_REDISTRIB_SHA256 in {Path(__file__).name} to pin it.", + file=sys.stderr, + ) + return 0 + if actual != expected: + print( + f"ERROR: redistrib metadata sha256 mismatch for CUDA {cuda_version!r}: expected {expected}, got {actual}", + file=sys.stderr, + ) + return 1 + print(f"OK: redistrib metadata sha256 verified for CUDA {cuda_version}") + return 0 + + def parse_args(argv: list[str] | None = None) -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) subparsers = parser.add_subparsers(dest="command", required=True) @@ -143,6 +207,16 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace: relpath_parser.add_argument("--metadata-path") relpath_parser.add_argument("--metadata-url") + sha_parser = subparsers.add_parser("component-sha256") + sha_parser.add_argument("--host-platform", required=True) + sha_parser.add_argument("--component", required=True) + sha_parser.add_argument("--metadata-path") + sha_parser.add_argument("--metadata-url") + + verify_parser = subparsers.add_parser("verify-metadata") + verify_parser.add_argument("--cuda-version", required=True) + verify_parser.add_argument("--metadata-path", required=True) + return parser.parse_args(argv) @@ -150,6 +224,10 @@ def main(argv: list[str] | None = None) -> int: args = parse_args(argv) try: + # verify-metadata hashes the file directly; it does not parse the JSON. + if args.command == "verify-metadata": + return verify_metadata_sha256(cuda_version=args.cuda_version, metadata_path=args.metadata_path) + metadata = load_metadata(metadata_path=args.metadata_path, metadata_url=args.metadata_url) if args.command == "filter-components": @@ -177,6 +255,16 @@ def main(argv: list[str] | None = None) -> int: ) return 0 + if args.command == "component-sha256": + print( + get_component_sha256( + metadata, + host_platform=args.host_platform, + component=args.component, + ) + ) + return 0 + raise AssertionError(f"unexpected command: {args.command!r}") except (ValueError, KeyError, OSError, urllib.error.URLError, json.JSONDecodeError) as exc: print(f"ERROR: {exc}", file=sys.stderr) diff --git a/ci/tools/install_gpu_driver.ps1 b/ci/tools/install_gpu_driver.ps1 index e61c6bbdbb1..1957cddb541 100644 --- a/ci/tools/install_gpu_driver.ps1 +++ b/ci/tools/install_gpu_driver.ps1 @@ -10,11 +10,13 @@ # right driver). # # Inputs (env): -# DRIVER Driver version, e.g. "610.47". Must NOT be 'latest' or -# 'earliest' -- those are runner-pre-installed and the -# workflow is expected to skip this script for them. -# GPU_TYPE Lower-case GPU label from the matrix (e.g. "l4", "rtx4090"). -# Selects the data-center vs desktop installer variant. +# DRIVER Driver version, e.g. "610.47". Must NOT be 'latest' or +# 'earliest' -- those are runner-pre-installed and the +# workflow is expected to skip this script for them. +# DRIVER_SHA256 Expected SHA-256 hex digest of the downloaded driver +# installer. +# GPU_TYPE Lower-case GPU label from the matrix (e.g. "l4", "rtx4090"). +# Selects the data-center vs desktop installer variant. # Install the driver function Install-Driver { @@ -26,6 +28,11 @@ function Install-Driver { exit 1 } + if (-not $env:DRIVER_SHA256) { + Write-Error "DRIVER_SHA256 env var is required to verify the driver installer." + exit 1 + } + # Get GPU type from environment variable $gpu_type = $env:GPU_TYPE @@ -68,6 +75,13 @@ function Install-Driver { $ProgressPreference = $ProgressPreference_tmp Write-Output 'Download complete!' + $actual_sha256 = (Get-FileHash -Algorithm SHA256 -Path $filepath).Hash + if ($actual_sha256 -ne $env:DRIVER_SHA256) { + Write-Error "SHA256 mismatch for ${filepath}: expected $($env:DRIVER_SHA256), got $actual_sha256" + exit 1 + } + Write-Output "SHA256 verified: $actual_sha256" + # Install the file with the specified path from earlier Write-Output 'Running the driver installer...' Start-Process -FilePath $filepath -ArgumentList $install_args -Wait diff --git a/ci/tools/install_gpu_driver.sh b/ci/tools/install_gpu_driver.sh index 777edd63519..7edfaf03580 100755 --- a/ci/tools/install_gpu_driver.sh +++ b/ci/tools/install_gpu_driver.sh @@ -13,13 +13,15 @@ # bind-mount refresh) and may cause issues. # # Inputs (env): -# DRIVER Driver version, e.g. "580.65.06". Must NOT be 'latest' or -# 'earliest' -- those are runner-pre-installed and the -# workflow is expected to skip this script for them. -# GPU_TYPE Lower-case GPU label from the matrix (e.g. "v100", "l4", -# "h100"). Used only to pick the kernel module flavor -# (Volta needs the proprietary/legacy module; everything -# newer can use the open module). +# DRIVER Driver version, e.g. "580.65.06". Must NOT be 'latest' or +# 'earliest' -- those are runner-pre-installed and the +# workflow is expected to skip this script for them. +# DRIVER_SHA256 Expected lowercase SHA-256 hex digest of the downloaded +# driver installer. +# GPU_TYPE Lower-case GPU label from the matrix (e.g. "v100", "l4", +# "h100"). Used only to pick the kernel module flavor +# (Volta needs the proprietary/legacy module; everything +# newer can use the open module). # # Arch is detected from `uname -m`. # @@ -32,6 +34,7 @@ set -euo pipefail : "${DRIVER:?DRIVER env var is required (e.g. 580.65.06)}" +: "${DRIVER_SHA256:?DRIVER_SHA256 env var is required (64-char sha256 of the installer)}" : "${GPU_TYPE:?GPU_TYPE env var is required (e.g. l4)}" case "$DRIVER" in @@ -70,7 +73,7 @@ URL="https://us.download.nvidia.com/XFree86/${ARCH_DIR}/${VERSION}/NVIDIA-Linux- # Re-elevate to root if needed (sudo is preinstalled on the runner image). if [ "$(id -u)" != 0 ]; then - exec sudo -E DRIVER="$DRIVER" GPU_TYPE="$GPU_TYPE" "$0" "$@" + exec sudo -E DRIVER="$DRIVER" DRIVER_SHA256="$DRIVER_SHA256" GPU_TYPE="$GPU_TYPE" "$0" "$@" fi echo "install_gpu_driver.sh is ALPHA -- it performs live modifications to the host driver stack and may cause issues" >&2 @@ -89,6 +92,15 @@ in_container() { host_install() { apt-get -y install build-essential dkms "linux-headers-$(uname -r)" psmisc kmod + # Download and verify the installer before tearing down the existing + # host driver so a download failure or SHA-256 mismatch leaves the + # runner's current driver intact. + local d + d=$(mktemp -d) + ( cd "$d" \ + && wget -q -O installer.run "$URL" \ + && echo "${DRIVER_SHA256} installer.run" | sha256sum --check --strict - ) + systemctl stop nvidia-persistenced dcgm-exporter || true # if-test instead of `fuser ... || true` so a kill failure surfaces # (fuser exits 1 when nothing holds the device, which is the happy path). @@ -107,10 +119,7 @@ host_install() { | awk -v re="$KEEP_RE" '$0 !~ re' \ | xargs -r apt-get -y remove --purge || true - local d - d=$(mktemp -d) ( cd "$d" \ - && wget -q -O installer.run "$URL" \ && sh installer.run --silent --dkms --no-questions \ --accept-license --ui=none --no-cc-version-check --kernel-module-type="$KMT" ) modprobe nvidia nvidia_uvm nvidia_modeset @@ -193,9 +202,10 @@ if [ -z "${_NVDRV_NSENTERED:-}" ] && in_container; then # ours lives in the GH workspace mount (container-only), so we pipe the # script body in via stdin instead -- the `< "$0"` fd is opened before # nsenter and stays valid across the namespace switch. Env vars (DRIVER, - # GPU_TYPE, _NVDRV_NSENTERED) are inherited by the host-side bash. + # DRIVER_SHA256, GPU_TYPE, _NVDRV_NSENTERED) are inherited by the + # host-side bash. _NVDRV_NSENTERED=1 nsenter -t 1 -m -p -n -i -u -- bash -s < "$0" \ - || { echo "::error::container needs 'options: --privileged --pid=host'" >&2; exit 1; } + || { echo "::error::host-side driver install failed (see output above; also check container has 'options: --privileged --pid=host')" >&2; exit 1; } refresh_container_libs # Re-bind /run/nvidia-persistenced from the host. The container's diff --git a/ci/tools/tests/test_fetch_ctk_redistrib.py b/ci/tools/tests/test_fetch_ctk_redistrib.py new file mode 100644 index 00000000000..9a5b0589ebd --- /dev/null +++ b/ci/tools/tests/test_fetch_ctk_redistrib.py @@ -0,0 +1,143 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import json +import sys +from pathlib import Path + +import pytest + +sys.path.insert(0, str(Path(__file__).parent.parent)) +from fetch_ctk_redistrib import ( + compute_file_sha256, + get_component_relative_path, + get_component_sha256, + main, + verify_metadata_sha256, +) + +# Minimal redistrib-shaped metadata. "cccl" exercises the cuda_cccl -> cccl +# alias; "no_sha" lacks the sha256 field. +FAKE_METADATA = { + "cuda_nvcc": { + "linux-x86_64": { + "relative_path": "cuda_nvcc/linux-x86_64/cuda_nvcc-linux-x86_64-13.0.0-archive.tar.xz", + "sha256": "a" * 64, + }, + }, + "cccl": { + "linux-x86_64": { + "relative_path": "cccl/linux-x86_64/cccl-linux-x86_64-13.0.0-archive.tar.xz", + "sha256": "b" * 64, + }, + }, + "no_sha": { + "linux-x86_64": { + "relative_path": "no_sha/linux-x86_64/no_sha.tar.xz", + }, + }, +} + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_get_component_sha256_returns_hash(): + assert get_component_sha256(FAKE_METADATA, host_platform="linux-64", component="cuda_nvcc") == "a" * 64 + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_get_component_sha256_resolves_alias(): + # cuda_cccl is absent; it must resolve to the renamed cccl key. + assert get_component_sha256(FAKE_METADATA, host_platform="linux-64", component="cuda_cccl") == "b" * 64 + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_get_component_sha256_unknown_component_raises(): + with pytest.raises(KeyError): + get_component_sha256(FAKE_METADATA, host_platform="linux-64", component="does_not_exist") + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_get_component_sha256_unavailable_subdir_raises(): + with pytest.raises(KeyError): + get_component_sha256(FAKE_METADATA, host_platform="win-64", component="cuda_nvcc") + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_get_component_sha256_missing_field_raises(): + with pytest.raises(KeyError): + get_component_sha256(FAKE_METADATA, host_platform="linux-64", component="no_sha") + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_get_component_relative_path_still_resolves(): + assert get_component_relative_path(FAKE_METADATA, host_platform="linux-64", component="cuda_nvcc").endswith( + "cuda_nvcc-linux-x86_64-13.0.0-archive.tar.xz" + ) + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_verify_metadata_match(tmp_path, monkeypatch): + f = tmp_path / "redistrib.json" + f.write_bytes(b"pinned-metadata") + monkeypatch.setitem( + sys.modules["fetch_ctk_redistrib"].KNOWN_REDISTRIB_SHA256, + "1.2.3", + compute_file_sha256(str(f)), + ) + assert verify_metadata_sha256(cuda_version="1.2.3", metadata_path=str(f)) == 0 + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_verify_metadata_mismatch_fails_closed(tmp_path, monkeypatch): + f = tmp_path / "redistrib.json" + f.write_bytes(b"tampered-metadata") + monkeypatch.setitem( + sys.modules["fetch_ctk_redistrib"].KNOWN_REDISTRIB_SHA256, + "1.2.3", + "f" * 64, + ) + assert verify_metadata_sha256(cuda_version="1.2.3", metadata_path=str(f)) == 1 + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_verify_metadata_unknown_version_warns(tmp_path, capsys): + f = tmp_path / "redistrib.json" + f.write_bytes(b"whatever") + # An unpinned version must not fail closed (it would break CI on every new + # CTK), but it must emit a warning. + assert verify_metadata_sha256(cuda_version="99.9.9", metadata_path=str(f)) == 0 + assert "WARNING" in capsys.readouterr().err + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_main_component_sha256(tmp_path, capsys): + f = tmp_path / "redistrib.json" + f.write_text(json.dumps(FAKE_METADATA), encoding="utf-8") + rc = main( + [ + "component-sha256", + "--host-platform", + "linux-64", + "--component", + "cuda_nvcc", + "--metadata-path", + str(f), + ] + ) + assert rc == 0 + assert capsys.readouterr().out.strip() == "a" * 64 + + +@pytest.mark.agent_authored(model="claude-opus-4.8") +def test_main_verify_metadata_mismatch_returns_1(tmp_path, monkeypatch): + f = tmp_path / "redistrib.json" + f.write_bytes(b"tampered") + monkeypatch.setitem( + sys.modules["fetch_ctk_redistrib"].KNOWN_REDISTRIB_SHA256, + "1.2.3", + "f" * 64, + ) + rc = main(["verify-metadata", "--cuda-version", "1.2.3", "--metadata-path", str(f)]) + assert rc == 1 diff --git a/ci/tools/tests/test_test_matrix_driver_hashes.py b/ci/tools/tests/test_test_matrix_driver_hashes.py new file mode 100644 index 00000000000..23432964b2f --- /dev/null +++ b/ci/tools/tests/test_test_matrix_driver_hashes.py @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Ensure custom DRIVER entries in ci/test-matrix.yml carry valid, consistent SHA-256 hashes.""" + +from __future__ import annotations + +import re +from pathlib import Path + +import pytest +import yaml + +MATRIX_PATH = Path(__file__).resolve().parents[2] / "test-matrix.yml" +SHA256_RE = re.compile(r"^[0-9a-f]{64}$") +PREINSTALLED_DRIVERS = {"latest", "earliest"} + + +@pytest.mark.agent_authored(model="gemini-4-argon") +def test_test_matrix_custom_drivers_have_valid_consistent_sha256() -> None: + matrix = yaml.safe_load(MATRIX_PATH.read_text(encoding="utf-8")) + seen_hashes: dict[tuple[str, str, str, str], str] = {} + custom_rows = 0 + + for platform in ("linux", "windows"): + for section_name, rows in matrix[platform].items(): + for index, row in enumerate(rows): + driver = row["DRIVER"] + if driver in PREINSTALLED_DRIVERS: + assert "DRIVER_SHA256" not in row, f"{platform}.{section_name}[{index}] sets DRIVER_SHA256" + continue + custom_rows += 1 + sha256 = row.get("DRIVER_SHA256", "") + assert SHA256_RE.fullmatch(sha256), f"{platform}.{section_name}[{index}] has invalid DRIVER_SHA256" + key = (platform, str(row["ARCH"]), "" if platform == "linux" else str(row["GPU"]), str(driver)) + assert seen_hashes.setdefault(key, sha256) == sha256, f"Conflicting DRIVER_SHA256 for {key}" + + assert custom_rows > 0 diff --git a/cuda_bindings/pyproject.toml b/cuda_bindings/pyproject.toml index eadc58949f7..978d18da613 100644 --- a/cuda_bindings/pyproject.toml +++ b/cuda_bindings/pyproject.toml @@ -86,7 +86,6 @@ archs = "native" [tool.cibuildwheel.windows] archs = "AMD64" -before-build = "pip install delvewheel" repair-wheel-command = "delvewheel repair --namespace-pkg cuda -w {dest_dir} {wheel}" [tool.pytest.ini_options] diff --git a/cuda_core/pyproject.toml b/cuda_core/pyproject.toml index 3eac3a4c0af..cdd70d45141 100644 --- a/cuda_core/pyproject.toml +++ b/cuda_core/pyproject.toml @@ -155,5 +155,4 @@ archs = "native" [tool.cibuildwheel.windows] archs = "AMD64" -before-build = "pip install delvewheel" repair-wheel-command = "delvewheel repair --namespace-pkg cuda --exclude \"torch_cpu.dll;torch_python.dll\" -w {dest_dir} {wheel}"