std/digest
import std/digest to use it. Source: src/std/digest.iyi at v0.18.0.
Nothing here reaches OpenSSL, zlib or compiler-rt. The words are the RFCs' and FIPS 180-4's and FIPS 202's (1321, 3174, 6234, 1950, ISO 3309); the arithmetic is std/int's wrapping &+ and the shifts. hexdigest answers lowercase hex. MD5 and SHA-1 are the checksums they always were, not a lock.
The ten hashes are each a Digest. SHA256.hexdigest(data) and SHA256.hash_bytes(data) hash one piece of data; SHA256.new, then update as often as the data arrives, then final or hexfinal once, hash it in pieces. Data is a String, whose bytes are hashed, or an Array(UInt8). A digest that has given its final refuses update and a second final until reset. copy_from takes another digest's state without allocating, which is how std/crypto's HMAC starts each message from its keyed state, and dup is a copy that shares nothing. CRC-32, CRC-64 and Adler-32 are checksums: a checksum of one piece, and an update that goes on from a checksum already taken, as std/compress's and std/xz's streams take theirs. SHAKE128 and SHAKE256 are not a Digest, having no one size: update as the data arrives, then read(n) as often as more output is wanted, each read going on where the last one stopped. XXH64 is the hash std/zstd's frames carry the low half of: checksum(data, seed), or update in pieces and a digest that can be asked midway.
Adler32struct
The same convention as CRC32: update(sum, data, count) goes on from a finished Adler-32, and 1 is the start (the Adler-32 of nothing), as in zlib's adler32(adler, buf, len).
pub struct Adler32
def self.checksum(data : String) : UInt32
def self.hexdigest(data : String) : String
def initialize
def self.update(sum : UInt32, data : Pointer(UInt8), count : Int32) : UInt32
endCRC32struct
checksum(data) is the CRC-32 of data. update(crc, data, count) goes on from a CRC-32 already taken, zlib's crc32(crc, buf, len) convention: what goes in and what comes out are finished checksums, the inversion before and after done inside, so 0 is the start (the CRC-32 of nothing) and update(update(0, a), b) is the CRC-32 of a then b, the same number checksum answers for the two joined.
pub struct CRC32
def self.checksum(data : String) : UInt32
def self.hexdigest(data : String) : String
def initialize
# Eight bytes a step through eight tables (slicing-by-8), where it had
# been one bit a step: twenty megabytes took 105 ms, which was most of
# what `Gzip.decompress` spent, the inflating itself taking 30.
def self.update(crc : UInt32, data : Pointer(UInt8), count : Int32) : UInt32
endCRC64struct
CRC-64 with ECMA-182's polynomial, reflected (0xc96c5795d7870f42), and inverted before and after: the CRC-64 the .xz format checks a block with (xz file format 1.0.4 §3.4, check ID 0x04), which reveng's catalogue names CRC-64/XZ. The same convention as CRC32: update(crc, data, count) goes on from a finished checksum, 0 is the start (the CRC-64 of nothing), and hexdigest writes the number most significant digit first, as xz --robot --list does; the stream stores it little-endian.
pub struct CRC64
def self.checksum(data : String) : UInt64
def self.hexdigest(data : String) : String
def initialize
# Eight bytes a step through eight tables, as `CRC32` goes: the bytes
# loaded as one little-endian word, xored into the checksum, and each
# of the eight looked up in the table for how many bytes of zeros
# follow it in the step.
def self.update(crc : UInt64, data : Pointer(UInt8), count : Int32) : UInt64
endDigestclass
What every block hash shares: the message is cut into blocks, the last one padded with a one bit, zeros and the message's length in bits, and each block is compressed into the state. A subclass is the state and its compression; this is the cutting and the padding.
pub abstract class Digest
# Bytes compressed at a time: 64, or 128 for SHA-384 and SHA-512. HMAC
# pads its key to this.
def block_size : Int32
# Becomes *other*: the same bytes taken, the same state, the same
# `final` given or not. Nothing is allocated. *other* must be the same
# algorithm, and a different one is refused by name.
def copy_from(other : Digest) : Nil
# Bytes in a digest: 16 for MD5, 20 for SHA-1, 28, 32, 48 and 64 for
# the SHA-2s.
abstract def digest_size : Int32
def dup : self
# The digest, into the first `digest_size` bytes of *dst*: no array is
# made, which is what a loop that hashes a hash wants.
def final(dst : Array(UInt8)) : Nil
def final : Array(UInt8)
def self.hash_bytes(data : String | Array(UInt8)) : Array(UInt8)
def self.hexdigest(data : String | Array(UInt8)) : String
def hexfinal : String
def initialize(block_size : Int32)
# The digest of no bytes yet, ready again after a `final`.
def reset : Nil
def update(data : Pointer(UInt8), count : Int32) : self
def update(data : String) : self
def update(data : Array(UInt8)) : self
endMD5class
pub class MD5 < Digest
def digest_size : Int32
def initialize
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHA1class
pub class SHA1 < Digest
def digest_size : Int32
def initialize
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHA224class
pub class SHA224 < SHA256
def digest_size : Int32
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHA256class
SHA-256, FIPS 180-4 §6.2. SHA-224 is the same compression from another starting state, its digest the first 28 bytes.
pub class SHA256 < Digest
def digest_size : Int32
def initialize
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHA3class
SHA-3, FIPS 202 section 6.1: Keccak[c] with a capacity twice the digest, so a rate (the block_size) of 200 bytes less two digests. The sponge has no length field, so the base's cutting into blocks is kept and its padding is replaced. The digest is shorter than the rate in all four, so it is the first bytes of the state after the last block, with no further permutation.
pub abstract class SHA3 < Digest
def initialize(rate : Int32)
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHA384class
pub class SHA384 < SHA512
def digest_size : Int32
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHA3_224class
pub class SHA3_224 < SHA3
def digest_size : Int32
def initialize
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHA3_256class
pub class SHA3_256 < SHA3
def digest_size : Int32
def initialize
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHA3_384class
pub class SHA3_384 < SHA3
def digest_size : Int32
def initialize
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHA3_512class
pub class SHA3_512 < SHA3
def digest_size : Int32
def initialize
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHA512class
SHA-512, FIPS 180-4 §6.4: 64-bit words, 128-byte blocks, 80 rounds. SHA-384 is the same compression from another starting state, its digest the first 48 bytes.
pub class SHA512 < Digest
def digest_size : Int32
def initialize
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHAKE128class
pub class SHAKE128 < Shake
def initialize
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endSHAKE256class
pub class SHAKE256 < Shake
def initialize
# The type's own name, which is what a value that has said nothing about
# itself can honestly print. A type that wants better defines its own.
def to_s : String
endShakeclass
SHAKE128 and SHAKE256, FIPS 202 section 6.2: the sponge at a capacity of 256 and 512 bits, as much output as is asked for. update absorbs; the first read pads and every read squeezes on from where the last stopped, so reads of 3 and 5 bytes are the first 8 bytes of one read. Absorbing after squeezing would be a different function, not more input, and is refused until reset.
pub abstract class Shake
# Bytes absorbed at a time: 168 for SHAKE128, 136 for SHAKE256.
def block_size : Int32
# *count* bytes of the output for *data*.
def self.hash_bytes(data : String | Array(UInt8), count : Int32) : Array(UInt8)
def self.hexdigest(data : String | Array(UInt8), count : Int32) : String
def initialize(rate : Int32)
# The next `dst.size` bytes of output into *dst*, with no array made.
# While squeezing, `@fill` counts the bytes of the state given out.
def read(dst : Array(UInt8)) : Nil
def read(count : Int32) : Array(UInt8)
def reset : Nil
def update(data : Pointer(UInt8), count : Int32) : self
def update(data : String) : self
def update(data : Array(UInt8)) : self
endXXH64class
XXH64, the 64-bit xxHash (Yann Collet; the algorithm as xxhash's doc/xxhash_spec.md gives it): the hash whose low 32 bits are a Zstandard frame's content checksum (RFC 8878 §3.1.1), which is what std/zstd takes it for. Not a cryptographic hash - a fast check that bytes arrived as they left, with nothing to keep anyone from making two messages that collide.
checksum(data, seed) hashes one piece. new(seed), then update as the data arrives, then digest, hashes it in pieces; digest leaves the state as it was, as xxhash's XXH64_digest does, so it can be asked midway and the hash go on. hexdigest is xxhash's canonical form, most significant byte first. A word of the message is loaded as one little-endian load, which is what every target iyi builds for is.
pub class XXH64
def self.checksum(data : String, seed : UInt64 = 0_u64) : UInt64
# The hash of everything given since the start or `reset`; the state is
# left as it was.
def digest : UInt64
def self.hexdigest(data : String, seed : UInt64 = 0_u64) : String
def initialize(seed : UInt64 = 0_u64)
def self.merge(acc : UInt64, v : UInt64) : UInt64
# Back to the hash of nothing, under the same seed.
def reset : Nil
def self.rotl(x : UInt64, n : Int32) : UInt64
def self.round(acc : UInt64, input : UInt64) : UInt64
def update(data : Pointer(UInt8), count : Int32) : Nil
def update(data : String) : Nil
end
Digests, written in iyi: MD5, SHA-1, the SHA-2 family (SHA-224, SHA-256, SHA-384, SHA-512), the SHA-3 family (SHA3-224, SHA3-256, SHA3-384, SHA3-512) with its extendable-output functions SHAKE128 and SHAKE256, CRC-32, CRC-64 and Adler-32, and the non-cryptographic XXH64.