script: add a script to hash files
Right now, we provide signed SHA-256 hashes for our releases. This is fine and sufficient, and also cryptographically secure. However, many distributors use other algorithms, and it would be convenient if we could provide easy access to those hashes as well. For example, NetBSD uses SHA-512 and BLAKE2s. Let's add a script to hash files with various algorithms and output them in the BSD format. The advantage of the BSD format over the traditional GNU format is that it includes the hash algorithm, which allows us to distinguish between hashes of the same length, such as SHA-256, SHA-512/256, and SHA3-256. It is generated by shasum, sha*sum, sha3sum, and b2sum with the --tag format, and all of these programs accept it for verification with no problems. Using the BSD format means that we need only provide one additional file with all the additional algorithms. There is therefore no need to add multiple new files, and if we desire to add additional algorithms in the future, that's easily done without modification. For aesthetics, we sort first by hash name and then by filename in the output. Unlike sorting with `sort`, this keeps the SHA-2 and SHA-3 algorithms separate instead of interspersing them, which aids in reading. Add some comments because the algorithm, while logical, is somewhat subtle.
This commit is contained in:
parent
bb25ccf879
commit
c7241259f4
59
script/hash-files
Executable file
59
script/hash-files
Executable file
@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "openssl"
|
||||
|
||||
# This maps the OpenSSL name to the name used in the output file.
|
||||
# The order used is the order they should appear in the output file.
|
||||
DIGESTS = {
|
||||
'BLAKE2b512' => 'BLAKE2b',
|
||||
'BLAKE2s256' => 'BLAKE2s',
|
||||
'SHA256' => 'SHA256',
|
||||
'SHA384' => 'SHA384',
|
||||
'SHA512' => 'SHA512',
|
||||
'SHA512-256' => 'SHA512/256',
|
||||
'SHA3-256' => 'SHA3-256',
|
||||
'SHA3-384' => 'SHA3-384',
|
||||
'SHA3-512' => 'SHA3-512',
|
||||
}
|
||||
|
||||
class Hasher
|
||||
def initialize(file)
|
||||
@file = file
|
||||
@hashers = DIGESTS.map do |openssl, output|
|
||||
[output, OpenSSL::Digest.new(openssl)]
|
||||
end.to_h
|
||||
end
|
||||
|
||||
def update(s)
|
||||
@hashers.values.each { |h| h.update(s) }
|
||||
end
|
||||
|
||||
def to_a
|
||||
@hashers.map do |name, ctx|
|
||||
"#{name} (#{@file}) = #{ctx.digest.unpack("H*")[0]}\n"
|
||||
end.to_a
|
||||
end
|
||||
end
|
||||
|
||||
results = []
|
||||
ARGV.each do |file|
|
||||
f = File.open(file)
|
||||
h = Hasher.new(file)
|
||||
while chunk = f.read(65536) do
|
||||
h.update(chunk)
|
||||
end
|
||||
results += h.to_a
|
||||
end
|
||||
|
||||
# Sort entries first by order of algorithm name in DIGESTS, then by filename,
|
||||
# then print them.
|
||||
|
||||
# Create a mapping of output name digest to order in the hash.
|
||||
names = DIGESTS.values.each_with_index.to_a.to_h
|
||||
results.sort_by do |s|
|
||||
# Split into digest name and remainder. The remainder starts with the
|
||||
# filename.
|
||||
pair = s.split(' ', 2).to_a
|
||||
# Order by the index of the digest and then the filename.
|
||||
[names[pair[0]], pair[1]]
|
||||
end.each { |l| puts l }
|
Loading…
Reference in New Issue
Block a user