Skip to content

Verify checksums and signatures

A self-updating tool downloads a binary and runs it. Verifying what it fetched is the difference between an update mechanism and a remote code execution mechanism.

This module supplies the retrieval half — getting the manifest and signature bytes from the forge. Verifying them is the consumer's job.

Two routes to the same bytes

Most releases ship a checksums.txt as an ordinary asset, so the default route is to find it by name in the asset list. Nothing special is required.

Some sources cannot do that — the direct provider composes a checksum URL from a template that may point outside any asset listing. For those, a provider opts into ChecksumProvider:

if cp, ok := provider.(forge.ChecksumProvider); ok {
    manifest, err := cp.DownloadChecksumManifest(ctx, rel, forge.DefaultMaxChecksumsSize)
    switch {
    case err == nil:
        return manifest, nil
    case errors.Is(err, forge.ErrNotSupported):
        // opted out for this configuration — fall through
    default:
        return nil, err
    }
}

// default route: locate checksums.txt among the release assets

SignatureProvider mirrors it exactly for a detached signature.

ErrNotSupported is not a failure

Treat "does not implement the interface" and "returned ErrNotSupported" identically: fall back. Only a different error is fatal. Getting this wrong turns a working provider into a broken one for every release where the capability happens to be unconfigured.

Bound the read, always

Both methods take a maxBytes cap. Pass one:

forge.DefaultMaxChecksumsSize  // 1 MiB
forge.DefaultMaxSignatureSize  // 1 MiB

These are defaults, not mandates — raise them if your tool genuinely ships larger artefacts. The provider enforces whatever you pass, because it is the only code that sees the response before it is buffered; a cap applied afterwards has already let the bytes into memory.

A manifest for a typical multi-OS release is around 1 KiB, so the default is roughly a thousandfold headroom while still stopping a hostile server streaming indefinitely.