philiprehberger-checksum
Simple file and string checksums with HMAC support and streaming for large files
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-checksum"
Or install directly:
gem install philiprehberger-checksum
Usage
require "philiprehberger/checksum"
Philiprehberger::Checksum.sha256('hello') # => "2cf24dba5fb0a30e..."
String Checksums
Philiprehberger::Checksum.md5('hello') # => "5d41402abc4b2a76..."
Philiprehberger::Checksum.sha1('hello') # => "aaf4c61ddcc5e8a2..."
Philiprehberger::Checksum.sha256('hello') # => "2cf24dba5fb0a30e..."
Philiprehberger::Checksum.sha384('hello') # => "59e1748777448c69..."
Philiprehberger::Checksum.sha512('hello') # => "9b71d224bd62f378..."
Philiprehberger::Checksum.crc32('hello') # => "3610a686"
File Checksums
File checksums use streaming reads in 8KB chunks for constant memory usage:
Philiprehberger::Checksum.file_md5('/path/to/file')
Philiprehberger::Checksum.file_sha256('/path/to/file')
Philiprehberger::Checksum.file_sha384('/path/to/file')
Philiprehberger::Checksum.file_sha512('/path/to/file')
Compare Files
Check if two files have the same checksum:
Philiprehberger::Checksum.compare_files('/path/to/file_a', '/path/to/file_b')
# => true or false
Philiprehberger::Checksum.compare_files('/path/to/file_a', '/path/to/file_b', algo: :md5)
# => true or false
Multi-File Hashing
Hash multiple files in one call:
digests = Philiprehberger::Checksum.files(['/path/to/a.txt', '/path/to/b.txt'], algo: :sha256)
# => { "/path/to/a.txt" => "abc123...", "/path/to/b.txt" => "def456..." }
HMAC
Compute HMAC digests with a secret key:
Philiprehberger::Checksum.hmac_sha1('message', key: 'secret')
# => "0caf649feee4953d87bf903ac1176c45e028df16"
Philiprehberger::Checksum.hmac_sha256('message', key: 'secret')
# => "8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b"
Philiprehberger::Checksum.hmac_sha384('message', key: 'secret')
# => hex string
Philiprehberger::Checksum.hmac_sha512('message', key: 'secret')
# => hex string
Philiprehberger::Checksum.hmac_sha256('message', key: 'secret', format: :base64)
# => base64 string
File HMAC
Stream an HMAC over a file's contents without loading it into memory:
Philiprehberger::Checksum.file_hmac('/path/to/file', key: 'secret')
# => "8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b"
Philiprehberger::Checksum.file_hmac('/path/to/file', key: 'secret', algo: :sha512)
# => hex string
Philiprehberger::Checksum.file_hmac('/path/to/file', key: 'secret', format: :base64)
HMAC Verification
Verify an HMAC with timing-safe comparison:
hmac = Philiprehberger::Checksum.hmac_sha256('message', key: 'secret')
Philiprehberger::Checksum.verify_hmac?('message', hmac, key: 'secret')
# => true
String Verification
Verify a string against an expected checksum with timing-safe comparison:
expected = Philiprehberger::Checksum.sha256('hello')
Philiprehberger::Checksum.verify_string?('hello', expected)
# => true
Philiprehberger::Checksum.verify_string?('hello', 'wrong', algo: :md5)
# => false
Generic Dispatch
Use digest and file_digest when the algorithm is determined at runtime:
Philiprehberger::Checksum.digest("hello", algo: :sha256)
# => "2cf24dba5fb0a30e..."
Philiprehberger::Checksum.file_digest("/path/to/file", algo: :crc32)
# => "3610a686"
Both accept :md5, :sha1, :sha256, :sha384, :sha512, and :crc32.
Directory Checksum
Compute a combined checksum of all files in a directory:
Philiprehberger::Checksum.directory_checksum('/path/to/dir')
# => "a1b2c3d4..." (single SHA-256 digest of all files)
Philiprehberger::Checksum.directory_checksum('/path/to/dir', algo: :md5)
Multi-Algorithm
Compute multiple checksums in a single read pass:
result = Philiprehberger::Checksum.file_multi('/path/to/file', :md5, :sha256)
# => { md5: "...", sha256: "..." }
Verification
Verify a file against expected checksums with timing-safe comparison:
Philiprehberger::Checksum.verify?('/path/to/file', sha256: 'expected_hex')
# => true or false
Base64 Output
All methods support an optional format parameter:
Philiprehberger::Checksum.sha256('hello', format: :base64)
# => "LPJNul+wow4m6DsqxbnO0eEWHiwe+nMzagMzC4uYK0Q="
API
| Method | Description |
|---|---|
Checksum.md5(string, format: :hex) |
MD5 checksum of a string |
Checksum.sha1(string, format: :hex) |
SHA-1 checksum of a string |
Checksum.sha256(string, format: :hex) |
SHA-256 checksum of a string |
Checksum.sha512(string, format: :hex) |
SHA-512 checksum of a string |
Checksum.crc32(string, format: :hex) |
CRC32 checksum of a string |
Checksum.hmac_sha1(string, key:, format: :hex) |
HMAC-SHA1 digest of a string |
Checksum.hmac_sha256(string, key:, format: :hex) |
HMAC-SHA256 digest of a string |
Checksum.hmac_sha512(string, key:, format: :hex) |
HMAC-SHA512 digest of a string |
Checksum.file_hmac(path, key:, algo: :sha256, format: :hex) |
Streaming HMAC digest of a file |
Checksum.digest(string, algo:, format: :hex) |
Checksum of a string using any algorithm |
Checksum.file_digest(path, algo:, format: :hex) |
Streaming file checksum using any algorithm |
Checksum.file_md5(path, format: :hex) |
Streaming MD5 checksum of a file |
Checksum.file_sha1(path, format: :hex) |
Streaming SHA-1 checksum of a file |
Checksum.file_sha256(path, format: :hex) |
Streaming SHA-256 checksum of a file |
Checksum.file_sha512(path, format: :hex) |
Streaming SHA-512 checksum of a file |
Checksum.file_crc32(path, format: :hex) |
Streaming CRC32 checksum of a file |
Checksum.compare_files(path1, path2, algo: :sha256) |
Compare two files by checksum |
Checksum.files(paths, algo:, format: :hex) |
Hash multiple files, returns { path => digest } |
Checksum.file_multi(path, *algos, format: :hex) |
Multi-algorithm single-pass file checksum |
Checksum.verify?(path, format: :hex, **expected) |
Verify file against expected checksums |
Checksum.verify_string?(string, expected, algo: :sha256, format: :hex) |
Timing-safe verification of a string checksum |
Checksum.directory_checksum(path, algo:, format:) |
Combined checksum of all files in a directory |
Checksum.verify_hmac?(string, expected, key:, algo:) |
Timing-safe HMAC verification |
Development
bundle install
bundle exec rspec
bundle exec rubocop
Support
If you find this project useful: