diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index 79ffafa0f1..9473687d43 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -13,6 +13,8 @@ on: - 'localization/**' - 'resources/**' - ".github/workflows/build_*.yml" + - 'signpath/**' + - 'scripts/*.ps1' - 'scripts/flatpak/**' pull_request: @@ -26,9 +28,11 @@ on: - '**/CMakeLists.txt' - 'version.inc' - ".github/workflows/build_*.yml" + - 'signpath/**' - 'build_linux.sh' - 'build_release_vs2022.bat' - 'build_release_macos.sh' + - 'scripts/*.ps1' - 'scripts/flatpak/**' diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index 7ccb52b0e6..92d7eb1c32 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -292,6 +292,50 @@ jobs: # WindowsSDKVersion: '10.0.26100.0\' run: .\build_release_vs.bat slicer + - name: Pack PDB + if: runner.os == 'Windows' && !vars.SELF_HOSTED + working-directory: ${{ github.workspace }}/build/src/Release + shell: cmd + run: '"C:/Program Files/7-Zip/7z.exe" a -m0=lzma2 -mx9 Debug_PDB_${{ env.ver }}_for_developers_only.7z *.pdb' + + - name: Upload unsigned Windows portable artifact for SignPath + id: upload-windows-portable + if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED + uses: actions/upload-artifact@v7 + with: + name: OrcaSlicer_Windows_${{ env.ver }}_portable_unsigned + path: ${{ github.workspace }}/build/OrcaSlicer + if-no-files-found: error + + - name: Submit Windows portable artifact to SignPath + if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED + uses: signpath/github-action-submit-signing-request@v2 + with: + api-token: ${{ secrets.SIGNPATH_API_TOKEN }} + organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }} + project-slug: OrcaSlicer + signing-policy-slug: test-signing + artifact-configuration-slug: windows-portable-v1 + github-artifact-id: ${{ steps.upload-windows-portable.outputs.artifact-id }} + wait-for-completion: true + output-artifact-directory: ${{ github.workspace }}/build/signpath/windows-portable + + - name: Verify SignPath Windows portable signatures + if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED + shell: pwsh + run: ./scripts/verify-authenticode.ps1 -ArtifactDirectory '${{ github.workspace }}/build/signpath/windows-portable' + + - name: Replace Windows portable bundle with signed output + if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED + shell: pwsh + run: | + $source = Join-Path "${{ github.workspace }}" "build/signpath/windows-portable" + $destination = Join-Path "${{ github.workspace }}" "build/OrcaSlicer" + if (-not (Test-Path -LiteralPath $source -PathType Container)) { + throw "SignPath output directory not found: $source" + } + Get-ChildItem -LiteralPath $source -Force | Copy-Item -Destination $destination -Recurse -Force + - name: Create installer Win if: runner.os == 'Windows' && !vars.SELF_HOSTED working-directory: ${{ github.workspace }}/build @@ -301,14 +345,14 @@ jobs: - name: Pack app if: runner.os == 'Windows' working-directory: ${{ github.workspace }}/build - shell: cmd - run: '"C:/Program Files/7-Zip/7z.exe" a -tzip OrcaSlicer_Windows_${{ env.ver }}_portable.zip ${{ github.workspace }}/build/OrcaSlicer' - - - name: Pack PDB - if: runner.os == 'Windows' && !vars.SELF_HOSTED - working-directory: ${{ github.workspace }}/build/src/Release - shell: cmd - run: '"C:/Program Files/7-Zip/7z.exe" a -m0=lzma2 -mx9 Debug_PDB_${{ env.ver }}_for_developers_only.7z *.pdb' + shell: pwsh + run: | + $zipPath = "OrcaSlicer_Windows_${{ env.ver }}_portable.zip" + Remove-Item -LiteralPath $zipPath -Force -ErrorAction SilentlyContinue + & "C:/Program Files/7-Zip/7z.exe" a -tzip $zipPath "${{ github.workspace }}/build/OrcaSlicer" + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } - name: Upload artifacts Win zip if: runner.os == 'Windows' @@ -316,6 +360,7 @@ jobs: with: name: OrcaSlicer_Windows_${{ env.ver }}_portable path: ${{ github.workspace }}/build/OrcaSlicer + if-no-files-found: error - name: Upload artifacts Win installer if: runner.os == 'Windows' && !vars.SELF_HOSTED diff --git a/scripts/inventory-authenticode.ps1 b/scripts/inventory-authenticode.ps1 new file mode 100644 index 0000000000..e15a94e9e6 --- /dev/null +++ b/scripts/inventory-authenticode.ps1 @@ -0,0 +1,48 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [ValidateScript({ Test-Path -LiteralPath $_ -PathType Container })] + [string]$ArtifactDirectory +) + +$ErrorActionPreference = "Stop" + +function Get-RelativePath { + param( + [Parameter(Mandatory = $true)] + [string]$Root, + + [Parameter(Mandatory = $true)] + [string]$Path + ) + + $rootWithSeparator = $Root.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar) + [IO.Path]::DirectorySeparatorChar + $rootUri = [Uri]$rootWithSeparator + $pathUri = [Uri]$Path + + [Uri]::UnescapeDataString($rootUri.MakeRelativeUri($pathUri).ToString()).Replace("/", [IO.Path]::DirectorySeparatorChar) +} + +$artifactRoot = (Resolve-Path -LiteralPath $ArtifactDirectory).Path +$binaries = Get-ChildItem -LiteralPath $artifactRoot -Recurse -File | + Where-Object { $_.Extension -in @(".exe", ".dll") } | + Sort-Object -Property FullName + +if (-not $binaries) { + Write-Warning "No .exe or .dll files found under '$artifactRoot'." + exit 0 +} + +$binaries | ForEach-Object { + $signature = Get-AuthenticodeSignature -LiteralPath $_.FullName + + [pscustomobject]@{ + RelativePath = Get-RelativePath -Root $artifactRoot -Path $_.FullName + Status = $signature.Status + SignatureType = $signature.SignatureType + Signer = if ($signature.SignerCertificate) { $signature.SignerCertificate.Subject } else { "" } + SignerThumbprint = if ($signature.SignerCertificate) { $signature.SignerCertificate.Thumbprint } else { "" } + Timestamped = [bool]$signature.TimeStamperCertificate + TimeStamper = if ($signature.TimeStamperCertificate) { $signature.TimeStamperCertificate.Subject } else { "" } + } +} | Format-Table -AutoSize diff --git a/scripts/verify-authenticode.ps1 b/scripts/verify-authenticode.ps1 new file mode 100644 index 0000000000..4176c99437 --- /dev/null +++ b/scripts/verify-authenticode.ps1 @@ -0,0 +1,73 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [ValidateScript({ Test-Path -LiteralPath $_ -PathType Container })] + [string]$ArtifactDirectory, + + [string[]]$Files = @( + "orca-slicer.exe", + "OrcaSlicer.dll" + ), + + [string]$SignToolPath +) + +$ErrorActionPreference = "Stop" + +function Resolve-SignToolPath { + param( + [string]$ExplicitPath + ) + + if ($ExplicitPath) { + if (Test-Path -LiteralPath $ExplicitPath -PathType Leaf) { + return (Resolve-Path -LiteralPath $ExplicitPath).Path + } + + throw "SignTool was not found at '$ExplicitPath'." + } + + $fromPath = Get-Command -Name "signtool.exe" -ErrorAction SilentlyContinue + if ($fromPath) { + return $fromPath.Source + } + + $candidateRoots = @( + "${env:ProgramFiles(x86)}\Windows Kits\10\bin", + "${env:ProgramFiles}\Windows Kits\10\bin" + ) | Where-Object { $_ -and (Test-Path -LiteralPath $_ -PathType Container) } + + foreach ($root in $candidateRoots) { + $candidate = Get-ChildItem -LiteralPath $root -Recurse -Filter "signtool.exe" -File -ErrorAction SilentlyContinue | + Where-Object { $_.FullName -match "\\(x64|arm64)\\signtool\.exe$" } | + Sort-Object -Property FullName -Descending | + Select-Object -First 1 + + if ($candidate) { + return $candidate.FullName + } + } + + throw "signtool.exe was not found. Install the Windows SDK or pass -SignToolPath." +} + +$artifactRoot = (Resolve-Path -LiteralPath $ArtifactDirectory).Path +$signtool = Resolve-SignToolPath -ExplicitPath $SignToolPath + +Write-Host "Using SignTool: $signtool" +Write-Host "Verifying Authenticode signatures in: $artifactRoot" + +foreach ($relativePath in $Files) { + $filePath = Join-Path $artifactRoot $relativePath + if (-not (Test-Path -LiteralPath $filePath -PathType Leaf)) { + throw "Expected signed file was not found: $filePath" + } + + Write-Host "Verifying $relativePath" + & $signtool verify /pa /all /tw /v $filePath + if ($LASTEXITCODE -ne 0) { + throw "SignTool verification failed for '$relativePath' with exit code $LASTEXITCODE." + } +} + +Write-Host "Authenticode verification passed." diff --git a/signpath/README.md b/signpath/README.md new file mode 100644 index 0000000000..6adac07dd2 --- /dev/null +++ b/signpath/README.md @@ -0,0 +1,16 @@ +# SignPath configurations + +This directory contains SignPath artifact configurations used by GitHub Actions. + +## `windows-portable-v1` + +`windows-portable-v1.xml` is the initial conservative Windows portable-bundle signing configuration. It signs only the two first-party binaries: + +- `orca-slicer.exe` +- `OrcaSlicer.dll` + +Do not broaden this to all DLLs without first confirming ownership, provenance, and whether upstream vendor signatures should be verified instead. + +The Windows workflow uploads `${{ github.workspace }}/build/OrcaSlicer` with `actions/upload-artifact`. GitHub stores that artifact as a ZIP, and the uploaded directory contents are rooted at the ZIP root. Because of that, the SignPath configuration uses `` with `orca-slicer.exe` and `OrcaSlicer.dll` directly beneath it. + +The release portable ZIP is a separate archive created with 7-Zip from `${{ github.workspace }}/build/OrcaSlicer`; that archive keeps the top-level `OrcaSlicer/` folder. After SignPath returns the signed artifact, the workflow copies the signed files back into `build/OrcaSlicer` and recreates the portable release ZIP so the public ZIP layout stays unchanged. diff --git a/signpath/windows-portable-v1.xml b/signpath/windows-portable-v1.xml new file mode 100644 index 0000000000..5d5b717ffa --- /dev/null +++ b/signpath/windows-portable-v1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + +