This page covers how PowerToys binaries are signed during the release pipeline and how those signatures are verified post-build. It focuses specifically on the ESRP signing configuration (`.pipelines/ESRPSigning_core.json`) and the post-build verification script (`.pipelines/versionAndSignCheck.ps1`).
For the broader CI/CD pipeline structure, see Build and Deployment Pipeline. For how packages and dependencies are acquired before signing, see Package Management.
PowerToys uses ESRP (Enterprise Security Responsibility Platform), Microsoft's internal code signing service, to apply Authenticode signatures to all shipping binaries. Signing happens as a pipeline step after compilation and before packaging. A separate verification script then validates that every binary in the final MSI has a valid signature and a non-default version number.
The pipeline flow is:
Signing and Verification Flow
Sources: .pipelines/ESRPSigning_core.json1-434 .pipelines/versionAndSignCheck.ps11-107
The configuration file at `.pipelines/ESRPSigning_core.json` drives the ESRP signing task. Its top-level structure is:
| Field | Value | Meaning |
|---|---|---|
Version | "1.0.0" | Configuration schema version |
UseMinimatch | false | File patterns are literal names, not glob patterns |
SignBatches | array of 2 objects | Two independent signing passes |
Each batch specifies a MatchedPath array (the list of files to sign) and a SigningInfo block describing the signing operations to perform.
There are two batches, distinguished by the signing key and the files they cover.
CP-230012)This batch covers all binaries that are authored and owned by Microsoft as part of PowerToys. The key code CP-230012 is Microsoft's standard Authenticode signing certificate for first-party software.
`.pipelines/ESRPSigning_core.json4-302
Operations performed:
| Operation | Tool | Parameters |
|---|---|---|
SigntoolSign | sign 1.0 | /fd SHA256, /NPH, RFC3161 timestamp |
SigntoolVerify | sign 1.0 | (no additional parameters) |
Signtool parameters (Batch 1):
| Parameter Name | Parameter Value |
|---|---|
OpusName | Microsoft |
OpusInfo | http://www.microsoft.com |
FileDigest | /fd "SHA256" |
PageHash | /NPH |
TimeStamp | /tr "http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer" /td sha256 |
The RFC3161 timestamp server (rfc3161.gtm.corp.microsoft.com) ensures that the signature timestamp is cryptographically bound, so signatures remain valid even after the signing certificate expires.
Binary categories covered by Batch 1:
Sources: .pipelines/ESRPSigning_core.json6-302
CP-231522)This batch covers third-party libraries that are redistributed with PowerToys. The key code CP-231522 is used for signing third-party components. A critical difference from Batch 1 is the /as (append signature) flag.
`.pipelines/ESRPSigning_core.json303-432
Additional signtool parameter (Batch 2 only):
| Parameter Name | Parameter Value | Effect |
|---|---|---|
Append | /as | Appends the Microsoft signature alongside any existing vendor signature, rather than replacing it |
This means third-party DLLs that already carry their own vendor Authenticode signature retain that original signature. The Microsoft signature is added as a second entry in the signature list.
Third-party libraries covered:
| Category | Examples |
|---|---|
| UI Frameworks | ModernWpf.dll, ModernWpf.Controls.dll, Wpf.Ui.dll |
| JSON / Serialization | Newtonsoft.Json.dll, MessagePack.dll |
| Logging | NLog.dll, NLog.Extensions.Logging.dll |
| IL Rewriting | Mono.Cecil.dll, Mono.Cecil.Mdb.dll, Mono.Cecil.Pdb.dll, Mono.Cecil.Rocks.dll |
| Web / Markdown | HtmlAgilityPack.dll, Markdig.Signed.dll, Markdig.dll |
| WebView2 | Microsoft.Web.WebView2.Core.dll, WebView2Loader.dll |
| SQLite | e_sqlite3.dll, SQLitePCLRaw.core.dll |
| Math / Calculator | Mages.Core.dll |
| VC++ Redistributables | msvcp140_app.dll, vcruntime140_app.dll, concrt140_app.dll, etc. |
| Boost | boost_regex-vc143-mt-x64-1_87.dll, boost_regex-vc143-mt-x32-1_87.dll, etc. |
| ML / AI | Microsoft.ML.OnnxRuntime.dll, Microsoft.SemanticKernel.Connectors.Ollama.dll |
| Office Interop | ScipBe.Common.Office.OneNote.dll, Interop.Microsoft.Office.Interop.OneNote.dll |
| Misc | UnicodeInformation.dll, UnitsNet.dll, WmiLight.dll, Shmuelie.WinRTServer.dll |
Sources: .pipelines/ESRPSigning_core.json303-432
| Key Code | Purpose | Append Flag | Files |
|---|---|---|---|
CP-230012 | Microsoft first-party binaries | No | PowerToys executables, DLLs, MSIXes, scripts |
CP-231522 | Third-party redistributed components | Yes (/as) | NuGet dependency DLLs, VC++ runtime DLLs |
After the MSI is produced, the pipeline runs `.pipelines/versionAndSignCheck.ps1` against the extracted MSI contents. The script targets the directory extractedMsi/File (configurable via the $targetDir parameter).
What the script checks:
Sources: .pipelines/versionAndSignCheck.ps11-107
Some binaries legitimately carry version numbers that would otherwise trigger failures. These are declared as pipe-delimited strings and matched with -notmatch.
$versionExceptions โ files that may have 0.0.0.0 or 1.0.0.0:
`.pipelines/versionAndSignCheck.ps111-31
| Exception File | Reason |
|---|---|
AdaptiveCards.Templating.dll | Third-party, version not updated |
Microsoft.Windows.ApplicationModel.* | WinAppSDK projection DLLs |
Microsoft.Xaml.Interactions.dll | XAML behaviors library |
Microsoft.ML.OnnxRuntime.dll | ML runtime |
TraceReloggerLib.dll | Trace utility |
hyjiacan.py4n.dll | Chinese Pinyin library |
ObjectModelCsProjection.dll / RendererCsProjection.dll | Projection DLLs |
$nullVersionExceptions โ files that have no FileVersion property at all:
`.pipelines/versionAndSignCheck.ps132-61
| Exception File | Notes |
|---|---|
libSkiaSharp.dll, SkiaSharp.Views.WinUI.Native.dll | Native Skia |
e_sqlite3.dll | SQLite native library |
codicon.ttf | Font file (no version info) |
vcamp140_app.dll, vcruntime140_app.dll, etc. | VC++ app-local runtimes |
Microsoft.UI.Xaml.Internal.dll | WinUI internal |
Microsoft.WindowsAppRuntime.dll | WinAppSDK runtime |
boost_regex_vc143_* | Boost regex native DLLs |
Note: The script uses
$_.Name -notmatch $exceptionswhere$exceptionsis a joined pipe string, so the match is a regex OR across all exception names.
For every file that passes the version checks, the script calls Get-AuthenticodeSignature on the full path. If .SignerCertificate is $null, the file is treated as unsigned and increments $totalFailure.
`.pipelines/versionAndSignCheck.ps194-99
This catches any binary that was accidentally missed by the ESRP signing step.
The following diagram maps the signing configuration entities to the files in the repository that define them.
Configuration to Code Entity Mapping
Sources: .pipelines/ESRPSigning_core.json1-434 .pipelines/versionAndSignCheck.ps11-107
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.