Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
141 lines (89 sloc) 4.14 KB
Create and verify GPG-signed SHA256 checksums for all files in a
directory tree.
The general purpose of creating checksums for files and comparing
them over time is to determine whether files have changed. Either
accidentally or maliciously.
Several intrusion detection systems already exist that serve this
purpose, among other things. So why this script?
* I don't want to bother with the complexities of an
enterprise-grade IDS.
* I don't want a central database of checksums.
Instead, I want one checksum file (.checksums) per directory,
in order to be able to easily copy directories around and still
be able to verify their checksums.
* Incremental creation/update of checksums.
Assuming that I/you trust data integrity on the system where
the checksums are generated.
- ruby >= 2.0
- gpgme ruby bindings version 2.0.0 (or possibly newer)
- either the gpgme gem
- or the Debian package ruby-gpgme
Create checksums for one or more directories
$ checksums create directory1 directory2 ...
Verify checksums
$ checksums verify directory1 directory2 ...
Update checksums (see considerations below)
$ checksums verify directory1 directory2 ...
When signing checksum files, checksums by default uses the first
suitable key from your GPG keyring. Such a key must have a private
key useable for signing.
When verifying checksum files, by default all such keys are considered
The --signer option allows to explicity specify a key to be used
for signing and verifying. The key can be given in any form that is
acceptable to GPG, such as key ID or fingerprint.
$ checksums create --signer 0123456789ABCDEF
To create checksums for directories and all their sub-directories,
add the --recursive option.
Directories can be excluded from consideration using the --exclude
option. This option can be given multiple times.
$ checksums create --recursive --exclude=michael /home
Creates checksums for directories in and under /home, excluding
everything below /home/michael.
$ checksums create --recursive --exclude='**/.git' /usr/local/src
Creates checksums for directories in and under /usr/local/src,
excluding .git directories.
$ checksums create --recursive --exclude='**/.*' .
Creates checksums for the current directory and below, excluding
any "hidden" directories whose name starts with ".".
When updating checksums, new checksums are only calculated for those
directories where at least one file has a modification timestamp newer
than that of the checksums file for that directory.
Therefore, if you don't trust the timestamps on your computer, don't
update checksums, but always create them afresh.
Furthermore, timestamps are only accurate to one second.
Assume a checksums file for a directory is written and in the same
second a file in that directory changes, is added or removed.
An update will not notice this change and so will not write a new
checksums file.
If files are changing so rapidly that the contents of a directory
change while the checksums for that same directory are calculated, it
is not possible to write a consistent checksums file at all.
In such a case, this tool just might not be appropriate for your needs.
Whose keys are they anyway?
When run as an ordinary user, the script uses that user's keyring.
When run in a root login session, i.e. `su -', not plain `su' or `sudo',
the script tries to use root's keyring. Possibly unsuccessfully, when
there's no gpg-agent running for root.
When run via `sudo -E', the keyring of the user running sudo is used!
When run via `sudo', depending on sudo configuration, this is either
the same as `sudo -E' or it fails when there's no gpg-agent for root.
In order to have sudo pass the GPG_AGENT_INFO environment variable
through to the checksums script, add the following lines to
/etc/sudoers or to a file in /etc/sudoers.d; use `visudo' or
`visudo -f' for editing these files.
Cmnd_Alias CHECKSUMS = /usr/local/bin/checksums # or wherever you install the script
Copyright (C) 2015 Michael Schürig <>
Something went wrong with that request. Please try again.