7. Hash Functions

The hash/ library computes digests of DUST values.

Table of Contents

7.1. Hash values ↑

#hash holds a digest together with the algorithm that produced it. It uses RON's #prefix value notation, and its payload is itself a tagged element: the tag names the algorithm and its value is the digest in lowercase hexadecimal.

#hash #sha256 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
{"#hash": {"#sha256": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}}

Import validates the payload: the algorithm tag must name one hash/digest defines, and the digest must be lowercase hexadecimal of that algorithm's length. Invalid payloads produce #err <invalid tag>; once readers use the cell decoders, an invalid payload is a parse error, invalid tag, at the tag.

DUST
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
→
RON
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
=
DUST Result
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
DUST
#hash #md5 900150983cd24fb0d6963f7d28e17f72
=
DUST Result
#err <invalid tag>
DUST
#hash #sha256 ba7816bf
=
DUST Result
#err <invalid tag>
DUST
#hash #xxh3-64 78AF5F94892F3950
=
DUST Result
#err <invalid tag>

Two #hash values are equal when both the algorithm and the digest are equal, so digests of the same input under different algorithms are unequal rather than a type mismatch. A #hash value never equals a string.

7.2. hash/digest ↑

Function

(hash/digest algorithm value) → hash

Returns a #hash of value under algorithm, a name from this table:

Algorithm Digest Kind
sha256 32 bytes SHA-2, cryptographic
sha512 64 bytes SHA-2, cryptographic
sha3-256 32 bytes SHA-3, cryptographic
sha3-512 64 bytes SHA-3, cryptographic
xxh3-64 8 bytes XXH3 64-bit, non-cryptographic
xxh3-128 16 bytes XXH3 128-bit, non-cryptographic

Every DUST value can be hashed. The digest covers the value's canonical DUST form: the bytes the DUST formatter writes for it, encoded as UTF-8, without the single terminal file newline. Internal newlines and newlines inside string contents are kept. Arguments evaluate before hashing, and a quoted form hashes its resulting data. The canonical form follows the formatter exactly:

  • A top-level list of two or more forms omits its brackets, so (hash/digest sha256 [1 2]) digests the bytes of 1 and 2 on separate lines; a one-element list keeps its brackets, and an empty list is empty.
  • Maps keep their braces and sort their keys, so maps with the same members hash identically regardless of member order.
  • Integer 1 and float 1.0 have different forms and digests.
  • #utc and #dur payloads use their canonical spelling, so every spelling of the same instant or duration hashes identically.
  • Comments are excluded: documenting a form never changes its digest.
  • A function hashes as its canonical form: a closure as its fn source, and a library function as its constant reference, such as :strings/case. Values captured by a closure are not part of its form.
  • Unicode text is not normalized.

A form whose known tag has an invalid payload, such as #link true, is rejected when it is read, so it cannot reach hash/digest.

The string abc has canonical form abc, so its digests match the standard test vectors for those three bytes, while the empty string has canonical form "":

Call Result
(hash/digest sha256 abc) #hash #sha256 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
(hash/digest sha3-256 abc) #hash #sha3-256 3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532
(hash/digest xxh3-64 abc) #hash #xxh3-64 78af5f94892f3950

The XXH3 variants are for keys, bucketing, deduplication, and change detection, never for integrity against an adversary. xxh3-128 makes accidental collisions negligible where 64 bits are too few, such as content addressing. An unknown algorithm produces #err <algorithm hash unknown>.

DUST
(hash/digest sha256 null)
(hash/digest sha256 abc)
(hash/digest sha256 "")
(hash/digest sha256 1)
(hash/digest sha256 1.0)
(hash/digest sha256 [é 猫])
(hash/digest sha256 `.name)
(hash/digest sha256 ``1)
(hash/digest sha256 #link 1)
(hash/digest sha256 #utc 1970-01-01T00:00:00Z)
(hash/digest sha256 #dur PT1S)
(hash/digest sha256 [])
(hash/digest sha256 {a 1 b 2})
(hash/digest sha256 #utc 1970-01-01T00:00:00.0000001Z)
(hash/digest sha256 #dur PT0.0000001S)
=
DUST Result
#hash #sha256 74234e98afe7498fb5daf1f36ac2d78acc339464f950703b8c019892f982b90b
#hash #sha256 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
#hash #sha256 12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126
#hash #sha256 6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b
#hash #sha256 d0ff5974b6aa52cf562bea5921840c032a860a91a3512f7fe8f768f6bbe005f6
#hash #sha256 251304c0f0e98b997852859fc1e885ec256ad7845938663a88f5983d237f385c
#hash #sha256 468f0dd394cbd3e8506524aa28faec25309cd9704c695aea1f9218e4f395b283
#hash #sha256 d135669e0056d96659a6969caa40dad4576a10121eaad744521e9a09cdca6a83
#hash #sha256 e64ae69a6b69823b59a98efc4eee9036bfd5dd2b4c171bd6b040bb649cd6d5e3
#hash #sha256 e6a04092b64d10308bda75ec4bbf7e5e9459b42fb8f2a425e319e304c445b078
#hash #sha256 9db6bc3f734529a972f45f22b2620c5151e35c2e5c5ec5714b31a55e4c6d4ff5
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
#hash #sha256 53a6fbfea11bafdfe07457e9d925dedda5ca93811d422d4af322379990194d45
#hash #sha256 69211abbe894a3a02fce9a6a6d217ec326d939097bda56b2621500f826569af8
#hash #sha256 85fcc02259a4615aee0271b48d9ecae27a581a0e50d2550fabc4ac15e5e220b0
DUST
(hash/digest sha512 abc)
(hash/digest sha3-256 abc)
(hash/digest sha3-512 abc)
(hash/digest xxh3-64 abc)
(hash/digest xxh3-128 abc)
=
DUST Result
#hash #sha512 ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
#hash #sha3-256 3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532
#hash #sha3-512 b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0
#hash #xxh3-64 78af5f94892f3950
#hash #xxh3-128 06b05ab6733a618578af5f94892f3950
DUST
(= (hash/digest sha256 {a 1 b 2}) (hash/digest sha256 {a 1 b 2}))
(comparison/not= (hash/digest sha256 1) (hash/digest sha256 1.0))
(comparison/not= (hash/digest sha256 é) (hash/digest sha256 é))
=
DUST Result
true
true
true
DUST
(comparison/not= (hash/digest sha256 abc) (hash/digest sha3-256 abc))
(comparison/not= (hash/digest xxh3-64 abc) (hash/digest xxh3-128 abc))
(= (hash/digest xxh3-64 abc) #hash #xxh3-64 78af5f94892f3950)
=
DUST Result
true
true
true
DUST
(hash/digest md5 abc)
=
DUST Result
#err <algorithm hash unknown>
DUST
(hash/digest 256 abc)
=
DUST Result
#err <mismatch type>
DUST
(hash/digest sha256 (fn [] 1))
=
DUST Result
#hash #sha256 06d0dff99f70155bfd64b168de5c36cede11e9c14ddf87358cbb89ea9f08ce69
DUST
(hash/digest sha256 [(fn [] 1)])
=
DUST Result
#hash #sha256 b6ca49a321d2250ae8533a020a3fbf07d9942b3af2e67728b342ed4b73f86d0e
DUST
(hash/digest)
=
DUST Result
#err <arity mismatch>