# windows-verify-signatures.ps1 ... - fail unless every file # carries a valid, timestamped Authenticode signature. # # The check that makes signing load-bearing rather than hopeful: Tauri skips # signing silently in some configurations (no sign command, --no-sign), and an # unsigned installer looks exactly like a signed one until SmartScreen blocks # it on a user's machine. Every pattern must match at least one file, so a # bundle that was never produced cannot pass either. param([Parameter(Mandatory = $true, ValueFromRemainingArguments = $true)][string[]]$Patterns) $ErrorActionPreference = 'Stop' if (-not $env:TRIPLE_C_SIGNTOOL) { throw 'TRIPLE_C_SIGNTOOL is not set - run windows-signing-setup.ps1 first' } $files = foreach ($pattern in $Patterns) { $found = @(Get-ChildItem -Path $pattern -File -ErrorAction SilentlyContinue) if ($found.Count -eq 0) { throw "Nothing to verify matches $pattern" } $found } $failed = @() foreach ($file in $files) { # signtool's own check: chain to a trusted root under the default # Authenticode policy. # Stop relaxed for the native call, as in windows-sign.ps1. $ErrorActionPreference = 'Continue' $verifyOutput = & $env:TRIPLE_C_SIGNTOOL verify /pa $file.FullName 2>&1 | ForEach-Object { "$_" } $signtoolOk = ($LASTEXITCODE -eq 0) $ErrorActionPreference = 'Stop' if (-not $signtoolOk) { $verifyOutput | Write-Host } # And the timestamp, which signtool verify does not require. $sig = Get-AuthenticodeSignature -FilePath $file.FullName $timestamped = $null -ne $sig.TimeStamperCertificate if ($signtoolOk -and $sig.Status -eq 'Valid' -and $timestamped) { Write-Host "OK $($file.Name) - $($sig.SignerCertificate.Subject)" } else { Write-Host "FAIL $($file.Name) - status $($sig.Status), signtool $(if ($signtoolOk) {'ok'} else {'failed'}), timestamped $timestamped" $failed += $file.Name } } # The NSIS uninstaller is signed from inside makensis, which ignores the sign # command's exit code, and it ends up embedded in the installer where the # checks above cannot reach it. windows-sign.ps1 logs every file it signs; the # uninstaller is the one makensis wrote under the job's temp directory (see # windows-signing-setup.ps1), so require at least one logged path there. $nsisBuilt = @($files | Where-Object { $_.FullName -match '\\bundle\\nsis\\' }).Count -gt 0 if ($nsisBuilt) { $tmp = $env:TRIPLE_C_SIGN_TMP $log = $env:TRIPLE_C_SIGN_LOG $signedInTmp = @() if ($tmp -and $log -and (Test-Path $log)) { $signedInTmp = @(Get-Content $log | Where-Object { $_.StartsWith($tmp, [StringComparison]::OrdinalIgnoreCase) }) } if ($signedInTmp.Count -eq 0) { Write-Host 'FAIL NSIS uninstaller - no successful signature was logged for it' $failed += 'NSIS uninstaller' } else { Write-Host "OK NSIS uninstaller - signed as $($signedInTmp[-1])" } } if ($failed.Count -gt 0) { throw "Not validly signed: $($failed -join ', ')" } Write-Host "All $(@($files).Count) files are signed and timestamped."