Verify downloaded files with b2sum and the CLI
A checksum check must cover the file you intend to use. GNU b2sum --check checks the filenames
inside a manifest; naming that manifest my-app.bin.b2 does not make it check my-app.bin. This
walkthrough creates a trusted checksum, copies a sample release into a download directory, and uses
Bash to verify the exact artifact passed to a script.
Use the matching BLAKE2 format
GNU b2sum uses
BLAKE2b with a default digest length of 512 bits, printed as 128 hexadecimal characters. BLAKE2s
is a different variant. Choosing b2sum --length=256 produces BLAKE2b-256, not BLAKE2s-256; it is
also not the first half of a BLAKE2b-512 digest. The algorithm and length must match the publisher’s
checksum. RFC 7693 describes how BLAKE2
incorporates the requested digest length.
The examples below use the default BLAKE2b-512 digest. A matching checksum detects changes relative to your trusted reference. It does not identify the publisher or make a download safe to execute.
Check the local tools
Use Linux with Bash, GNU coreutils (b2sum), and GNU diffutils (cmp). Check them before starting:
bash --version && b2sum --version && cmp --version
If a command is missing, install your distribution’s bash, coreutils, or diffutils package.
This walkthrough was tested with GNU coreutils 9.11. It uses a local copy to demonstrate the
producer and consumer steps; it does not configure a hosted CI/CD service or perform a network
download.
Create a sample release
Run every snippet from the same parent directory. The subshells keep directory changes local to
each block. Setup creates b2sum-demo and refuses to reuse an existing directory, so rerunning it
will not overwrite an earlier experiment.
(
set -eu
mkdir -- b2sum-demo
cd -- b2sum-demo
mkdir -- release
printf 'example release\n' > 'release/my app.bin'
)
The sample artifact contains text to make changes easy to see. The same commands work on binary files and empty files.
Generate a single-file manifest
On the producer side, hash the finished artifact from its containing directory:
(
set -eu
set -o noclobber
cd -- b2sum-demo/release
b2sum --binary -- './my app.bin' > './my app.bin.b2'
)
Keep this output unchanged. It contains the digest, a space, the binary-mode marker *, and the
relative filename ./my app.bin, followed by a newline. GNU documents this
checksum record format.
The ./ is part of the recorded filename and also protects a leading hyphen from being treated as
an option.
noclobber refuses to replace an existing manifest. A failed hash command can leave an incomplete
new sidecar because the shell opens the output first; do not publish that failed output. Generate
checksums from the trusted release, never from a suspect download merely to make it pass.
Copy and check the release
Copy both files into a new directory to stand in for downloading them. This block also refuses to reuse its destination directory:
(
set -eu
mkdir -- b2sum-demo/downloads
cp -- 'b2sum-demo/release/my app.bin' 'b2sum-demo/release/my app.bin.b2' b2sum-demo/downloads/
)
For this manifest, the root is the directory containing the artifact. Change into that directory before using the standard checker:
(
cd -- b2sum-demo/downloads &&
b2sum --check --strict -- './my app.bin.b2'
)
Expected output with coreutils 9.11 in the C locale; filename quoting can vary by version and locale:
'./my app.bin': OK
Relative filenames in a manifest resolve from the checker’s working directory, not the manifest’s
location. Running b2sum --check b2sum-demo/downloads/my\ app.bin.b2 from the parent directory
would look for ./my app.bin in the parent. Moving the artifact and manifest together works when
you also run the check from their new directory.
The --strict option
makes malformed records fail. It does not check that the manifest names your intended artifact.
A perfectly valid record for other.bin can succeed while my app.bin is corrupted or absent.
Use the following script when a caller requests one specific file.
Automating integrity checks
Save this as b2sum-demo/verify-integrity.sh. It accepts one artifact path and reads the adjacent
.b2 file. Its manifest contract is deliberately narrow: exactly the single record produced by
b2sum --binary -- './filename', using the artifact’s basename and the default digest length.
Tagged output, extra records, edited whitespace, and different path spellings are rejected.
#!/usr/bin/env bash
set -euo pipefail
if (( $# != 1 )) || [[ -z $1 ]]; then
printf 'Usage: %s <artifact>\n' "$0" >&2
exit 2
fi
artifact=$1
[[ $artifact == /* ]] || artifact="./$artifact"
if [[ ! -f $artifact || ! -f $artifact.b2 ]]; then
printf 'Artifact or manifest missing: %s\n' "$1" >&2
exit 1
fi
cd -P -- "${artifact%/*}/"
name=${artifact##*/}
if b2sum --binary -- "./$name" | cmp --silent -- "./$name.b2" -; then
printf 'Verified: %s\n' "$1"
else
printf 'Verification failed: %s\n' "$1" >&2
exit 1
fi
The script hashes the requested file and compares the entire newly generated record, including its
filename, with the trusted manifest. cmp
returns success only when the bytes match. Bash’s
pipefail also makes a failed
hashing process fail the pipeline. Spaces and leading hyphens in filenames are supported.
The script changes into the artifact’s directory itself, so callers can use a relative or absolute
path from another directory. Physical directory resolution with
cd -P preserves the
meaning of paths containing symlinks followed by ... It writes neither the artifact nor its manifest. Keep
both unchanged through verification and subsequent use; this script does not lock files against
concurrent changes.
Stop subsequent work when verification fails
Run the verifier through Bash, and gate the next command on its exit status:
bash b2sum-demo/verify-integrity.sh 'b2sum-demo/downloads/my app.bin' &&
printf 'Ready to use the verified artifact\n'
A successful run prints:
Verified: b2sum-demo/downloads/my app.bin
Ready to use the verified artifact
To try a failure, change only the disposable downloaded copy:
printf 'changed\n' >> 'b2sum-demo/downloads/my app.bin' &&
bash b2sum-demo/verify-integrity.sh 'b2sum-demo/downloads/my app.bin'
This prints Verification failed: b2sum-demo/downloads/my app.bin to standard error and exits
with status 1. In a CI/CD job, the consuming or deployment step must likewise depend on successful
verification; a later successful shell command must not conceal the failure.
Troubleshooting common issues
- The script rejects a manifest that
b2sum --checkaccepts: check its filename and format. This script requires one binary-mode record with./basename, including the final newline. For a publisher’s multi-file manifest, follow its documented directory layout and inspect which files it covers before using the standard checker. - The bytes differ: check that you downloaded the intended release. Re-download from the trusted source and investigate repeated mismatches. Text line-ending changes also change a hash.
- A file is missing or unreadable: check both the artifact path and its
.b2sidecar, plus directory permissions. An empty artifact is valid when its checksum matches; an empty manifest is invalid. - A command is missing: the verifier needs both GNU
b2sumandcmpon its execution path. A hashing or comparison failure returns a nonzero status.
Keep the expected checksum trustworthy
Obtain the expected checksum through a release channel you trust, or verify a signed manifest using a publisher key whose identity you have independently established. An attacker who can replace both the artifact and its checksum can make this check pass. Downloading both from the same compromised location provides no authenticity guarantee.
Our local copy demonstrates integrity verification after transfer. Generating a new checksum from the downloaded bytes would only describe what arrived. Keep the producer’s trusted checksum as the reference, and verify before consuming the artifact.
