Signature & Manifest Verification
The updater supports multiple, layered security checks to ensure the integrity and authenticity of everything it downloads and processes. All of these layers are optional by default; you can harden them progressively depending on your deployment requirements.
Overview
The verification pipeline runs in this order after a file is downloaded:
1. Checksum verification (release.checksum)
2. Authenticode chain (WinVerifyTrust)
3. Publisher pin (cert field matching)
For the update manifest fetched from your server, a fourth, build-time layer is available:
4. Manifest signature (Ed25519 / minisign, requires NV_MANIFEST_PUBLIC_KEY at build time)
5. Downgrade protection (manifestVersion field)
Setup Authenticode Verification
These settings live in the shared section of the remote configuration or in the local configuration file. They can also be overridden at the individual release level.
signatureVerificationMode
Controls whether the Authenticode signature of the downloaded setup binary is checked.
| Value | Description |
|---|---|
Disabled |
No Authenticode check is performed. |
WhenPresent |
Check only if the file is signed; unsigned setups are accepted. (Default) |
Required |
A valid Authenticode chain is mandatory; unsigned setups are rejected. |
Example — require every release to be signed
{
"shared": {
"signatureVerificationMode": "Required"
}
}
signaturePolicy
When the Authenticode chain is valid, this policy controls whether the signing certificate identity must also match a configured pin.
| Value | Description |
|---|---|
Relaxed |
A valid, trusted Authenticode chain is sufficient. No identity pinning. (Default) |
Strict |
The certificate identity must also match the configured publisher pin. Fails with an error if no pin can be resolved. |
Strict policy requires a resolvable pin
If you set Strict but neither signatureConfig nor a per-release signature can be resolved (and the updater binary itself is unsigned when using FromUpdaterBinary), the update will always be rejected.
signatureStrategy
When signaturePolicy is Strict, this setting controls where the expected certificate identity comes from.
| Value | Description |
|---|---|
FromUpdaterBinary |
The updater's own signing certificate (subject name) is used as the expected publisher. (Default) This is renewal-safe because only the subject name is compared. |
FromConfiguration |
The pin is read from signatureConfig (global) or per-release signature object. |
signatureConfig
An object providing explicit certificate pin fields for the FromConfiguration strategy. All fields are optional; only the fields you provide are compared.
Cert field stability
Not all certificate fields are equally stable across certificate renewals:
- Stable (safe to pin long-term):
subjectName— tied to the legal entity name. - Semi-stable (use with caution):
issuerName— may change when you switch CA or the CA rotates its intermediates. - Volatile (change on every renewal):
serialNumber,thumbprintSha1,thumbprintSha256— only safe when these values travel inside a signed manifest (FromConfiguration), so they can be updated server-side without redeploying the updater.
| Field | Type | Description |
|---|---|---|
subjectName |
string |
The certificate subject / organization name. |
issuerName |
string |
The CA / issuer display name. |
serialNumber |
string |
The certificate serial number (hex). |
thumbprintSha1 |
string |
The SHA-1 thumbprint (hex). |
thumbprintSha256 |
string |
The SHA-256 thumbprint (hex). |
Global pin from the remote configuration (recommended pattern)
{
"shared": {
"signatureVerificationMode": "Required",
"signaturePolicy": "Strict",
"signatureStrategy": "FromConfiguration",
"signatureConfig": {
"subjectName": "Nefarius Software Solutions e.U."
}
}
}
Per-release overrides
Individual releases can override the global policy by including any of the following fields directly in the release object:
| Field | Description |
|---|---|
signaturePolicy |
Override the global signaturePolicy for this release. |
signatureStrategy |
Override the global signatureStrategy for this release. |
signature |
An explicit SignatureConfig pin for this release. Presence implies FromConfiguration for this release regardless of the global signatureStrategy. |
Per-release cert pin (volatile fields safe here because the manifest is signed)
{
"releases": [
{
"name": "My Product",
"version": "2.0.0",
"downloadUrl": "https://example.com/setup.exe",
"signature": {
"subjectName": "Nefarius Software Solutions e.U.",
"thumbprintSha256": "aabbcc..."
}
}
]
}
Manifest Signature (Ed25519 / minisign)
This is a build-time feature that, when enabled, makes the updater verify the signature of the JSON update manifest before trusting any of its content. It is transparent to server operators — you only need to sign your manifest file with minisign and serve the sidecar alongside it.
Enabling
In CustomizeMe.h, set NV_MANIFEST_PUBLIC_KEY to the base64-encoded public key string from your minisign keypair (the second line of the generated .pub file):
#define NV_MANIFEST_PUBLIC_KEY "RWS..."
When this is defined:
- Verification becomes mandatory. If the
.minisigsidecar cannot be fetched or the signature is invalid, the manifest is rejected and the update check fails. - The check runs before
json::parse, so a tampered body is never trusted.
Backward compatibility
If NV_MANIFEST_PUBLIC_KEY is not defined in the build, manifest verification is skipped entirely and unsigned deployments continue to work unchanged.
Key generation
minisign -G
# Creates ~/.minisign/minisign.key (private) and ~/.minisign/minisign.pub (public)
Set NV_MANIFEST_PUBLIC_KEY to the second line of minisign.pub.
Signing your manifest
minisign -S -s ~/.minisign/minisign.key -m updates.json
# Produces updates.json.minisig alongside updates.json
Serve both files from the same URL root. The updater derives the sidecar URL automatically by appending .minisig to the manifest URL.
Signing new manifests after a cert renewal
Sign your updated manifest with the same Ed25519 key. No redeployment of the updater binary is required.
Downgrade / Rollback Protection
When manifest signing is enabled (NV_MANIFEST_PUBLIC_KEY defined), the updater also enforces a monotonically increasing manifestVersion field.
Add an unsigned integer to your manifest's instance block:
{
"instance": {
"manifestVersion": 42
}
}
If the server delivers a manifest whose manifestVersion is lower than the last successfully processed version on that client, the update check is rejected with error code 119. This prevents an attacker from replaying an older, legitimately signed manifest that contained a vulnerable release.
Only active when NV_MANIFEST_PUBLIC_KEY is defined
The downgrade check is only executed when manifest signing is enabled. Without a trusted signature, the manifest version field cannot be trusted.
--strict-verification
Passing --strict-verification on the command line activates a client-side hardened mode with two effects:
- Checksum required — if the selected release has no
checksumfield, the update is rejected immediately (exit code115). - Server cannot downgrade security — the server-provided
signatureVerificationMode,signaturePolicy,signatureStrategy, andsignatureConfigfields insharedare ignored. Only the settings already baked into the local configuration or forced by the build take effect.
See Command Line Arguments for the full argument reference.
Related exit codes
| Code | Description |
|---|---|
115 |
Download checksum mismatch (computed hash differs from release.checksum). |
116 |
Setup Authenticode signature is invalid or the chain could not be validated. |
117 |
Publisher certificate does not match the configured pin. |
118 |
Manifest Ed25519 signature is invalid or the .minisig sidecar could not be fetched. |
119 |
Manifest version rollback detected; the server delivered an older manifest version. |
See Exit Codes for the full list.