Skip to main content

Function: canonicalizeForSigning()

canonicalizeForSigning(info): string

Produces the exact byte string that is Ed25519-signed at publish time and verified at update time.

Only integrity- and rollout-critical fields are covered:

  • version — prevents version downgrade/substitution
  • stagingPercentage — prevents tampering with staged-rollout gating
  • minimumSystemVersion — prevents bypassing or forging the OS-version gate (its absence is signed too, so one cannot be added after the fact)
  • allowCatalogSignature — (WindowsUpdateInfo) only when it is exactly true, as an extra line; a manifest without it, or with false, canonicalizes exactly as before, and adding true after signing breaks the signature
  • each file's url, sha512, size — the artifact identity + integrity hash the updater enforces — and its blockMapUrl when present (an extra field on the file record only then, so other manifests are unaffected)
  • each NSIS web-installer package's arch key and path, sha512, size, blockMapSize, isAdminRightsRequired (WindowsUpdateInfo.packages, keyed by arch) — the payload the web installer downloads and verifies

Cosmetic/operational fields (releaseDate, releaseNotes, releaseName) are intentionally excluded so they can be edited post-signing without invalidating the signature. The signature/signatures fields are not part of the payload either, so signatures can be added or removed independently of one another. The deprecated top-level path/sha512 mirror of files[0] is not signed; instead the verifier refuses a signed manifest without a non-empty files list (see validateSignedManifestShape), so the updater never falls back to those legacy fields for a verified manifest.

files[].isAdminRightsRequired is intentionally NOT signed. It is only a launch hint (spawn the installer directly vs. through elevate.exe); the installer itself is pinned by the signed url/sha512/size, so tampering with the flag cannot change what gets installed — the worst case is an unexpected UAC prompt or an unnecessary elevate.exe launch. Leaving it unsigned lets a publisher correct a misconfigured value in latest.yml without re-signing. The isAdminRightsRequired column of the package: records below is part of the EBUM1 wire format for historic reasons; electron-updater does not read it from packages.

Wire format (EBUM1): one record per line, label: followed by tab-separated fields, e.g.

EBUM1
version:"1.2.3"
staging:25
minimumSystemVersion:"10.0.19041"
allowCatalogSignature:true (only with allowCatalogSignature: true)
file:"App-1.2.3.exe"<TAB>"<sha512>"<TAB>8123456
file:"App-1.2.4.exe"<TAB>"<sha512>"<TAB>8123456<TAB>"https://cdn.example.com/App-1.2.4.exe.blockmap?sig=…" (with a blockMapUrl)
package:"x64"<TAB>"App-1.2.3-x64.nsis.7z"<TAB>"<sha512>"<TAB>5000<TAB>120<TAB>1

Every value goes through encodeField (JSON-quoted strings, bare numbers, empty for absent), which is what makes the encoding injective: a value can never contain an unescaped newline or tab, so no choice of field values in one manifest can reproduce the record structure of another (e.g. an empty files list plus a minimumSystemVersion that spells out the original file: lines). The format is deterministic regardless of object key order or YAML formatting: file records are sorted, package records are sorted, and a version prefix anchors the scheme. Both the signer (build) and verifier (runtime) MUST call this identical function — it is a wire contract — and both operate on the manifest exactly as written to latest*.yml: the signer runs on the final UpdateInfo right before serialization, the verifier on the parsed manifest before the provider resolves files[].url / packages[arch].path against the feed base URL. This function never throws; shape problems are reported by validateSignedManifestShape instead.

Parameters​

info​

UpdateInfo

Returns​

string