Hashing#
A hash is a fixed-size fingerprint of arbitrary-length data. For the operator, hashing is the first integrity check on contact. The operator hashes a downloaded payload to confirm it matches the vendor’s published digest, fingerprints every binary on the target to compare against an indicator-of-compromise list, baselines a directory tree before an engagement so any tampering shows up later, and de-duplicates a collection of pulled artifacts by their content rather than their names.
Hashes are one-way and collision-resistant by design. Same input, same digest, every time. Different input, almost certainly a different digest. That property is what makes a hash a trustworthy fingerprint on contact, and what makes a broken hash family (MD5, SHA-1) dangerous for adversarial work even though it is still fine for casual checksumming.
The sections below walk the anatomy, the GNU coreutils family the
operator reaches for first, the broader catalog openssl dgst
exposes, the patterns for hashing whole trees, the constraints
that bite, and the muscle-memory tasks.
A hash function eats bytes and emits a fixed-size digest. The input can be a file, a pipe, a TCP stream, or the contents of memory. The output is always the same width for a given algorithm.
flowchart LR
input["input bytes<br/>(any length)"] --> fn["hash function"]
fn --> digest["digest<br/>(fixed width)"]
digest --> hex["hex string<br/>(operator-facing)"]
Algorithm |
Digest size |
Posture |
|---|---|---|
MD5 |
128 bit |
Broken for adversarial use. Fine as a non-crypto checksum. |
SHA-1 |
160 bit |
Broken (SHAttered, 2017). Treat as MD5. |
SHA-256 |
256 bit |
Standard for integrity. The operator’s default. |
SHA-512 |
512 bit |
Same family, larger digest. Faster than SHA-256 on 64-bit hardware. |
BLAKE2b |
up to 512 bit |
Faster than SHA-2, same security posture. Native |
BLAKE3 |
256 bit (extendable) |
Faster still, tree-parallel. Separate binary, not in coreutils. |
Concretely, md5sum over a file emits a single line of
thirty-two hex characters, a space, a mode marker, and the path.
$ md5sum /etc/hostname
b6d81b360a...3e2c /etc/hostname
sha256sum produces the same format, with sixty-four hex
characters instead of thirty-two.
$ sha256sum /etc/hostname
9eec28886f...04efd /etc/hostname
GNU coreutils hashing#
The GNU coreutils ship a family of small one-job binaries the
operator reaches for first. md5sum, sha1sum, sha256sum,
sha384sum, sha512sum, and b2sum all share the same
flags, the same output format, and the same -c verify mode that
reads back a previously written list of digest+path lines. The
operator picks the digest by name; everything else is identical.
Hash one file. The default mode the operator runs ten times a day.
$ sha256sum payload.bin
3a9d4e...c102 payload.bin
Verify a downloaded archive against the project’s published digest.
$ echo '3a9d4e...c102 payload.bin' | sha256sum -c -
payload.bin: OK
Hash every regular file in the current directory, write to a manifest.
$ sha256sum * 2>/dev/null > SHA256SUMS
The -c flag is the verify side of the same coin. Hand it a
manifest, get a per-file OK / FAILED report and a non-zero
exit on any miss. This is the pattern packagers and operators use
to detect tampering at rest.
openssl dgst#
openssl dgst exposes the same algorithms plus a wider catalog
(SHA-3, RIPEMD, Whirlpool, SM3), HMAC mode for keyed integrity, and
detached-signature workflows. Reach for it when the algorithm the
operator wants is not in coreutils, when an HMAC is needed instead
of a plain hash, or when signing or verifying with a key.
Command |
Effect |
|---|---|
|
Plain SHA-256, BSD-style output. |
|
SHA-3 family (FIPS 202). |
|
Keyed HMAC-SHA-256 over the file. |
|
Raw digest piped to hex for inline use. |
|
Detached signature over the digest with a private key. |
|
Verify a detached signature. |
Plain digest.
$ openssl dgst -sha256 payload.bin
SHA2-256(payload.bin)= 3a9d4e...c102
Keyed HMAC. A hash alone authenticates content against a publisher’s digest; an HMAC authenticates against a shared secret, so an adversary who can replace the file cannot also replace the expected digest.
$ openssl dgst -sha256 -hmac "$KEY" payload.bin
HMAC-SHA2-256(payload.bin)= 7c12...91a4
Closing pointer. For an MD5/SHA-1/SHA-256 file fingerprint the
operator wants every day, stick to coreutils. For everything else,
openssl dgst is the broader knife.
Bundled hash tools#
What ships in the box without apt or dnf install. Most
Linux distros come with coreutils, openssl, perl, and gnupg out
of the box; together they cover every common hash family the
operator needs.
Tool |
Algorithms |
From |
|---|---|---|
|
MD5 |
coreutils |
|
SHA-1 |
coreutils |
|
SHA-224, SHA-256, SHA-384, SHA-512 |
coreutils |
|
BLAKE2b |
coreutils |
|
CRC32 (POSIX); |
coreutils |
|
BSD checksum (legacy compatibility) |
coreutils |
|
SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256 |
perl ( |
|
MD4, MD5, SHA-1, SHA-2, SHA-3, RIPEMD-160, Whirlpool, SM3, BLAKE2; HMAC mode |
openssl |
|
MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, RIPEMD-160 |
gnupg |
|
sha256crypt, sha512crypt, yescrypt, MD5 crypt ( |
openssl |
|
yescrypt, sha512crypt, bcrypt ( |
|
Common add-ons that live in the default repos but are not always installed on a fresh image.
Tool |
Algorithms |
Package |
|---|---|---|
|
XXH32, XXH64, XXH128, XXH3 (fast non-crypto) |
|
|
BLAKE3 |
|
|
30+ algorithms in one binary (CRC32, MD5, SHA family, BLAKE2, ED2K, Tiger, Whirlpool, GOST) |
|
|
Recursive, multi-algorithm forensic hashing |
|
Two takeaways. The operator who knows only sha256sum and
openssl dgst covers 95% of integrity work on any Linux host.
When the algorithm is exotic (Tiger, Whirlpool, ED2K, GOST), reach
for openssl dgst first; if that does not have it, rhash is
the catch-all add-on.
Hashing trees#
A whole-tree hash is two operations. First, walk the tree in a
deterministic order and hash every file. Second, hash the list of
per-file hashes so the tree as a whole has one digest the operator
can compare. find ... -print0 | sort -z | xargs -0 sha256sum
gives the per-file pass; piping the result through sha256sum
collapses it to a single digest.
Command |
Effect |
|---|---|
|
Per-file digest list, deterministic order. |
The above |
One digest summarizing the whole tree. |
|
Verify the tree against a saved manifest. |
|
Sync by content hash, not by mtime+size. |
Baseline a directory. Run before the engagement, store the manifest off the target, compare on return.
$ find /opt/svc -type f -print0 | sort -z | \
xargs -0 sha256sum > /tmp/svc.sums
Compare a tree against its baseline.
$ sha256sum -c /tmp/svc.sums --quiet
/opt/svc/bin/agent: FAILED
Reduce a tree to a single digest for at-a-glance comparison.
$ find DIR -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum
The sort step matters. find does not promise an order; without
sort the same content can produce different digests on different
runs. -print0 and sort -z together keep filenames with
spaces and newlines from breaking the pipeline.
Constraints#
MD5 and SHA-1 are collision-broken. Use them only as non-adversarial checksums where an attacker has no incentive to forge.
Hashes are not MACs. A plain SHA-256 digest authenticates content against a value the operator trusts out-of-band; it does not authenticate the source. Use HMAC or a signature when an adversary can replace both the content and the posted digest.
The shell output of
sha256sumis hex; the algorithm itself emits bytes. Use-binary(openssl) or convert carefully when scripting against the raw digest.sha256sum -cis line-oriented. Filenames with newlines or backslashes need--strictand the\escape prefix coreutils writes automatically; do not edit those manifests by hand.On most Linux distros
b2sumis present but BLAKE3 is not. Installb3sumseparately if BLAKE3 is required.
Common Tasks#
Verify a downloaded archive against a posted digest.
$ echo 'EXPECTED_HEX payload.tar.gz' | sha256sum -c -
payload.tar.gz: OK
Hash a payload streamed from the network without writing to disk.
$ curl -sL https://example.org/payload | sha256sum
Fingerprint every binary in a tree against an IOC list.
$ find /usr/bin -type f -print0 | xargs -0 sha256sum | \
awk '{print $1}' | grep -F -f /tmp/ioc.sha256
Find content-duplicate files in a collection.
$ find . -type f -print0 | xargs -0 sha256sum | \
sort | uniq -d -w 64
Build a baseline of a directory and verify against it later.
$ find /etc -type f -print0 | sort -z | \
xargs -0 sha256sum > /tmp/etc.sums
$ sha256sum -c /tmp/etc.sums --quiet
Hash only the visible content of a file, ignoring metadata (useful when names or paths have changed).
$ sha256sum < file.bin
Compare two trees by content without copying anything.
$ rsync -an --checksum --itemize-changes src/ dst/ | head
HMAC a payload with a shared secret (authenticated integrity).
$ openssl dgst -sha256 -hmac "$KEY" payload.bin
Roll up a whole directory to one digest.
$ find DIR -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum
Verify a detached signature over a digest.
$ openssl dgst -sha256 -verify pub.pem -signature payload.sig payload.bin
Verified OK
References#
man 1 sha256sum,man 1 sha512sum,man 1 md5sum,man 1 b2sum(coreutils digest tools).man 1 openssl-dgst(algorithms, HMAC, signing).man 1 find,man 1 xargs,man 1 sort(tree traversal in deterministic order).Files for the underlying file abstraction.
Reading and Writing for the read/write surface the hash tools consume.
Encryption for at-rest encryption (the operator pairs hashes with).
Linux for the command quick-reference.