diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 12072072b9..34141b6723 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -268,6 +268,63 @@ jobs: shell: pwsh run: Get-ChildItem publish -Filter *.pdb -Recurse | Remove-Item + - name: Authenticode sign Windows executable + if: runner.os == 'Windows' + shell: pwsh + env: + WIN_SIGNING_CERT_BASE64: ${{ secrets.WIN_SIGNING_CERT_BASE64 }} + WIN_SIGNING_CERT_PASSWORD: ${{ secrets.WIN_SIGNING_CERT_PASSWORD }} + run: | + if ([string]::IsNullOrWhiteSpace($env:WIN_SIGNING_CERT_BASE64)) { + throw "WIN_SIGNING_CERT_BASE64 secret is required to Authenticode-sign Windows release binaries." + } + if ([string]::IsNullOrWhiteSpace($env:WIN_SIGNING_CERT_PASSWORD)) { + throw "WIN_SIGNING_CERT_PASSWORD secret is required to Authenticode-sign Windows release binaries." + } + + $exe = Join-Path (Resolve-Path publish) "cdidx.exe" + if (-not (Test-Path -LiteralPath $exe)) { + throw "Published Windows executable was not found: $exe" + } + + $pfxPath = Join-Path $env:RUNNER_TEMP "cdidx-signing.pfx" + [IO.File]::WriteAllBytes($pfxPath, [Convert]::FromBase64String($env:WIN_SIGNING_CERT_BASE64)) + + $password = ConvertTo-SecureString $env:WIN_SIGNING_CERT_PASSWORD -AsPlainText -Force + $cert = Import-PfxCertificate ` + -FilePath $pfxPath ` + -CertStoreLocation Cert:\CurrentUser\My ` + -Password $password ` + -Exportable:$false + try { + if (-not $cert.Thumbprint) { + throw "Imported signing certificate did not expose a thumbprint." + } + + $signtool = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin" -Recurse -Filter signtool.exe | + Where-Object { $_.FullName -match '\\x64\\signtool\.exe$' } | + Sort-Object FullName -Descending | + Select-Object -First 1 + if (-not $signtool) { + throw "signtool.exe was not found in the Windows Kits installation." + } + + & $signtool.FullName sign /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /sha1 $cert.Thumbprint $exe + if ($LASTEXITCODE -ne 0) { + throw "signtool sign failed with exit code $LASTEXITCODE." + } + + & $signtool.FullName verify /pa /v $exe + if ($LASTEXITCODE -ne 0) { + throw "signtool verify failed with exit code $LASTEXITCODE." + } + } finally { + if ($cert.Thumbprint) { + Remove-Item -LiteralPath "Cert:\CurrentUser\My\$($cert.Thumbprint)" -Force -ErrorAction SilentlyContinue + } + Remove-Item -LiteralPath $pfxPath -Force -ErrorAction SilentlyContinue + } + - name: Add license and trademark notices to publish output (Linux/macOS) if: runner.os != 'Windows' run: cp LICENSE LICENSES/FSL-1.1-ALv2.txt LICENSES/Apache-2.0.txt COMMERCIAL_LICENSE.md INTEGRATION_POLICY.md TRADEMARKS.md publish/ && cp -R LICENSES publish/ diff --git a/README.md b/README.md index ba52e204ff..54a5f1aa49 100644 --- a/README.md +++ b/README.md @@ -233,6 +233,14 @@ the per-platform `CodeIndex-.tar.gz` / `.zip` binaries: | `sha256sums.txt` | SHA-256 of every release asset (including the SBOM). `install.sh` uses it to verify the downloaded tarball before placing anything under `$HOME/.local/bin/`. | | `cdidx.sbom.cdx.json` | CycloneDX 1.x JSON Software Bill of Materials covering every NuGet dependency (including the bundled `SQLitePCLRaw` native asset) so compliance reviewers (SOC2, FedRAMP-style) and scanners (Snyk, Trivy, Grype) can audit transitive dependencies without re-deriving them from `.deps.json`. | +Windows ZIP releases contain an Authenticode-signed `cdidx.exe`. After +extracting the archive on Windows, verify the signature and timestamp before +trusting the executable: + +```powershell +Get-AuthenticodeSignature .\cdidx.exe | Format-List Status,SignerCertificate,TimeStamperCertificate +``` + Quick check after downloading both files from the release page: ```bash @@ -457,6 +465,14 @@ NuGet パッケージは .NET グローバルツールとして公開されて | `sha256sums.txt` | 各リリースアセット(SBOM を含む)の SHA-256。`install.sh` は `$HOME/.local/bin/` に何も書き込む前に tarball をこのファイルで検証します。 | | `cdidx.sbom.cdx.json` | CycloneDX 1.x JSON 形式の Software Bill of Materials。同梱の `SQLitePCLRaw` ネイティブアセットを含む全 NuGet 依存を列挙するため、SOC2 / FedRAMP 系のコンプライアンスレビューや Snyk / Trivy / Grype などのスキャナーが `.deps.json` から再構築せずに推移的依存を監査できます。 | +Windows ZIP release に含まれる `cdidx.exe` は Authenticode 署名済みです。 +Windows で archive を展開したあと、実行ファイルを信頼する前に署名と +timestamp を確認してください。 + +```powershell +Get-AuthenticodeSignature .\cdidx.exe | Format-List Status,SignerCertificate,TimeStamperCertificate +``` + リリースページから両ファイルをダウンロードしたあとの簡易チェック例: ```bash diff --git a/USER_GUIDE.md b/USER_GUIDE.md index e486388045..7d072b16b1 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -450,6 +450,13 @@ sha256sum -c sha256sums.txt The GPG signature verifies the checksum manifest through the release signing key. +Windows release ZIPs also contain an Authenticode-signed `cdidx.exe`. After +extracting the archive, verify that Windows trusts the signature and timestamp: + +```powershell +Get-AuthenticodeSignature .\cdidx.exe | Format-List Status,SignerCertificate,TimeStamperCertificate +``` + Release workflows also emit GitHub build provenance attestations for the published archives, SBOM, checksum manifest, and checksum signature. Verify that an artifact was produced by this repository's GitHub Actions release @@ -2437,6 +2444,39 @@ runtime の管理方法とネットワーク条件に合わせて install channe 完全な比較、package maintainer guidance、winget / apt / rpm / Snap / Flatpak などの予定チャネルは [DISTRIBUTION.md](DISTRIBUTION.md) を参照してください。 +### リリースアセットの検証 + +GitHub releases は、すべての archive と SBOM asset を対象にした +`sha256sums.txt` と、detached GPG signature の `sha256sums.txt.asc` を +公開します。download した release artifact を信頼する前に checksum manifest +を検証してください。 + +```bash +gpg --verify sha256sums.txt.asc sha256sums.txt +sha256sum -c sha256sums.txt +``` + +GPG signature は release signing key を通じて checksum manifest を検証します。 + +Windows release ZIP にも Authenticode 署名済みの `cdidx.exe` が含まれます。 +archive を展開したあと、Windows が署名と timestamp を信頼していることを +確認してください。 + +```powershell +Get-AuthenticodeSignature .\cdidx.exe | Format-List Status,SignerCertificate,TimeStamperCertificate +``` + +release workflow は、公開された archive、SBOM、checksum manifest、checksum +signature に対する GitHub build provenance attestation も出力します。artifact が +この repository の GitHub Actions release workflow で生成されたことを検証できます。 + +```bash +gh attestation verify CodeIndex-linux-x64.tar.gz -R Widthdom/CodeIndex +``` + +GitHub attestation は、その artifact が repository workflow identity により +生成されたことを検証します。 + ### 方法A: ワンライナーインストール(.NET 不要) コンテナ、CI、Linux/macOS 環境で .NET SDK なしで使えます。 diff --git a/changelog.d/unreleased/1845.security.md b/changelog.d/unreleased/1845.security.md new file mode 100644 index 0000000000..3291806223 --- /dev/null +++ b/changelog.d/unreleased/1845.security.md @@ -0,0 +1,17 @@ +--- +category: security +issues: + - 1845 +affected: + - .github/workflows/release.yml + - README.md + - USER_GUIDE.md +--- + +## English + +- **Windows release binaries are Authenticode-signed (#1845)** — Windows release builds now import the release signing certificate from GitHub Actions secrets, sign `cdidx.exe`, and verify the signature before the ZIP artifact is uploaded. + +## 日本語 + +- **Windows release binary を Authenticode 署名するようになりました (#1845)** — Windows release build は GitHub Actions secrets から release signing certificate を import し、`cdidx.exe` に署名してから、ZIP artifact の upload 前に署名検証を行います。