Skip to content

Instantly share code, notes, and snippets.

@favila
Created February 17, 2014 03:27
Show Gist options
  • Save favila/9044218 to your computer and use it in GitHub Desktop.
Save favila/9044218 to your computer and use it in GitHub Desktop.
Generate version 5 (sha1-based) uuids in clojure.
(ns breeze.util.uuid
"Utilities for generating version 5 (SHA-1 hashing) uuids.
This implementation should conform to RFC 4122, section 4.3.
Includes shortcuts for generating uuids for breeze rule hashes."
(import java.security.MessageDigest
java.util.UUID
java.nio.ByteBuffer
java.nio.charset.StandardCharsets))
(def rule-namespace-uuid
"Namespace uuid for breeze rule-entity hashes."
#uuid "be6e7e44-d49f-4eab-a12c-04d6f1608384")
;; The following namespace uuids are from RFC 4122 Appendix C.
(def dns-namespace-uuid #uuid "6ba7b810-9dad-11d1-80b4-00c04fd430c8")
(def url-namespace-uuid #uuid "6ba7b811-9dad-11d1-80b4-00c04fd430c8")
(def oid-namespace-uuid #uuid "6ba7b812-9dad-11d1-80b4-00c04fd430c8")
(def x500-namespace-uuid #uuid "6ba7b814-9dad-11d1-80b4-00c04fd430c8")
(defn messagedigest-sha1 [] (MessageDigest/getInstance "SHA-1"))
(defn putUUID [^ByteBuffer bb ^UUID uuid]
(doto bb
(.putLong (.getMostSignificantBits uuid))
(.putLong (.getLeastSignificantBits uuid))))
(defprotocol Bytes
(as-bytes [o] o))
(extend-protocol Bytes
(Class/forName "[B") ;; primitive byte array
(as-bytes [o] o)
java.nio.ByteBuffer
(as-bytes [o] (.array o))
java.util.UUID
(as-bytes [o] (-> (ByteBuffer/wrap (byte-array 16))
(putUUID o)
(.array)))
java.lang.String
(as-bytes [o] (.getBytes o StandardCharsets/UTF_8)))
(def rule-namespace-uuid-bytes (as-bytes rule-namespace-uuid))
(defn sha1-bytes [namespace-uuid name]
(let [md (messagedigest-sha1)]
(.update md (as-bytes namespace-uuid))
(.digest md (as-bytes name))))
(defn sha1->v5uuid
"Return a version 5 (name-based) uuid from sha1 hash byte-array.
The sha1 hash should already have the namespace uuid and the name bytes
hashed into it. The uuid will use 122 bits of entropy from the sha1 hash."
[^bytes sha1]
(let [bb (ByteBuffer/wrap sha1 0 16)
;; set bits 12-15 to 5 (version number for sha1 name-based uuid)
msb (-> (.getLong bb)
(bit-and-not 0xa000)
(bit-or 0x5000))
;; set highest 2 bits to 0b10 (variant bits for RFC 4122 UUID)
lsb (-> (.getLong bb)
(bit-and-not 0x4000000000000000)
(bit-or Long/MIN_VALUE))]
(UUID. msb lsb)))
(defn sha1-name-uuid
"Return a sha1 name-based uuid (version 5) from a namespace uuid and a name.
Either argument can be anything coercible to byte-arrays"
[namespace-uuid name]
(sha1->v5uuid (sha1-bytes namespace-uuid name)))
(defn rule-uuid
"Return a uuid for an *unhashed* breeze rule-entity canonical string
representation."
[canonical-rule-str]
(sha1-name-uuid rule-namespace-uuid-bytes canonical-rule-str))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment